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 @Override
86 public void clear() {
87 super.clear();
88 positionsAllocated = 0;
89 freePositions.clear();
90 activeLanes.clear();
91 laneLength.clear();
92 }
93
94 @Override
95 public void source(final RevWalk w) {
96 if (!(w instanceof PlotWalk))
97 throw new ClassCastException(MessageFormat.format(JGitText.get().classCastNotA, PlotWalk.class.getName()));
98 super.source(w);
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 @SuppressWarnings("unchecked")
119 public void findPassingThrough(final PlotCommit<L> currCommit,
120 final Collection<L> result) {
121 for (final PlotLane p : currCommit.passingLanes)
122 result.add((L) p);
123 }
124
125 @Override
126 protected void enter(final int index, final PlotCommit<L> currCommit) {
127 setupChildren(currCommit);
128
129 final int nChildren = currCommit.getChildCount();
130 if (nChildren == 0) {
131 currCommit.lane = nextFreeLane();
132 } else if (nChildren == 1
133 && currCommit.children[0].getParentCount() < 2) {
134
135
136
137 @SuppressWarnings("unchecked")
138 final PlotCommit<L> c = currCommit.children[0];
139 currCommit.lane = c.lane;
140 Integer len = laneLength.get(currCommit.lane);
141 len = Integer.valueOf(len.intValue() + 1);
142 laneLength.put(currCommit.lane, len);
143 } else {
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 PlotLane reservedLane = null;
159 PlotCommit childOnReservedLane = null;
160 int lengthOfReservedLane = -1;
161
162 for (int i = 0; i < nChildren; i++) {
163 @SuppressWarnings("unchecked")
164 final PlotCommit<L> c = currCommit.children[i];
165 if (c.getParent(0) == currCommit) {
166 Integer len = laneLength.get(c.lane);
167
168
169 if (len.intValue() > lengthOfReservedLane) {
170 reservedLane = c.lane;
171 childOnReservedLane = c;
172 lengthOfReservedLane = len.intValue();
173 }
174 }
175 }
176
177 if (reservedLane != null) {
178 currCommit.lane = reservedLane;
179 laneLength.put(reservedLane,
180 Integer.valueOf(lengthOfReservedLane + 1));
181 handleBlockedLanes(index, currCommit, childOnReservedLane);
182 } else {
183 currCommit.lane = nextFreeLane();
184 handleBlockedLanes(index, currCommit, null);
185 }
186
187
188
189 for (int i = 0; i < nChildren; i++) {
190 final PlotCommit c = currCommit.children[i];
191 PlotCommit firstParent = (PlotCommit) c.getParent(0);
192 if (firstParent.lane != null && firstParent.lane != c.lane)
193 closeLane(c.lane);
194 }
195 }
196
197 continueActiveLanes(currCommit);
198 if (currCommit.getParentCount() == 0)
199 closeLane(currCommit.lane);
200 }
201
202 private void continueActiveLanes(final PlotCommit currCommit) {
203 for (PlotLane lane : activeLanes)
204 if (lane != currCommit.lane)
205 currCommit.addPassingLane(lane);
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220 private void handleBlockedLanes(final int index, final PlotCommit currCommit,
221 final PlotCommit childOnLane) {
222 for (PlotCommit child : currCommit.children) {
223 if (child == childOnLane)
224 continue;
225
226
227
228 boolean childIsMerge = child.getParent(0) != currCommit;
229 if (childIsMerge) {
230 PlotLane laneToUse = currCommit.lane;
231 laneToUse = handleMerge(index, currCommit, childOnLane, child,
232 laneToUse);
233 child.addMergingLane(laneToUse);
234 } else {
235
236
237
238
239 PlotLane laneToUse = child.lane;
240 currCommit.addForkingOffLane(laneToUse);
241 }
242 }
243 }
244
245
246 private PlotLane handleMerge(final int index, final PlotCommit currCommit,
247 final PlotCommit childOnLane, PlotCommit child, PlotLane laneToUse) {
248
249
250
251 int childIndex = index;
252
253 BitSet blockedPositions = new BitSet();
254 for (int r = index - 1; r >= 0; r--) {
255 final PlotCommit rObj = get(r);
256 if (rObj == child) {
257 childIndex = r;
258 break;
259 }
260 addBlockedPosition(blockedPositions, rObj);
261 }
262
263
264
265 if (blockedPositions.get(laneToUse.getPosition())) {
266
267
268
269
270
271 boolean needDetour = false;
272 if (childOnLane != null) {
273 for (int r = index - 1; r > childIndex; r--) {
274 final PlotCommit rObj = get(r);
275 if (rObj == childOnLane) {
276 needDetour = true;
277 break;
278 }
279 }
280 }
281
282 if (needDetour) {
283
284
285
286
287
288 laneToUse = nextFreeLane(blockedPositions);
289 currCommit.addForkingOffLane(laneToUse);
290 closeLane(laneToUse);
291 } else {
292
293
294
295
296
297
298
299 int newPos = getFreePosition(blockedPositions);
300 freePositions.add(Integer.valueOf(laneToUse
301 .getPosition()));
302 laneToUse.position = newPos;
303 }
304 }
305
306
307 drawLaneToChild(index, child, laneToUse);
308 return laneToUse;
309 }
310
311
312
313
314
315
316
317
318
319 private void drawLaneToChild(final int commitIndex, PlotCommit child,
320 PlotLane laneToContinue) {
321 for (int r = commitIndex - 1; r >= 0; r--) {
322 final PlotCommit rObj = get(r);
323 if (rObj == child)
324 break;
325 if (rObj != null)
326 rObj.addPassingLane(laneToContinue);
327 }
328 }
329
330 private static void addBlockedPosition(BitSet blockedPositions,
331 final PlotCommit rObj) {
332 if (rObj != null) {
333 PlotLane lane = rObj.getLane();
334
335 if (lane != null)
336 blockedPositions.set(lane.getPosition());
337
338
339
340 for (PlotLane l : rObj.forkingOffLanes)
341 blockedPositions.set(l.getPosition());
342 for (PlotLane l : rObj.mergingLanes)
343 blockedPositions.set(l.getPosition());
344 }
345 }
346
347 @SuppressWarnings("unchecked")
348 private void closeLane(PlotLane lane) {
349 if (activeLanes.remove(lane)) {
350 recycleLane((L) lane);
351 laneLength.remove(lane);
352 freePositions.add(Integer.valueOf(lane.getPosition()));
353 }
354 }
355
356 private void setupChildren(final PlotCommit<L> currCommit) {
357 final int nParents = currCommit.getParentCount();
358 for (int i = 0; i < nParents; i++)
359 ((PlotCommit) currCommit.getParent(i)).addChild(currCommit);
360 }
361
362 private PlotLane nextFreeLane() {
363 return nextFreeLane(null);
364 }
365
366 private PlotLane nextFreeLane(BitSet blockedPositions) {
367 final PlotLane p = createLane();
368 p.position = getFreePosition(blockedPositions);
369 activeLanes.add(p);
370 laneLength.put(p, Integer.valueOf(1));
371 return p;
372 }
373
374
375
376
377
378
379 private int getFreePosition(BitSet blockedPositions) {
380 if (freePositions.isEmpty())
381 return positionsAllocated++;
382
383 if (blockedPositions != null) {
384 for (Integer pos : freePositions)
385 if (!blockedPositions.get(pos.intValue())) {
386 freePositions.remove(pos);
387 return pos.intValue();
388 }
389 return positionsAllocated++;
390 } else {
391 final Integer min = freePositions.first();
392 freePositions.remove(min);
393 return min.intValue();
394 }
395 }
396
397
398
399
400 @SuppressWarnings("unchecked")
401 protected L createLane() {
402 return (L) new PlotLane();
403 }
404
405
406
407
408
409
410
411 protected void recycleLane(final L lane) {
412
413 }
414 }