PageRenderTime 61ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/eclipse_SDK-3.7.1/plugins/org.eclipse.osgi.source_3.7.1.R37x_v20110808-1106/org/eclipse/osgi/internal/resolver/ComputeNodeOrder.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 514 lines | 206 code | 32 blank | 276 comment | 42 complexity | 976df30ceefa898bb6824e1fbd85f05b MD5 | raw file
  1. /*******************************************************************************
  2. * Copyright (c) 2004, 2010 IBM Corporation and others.
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * http://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors:
  9. * IBM Corporation - initial API and implementation
  10. *******************************************************************************/
  11. package org.eclipse.osgi.internal.resolver;
  12. import java.util.*;
  13. /**
  14. * Borrowed from org.eclipse.core.internal.resources.ComputeProjectOrder
  15. * to be used when computing the stop order.
  16. * Implementation of a sort algorithm for computing the node order. This
  17. * algorithm handles cycles in the node reference graph in a reasonable way.
  18. *
  19. * @since 3.0
  20. */
  21. public class ComputeNodeOrder {
  22. /*
  23. * Prevent class from being instantiated.
  24. */
  25. private ComputeNodeOrder() {
  26. // not allowed
  27. }
  28. /**
  29. * A directed graph. Once the vertexes and edges of the graph have been
  30. * defined, the graph can be queried for the depth-first finish time of each
  31. * vertex.
  32. * <p>
  33. * Ref: Cormen, Leiserson, and Rivest <it>Introduction to Algorithms</it>,
  34. * McGraw-Hill, 1990. The depth-first search algorithm is in section 23.3.
  35. * </p>
  36. */
  37. private static class Digraph {
  38. /**
  39. * struct-like object for representing a vertex along with various
  40. * values computed during depth-first search (DFS).
  41. */
  42. public static class Vertex {
  43. /**
  44. * White is for marking vertexes as unvisited.
  45. */
  46. public static final String WHITE = "white"; //$NON-NLS-1$
  47. /**
  48. * Grey is for marking vertexes as discovered but visit not yet
  49. * finished.
  50. */
  51. public static final String GREY = "grey"; //$NON-NLS-1$
  52. /**
  53. * Black is for marking vertexes as visited.
  54. */
  55. public static final String BLACK = "black"; //$NON-NLS-1$
  56. /**
  57. * Color of the vertex. One of <code>WHITE</code> (unvisited),
  58. * <code>GREY</code> (visit in progress), or <code>BLACK</code>
  59. * (visit finished). <code>WHITE</code> initially.
  60. */
  61. public String color = WHITE;
  62. /**
  63. * The DFS predecessor vertex, or <code>null</code> if there is no
  64. * predecessor. <code>null</code> initially.
  65. */
  66. public Vertex predecessor = null;
  67. /**
  68. * Timestamp indicating when the vertex was finished (became BLACK)
  69. * in the DFS. Finish times are between 1 and the number of
  70. * vertexes.
  71. */
  72. public int finishTime;
  73. /**
  74. * The id of this vertex.
  75. */
  76. public Object id;
  77. /**
  78. * Ordered list of adjacent vertexes. In other words, "this" is the
  79. * "from" vertex and the elements of this list are all "to"
  80. * vertexes.
  81. *
  82. * Element type: <code>Vertex</code>
  83. */
  84. public List<Vertex> adjacent = new ArrayList<Vertex>(3);
  85. /**
  86. * Creates a new vertex with the given id.
  87. *
  88. * @param id the vertex id
  89. */
  90. public Vertex(Object id) {
  91. this.id = id;
  92. }
  93. }
  94. /**
  95. * Ordered list of all vertexes in this graph.
  96. *
  97. * Element type: <code>Vertex</code>
  98. */
  99. private List<Vertex> vertexList = new ArrayList<Vertex>(100);
  100. /**
  101. * Map from id to vertex.
  102. *
  103. * Key type: <code>Object</code>; value type: <code>Vertex</code>
  104. */
  105. private Map<Object, Vertex> vertexMap = new HashMap<Object, Vertex>(100);
  106. /**
  107. * DFS visit time. Non-negative.
  108. */
  109. private int time;
  110. /**
  111. * Indicates whether the graph has been initialized. Initially
  112. * <code>false</code>.
  113. */
  114. private boolean initialized = false;
  115. /**
  116. * Indicates whether the graph contains cycles. Initially
  117. * <code>false</code>.
  118. */
  119. private boolean cycles = false;
  120. /**
  121. * Creates a new empty directed graph object.
  122. * <p>
  123. * After this graph's vertexes and edges are defined with
  124. * <code>addVertex</code> and <code>addEdge</code>, call
  125. * <code>freeze</code> to indicate that the graph is all there, and then
  126. * call <code>idsByDFSFinishTime</code> to read off the vertexes ordered
  127. * by DFS finish time.
  128. * </p>
  129. */
  130. public Digraph() {
  131. super();
  132. }
  133. /**
  134. * Freezes this graph. No more vertexes or edges can be added to this
  135. * graph after this method is called. Has no effect if the graph is
  136. * already frozen.
  137. */
  138. public void freeze() {
  139. if (!initialized) {
  140. initialized = true;
  141. // only perform depth-first-search once
  142. DFS();
  143. }
  144. }
  145. /**
  146. * Defines a new vertex with the given id. The depth-first search is
  147. * performed in the relative order in which vertexes were added to the
  148. * graph.
  149. *
  150. * @param id the id of the vertex
  151. * @exception IllegalArgumentException if the vertex id is
  152. * already defined or if the graph is frozen
  153. */
  154. public void addVertex(Object id) throws IllegalArgumentException {
  155. if (initialized) {
  156. throw new IllegalArgumentException();
  157. }
  158. Vertex vertex = new Vertex(id);
  159. Object existing = vertexMap.put(id, vertex);
  160. // nip problems with duplicate vertexes in the bud
  161. if (existing != null) {
  162. throw new IllegalArgumentException();
  163. }
  164. vertexList.add(vertex);
  165. }
  166. /**
  167. * Adds a new directed edge between the vertexes with the given ids.
  168. * Vertexes for the given ids must be defined beforehand with
  169. * <code>addVertex</code>. The depth-first search is performed in the
  170. * relative order in which adjacent "to" vertexes were added to a given
  171. * "from" index.
  172. *
  173. * @param fromId the id of the "from" vertex
  174. * @param toId the id of the "to" vertex
  175. * @exception IllegalArgumentException if either vertex is undefined or
  176. * if the graph is frozen
  177. */
  178. public void addEdge(Object fromId, Object toId) throws IllegalArgumentException {
  179. if (initialized) {
  180. throw new IllegalArgumentException();
  181. }
  182. Vertex fromVertex = vertexMap.get(fromId);
  183. Vertex toVertex = vertexMap.get(toId);
  184. // ignore edges when one of the vertices is unknown
  185. if (fromVertex == null || toVertex == null)
  186. return;
  187. fromVertex.adjacent.add(toVertex);
  188. }
  189. /**
  190. * Returns the ids of the vertexes in this graph ordered by depth-first
  191. * search finish time. The graph must be frozen.
  192. *
  193. * @param increasing <code>true</code> if objects are to be arranged
  194. * into increasing order of depth-first search finish time, and
  195. * <code>false</code> if objects are to be arranged into decreasing
  196. * order of depth-first search finish time
  197. * @return the list of ids ordered by depth-first search finish time
  198. * (element type: <code>Object</code>)
  199. * @exception IllegalArgumentException if the graph is not frozen
  200. */
  201. public List<Object> idsByDFSFinishTime(boolean increasing) {
  202. if (!initialized) {
  203. throw new IllegalArgumentException();
  204. }
  205. int len = vertexList.size();
  206. Object[] r = new Object[len];
  207. for (Iterator<Vertex> allV = vertexList.iterator(); allV.hasNext();) {
  208. Vertex vertex = allV.next();
  209. int f = vertex.finishTime;
  210. // note that finish times start at 1, not 0
  211. if (increasing) {
  212. r[f - 1] = vertex.id;
  213. } else {
  214. r[len - f] = vertex.id;
  215. }
  216. }
  217. return Arrays.asList(r);
  218. }
  219. /**
  220. * Returns whether the graph contains cycles. The graph must be frozen.
  221. *
  222. * @return <code>true</code> if this graph contains at least one cycle,
  223. * and <code>false</code> if this graph is cycle free
  224. * @exception IllegalArgumentException if the graph is not frozen
  225. */
  226. public boolean containsCycles() {
  227. if (!initialized) {
  228. throw new IllegalArgumentException();
  229. }
  230. return cycles;
  231. }
  232. /**
  233. * Returns the non-trivial components of this graph. A non-trivial
  234. * component is a set of 2 or more vertexes that were traversed
  235. * together. The graph must be frozen.
  236. *
  237. * @return the possibly empty list of non-trivial components, where
  238. * each component is an array of ids (element type:
  239. * <code>Object[]</code>)
  240. * @exception IllegalArgumentException if the graph is not frozen
  241. */
  242. public List<Object[]> nonTrivialComponents() {
  243. if (!initialized) {
  244. throw new IllegalArgumentException();
  245. }
  246. // find the roots of each component
  247. // Map<Vertex,List<Object>> components
  248. Map<Vertex, List<Object>> components = new HashMap<Vertex, List<Object>>();
  249. for (Iterator<Vertex> it = vertexList.iterator(); it.hasNext();) {
  250. Vertex vertex = it.next();
  251. if (vertex.predecessor == null) {
  252. // this vertex is the root of a component
  253. // if component is non-trivial we will hit a child
  254. } else {
  255. // find the root ancestor of this vertex
  256. Vertex root = vertex;
  257. while (root.predecessor != null) {
  258. root = root.predecessor;
  259. }
  260. List<Object> component = components.get(root);
  261. if (component == null) {
  262. component = new ArrayList<Object>(2);
  263. component.add(root.id);
  264. components.put(root, component);
  265. }
  266. component.add(vertex.id);
  267. }
  268. }
  269. List<Object[]> result = new ArrayList<Object[]>(components.size());
  270. for (Iterator<List<Object>> it = components.values().iterator(); it.hasNext();) {
  271. List<Object> component = it.next();
  272. if (component.size() > 1) {
  273. result.add(component.toArray());
  274. }
  275. }
  276. return result;
  277. }
  278. // /**
  279. // * Performs a depth-first search of this graph and records interesting
  280. // * info with each vertex, including DFS finish time. Employs a recursive
  281. // * helper method <code>DFSVisit</code>.
  282. // * <p>
  283. // * Although this method is not used, it is the basis of the
  284. // * non-recursive <code>DFS</code> method.
  285. // * </p>
  286. // */
  287. // private void recursiveDFS() {
  288. // // initialize
  289. // // all vertex.color initially Vertex.WHITE;
  290. // // all vertex.predecessor initially null;
  291. // time = 0;
  292. // for (Iterator allV = vertexList.iterator(); allV.hasNext();) {
  293. // Vertex nextVertex = (Vertex) allV.next();
  294. // if (nextVertex.color == Vertex.WHITE) {
  295. // DFSVisit(nextVertex);
  296. // }
  297. // }
  298. // }
  299. //
  300. // /**
  301. // * Helper method. Performs a depth first search of this graph.
  302. // *
  303. // * @param vertex the vertex to visit
  304. // */
  305. // private void DFSVisit(Vertex vertex) {
  306. // // mark vertex as discovered
  307. // vertex.color = Vertex.GREY;
  308. // List adj = vertex.adjacent;
  309. // for (Iterator allAdjacent=adj.iterator(); allAdjacent.hasNext();) {
  310. // Vertex adjVertex = (Vertex) allAdjacent.next();
  311. // if (adjVertex.color == Vertex.WHITE) {
  312. // // explore edge from vertex to adjVertex
  313. // adjVertex.predecessor = vertex;
  314. // DFSVisit(adjVertex);
  315. // } else if (adjVertex.color == Vertex.GREY) {
  316. // // back edge (grey vertex means visit in progress)
  317. // cycles = true;
  318. // }
  319. // }
  320. // // done exploring vertex
  321. // vertex.color = Vertex.BLACK;
  322. // time++;
  323. // vertex.finishTime = time;
  324. // }
  325. /**
  326. * Performs a depth-first search of this graph and records interesting
  327. * info with each vertex, including DFS finish time. Does not employ
  328. * recursion.
  329. */
  330. private void DFS() {
  331. // state machine rendition of the standard recursive DFS algorithm
  332. int state;
  333. final int NEXT_VERTEX = 1;
  334. final int START_DFS_VISIT = 2;
  335. final int NEXT_ADJACENT = 3;
  336. final int AFTER_NEXTED_DFS_VISIT = 4;
  337. // use precomputed objects to avoid garbage
  338. final Integer NEXT_VERTEX_OBJECT = new Integer(NEXT_VERTEX);
  339. final Integer AFTER_NEXTED_DFS_VISIT_OBJECT = new Integer(AFTER_NEXTED_DFS_VISIT);
  340. // initialize
  341. // all vertex.color initially Vertex.WHITE;
  342. // all vertex.predecessor initially null;
  343. time = 0;
  344. // for a stack, append to the end of an array-based list
  345. List<Object> stack = new ArrayList<Object>(Math.max(1, vertexList.size()));
  346. Iterator<Vertex> allAdjacent = null;
  347. Vertex vertex = null;
  348. Iterator<Vertex> allV = vertexList.iterator();
  349. state = NEXT_VERTEX;
  350. nextStateLoop: while (true) {
  351. switch (state) {
  352. case NEXT_VERTEX :
  353. // on entry, "allV" contains vertexes yet to be visited
  354. if (!allV.hasNext()) {
  355. // all done
  356. break nextStateLoop;
  357. }
  358. Vertex nextVertex = allV.next();
  359. if (nextVertex.color == Vertex.WHITE) {
  360. stack.add(NEXT_VERTEX_OBJECT);
  361. vertex = nextVertex;
  362. state = START_DFS_VISIT;
  363. continue nextStateLoop;
  364. }
  365. state = NEXT_VERTEX;
  366. continue nextStateLoop;
  367. case START_DFS_VISIT :
  368. // on entry, "vertex" contains the vertex to be visited
  369. // top of stack is return code
  370. // mark the vertex as discovered
  371. vertex.color = Vertex.GREY;
  372. allAdjacent = vertex.adjacent.iterator();
  373. state = NEXT_ADJACENT;
  374. continue nextStateLoop;
  375. case NEXT_ADJACENT :
  376. // on entry, "allAdjacent" contains adjacent vertexes to
  377. // be visited; "vertex" contains vertex being visited
  378. if (allAdjacent.hasNext()) {
  379. Vertex adjVertex = allAdjacent.next();
  380. if (adjVertex.color == Vertex.WHITE) {
  381. // explore edge from vertex to adjVertex
  382. adjVertex.predecessor = vertex;
  383. stack.add(allAdjacent);
  384. stack.add(vertex);
  385. stack.add(AFTER_NEXTED_DFS_VISIT_OBJECT);
  386. vertex = adjVertex;
  387. state = START_DFS_VISIT;
  388. continue nextStateLoop;
  389. }
  390. if (adjVertex.color == Vertex.GREY) {
  391. // back edge (grey means visit in progress)
  392. cycles = true;
  393. }
  394. state = NEXT_ADJACENT;
  395. continue nextStateLoop;
  396. }
  397. // done exploring vertex
  398. vertex.color = Vertex.BLACK;
  399. time++;
  400. vertex.finishTime = time;
  401. state = ((Integer) stack.remove(stack.size() - 1)).intValue();
  402. continue nextStateLoop;
  403. case AFTER_NEXTED_DFS_VISIT :
  404. // on entry, stack contains "vertex" and "allAjacent"
  405. vertex = (Vertex) stack.remove(stack.size() - 1);
  406. @SuppressWarnings("unchecked")
  407. Iterator<Vertex> unchecked = (Iterator<Vertex>) stack.remove(stack.size() - 1);
  408. allAdjacent = unchecked;
  409. state = NEXT_ADJACENT;
  410. continue nextStateLoop;
  411. }
  412. }
  413. }
  414. }
  415. /**
  416. * Sorts the given list of projects in a manner that honors the given
  417. * project reference relationships. That is, if project A references project
  418. * B, then the resulting order will list B before A if possible. For graphs
  419. * that do not contain cycles, the result is the same as a conventional
  420. * topological sort. For graphs containing cycles, the order is based on
  421. * ordering the strongly connected components of the graph. This has the
  422. * effect of keeping each knot of projects together without otherwise
  423. * affecting the order of projects not involved in a cycle. For a graph G,
  424. * the algorithm performs in O(|G|) space and time.
  425. * <p>
  426. * When there is an arbitrary choice, vertexes are ordered as supplied.
  427. * Arranged projects in descending alphabetical order generally results in
  428. * an order that builds "A" before "Z" when there are no other constraints.
  429. * </p>
  430. * <p> Ref: Cormen, Leiserson, and Rivest <it>Introduction to
  431. * Algorithms</it>, McGraw-Hill, 1990. The strongly-connected-components
  432. * algorithm is in section 23.5.
  433. * </p>
  434. *
  435. * @param objects a list of projects (element type:
  436. * <code>IProject</code>)
  437. * @param references a list of project references [A,B] meaning that A
  438. * references B (element type: <code>IProject[]</code>)
  439. * @return an object describing the resulting project order
  440. */
  441. public static Object[][] computeNodeOrder(Object[] objects, Object[][] references) {
  442. // Step 1: Create the graph object.
  443. final Digraph g1 = new Digraph();
  444. // add vertexes
  445. for (int i = 0; i < objects.length; i++)
  446. g1.addVertex(objects[i]);
  447. // add edges
  448. for (int i = 0; i < references.length; i++)
  449. // create an edge from q to p
  450. // to cause q to come before p in eventual result
  451. g1.addEdge(references[i][1], references[i][0]);
  452. g1.freeze();
  453. // Step 2: Create the transposed graph. This time, define the vertexes
  454. // in decreasing order of depth-first finish time in g1
  455. // interchange "to" and "from" to reverse edges from g1
  456. final Digraph g2 = new Digraph();
  457. // add vertexes
  458. List<Object> resortedVertexes = g1.idsByDFSFinishTime(false);
  459. for (Iterator<Object> it = resortedVertexes.iterator(); it.hasNext();)
  460. g2.addVertex(it.next());
  461. // add edges
  462. for (int i = 0; i < references.length; i++)
  463. g2.addEdge(references[i][0], references[i][1]);
  464. g2.freeze();
  465. // Step 3: Return the vertexes in increasing order of depth-first finish
  466. // time in g2
  467. List<Object> sortedProjectList = g2.idsByDFSFinishTime(true);
  468. Object[] orderedNodes = new Object[sortedProjectList.size()];
  469. sortedProjectList.toArray(orderedNodes);
  470. Object[][] knots;
  471. boolean hasCycles = g2.containsCycles();
  472. if (hasCycles) {
  473. List<Object[]> knotList = g2.nonTrivialComponents();
  474. knots = knotList.toArray(new Object[knotList.size()][]);
  475. } else {
  476. knots = new Object[0][];
  477. }
  478. for (int i = 0; i < orderedNodes.length; i++)
  479. objects[i] = orderedNodes[i];
  480. return knots;
  481. }
  482. }