1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.util;
12
13 import java.util.AbstractMap;
14 import java.util.AbstractSet;
15 import java.util.Iterator;
16 import java.util.Map;
17 import java.util.NoSuchElementException;
18 import java.util.Set;
19 import java.util.function.BinaryOperator;
20 import java.util.stream.Collector;
21 import java.util.stream.Collectors;
22
23 import org.eclipse.jgit.lib.AnyObjectId;
24 import org.eclipse.jgit.lib.ObjectId;
25 import org.eclipse.jgit.lib.Ref;
26 import org.eclipse.jgit.lib.RefComparator;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class RefMap extends AbstractMap<String, Ref> {
47
48
49
50
51
52
53 final String prefix;
54
55
56 RefList<Ref> packed;
57
58
59
60
61
62
63
64
65
66 RefList<Ref> loose;
67
68
69
70
71
72
73
74
75
76 RefList<Ref> resolved;
77
78 int size;
79
80 boolean sizeIsValid;
81
82 private Set<Entry<String, Ref>> entrySet;
83
84
85
86
87 public RefMap() {
88 prefix = "";
89 packed = RefList.emptyList();
90 loose = RefList.emptyList();
91 resolved = RefList.emptyList();
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 @SuppressWarnings("unchecked")
114 public RefMap(String prefix, RefList<? extends Ref> packed,
115 RefList<? extends Ref> loose, RefList<? extends Ref> resolved) {
116 this.prefix = prefix;
117 this.packed = (RefList<Ref>) packed;
118 this.loose = (RefList<Ref>) loose;
119 this.resolved = (RefList<Ref>) resolved;
120 }
121
122
123 @Override
124 public boolean containsKey(Object name) {
125 return get(name) != null;
126 }
127
128
129 @Override
130 public Ref get(Object key) {
131 String name = toRefName((String) key);
132 Ref ref = resolved.get(name);
133 if (ref == null)
134 ref = loose.get(name);
135 if (ref == null)
136 ref = packed.get(name);
137 return ref;
138 }
139
140
141 @Override
142 public Ref put(String keyName, Ref value) {
143 String name = toRefName(keyName);
144
145 if (!name.equals(value.getName()))
146 throw new IllegalArgumentException();
147
148 if (!resolved.isEmpty()) {
149
150
151 for (Ref ref : resolved)
152 loose = loose.put(ref);
153 resolved = RefList.emptyList();
154 }
155
156 int idx = loose.find(name);
157 if (0 <= idx) {
158 Ref prior = loose.get(name);
159 loose = loose.set(idx, value);
160 return prior;
161 }
162 Ref prior = get(keyName);
163 loose = loose.add(idx, value);
164 sizeIsValid = false;
165 return prior;
166 }
167
168
169 @Override
170 public Ref remove(Object key) {
171 String name = toRefName((String) key);
172 Ref res = null;
173 int idx;
174 if (0 <= (idx = packed.find(name))) {
175 res = packed.get(name);
176 packed = packed.remove(idx);
177 sizeIsValid = false;
178 }
179 if (0 <= (idx = loose.find(name))) {
180 res = loose.get(name);
181 loose = loose.remove(idx);
182 sizeIsValid = false;
183 }
184 if (0 <= (idx = resolved.find(name))) {
185 res = resolved.get(name);
186 resolved = resolved.remove(idx);
187 sizeIsValid = false;
188 }
189 return res;
190 }
191
192
193 @Override
194 public boolean isEmpty() {
195 return entrySet().isEmpty();
196 }
197
198
199 @Override
200 public Set<Entry<String, Ref>> entrySet() {
201 if (entrySet == null) {
202 entrySet = new AbstractSet<Entry<String, Ref>>() {
203 @Override
204 public Iterator<Entry<String, Ref>> iterator() {
205 return new SetIterator();
206 }
207
208 @Override
209 public int size() {
210 if (!sizeIsValid) {
211 size = 0;
212 Iterator<?> i = entrySet().iterator();
213 for (; i.hasNext(); i.next())
214 size++;
215 sizeIsValid = true;
216 }
217 return size;
218 }
219
220 @Override
221 public boolean isEmpty() {
222 if (sizeIsValid)
223 return 0 == size;
224 return !iterator().hasNext();
225 }
226
227 @Override
228 public void clear() {
229 packed = RefList.emptyList();
230 loose = RefList.emptyList();
231 resolved = RefList.emptyList();
232 size = 0;
233 sizeIsValid = true;
234 }
235 };
236 }
237 return entrySet;
238 }
239
240
241 @Override
242 public String toString() {
243 StringBuilder r = new StringBuilder();
244 boolean first = true;
245 r.append('[');
246 for (Ref ref : values()) {
247 if (first)
248 first = false;
249 else
250 r.append(", ");
251 r.append(ref);
252 }
253 r.append(']');
254 return r.toString();
255 }
256
257
258
259
260
261
262
263
264 public static Collector<Ref, ?, RefMap> toRefMap(
265 BinaryOperator<Ref> mergeFunction) {
266 return Collectors.collectingAndThen(RefList.toRefList(mergeFunction),
267 (refs) -> new RefMap("",
268 refs, RefList.emptyList(),
269 RefList.emptyList()));
270 }
271
272 private String toRefName(String name) {
273 if (0 < prefix.length())
274 name = prefix + name;
275 return name;
276 }
277
278 String toMapKey(Ref ref) {
279 String name = ref.getName();
280 if (0 < prefix.length())
281 name = name.substring(prefix.length());
282 return name;
283 }
284
285 private class SetIterator implements Iterator<Entry<String, Ref>> {
286 private int packedIdx;
287
288 private int looseIdx;
289
290 private int resolvedIdx;
291
292 private Entry<String, Ref> next;
293
294 SetIterator() {
295 if (0 < prefix.length()) {
296 packedIdx = -(packed.find(prefix) + 1);
297 looseIdx = -(loose.find(prefix) + 1);
298 resolvedIdx = -(resolved.find(prefix) + 1);
299 }
300 }
301
302 @Override
303 public boolean hasNext() {
304 if (next == null)
305 next = peek();
306 return next != null;
307 }
308
309 @Override
310 public Entry<String, Ref> next() {
311 if (hasNext()) {
312 Entry<String, Ref> r = next;
313 next = peek();
314 return r;
315 }
316 throw new NoSuchElementException();
317 }
318
319 public Entry<String, Ref> peek() {
320 if (packedIdx < packed.size() && looseIdx < loose.size()) {
321 Ref p = packed.get(packedIdx);
322 Ref l = loose.get(looseIdx);
323 int cmp = RefComparator.compareTo(p, l);
324 if (cmp < 0) {
325 packedIdx++;
326 return toEntry(p);
327 }
328
329 if (cmp == 0)
330 packedIdx++;
331 looseIdx++;
332 return toEntry(resolveLoose(l));
333 }
334
335 if (looseIdx < loose.size())
336 return toEntry(resolveLoose(loose.get(looseIdx++)));
337 if (packedIdx < packed.size())
338 return toEntry(packed.get(packedIdx++));
339 return null;
340 }
341
342 private Ref resolveLoose(Ref l) {
343 if (resolvedIdx < resolved.size()) {
344 Ref r = resolved.get(resolvedIdx);
345 int cmp = RefComparator.compareTo(l, r);
346 if (cmp == 0) {
347 resolvedIdx++;
348 return r;
349 } else if (cmp > 0) {
350
351
352 throw new IllegalStateException();
353 }
354 }
355 return l;
356 }
357
358 private Ent toEntry(Ref p) {
359 if (p.getName().startsWith(prefix))
360 return new Ent(p);
361 packedIdx = packed.size();
362 looseIdx = loose.size();
363 resolvedIdx = resolved.size();
364 return null;
365 }
366
367 @Override
368 public void remove() {
369 throw new UnsupportedOperationException();
370 }
371 }
372
373 private class Ent implements Entry<String, Ref> {
374 private Ref ref;
375
376 Ent(Ref ref) {
377 this.ref = ref;
378 }
379
380 @Override
381 public String getKey() {
382 return toMapKey(ref);
383 }
384
385 @Override
386 public Ref getValue() {
387 return ref;
388 }
389
390 @Override
391 public Ref setValue(Ref value) {
392 Ref prior = put(getKey(), value);
393 ref = value;
394 return prior;
395 }
396
397 @Override
398 public int hashCode() {
399 return getKey().hashCode();
400 }
401
402 @Override
403 public boolean equals(Object obj) {
404 if (obj instanceof Map.Entry) {
405 final Object key = ((Map.Entry) obj).getKey();
406 final Object val = ((Map.Entry) obj).getValue();
407 if (key instanceof String && val instanceof Ref) {
408 final Ref r = (Ref) val;
409 if (r.getName().equals(ref.getName())) {
410 final ObjectId a = r.getObjectId();
411 final ObjectId b = ref.getObjectId();
412 if (a != null && b != null
413 && AnyObjectId.isEqual(a, b)) {
414 return true;
415 }
416 }
417 }
418 }
419 return false;
420 }
421
422 @Override
423 public String toString() {
424 return ref.toString();
425 }
426 }
427 }