Computes lanes in the topological sorter
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / lib / TopologicalSorter.java
blob91d73e1a73ec92e746949a906c57e7c7ce442a92
1 /*
2 * Copyright (C) 2007 Robin Rosenberg
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.lib;
19 import java.lang.reflect.Array;
20 import java.util.AbstractList;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.TreeMap;
32 import java.util.TreeSet;
34 /**
35 * A topological sorter. Use it by adding elemens and/or edges. Then the nodes
36 * can be retrieved in sequence by either invoking the iterator() or calling
37 * getEntries(). Both are lazy methods that do not wait for the sort to complete
38 * before returning data. The sort is executed incrementally.
40 * This object is not reusable and nodes cannot be added after sorting has
41 * commenced.
43 * The methods size() and filter() can be overridden to include only a subset of
44 * the elements in the set.
46 * @author Robin Rosenberg
48 * @param <T>
49 * The node type in the graph
51 public class TopologicalSorter<T> {
53 private Comparator<T> comparator;
55 private Set<T> allNodes = new HashSet<T>();
57 private Map<T, List<Edge<T>>> allEdges = new TreeMap<T, List<Edge<T>>>();
59 private Map<T, Integer> inCount = new HashMap<T, Integer>();
61 private Collection<T> zeroIn; // init later, when we know which comparator to use
63 private List<T> entries;
65 private Map<T, Integer> internalOrder = new HashMap<T, Integer>();
67 public List<Lane> currentLanes = new ArrayList<Lane>();
69 int lastLane = 0;
71 Map<T, Lane> lane = new HashMap<T, Lane>();
73 /**
74 * Construct a topological sorter.
76 * @param comparator
77 * A comparator that orders nodes that have no defined order in
78 * the graph. For those element for which the graph conclusively
79 * defined the order the result has no relevance.
81 public TopologicalSorter(final Comparator<T> comparator) {
82 this.comparator = comparator;
85 /**
86 * Construct a topological sorter. You will need to invoke setComparator to
87 * complete construction.
89 public TopologicalSorter() {
90 // empty
93 /**
94 * Set the comparator to use. Use this method if the constructor cannot be
95 * used.
97 * @param comparator
98 * See constructor.
100 public void setComparator(final Comparator<T> comparator) {
101 this.comparator = comparator;
104 /** Defines a directed edge
106 * @param <T>
107 * Node type
109 public static class Edge<T> {
110 final T from;
112 final T to;
115 * Construct an edge from a node to another,
117 * @param from
118 * @param to
120 public Edge(T from, T to) {
121 this.from = from;
122 this.to = to;
125 @Override
126 public String toString() {
127 return from + " -> " + to;
130 @Override
131 public int hashCode() {
132 return from.hashCode() | to.hashCode();
136 * @return the origin of this edge
138 public T getTo() {
139 return to;
143 * @return the destination of this edge
145 public T getFrom() {
146 return from;
149 @SuppressWarnings("unchecked")
150 @Override
151 public boolean equals(Object obj) {
152 Edge<T> o = (Edge<T>) obj;
153 return from == o.from && to == o.to
154 || from.equals(((Edge<T>) obj).from)
155 && to.equals(((Edge<T>) obj).to);
160 * Copy a sorter. This must be invoked <em>before</em> iteration starts.
161 * Mostly for testing.
163 * @param o Another sorter whose raw state we will copy
165 public TopologicalSorter(TopologicalSorter<T> o) {
166 if (zeroIn != null)
167 throw new IllegalStateException("Cannot clone after iteration starts");
168 comparator = o.comparator;
169 allNodes.addAll(o.allNodes);
170 allEdges.putAll(o.allEdges);
171 inCount.putAll(o.inCount);
175 * Add an edge to the graph
177 * @param edge
179 public void put(Edge<T> edge) {
180 // System.out.println("put("+edge+")");
181 assert !edge.from.equals(edge.to);
182 List<Edge<T>> edges = allEdges.get(edge.from);
183 // optimize for very small arrays, one or two parents are overwhelmingly
184 // the most common ones, the rest can be counted on one or two hands in
185 // most repos. Not even the git repo has more than ~40 merges with more than
186 // two parents.
187 if (edges == null) {
188 edges = Collections.singletonList(edge);
189 allEdges.put(edge.from, edges);
190 } else {
191 if (edges.contains(edge)) {
192 System.out.println("duplicate edge "+edge+" added");
193 return; // in case the frontend feeds duplicates we ignore them
195 if (edges.size() == 1) {
196 List<Edge<T>> old = edges;
197 edges = new ArrayList<Edge<T>>(2);
198 edges.add(old.get(0));
200 edges.add(edge);
201 allEdges.put(edge.from, edges);
203 allNodes.add(edge.from);
204 allNodes.add(edge.to);
205 Integer n = inCount.get(edge.to);
206 if (n == null)
207 inCount.put(edge.to, new Integer(1));
208 else
209 inCount.put(edge.to, new Integer(n.intValue() + 1));
213 * @param from A node
214 * @return All edges from the node 'from'. may be null
216 public List<Edge<T>> getEdgeFrom(T from) {
217 return allEdges.get(from);
221 * Add a single node to the graph.
223 * @param node
225 public void put(T node) {
226 // System.out.println("put("+node+")");
227 allNodes.add(node);
230 private void removeallfrom(T from) {
231 Collection<Edge<T>> fromEdges = allEdges.get(from);
232 if (fromEdges != null) {
233 for (Iterator<Edge<T>> i = fromEdges.iterator(); i.hasNext();) {
234 Edge<T> e = i.next();
235 Lane l = lane.get(e.to);
236 if (l == null) {
237 for (Lane m : currentLanes) {
238 if (m.endsAt == from) {
239 l = m;
240 break;
243 if (l == null) {
244 l = newLane();
245 l.startsAt = e.from;
247 l.endsAt = e.to;
248 lane.put(e.to, l);
249 } else {
250 l.endsAt = e.to;
252 Integer c = inCount.get(e.to);
253 if (c.intValue() == 1) {
254 zeroIn.add(e.to);
255 inCount.remove(e.to);
256 } else {
257 inCount.put(e.to, new Integer(c.intValue() - 1));
260 // allEdges.remove(from);
261 int n = inCount.size();
262 if (n % 10000 == 0)
263 System.out.println("node left:" + n);
267 public Lane getLane(T node) {
268 return lane.get(node);
271 Lane newLane() {
272 Lane ret = new Lane();
273 currentLanes.add(ret);
274 return ret;
277 public class Lane {
278 public T endsAt;
280 public T startsAt;
282 private int number = -1;
284 public int numbe1;
286 Lane() {
289 @Override
290 public boolean equals(Object obj) {
291 return number == ((Lane) obj).number;
294 @Override
295 public int hashCode() {
296 return number;
299 @Override
300 public String toString() {
301 return "L[" + number + "]("+startsAt+" to "+endsAt+")";
304 public List<Lane> getAllLanes() {
305 return currentLanes;
308 public int getNumber() {
309 if (number == -1)
310 number = lastLane++;
311 return number;
314 public TopologicalSorter<T> getSorter() {
315 return TopologicalSorter.this;
319 T nextZero() {
320 if (zeroIn == null) {
321 zeroIn = new TreeSet<T>(comparator);
322 for (T i : allNodes) {
323 if (inCount.get(i) == null) {
324 zeroIn.add(i);
325 Lane l = newLane();
326 lane.put(i, l);
327 l.startsAt = i;
328 l.endsAt = i;
332 T ret;
333 if (zeroIn.isEmpty()) {
334 if (inCount.size() > 0)
335 throw new IllegalStateException("Topological sort failed, "
336 + inCount.size() + " nodes left");
337 return null;
339 Iterator<T> i = zeroIn.iterator();
340 ret = i.next();
341 internalOrder.put(ret, new Integer(internalOrder.size()));
342 i.remove();
343 removeallfrom(ret);
344 return ret;
347 private class TopologicalIterator implements Iterator<T> {
349 private T nextValue;
351 TopologicalIterator() {
352 nextValue = nextZero();
355 public boolean hasNext() {
356 return nextValue != null;
359 public T next() {
360 T ret = nextValue;
361 nextValue = nextZero();
362 return ret;
365 public void remove() {
366 throw new IllegalStateException("Cannot remove element from "
367 + getClass().getName());
370 public TopologicalSorter<T> getSorter() {
371 return TopologicalSorter.this;
376 * @param element
377 * is the candidate element from the input set
378 * @return true if the element should be included in the final result
380 @SuppressWarnings("unused")
381 protected boolean filter(T element) {
382 return true;
386 * Compute the number of elements in the filtered output. This number is
387 * typically known before the order of elements is known.
389 * @return number of elements in the result set, after filtering.
391 public int size() {
392 return allNodes.size();
396 * Get all sorted nodes as a list. The list is lazy, so nodes at the
397 * beginning may be retrieved before the full sort is completed.
399 * @return The sorted list of nodes
401 public List<T> getEntries() {
402 if (entries == null) {
403 entries = new AbstractList<T>() {
404 private Object[] elements = new Object[TopologicalSorter.this.size()];
406 private int fillCount;
408 private Iterator<T> filler = new TopologicalIterator();
410 public boolean add(T o) {
411 throw new UnsupportedOperationException();
414 public void add(int index, T element) {
415 throw new UnsupportedOperationException();
418 public boolean addAll(Collection<? extends T> c) {
419 throw new UnsupportedOperationException();
422 public boolean addAll(int index, Collection<? extends T> c) {
423 throw new UnsupportedOperationException();
426 public void clear() {
427 throw new UnsupportedOperationException();
430 public boolean contains(Object o) {
431 return super.contains(o);
434 public boolean containsAll(Collection<?> c) {
435 return super.containsAll(c);
438 @SuppressWarnings("unchecked")
439 public T get(int index) {
440 fill(index + 1);
441 return (T) elements[index];
444 public Iterator<T> iterator() {
445 return super.iterator();
448 public int lastIndexOf(Object o) {
449 return super.lastIndexOf(o);
452 public boolean remove(Object o) {
453 throw new UnsupportedOperationException();
456 public T remove(int index) {
457 throw new UnsupportedOperationException();
460 public boolean removeAll(Collection<?> c) {
461 throw new UnsupportedOperationException();
464 public boolean retainAll(Collection<?> c) {
465 throw new UnsupportedOperationException();
468 public T set(int index, T element) {
469 throw new UnsupportedOperationException();
472 public int size() {
473 return elements.length;
476 public Object[] toArray() {
477 return toArray(new Object[size()]);
480 @SuppressWarnings("unchecked")
481 public <AT> AT[] toArray(AT[] a) {
482 fill(size());
483 if (a.length != size())
484 a = (AT[]) Array.newInstance(a.getClass()
485 .getComponentType(), size());
486 System.arraycopy(elements, 0, a, 0, a.length);
487 return a;
490 private void fill(int tosize) {
491 while (fillCount < tosize) {
492 if (!filler.hasNext())
493 throw new IllegalStateException("Wrong size or filter");
494 T e = filler.next();
495 if (filter(e)) {
496 elements[fillCount++] = e;
502 return entries;
506 * Get the internal order, before filtering, of this node. Since the algorithm is
507 * incremental a null can be returned to indicate the sorted position is not yet
508 * known.
510 * @param node
511 * @return The position of a particular node
513 public Integer getInternalPosition(T node) {
514 return internalOrder.get(node);