PageRenderTime 61ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/nbbuild/antsrc/org/netbeans/nbbuild/SortSuiteModules.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 656 lines | 399 code | 95 blank | 162 comment | 89 complexity | 9f512672853edb8368ebcd5f9246ffd1 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.netbeans.nbbuild;
  45. import java.io.File;
  46. import java.io.IOException;
  47. import java.io.PrintWriter;
  48. import java.io.StringWriter;
  49. import java.util.ArrayList;
  50. import java.util.Collection;
  51. import java.util.Collections;
  52. import java.util.HashMap;
  53. import java.util.HashSet;
  54. import java.util.Iterator;
  55. import java.util.LinkedList;
  56. import java.util.List;
  57. import java.util.Map;
  58. import java.util.Set;
  59. import java.util.Stack;
  60. import java.util.TreeMap;
  61. import org.apache.tools.ant.BuildException;
  62. import org.apache.tools.ant.Project;
  63. import org.apache.tools.ant.Task;
  64. import org.apache.tools.ant.types.Path;
  65. import org.w3c.dom.Document;
  66. import org.w3c.dom.Element;
  67. import org.xml.sax.InputSource;
  68. import org.xml.sax.SAXException;
  69. /**
  70. * Task to sort the list of modules in a suite by their declared build dependencies.
  71. * @author Jesse Glick
  72. */
  73. public class SortSuiteModules extends Task {
  74. private boolean sortTests;
  75. private Path unsortedModules;
  76. /**
  77. * Set a list of modules in the suite.
  78. * Each entry should be a project base directory.
  79. */
  80. public void setUnsortedModules(Path unsortedModules) {
  81. this.unsortedModules = unsortedModules;
  82. }
  83. private String sortedModulesProperty;
  84. /**
  85. * Set a property name in which to store a sorted path of module base directories.
  86. */
  87. public void setSortedModulesProperty(String sortedModulesProperty) {
  88. this.sortedModulesProperty = sortedModulesProperty;
  89. }
  90. /** Is enabled sorting test dependencies?
  91. */
  92. public boolean isSortTests() {
  93. return sortTests;
  94. }
  95. /** Enable or disable sorting test dependenciens. Default value is false.
  96. */
  97. public void setSortTests(boolean sortTests) {
  98. this.sortTests = sortTests;
  99. }
  100. public SortSuiteModules() {}
  101. public @Override void execute() throws BuildException {
  102. if (unsortedModules == null) {
  103. throw new BuildException("Must set unsortedModules");
  104. }
  105. if (sortedModulesProperty == null) {
  106. throw new BuildException("Must set sortedModulesProperty");
  107. }
  108. Map<String,File> basedirsByCNB = new TreeMap<String,File>();
  109. Map<String,List<String>> buildDeps = new HashMap<String,List<String>>();
  110. for (String piece : unsortedModules.list()) {
  111. File d = new File(piece);
  112. File projectXml = new File(d, "nbproject" + File.separatorChar + "project.xml");
  113. if (!projectXml.isFile()) {
  114. throw new BuildException("Cannot open " + projectXml, getLocation());
  115. }
  116. Document doc;
  117. try {
  118. doc = XMLUtil.parse(new InputSource(projectXml.toURI().toString()), false, true, null, null);
  119. } catch (IOException e) {
  120. throw new BuildException("Error parsing " + projectXml + ": " + e, e, getLocation());
  121. } catch (SAXException e) {
  122. throw new BuildException("Error parsing " + projectXml + ": " + e, e, getLocation());
  123. }
  124. Element config = XMLUtil.findElement(doc.getDocumentElement(), "configuration", ParseProjectXml.PROJECT_NS);
  125. if (config == null) {
  126. throw new BuildException("Malformed project file " + projectXml, getLocation());
  127. }
  128. Element data = ParseProjectXml.findNBMElement(config, "data");
  129. if (data == null) {
  130. log("Skipping " + projectXml + " as it does not look like a module project", Project.MSG_WARN);
  131. continue;
  132. }
  133. Element cnbEl = ParseProjectXml.findNBMElement(data, "code-name-base");
  134. if (cnbEl == null) {
  135. throw new BuildException("Malformed project file " + projectXml, getLocation());
  136. }
  137. String cnb = XMLUtil.findText(cnbEl);
  138. basedirsByCNB.put(cnb, d);
  139. List<String> deps = new LinkedList<String>();
  140. Element depsEl = ParseProjectXml.findNBMElement(data, "module-dependencies");
  141. if (depsEl == null) {
  142. throw new BuildException("Malformed project file " + projectXml, getLocation());
  143. }
  144. for (Element dep : XMLUtil.findSubElements(depsEl)) {
  145. if (ParseProjectXml.findNBMElement(dep, "build-prerequisite") == null &&
  146. // Just build-prerequisite would not prevent "...will first try to build..." from ParseProjectXml,
  147. // since that builds transitive runtime dependencies (e.g. from *.kit) first.
  148. ParseProjectXml.findNBMElement(dep, "run-dependency") == null) {
  149. continue;
  150. }
  151. Element cnbEl2 = ParseProjectXml.findNBMElement(dep, "code-name-base");
  152. if (cnbEl2 == null) {
  153. throw new BuildException("Malformed project file " + projectXml, getLocation());
  154. }
  155. String cnb2 = XMLUtil.findText(cnbEl2);
  156. deps.add(cnb2);
  157. }
  158. buildDeps.put(cnb, deps);
  159. // create test dependencies
  160. if (isSortTests()) {
  161. Element testDepsEl = ParseProjectXml.findNBMElement(data,"test-dependencies");
  162. if (testDepsEl != null) {
  163. // <test-type>
  164. Iterator itTType = XMLUtil.findSubElements(testDepsEl).iterator();
  165. while (itTType.hasNext()) {
  166. Iterator itt = XMLUtil.findSubElements((Element)itTType.next()).iterator();
  167. while (itt.hasNext()) {
  168. Element dep = (Element) itt.next();
  169. if (ParseProjectXml.findNBMElement(dep, "test") == null) {
  170. continue;
  171. }
  172. Element cnbEl2 = ParseProjectXml.findNBMElement(dep, "code-name-base");
  173. if (cnbEl2 == null) {
  174. throw new BuildException("No cobase found for test-dependency");
  175. }
  176. String cnb2 = XMLUtil.findText(cnbEl2);
  177. deps.add(cnb2);
  178. }
  179. }
  180. }
  181. }
  182. }
  183. for (List<String> deps: buildDeps.values()) {
  184. deps.retainAll(basedirsByCNB.keySet());
  185. }
  186. Map<String,List<String>> reversedDeps = new HashMap<String,List<String>>();
  187. for (Map.Entry<String,List<String>> entry : buildDeps.entrySet()) {
  188. for (String from : entry.getValue()) {
  189. String to = entry.getKey();
  190. List<String> tos = reversedDeps.get(from);
  191. if (tos == null) {
  192. reversedDeps.put(from, tos = new ArrayList<String>());
  193. }
  194. tos.add(to);
  195. }
  196. }
  197. List<String> cnbs;
  198. try {
  199. cnbs = topologicalSort(basedirsByCNB.keySet(), reversedDeps);
  200. } catch (TopologicalSortException x) {
  201. throw new BuildException(x.getMessage(), x, getLocation());
  202. }
  203. StringBuffer path = new StringBuffer();
  204. for (String cnb: cnbs) {
  205. assert basedirsByCNB.containsKey(cnb);
  206. if (path.length() > 0) {
  207. path.append(File.pathSeparatorChar);
  208. }
  209. path.append(basedirsByCNB.get(cnb).getAbsolutePath());
  210. }
  211. getProject().setNewProperty(sortedModulesProperty, path.toString());
  212. }
  213. // Stolen from org.openide.util.Utilities:
  214. private static <T> List<T> topologicalSort(Collection<T> c, Map<? super T, ? extends Collection<? extends T>> edges)
  215. throws TopologicalSortException {
  216. Map<T,Boolean> finished = new HashMap<T,Boolean>();
  217. List<T> r = new ArrayList<T>(Math.max(c.size(), 1));
  218. List<T> cRev = new ArrayList<T>(c);
  219. Collections.reverse(cRev);
  220. Iterator<T> it = cRev.iterator();
  221. while (it.hasNext()) {
  222. List<T> cycle = visit(it.next(), edges, finished, r);
  223. if (cycle != null) {
  224. throw new TopologicalSortException(cRev, edges);
  225. }
  226. }
  227. Collections.reverse(r);
  228. if (r.size() != c.size()) {
  229. r.retainAll(c);
  230. }
  231. return r;
  232. }
  233. private static <T> List<T> visit(
  234. T node,
  235. Map<? super T, ? extends Collection<? extends T>> edges,
  236. Map<T,Boolean> finished,
  237. List<T> r
  238. ) {
  239. Boolean b = finished.get(node);
  240. //System.err.println("node=" + node + " color=" + b);
  241. if (b != null) {
  242. if (b.booleanValue()) {
  243. return null;
  244. }
  245. ArrayList<T> cycle = new ArrayList<T>();
  246. cycle.add(node);
  247. finished.put(node, null);
  248. return cycle;
  249. }
  250. Collection<? extends T> e = edges.get(node);
  251. if (e != null) {
  252. finished.put(node, Boolean.FALSE);
  253. Iterator<? extends T> it = e.iterator();
  254. while (it.hasNext()) {
  255. List<T> cycle = visit(it.next(), edges, finished, r);
  256. if (cycle != null) {
  257. if (cycle instanceof ArrayList) {
  258. // if cycle instanceof ArrayList we are still in the
  259. // cycle and we want to collect new members
  260. if (Boolean.FALSE == finished.get(node)) {
  261. // another member in the cycle
  262. cycle.add(node);
  263. } else {
  264. // we have reached the head of the cycle
  265. // do not add additional cycles anymore
  266. Collections.reverse(cycle);
  267. // changing cycle to not be ArrayList
  268. cycle = Collections.unmodifiableList(cycle);
  269. }
  270. }
  271. // mark this node as tested
  272. finished.put(node, Boolean.TRUE);
  273. // and report an error
  274. return cycle;
  275. }
  276. }
  277. }
  278. finished.put(node, Boolean.TRUE);
  279. r.add(node);
  280. return null;
  281. }
  282. private static final class TopologicalSortException extends Exception {
  283. /** all vertexes */
  284. private Collection vertexes;
  285. /** map with edges */
  286. private Map<?,? extends Collection<?>> edges;
  287. /** result if called twice */
  288. private Set[] result;
  289. /** counter to number the vertexes */
  290. private int counter;
  291. /** vertexes sorted by increasing value of y */
  292. private Stack<Vertex> dualGraph = new Stack<Vertex>();
  293. TopologicalSortException(Collection vertexes, Map<?,? extends Collection<?>> edges) {
  294. this.vertexes = vertexes;
  295. this.edges = edges;
  296. }
  297. /** Because the full sort was not possible, this methods
  298. * returns the best possible substitute for it that is available.
  299. *
  300. * @return list of partially sorted objects, the list can be freely modified
  301. */
  302. public final List partialSort() {
  303. Set[] all = topologicalSets();
  304. ArrayList<Object> res = new ArrayList<Object>(vertexes.size());
  305. for (int i = 0; i < all.length; i++) {
  306. for (Object e : all[i]) {
  307. res.add(e);
  308. }
  309. }
  310. return res;
  311. }
  312. /** The topological sort could not be finished as there
  313. * are some objects that are mutually refering to each other.
  314. * This methods finds such objects and partition them into
  315. * separate sets. All objects in one set (transitively) refer to
  316. * each other and thus prevent the sort from succeding. As
  317. * there can be more of such "unsortable sets" an array
  318. * of them is returned.
  319. *
  320. * @return array of sets that contain some of the original objects, result
  321. * shall not be modified
  322. */
  323. public final Set[] unsortableSets() {
  324. Set[] all = topologicalSets();
  325. ArrayList<Set> unsort = new ArrayList<Set>();
  326. for (int i = 0; i < all.length; i++) {
  327. if ((all[i].size() > 1) || !(all[i] instanceof HashSet)) {
  328. unsort.add(all[i]);
  329. }
  330. }
  331. return unsort.toArray(new Set[0]);
  332. }
  333. @Override
  334. public String getMessage() {
  335. StringWriter w = new StringWriter();
  336. PrintWriter pw = new PrintWriter(w);
  337. printDebug(pw);
  338. pw.close();
  339. return w.toString();
  340. }
  341. @Override
  342. public String toString() {
  343. String s = getClass().getName();
  344. return s;
  345. }
  346. private void printDebug(java.io.PrintWriter w) {
  347. Set<Object> relevantVertices = new HashSet<Object>();
  348. Set<?>[] bad = unsortableSets();
  349. for (Set<?> s : bad) {
  350. relevantVertices.addAll(s);
  351. }
  352. Map<Object,Collection<?>> relevantEdges = new HashMap<Object,Collection<?>>();
  353. for (Map.Entry<?,? extends Collection<?>> entry : edges.entrySet()) {
  354. Set<Object> relevant = new HashSet<Object>(entry.getValue());
  355. relevant.add(entry.getKey());
  356. relevant.retainAll(relevantVertices);
  357. if (!relevant.isEmpty()) {
  358. relevantEdges.put(entry.getKey(), entry.getValue());
  359. }
  360. }
  361. w.print("TopologicalSortException - Collection with relevant edges "); // NOI18N
  362. w.print(relevantEdges);
  363. w.println(" cannot be sorted"); // NOI18N
  364. for (int i = 0; i < bad.length; i++) {
  365. w.print(" Conflict #"); // NOI18N
  366. w.print(i);
  367. w.print(": "); // NOI18N
  368. w.println(bad[i]);
  369. }
  370. }
  371. /** Adds description why the graph cannot be sorted.
  372. * @param w writer to write to
  373. */
  374. public @Override final void printStackTrace(java.io.PrintWriter w) {
  375. printDebug(w);
  376. super.printStackTrace(w);
  377. }
  378. /** Adds description why the graph cannot be sorted.
  379. * @param s stream to write to
  380. */
  381. public @Override final void printStackTrace(java.io.PrintStream s) {
  382. java.io.PrintWriter w = new java.io.PrintWriter(s);
  383. this.printStackTrace(w);
  384. w.flush();
  385. }
  386. /** As the full topological sort cannot be finished due to cycles
  387. * in the graph this methods performs a partition topological sort.
  388. * <P>
  389. * First of all it identifies unsortable parts of the graph and
  390. * partitions the graph into sets of original objects. Each set contains
  391. * objects that are mutually unsortable (there is a cycle between them).
  392. * Then the topological sort is performed again on those sets, this
  393. * sort succeeds because the graph of sets is DAG (all problematic edges
  394. * were only between objects now grouped inside the sets) and the
  395. * result forms the return value of this method.
  396. *
  397. * @return array of sorted sets that contain the original objects, each
  398. * object from the original collection is exactly in one set, result
  399. * shall not be modified
  400. */
  401. public final Set[] topologicalSets() {
  402. if (result != null) {
  403. return result;
  404. }
  405. HashMap<Object, Vertex> vertexInfo = new HashMap<Object, Vertex>();
  406. // computes value X and Y for each vertex
  407. counter = 0;
  408. Iterator it = vertexes.iterator();
  409. while (it.hasNext()) {
  410. constructDualGraph(counter, it.next(), vertexInfo);
  411. }
  412. // now connect vertexes that cannot be sorted into own
  413. // sets
  414. // map from the original objects to
  415. Map<Object, Set> objectsToSets = new HashMap<Object, Set>();
  416. ArrayList<Set> sets = new ArrayList<Set>();
  417. while (!dualGraph.isEmpty()) {
  418. Vertex v = dualGraph.pop();
  419. if (!v.visited) {
  420. Set<Object> set = new HashSet<Object>();
  421. visitDualGraph(v, set);
  422. if ((set.size() == 1) && v.edgesFrom.contains(v)) {
  423. // mark if there is a self reference and the
  424. // set is only one element big, it means that there
  425. // is a self cycle
  426. //
  427. // do not use HashSet but Collections.singleton
  428. // to recognize such cycles
  429. set = Collections.singleton(v.object);
  430. }
  431. sets.add(set);
  432. // fill the objectsToSets mapping
  433. it = set.iterator();
  434. while (it.hasNext()) {
  435. objectsToSets.put(it.next(), set);
  436. }
  437. }
  438. }
  439. // now topologically sort the sets
  440. // 1. prepare the map
  441. HashMap<Set, Collection<Set>> edgesBetweenSets = new HashMap<Set, Collection<Set>>();
  442. it = edges.entrySet().iterator();
  443. while (it.hasNext()) {
  444. Map.Entry entry = (Map.Entry) it.next();
  445. Collection leadsTo = (Collection) entry.getValue();
  446. if ((leadsTo == null) || leadsTo.isEmpty()) {
  447. continue;
  448. }
  449. Set from = objectsToSets.get(entry.getKey());
  450. Collection<Set> setsTo = edgesBetweenSets.get(from);
  451. if (setsTo == null) {
  452. setsTo = new ArrayList<Set>();
  453. edgesBetweenSets.put(from, setsTo);
  454. }
  455. Iterator convert = leadsTo.iterator();
  456. while (convert.hasNext()) {
  457. Set to = objectsToSets.get(convert.next());
  458. if (from != to) {
  459. // avoid self cycles
  460. setsTo.add(to);
  461. }
  462. }
  463. }
  464. // 2. do the sort
  465. try {
  466. List<Set> listResult = topologicalSort(sets, edgesBetweenSets);
  467. result = listResult.toArray(new Set[0]);
  468. } catch (TopologicalSortException ex) {
  469. throw new IllegalStateException("Cannot happen"); // NOI18N
  470. }
  471. return result;
  472. }
  473. /** Traverses the tree
  474. * @param counter current value
  475. * @param vertex current vertex
  476. * @param vertexInfo the info
  477. */
  478. private Vertex constructDualGraph(int counter, Object vertex, HashMap<Object, Vertex> vertexInfo) {
  479. Vertex info = vertexInfo.get(vertex);
  480. if (info == null) {
  481. info = new Vertex(vertex, counter++);
  482. vertexInfo.put(vertex, info);
  483. } else {
  484. // already (being) processed
  485. return info;
  486. }
  487. // process children
  488. Collection c = (Collection) edges.get(vertex);
  489. if (c != null) {
  490. Iterator it = c.iterator();
  491. while (it.hasNext()) {
  492. Vertex next = constructDualGraph(counter, it.next(), vertexInfo);
  493. next.edgesFrom.add(info);
  494. }
  495. }
  496. // leaving the vertex
  497. info.y = counter++;
  498. dualGraph.push(info);
  499. return info;
  500. }
  501. /** Visit dual graph. Decreasing value of Y gives the order.
  502. * Number
  503. *
  504. * @param vertex vertex to start from
  505. * @param visited list of all objects that we've been to
  506. */
  507. private void visitDualGraph(Vertex vertex, Collection<Object> visited) {
  508. if (vertex.visited) {
  509. return;
  510. }
  511. visited.add(vertex.object);
  512. vertex.visited = true;
  513. Iterator it = vertex.edges();
  514. while (it.hasNext()) {
  515. Vertex v = (Vertex) it.next();
  516. visitDualGraph(v, visited);
  517. }
  518. }
  519. /** Represents info about a vertex in the graph. Vertexes are
  520. * comparable by the value of Y, but only after the value is set,
  521. * so the sort has to be done latelly.
  522. */
  523. private static final class Vertex implements Comparable<Vertex> {
  524. /** the found object */
  525. public Object object;
  526. /** list of vertexes that point to this one */
  527. public List<Vertex> edgesFrom = new ArrayList<Vertex>();
  528. /** the counter state when we entered the vertex */
  529. public final int x;
  530. /** the counter when we exited the vertex */
  531. public int y;
  532. /** already sorted, true if the edges has been sorted */
  533. public boolean sorted;
  534. /** true if visited in dual graph */
  535. public boolean visited;
  536. public Vertex(Object obj, int x) {
  537. this.x = x;
  538. this.object = obj;
  539. }
  540. /** Iterator over edges
  541. * @return iterator of Vertex items
  542. */
  543. public Iterator edges() {
  544. if (!sorted) {
  545. Collections.sort(edgesFrom);
  546. sorted = true;
  547. }
  548. return edgesFrom.iterator();
  549. }
  550. /** Comparing based on value of Y.
  551. *
  552. * @param o the Object to be compared.
  553. * @return a negative integer, zero, or a positive integer as this object
  554. * is less than, equal to, or greater than the specified object.
  555. */
  556. public int compareTo(Vertex o) {
  557. return o.y - y;
  558. }
  559. }
  560. }
  561. }