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