1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.eclipse.jgit.revplot;
47
48 import java.text.MessageFormat;
49 import java.util.BitSet;
50 import java.util.Collection;
51 import java.util.HashMap;
52 import java.util.HashSet;
53 import java.util.TreeSet;
54
55 import org.eclipse.jgit.internal.JGitText;
56 import org.eclipse.jgit.revwalk.RevCommitList;
57 import org.eclipse.jgit.revwalk.RevWalk;
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class PlotCommitList<L extends PlotLane> extends
72 RevCommitList<PlotCommit<L>> {
73 static final int MAX_LENGTH = 25;
74
75 private int positionsAllocated;
76
77 private final TreeSet<Integer> freePositions = new TreeSet<>();
78
79 private final HashSet<PlotLane> activeLanes = new HashSet<>(32);
80
81
82 private final HashMap<PlotLane, Integer> laneLength = new HashMap<>(
83 32);
84
85
86 @Override
87 public void clear() {
88 super.clear();
89 positionsAllocated = 0;
90 freePositions.clear();
91 activeLanes.clear();
92 laneLength.clear();
93 }
94
95
96 @Override
97 public void source(RevWalk w) {
98 if (!(w instanceof PlotWalk))
99 throw new ClassCastException(MessageFormat.format(JGitText.get().classCastNotA, PlotWalk.class.getName()));
100 super.source(w);
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120 @SuppressWarnings("unchecked")
121 public void findPassingThrough(final PlotCommit<L> currCommit,
122 final Collection<L> result) {
123 for (PlotLane p : currCommit.passingLanes)
124 result.add((L) p);
125 }
126
127
128 @Override
129 protected void enter(int index, PlotCommit<L> currCommit) {
130 setupChildren(currCommit);
131
132 final int nChildren = currCommit.getChildCount();
133 if (nChildren == 0) {
134 currCommit.lane = nextFreeLane();
135 } else if (nChildren == 1
136 && currCommit.children[0].getParentCount() < 2) {
137
138
139
140 @SuppressWarnings("unchecked")
141 final PlotCommit<L> c = currCommit.children[0];
142 currCommit.lane = c.lane;
143 Integer len = laneLength.get(currCommit.lane);
144 len = len != null ? Integer.valueOf(len.intValue() + 1)
145 : Integer.valueOf(0);
146 laneLength.put(currCommit.lane, len);
147 } else {
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 PlotLane reservedLane = null;
163 PlotCommit childOnReservedLane = null;
164 int lengthOfReservedLane = -1;
165
166 for (int i = 0; i < nChildren; i++) {
167 @SuppressWarnings("unchecked")
168 final PlotCommit<L> c = currCommit.children[i];
169 if (c.getParent(0) == currCommit) {
170 Integer len = laneLength.get(c.lane);
171
172
173 if (len.intValue() > lengthOfReservedLane) {
174 reservedLane = c.lane;
175 childOnReservedLane = c;
176 lengthOfReservedLane = len.intValue();
177 }
178 }
179 }
180
181 if (reservedLane != null) {
182 currCommit.lane = reservedLane;
183 laneLength.put(reservedLane,
184 Integer.valueOf(lengthOfReservedLane + 1));
185 handleBlockedLanes(index, currCommit, childOnReservedLane);
186 } else {
187 currCommit.lane = nextFreeLane();
188 handleBlockedLanes(index, currCommit, null);
189 }
190
191
192
193 for (int i = 0; i < nChildren; i++) {
194 final PlotCommit c = currCommit.children[i];
195 PlotCommit firstParent = (PlotCommit) c.getParent(0);
196 if (firstParent.lane != null && firstParent.lane != c.lane)
197 closeLane(c.lane);
198 }
199 }
200
201 continueActiveLanes(currCommit);
202 if (currCommit.getParentCount() == 0)
203 closeLane(currCommit.lane);
204 }
205
206 private void continueActiveLanes(PlotCommit currCommit) {
207 for (PlotLane lane : activeLanes)
208 if (lane != currCommit.lane)
209 currCommit.addPassingLane(lane);
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224 private void handleBlockedLanes(final int index, final PlotCommit currCommit,
225 final PlotCommit childOnLane) {
226 for (PlotCommit child : currCommit.children) {
227 if (child == childOnLane)
228 continue;
229
230
231
232 boolean childIsMerge = child.getParent(0) != currCommit;
233 if (childIsMerge) {
234 PlotLane laneToUse = currCommit.lane;
235 laneToUse = handleMerge(index, currCommit, childOnLane, child,
236 laneToUse);
237 child.addMergingLane(laneToUse);
238 } else {
239
240
241
242
243 PlotLane laneToUse = child.lane;
244 currCommit.addForkingOffLane(laneToUse);
245 }
246 }
247 }
248
249
250 private PlotLane handleMerge(final int index, final PlotCommit currCommit,
251 final PlotCommit./../org/eclipse/jgit/revplot/PlotCommit.html#PlotCommit">PlotCommit childOnLane, PlotCommit child, PlotLane laneToUse) {
252
253
254
255 int childIndex = index;
256
257 BitSet blockedPositions = new BitSet();
258 for (int r = index - 1; r >= 0; r--) {
259 final PlotCommit rObj = get(r);
260 if (rObj == child) {
261 childIndex = r;
262 break;
263 }
264 addBlockedPosition(blockedPositions, rObj);
265 }
266
267
268
269 if (blockedPositions.get(laneToUse.getPosition())) {
270
271
272
273
274
275 boolean needDetour = false;
276 if (childOnLane != null) {
277 for (int r = index - 1; r > childIndex; r--) {
278 final PlotCommit rObj = get(r);
279 if (rObj == childOnLane) {
280 needDetour = true;
281 break;
282 }
283 }
284 }
285
286 if (needDetour) {
287
288
289
290
291
292 laneToUse = nextFreeLane(blockedPositions);
293 currCommit.addForkingOffLane(laneToUse);
294 closeLane(laneToUse);
295 } else {
296
297
298
299
300
301
302
303 int newPos = getFreePosition(blockedPositions);
304 freePositions.add(Integer.valueOf(laneToUse
305 .getPosition()));
306 laneToUse.position = newPos;
307 }
308 }
309
310
311 drawLaneToChild(index, child, laneToUse);
312 return laneToUse;
313 }
314
315
316
317
318
319
320
321
322
323 private void drawLaneToChild(final int commitIndex, PlotCommit child,
324 PlotLane laneToContinue) {
325 for (int r = commitIndex - 1; r >= 0; r--) {
326 final PlotCommit rObj = get(r);
327 if (rObj == child)
328 break;
329 if (rObj != null)
330 rObj.addPassingLane(laneToContinue);
331 }
332 }
333
334 private static void addBlockedPosition(BitSet blockedPositions,
335 final PlotCommit rObj) {
336 if (rObj != null) {
337 PlotLane lane = rObj.getLane();
338
339 if (lane != null)
340 blockedPositions.set(lane.getPosition());
341
342
343
344 for (PlotLane l : rObj.forkingOffLanes)
345 blockedPositions.set(l.getPosition());
346 for (PlotLane l : rObj.mergingLanes)
347 blockedPositions.set(l.getPosition());
348 }
349 }
350
351 @SuppressWarnings("unchecked")
352 private void closeLane(PlotLane lane) {
353 if (activeLanes.remove(lane)) {
354 recycleLane((L) lane);
355 laneLength.remove(lane);
356 freePositions.add(Integer.valueOf(lane.getPosition()));
357 }
358 }
359
360 private void setupChildren(PlotCommit<L> currCommit) {
361 final int nParents = currCommit.getParentCount();
362 for (int i = 0; i < nParents; i++)
363 ((PlotCommit) currCommit.getParent(i)).addChild(currCommit);
364 }
365
366 private PlotLane nextFreeLane() {
367 return nextFreeLane(null);
368 }
369
370 private PlotLane nextFreeLane(BitSet blockedPositions) {
371 final PlotLane p = createLane();
372 p.position = getFreePosition(blockedPositions);
373 activeLanes.add(p);
374 laneLength.put(p, Integer.valueOf(1));
375 return p;
376 }
377
378
379
380
381
382
383 private int getFreePosition(BitSet blockedPositions) {
384 if (freePositions.isEmpty())
385 return positionsAllocated++;
386
387 if (blockedPositions != null) {
388 for (Integer pos : freePositions)
389 if (!blockedPositions.get(pos.intValue())) {
390 freePositions.remove(pos);
391 return pos.intValue();
392 }
393 return positionsAllocated++;
394 } else {
395 final Integer min = freePositions.first();
396 freePositions.remove(min);
397 return min.intValue();
398 }
399 }
400
401
402
403
404
405
406
407
408 @SuppressWarnings("unchecked")
409 protected L createLane() {
410 return (L) new PlotLane();
411 }
412
413
414
415
416
417
418
419
420 protected void recycleLane(L lane) {
421
422 }
423 }