You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PlotCommitList.java 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>,
  3. * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.revplot;
  45. import java.text.MessageFormat;
  46. import java.util.BitSet;
  47. import java.util.Collection;
  48. import java.util.HashSet;
  49. import java.util.TreeSet;
  50. import org.eclipse.jgit.internal.JGitText;
  51. import org.eclipse.jgit.revwalk.RevCommitList;
  52. import org.eclipse.jgit.revwalk.RevWalk;
  53. /**
  54. * An ordered list of {@link PlotCommit} subclasses.
  55. * <p>
  56. * Commits are allocated into lanes as they enter the list, based upon their
  57. * connections between descendant (child) commits and ancestor (parent) commits.
  58. * <p>
  59. * The source of the list must be a {@link PlotWalk} and {@link #fillTo(int)}
  60. * must be used to populate the list.
  61. *
  62. * @param <L>
  63. * type of lane used by the application.
  64. */
  65. public class PlotCommitList<L extends PlotLane> extends
  66. RevCommitList<PlotCommit<L>> {
  67. static final int MAX_LENGTH = 25;
  68. private int positionsAllocated;
  69. private final TreeSet<Integer> freePositions = new TreeSet<Integer>();
  70. private final HashSet<PlotLane> activeLanes = new HashSet<PlotLane>(32);
  71. @Override
  72. public void clear() {
  73. super.clear();
  74. positionsAllocated = 0;
  75. freePositions.clear();
  76. activeLanes.clear();
  77. }
  78. @Override
  79. public void source(final RevWalk w) {
  80. if (!(w instanceof PlotWalk))
  81. throw new ClassCastException(MessageFormat.format(JGitText.get().classCastNotA, PlotWalk.class.getName()));
  82. super.source(w);
  83. }
  84. /**
  85. * Find the set of lanes passing through a commit's row.
  86. * <p>
  87. * Lanes passing through a commit are lanes that the commit is not directly
  88. * on, but that need to travel through this commit to connect a descendant
  89. * (child) commit to an ancestor (parent) commit. Typically these lanes will
  90. * be drawn as lines in the passed commit's box, and the passed commit won't
  91. * appear to be connected to those lines.
  92. * <p>
  93. * This method modifies the passed collection by adding the lanes in any
  94. * order.
  95. *
  96. * @param currCommit
  97. * the commit the caller needs to get the lanes from.
  98. * @param result
  99. * collection to add the passing lanes into.
  100. */
  101. public void findPassingThrough(final PlotCommit<L> currCommit,
  102. final Collection<L> result) {
  103. for (final PlotLane p : currCommit.passingLanes)
  104. result.add((L) p);
  105. }
  106. @Override
  107. protected void enter(final int index, final PlotCommit<L> currCommit) {
  108. setupChildren(currCommit);
  109. final int nChildren = currCommit.getChildCount();
  110. if (nChildren == 0) {
  111. currCommit.lane = nextFreeLane();
  112. activeLanes.add(currCommit.lane);
  113. closeLane(currCommit.lane);
  114. return;
  115. }
  116. if (nChildren == 1 && currCommit.children[0].getParentCount() < 2) {
  117. // Only one child, child has only us as their parent.
  118. // Stay in the same lane as the child.
  119. //
  120. final PlotCommit c = currCommit.children[0];
  121. if (c.lane == null) {
  122. // Hmmph. This child must be the first along this lane.
  123. //
  124. c.lane = nextFreeLane();
  125. activeLanes.add(c.lane);
  126. }
  127. for (int r = index - 1; r >= 0; r--) {
  128. final PlotCommit rObj = get(r);
  129. if (rObj == c)
  130. break;
  131. rObj.addPassingLane(c.lane);
  132. }
  133. currCommit.lane = c.lane;
  134. handleBlockedLanes(index, currCommit, nChildren);
  135. } else {
  136. // More than one child, or our child is a merge.
  137. // Use a different lane.
  138. //
  139. // Process all our children. Especially important when there is more
  140. // than one child (e.g. a commit is processed where other branches
  141. // fork out). For each child the following is done
  142. // 1. If no lane was assigned to the child a new lane is created and
  143. // assigned
  144. // 2. The lane of the child is closed. If this frees a position,
  145. // this position will be added freePositions list.
  146. // If we have multiple children which where previously not on a lane
  147. // each such child will get his own new lane but all those new lanes
  148. // will be on the same position. We have to take care that not
  149. // multiple newly created (in step 1) lanes occupy that position on
  150. // which the
  151. // parent's lane will be on. Therefore we delay closing the lane
  152. // with the parents position until all children are processed.
  153. // The lane on that position the current commit will be on
  154. PlotLane reservedLane = null;
  155. for (int i = 0; i < nChildren; i++) {
  156. final PlotCommit c = currCommit.children[i];
  157. // don't forget to position all of your children if they are
  158. // not already positioned.
  159. if (c.lane == null) {
  160. c.lane = nextFreeLane();
  161. activeLanes.add(c.lane);
  162. if (reservedLane != null)
  163. closeLane(c.lane);
  164. else
  165. reservedLane = c.lane;
  166. } else if (reservedLane == null && activeLanes.contains(c.lane))
  167. reservedLane = c.lane;
  168. else
  169. closeLane(c.lane);
  170. }
  171. // finally all children are processed. We can close the lane on that
  172. // position our current commit will be on.
  173. if (reservedLane != null)
  174. closeLane(reservedLane);
  175. currCommit.lane = nextFreeLane();
  176. activeLanes.add(currCommit.lane);
  177. handleBlockedLanes(index, currCommit, nChildren);
  178. }
  179. }
  180. /**
  181. * when connecting a plotcommit to the child make sure that you will not be
  182. * located on a lane on which a passed commit is located on. Otherwise we
  183. * would have to draw a line through a commit.
  184. *
  185. * @param index
  186. * @param commit
  187. * @param nChildren
  188. */
  189. private void handleBlockedLanes(final int index,
  190. final PlotCommit<L> commit, final int nChildren) {
  191. // take care:
  192. int remaining = nChildren;
  193. BitSet blockedPositions = new BitSet();
  194. for (int r = index - 1; r >= 0; r--) {
  195. final PlotCommit rObj = get(r);
  196. if (commit.isChild(rObj)) {
  197. if (--remaining == 0)
  198. break;
  199. }
  200. if (rObj != null) {
  201. PlotLane lane = rObj.getLane();
  202. if (lane != null)
  203. blockedPositions.set(lane.getPosition());
  204. rObj.addPassingLane(commit.lane);
  205. }
  206. }
  207. // Now let's check whether we have to reposition the lane
  208. if (blockedPositions.get(commit.lane.getPosition())) {
  209. int newPos = -1;
  210. for (Integer pos : freePositions)
  211. if (!blockedPositions.get(pos.intValue())) {
  212. newPos = pos.intValue();
  213. break;
  214. }
  215. if (newPos == -1)
  216. newPos = positionsAllocated++;
  217. freePositions.add(Integer.valueOf(commit.lane.getPosition()));
  218. commit.lane.position = newPos;
  219. activeLanes.add(commit.lane);
  220. }
  221. }
  222. private void closeLane(PlotLane lane) {
  223. recycleLane((L) lane);
  224. if (activeLanes.remove(lane)) {
  225. freePositions.add(Integer.valueOf(lane.getPosition()));
  226. }
  227. }
  228. private void setupChildren(final PlotCommit<L> currCommit) {
  229. final int nParents = currCommit.getParentCount();
  230. for (int i = 0; i < nParents; i++)
  231. ((PlotCommit) currCommit.getParent(i)).addChild(currCommit);
  232. }
  233. private PlotLane nextFreeLane() {
  234. final PlotLane p = createLane();
  235. if (freePositions.isEmpty()) {
  236. p.position = positionsAllocated++;
  237. } else {
  238. final Integer min = freePositions.first();
  239. p.position = min.intValue();
  240. freePositions.remove(min);
  241. }
  242. return p;
  243. }
  244. /**
  245. * @return a new Lane appropriate for this particular PlotList.
  246. */
  247. protected L createLane() {
  248. return (L) new PlotLane();
  249. }
  250. /**
  251. * Return colors and other reusable information to the plotter when a lane
  252. * is no longer needed.
  253. *
  254. * @param lane
  255. */
  256. protected void recycleLane(final L lane) {
  257. // Nothing.
  258. }
  259. }