PageRenderTime 52ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/openide.util/src/org/openide/util/TopologicalSortException.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 402 lines | 193 code | 68 blank | 141 comment | 36 complexity | 4beb947c13d55c1580de53277e3a38b3 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.openide.util;
  45. import java.io.PrintWriter;
  46. import java.io.StringWriter;
  47. import java.util.*;
  48. /** Exception that signals that a topological sort failed due to
  49. * unsortable nature of the graph and that provides support for
  50. * reporting and recovering from that state.
  51. *
  52. * @author Jaroslav Tulach
  53. * @since 3.30
  54. * @see Utilities#topologicalSort
  55. */
  56. public final class TopologicalSortException extends Exception {
  57. /** all vertexes */
  58. private Collection vertexes;
  59. /** map with edges */
  60. private Map<?,? extends Collection<?>> edges;
  61. /** result if called twice */
  62. private Set[] result;
  63. /** counter to number the vertexes */
  64. private int counter;
  65. /** vertexes sorted by increasing value of y */
  66. private Stack<Vertex> dualGraph = new Stack<Vertex>();
  67. TopologicalSortException(Collection vertexes, Map<?,? extends Collection<?>> edges) {
  68. this.vertexes = vertexes;
  69. this.edges = edges;
  70. }
  71. /** Because the full sort was not possible, this methods
  72. * returns the best possible substitute for it that is available.
  73. *
  74. * @return list of partially sorted objects, the list can be freely modified
  75. */
  76. public final List partialSort() {
  77. Set[] all = topologicalSets();
  78. ArrayList<Object> res = new ArrayList<Object>(vertexes.size());
  79. for (int i = 0; i < all.length; i++) {
  80. for (Object e : all[i]) {
  81. res.add(e);
  82. }
  83. }
  84. return res;
  85. }
  86. /** The topological sort could not be finished as there
  87. * are some objects that are mutually refering to each other.
  88. * This methods finds such objects and partition them into
  89. * separate sets. All objects in one set (transitively) refer to
  90. * each other and thus prevent the sort from succeding. As
  91. * there can be more of such "unsortable sets" an array
  92. * of them is returned.
  93. *
  94. * @return array of sets that contain some of the original objects, result
  95. * shall not be modified
  96. */
  97. public final Set[] unsortableSets() {
  98. Set[] all = topologicalSets();
  99. ArrayList<Set> unsort = new ArrayList<Set>();
  100. for (int i = 0; i < all.length; i++) {
  101. if ((all[i].size() > 1) || !(all[i] instanceof HashSet)) {
  102. unsort.add(all[i]);
  103. }
  104. }
  105. return unsort.toArray(new Set[0]);
  106. }
  107. @Override
  108. public String getMessage() {
  109. StringWriter w = new StringWriter();
  110. PrintWriter pw = new PrintWriter(w);
  111. printDebug(pw);
  112. pw.close();
  113. return w.toString();
  114. }
  115. @Override
  116. public String toString() {
  117. String s = getClass().getName();
  118. return s;
  119. }
  120. private void printDebug(java.io.PrintWriter w) {
  121. Set<Object> relevantVertices = new HashSet<Object>();
  122. Set<?>[] bad = unsortableSets();
  123. for (Set<?> s : bad) {
  124. relevantVertices.addAll(s);
  125. }
  126. Map<Object,Collection<?>> relevantEdges = new HashMap<Object,Collection<?>>();
  127. for (Map.Entry<?,? extends Collection<?>> entry : edges.entrySet()) {
  128. Set<Object> relevant = new HashSet<Object>(entry.getValue());
  129. relevant.add(entry.getKey());
  130. relevant.retainAll(relevantVertices);
  131. if (!relevant.isEmpty()) {
  132. relevantEdges.put(entry.getKey(), entry.getValue());
  133. }
  134. }
  135. w.print("TopologicalSortException - Collection with relevant edges "); // NOI18N
  136. w.print(relevantEdges);
  137. w.println(" cannot be sorted"); // NOI18N
  138. for (int i = 0; i < bad.length; i++) {
  139. w.print(" Conflict #"); // NOI18N
  140. w.print(i);
  141. w.print(": "); // NOI18N
  142. w.println(bad[i]);
  143. }
  144. }
  145. /** Adds description why the graph cannot be sorted.
  146. * @param w writer to write to
  147. */
  148. public final void printStackTrace(java.io.PrintWriter w) {
  149. printDebug(w);
  150. super.printStackTrace(w);
  151. }
  152. /** Adds description why the graph cannot be sorted.
  153. * @param s stream to write to
  154. */
  155. public final void printStackTrace(java.io.PrintStream s) {
  156. java.io.PrintWriter w = new java.io.PrintWriter(s);
  157. this.printStackTrace(w);
  158. w.flush();
  159. }
  160. /** As the full topological sort cannot be finished due to cycles
  161. * in the graph this methods performs a partition topological sort.
  162. * <P>
  163. * First of all it identifies unsortable parts of the graph and
  164. * partitions the graph into sets of original objects. Each set contains
  165. * objects that are mutually unsortable (there is a cycle between them).
  166. * Then the topological sort is performed again on those sets, this
  167. * sort succeeds because the graph of sets is DAG (all problematic edges
  168. * were only between objects now grouped inside the sets) and the
  169. * result forms the return value of this method.
  170. *
  171. * @return array of sorted sets that contain the original objects, each
  172. * object from the original collection is exactly in one set, result
  173. * shall not be modified
  174. */
  175. public final Set[] topologicalSets() {
  176. if (result != null) {
  177. return result;
  178. }
  179. HashMap<Object,Vertex> vertexInfo = new HashMap<Object,Vertex>();
  180. // computes value X and Y for each vertex
  181. counter = 0;
  182. Iterator it = vertexes.iterator();
  183. while (it.hasNext()) {
  184. constructDualGraph(counter, it.next(), vertexInfo);
  185. }
  186. // now connect vertexes that cannot be sorted into own
  187. // sets
  188. // map from the original objects to
  189. Map<Object,Set> objectsToSets = new HashMap<Object,Set>();
  190. ArrayList<Set> sets = new ArrayList<Set>();
  191. while (!dualGraph.isEmpty()) {
  192. Vertex v = dualGraph.pop();
  193. if (!v.visited) {
  194. Set<Object> set = new HashSet<Object>();
  195. visitDualGraph(v, set);
  196. if ((set.size() == 1) && v.edgesFrom.contains(v)) {
  197. // mark if there is a self reference and the
  198. // set is only one element big, it means that there
  199. // is a self cycle
  200. //
  201. // do not use HashSet but Collections.singleton
  202. // to recognize such cycles
  203. set = Collections.singleton(v.object);
  204. }
  205. sets.add(set);
  206. // fill the objectsToSets mapping
  207. it = set.iterator();
  208. while (it.hasNext()) {
  209. objectsToSets.put(it.next(), set);
  210. }
  211. }
  212. }
  213. // now topologically sort the sets
  214. // 1. prepare the map
  215. HashMap<Set,Collection<Set>> edgesBetweenSets = new HashMap<Set,Collection<Set>>();
  216. it = edges.entrySet().iterator();
  217. while (it.hasNext()) {
  218. Map.Entry entry = (Map.Entry) it.next();
  219. Collection leadsTo = (Collection) entry.getValue();
  220. if ((leadsTo == null) || leadsTo.isEmpty()) {
  221. continue;
  222. }
  223. Set from = objectsToSets.get(entry.getKey());
  224. Collection<Set> setsTo = edgesBetweenSets.get(from);
  225. if (setsTo == null) {
  226. setsTo = new ArrayList<Set>();
  227. edgesBetweenSets.put(from, setsTo);
  228. }
  229. Iterator convert = leadsTo.iterator();
  230. while (convert.hasNext()) {
  231. Set to = objectsToSets.get(convert.next());
  232. if (from != to) {
  233. // avoid self cycles
  234. setsTo.add(to);
  235. }
  236. }
  237. }
  238. // 2. do the sort
  239. try {
  240. List<Set> listResult = Utilities.topologicalSort(sets, edgesBetweenSets);
  241. result = listResult.toArray(new Set[0]);
  242. } catch (TopologicalSortException ex) {
  243. throw new IllegalStateException("Cannot happen"); // NOI18N
  244. }
  245. return result;
  246. }
  247. /** Traverses the tree
  248. * @param counter current value
  249. * @param vertex current vertex
  250. * @param vertexInfo the info
  251. */
  252. private Vertex constructDualGraph(int counter, Object vertex, HashMap<Object,Vertex> vertexInfo) {
  253. Vertex info = vertexInfo.get(vertex);
  254. if (info == null) {
  255. info = new Vertex(vertex, counter++);
  256. vertexInfo.put(vertex, info);
  257. } else {
  258. // already (being) processed
  259. return info;
  260. }
  261. // process children
  262. Collection c = (Collection) edges.get(vertex);
  263. if (c != null) {
  264. Iterator it = c.iterator();
  265. while (it.hasNext()) {
  266. Vertex next = constructDualGraph(counter, it.next(), vertexInfo);
  267. next.edgesFrom.add(info);
  268. }
  269. }
  270. // leaving the vertex
  271. info.y = counter++;
  272. dualGraph.push(info);
  273. return info;
  274. }
  275. /** Visit dual graph. Decreasing value of Y gives the order.
  276. * Number
  277. *
  278. * @param vertex vertex to start from
  279. * @param visited list of all objects that we've been to
  280. */
  281. private void visitDualGraph(Vertex vertex, Collection<Object> visited) {
  282. if (vertex.visited) {
  283. return;
  284. }
  285. visited.add(vertex.object);
  286. vertex.visited = true;
  287. Iterator it = vertex.edges();
  288. while (it.hasNext()) {
  289. Vertex v = (Vertex) it.next();
  290. visitDualGraph(v, visited);
  291. }
  292. }
  293. /** Represents info about a vertex in the graph. Vertexes are
  294. * comparable by the value of Y, but only after the value is set,
  295. * so the sort has to be done latelly.
  296. */
  297. private static final class Vertex implements Comparable<Vertex> {
  298. /** the found object */
  299. public Object object;
  300. /** list of vertexes that point to this one */
  301. public List<Vertex> edgesFrom = new ArrayList<Vertex>();
  302. /** the counter state when we entered the vertex */
  303. public final int x;
  304. /** the counter when we exited the vertex */
  305. public int y;
  306. /** already sorted, true if the edges has been sorted */
  307. public boolean sorted;
  308. /** true if visited in dual graph */
  309. public boolean visited;
  310. public Vertex(Object obj, int x) {
  311. this.x = x;
  312. this.object = obj;
  313. }
  314. /** Iterator over edges
  315. * @return iterator of Vertex items
  316. */
  317. public Iterator edges() {
  318. if (!sorted) {
  319. Collections.sort(edgesFrom);
  320. sorted = true;
  321. }
  322. return edgesFrom.iterator();
  323. }
  324. /** Comparing based on value of Y.
  325. *
  326. * @param o the Object to be compared.
  327. * @return a negative integer, zero, or a positive integer as this object
  328. * is less than, equal to, or greater than the specified object.
  329. */
  330. public int compareTo(Vertex o) {
  331. return o.y - y;
  332. }
  333. }
  334. }