PackageSorter.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /* ========================================================================
  2. * JCommon : a free general purpose class library for the Java(tm) platform
  3. * ========================================================================
  4. *
  5. * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jcommon/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU Lesser General Public License as published by
  11. * the Free Software Foundation; either version 2.1 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17. * License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  22. * USA.
  23. *
  24. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  25. * in the United States and other countries.]
  26. *
  27. * ------------------
  28. * PackageSorter.java
  29. * ------------------
  30. * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
  31. *
  32. * Original Author: Thomas Morgner;
  33. * Contributor(s): David Gilbert (for Object Refinery Limited);
  34. *
  35. * $Id: PackageSorter.java,v 1.3 2007/05/15 12:32:15 taqua Exp $
  36. *
  37. * Changes
  38. * -------
  39. * 02-Sep-2003 : Initial version
  40. * 07-Jun-2004 : Added JCommon header (DG);
  41. *
  42. */
  43. package org.jfree.base.modules;
  44. import java.util.ArrayList;
  45. import java.util.Arrays;
  46. import java.util.HashMap;
  47. import java.util.Iterator;
  48. import java.util.List;
  49. import org.jfree.util.Log;
  50. /**
  51. * Compares two modules for order. A module is considered less than an other
  52. * module if the module is a required module of the compared module. Modules
  53. * are considered equal if they have no relation.
  54. * <p>
  55. * When sorting, we match this modules position against all dependent
  56. * modules until all positions are stable. Circular references are evil
  57. * and are filtered during the module loading process in the package manager.
  58. *
  59. * @author Thomas Morgner
  60. */
  61. public final class PackageSorter
  62. {
  63. /**
  64. * An Internal wrapper class which collects additional information
  65. * on the given module. Every module has a position, which is heigher
  66. * than the position of all dependent modules.
  67. *
  68. * @author Thomas Morgner
  69. */
  70. private static class SortModule implements Comparable
  71. {
  72. /** stores the relative position of the module in the global list. */
  73. private int position;
  74. /** The package state of the to be matched module. */
  75. private final PackageState state;
  76. /** A list of all directly dependent subsystems. */
  77. private ArrayList dependSubsystems;
  78. // direct dependencies, indirect ones are handled by the
  79. // dependent classes ...
  80. /**
  81. * Creates a new SortModule for the given package state.
  82. *
  83. * @param state the package state object, that should be wrapped up
  84. * by this class.
  85. */
  86. public SortModule(final PackageState state)
  87. {
  88. this.position = -1;
  89. this.state = state;
  90. }
  91. /**
  92. * Returns the list of all dependent subsystems. The list gets defined
  93. * when the sorting is started.
  94. *
  95. * @return the list of all dependent subsystems.
  96. */
  97. public ArrayList getDependSubsystems()
  98. {
  99. return this.dependSubsystems;
  100. }
  101. /**
  102. * Defines a list of dependent subsystems for this module. The list contains
  103. * the names of the dependent subsystems as strings.
  104. *
  105. * @param dependSubsystems a list of all dependent subsystems.
  106. */
  107. public void setDependSubsystems(final ArrayList dependSubsystems)
  108. {
  109. this.dependSubsystems = dependSubsystems;
  110. }
  111. /**
  112. * Returns the current position of this module in the global list.
  113. * The position is computed by comparing all positions of all dependent
  114. * subsystem modules.
  115. *
  116. * @return the current module position.
  117. */
  118. public int getPosition()
  119. {
  120. return this.position;
  121. }
  122. /**
  123. * Defines the position of this module in the global list of all
  124. * known modules.
  125. *
  126. * @param position the position.
  127. */
  128. public void setPosition(final int position)
  129. {
  130. this.position = position;
  131. }
  132. /**
  133. * Returns the package state contained in this SortModule.
  134. *
  135. * @return the package state of this module.
  136. */
  137. public PackageState getState()
  138. {
  139. return this.state;
  140. }
  141. /**
  142. * Returns a basic string representation of this SortModule. This
  143. * should be used for debugging purposes only.
  144. * @see java.lang.Object#toString()
  145. *
  146. * @return a string representation of this module.
  147. */
  148. public String toString ()
  149. {
  150. final StringBuffer buffer = new StringBuffer();
  151. buffer.append("SortModule: ");
  152. buffer.append(this.position);
  153. buffer.append(" ");
  154. buffer.append(this.state.getModule().getName());
  155. buffer.append(" ");
  156. buffer.append(this.state.getModule().getModuleClass());
  157. return buffer.toString();
  158. }
  159. /**
  160. * Compares this module against an other sort module.
  161. *
  162. * @see java.lang.Comparable#compareTo(java.lang.Object)
  163. *
  164. * @param o the other sort module instance.
  165. * @return -1 if the other's module position is less than
  166. * this modules position, +1 if this module is less than the
  167. * other module or 0 if both modules have an equal position in
  168. * the list.
  169. */
  170. public int compareTo(final Object o)
  171. {
  172. final SortModule otherModule = (SortModule) o;
  173. if (this.position > otherModule.position)
  174. {
  175. return +1;
  176. }
  177. if (this.position < otherModule.position)
  178. {
  179. return -1;
  180. }
  181. return 0;
  182. }
  183. }
  184. /**
  185. * DefaultConstructor.
  186. */
  187. private PackageSorter() {
  188. // nothing required.
  189. }
  190. /**
  191. * Sorts the given list of package states. The packages
  192. * are sorted by their dependencies in a way so that all
  193. * dependent packages are placed on lower positions than
  194. * the packages which declared the dependency.
  195. *
  196. * @param modules the list of modules.
  197. */
  198. public static void sort (final List modules)
  199. {
  200. final HashMap moduleMap = new HashMap();
  201. final ArrayList errorModules = new ArrayList();
  202. final ArrayList weightModules = new ArrayList();
  203. for (int i = 0; i < modules.size(); i++)
  204. {
  205. final PackageState state = (PackageState) modules.get(i);
  206. if (state.getState() == PackageState.STATE_ERROR)
  207. {
  208. errorModules.add (state);
  209. }
  210. else
  211. {
  212. final SortModule mod = new SortModule(state);
  213. weightModules.add (mod);
  214. moduleMap.put(state.getModule().getModuleClass(), mod);
  215. }
  216. }
  217. final SortModule[] weigths = (SortModule[])
  218. weightModules.toArray(new SortModule[weightModules.size()]);
  219. for (int i = 0; i < weigths.length; i++)
  220. {
  221. final SortModule sortMod = weigths[i];
  222. sortMod.setDependSubsystems
  223. (collectSubsystemModules(sortMod.getState().getModule(),
  224. moduleMap));
  225. }
  226. // repeat the computation until all modules have a matching
  227. // position. This is not the best algorithm, but it works
  228. // and is relativly simple. It will need some optimizations
  229. // in the future, but as this is only executed once, we don't
  230. // have to care much about it.
  231. boolean doneWork = true;
  232. while (doneWork)
  233. {
  234. doneWork = false;
  235. for (int i = 0; i < weigths.length; i++)
  236. {
  237. final SortModule mod = weigths[i];
  238. final int position = searchModulePosition(mod, moduleMap);
  239. if (position != mod.getPosition())
  240. {
  241. mod.setPosition(position);
  242. doneWork = true;
  243. }
  244. }
  245. }
  246. Arrays.sort(weigths);
  247. modules.clear();
  248. for (int i = 0; i < weigths.length; i++)
  249. {
  250. modules.add (weigths[i].getState());
  251. }
  252. for (int i = 0; i < errorModules.size(); i++)
  253. {
  254. modules.add (errorModules.get(i));
  255. }
  256. }
  257. /**
  258. * Computes the new module position. This position is computed
  259. * according to the dependent modules and subsystems. The returned
  260. * position will be higher than the highest dependent module position.
  261. *
  262. * @param smodule the sort module for that we compute the new positon.
  263. * @param moduleMap the map with all modules.
  264. * @return the new positon.
  265. */
  266. private static int searchModulePosition
  267. (final SortModule smodule, final HashMap moduleMap)
  268. {
  269. final Module module = smodule.getState().getModule();
  270. int position = 0;
  271. // check the required modules. Increase our level to at least
  272. // one point over the highest dependent module
  273. // ignore missing modules.
  274. ModuleInfo[] modInfo = module.getOptionalModules();
  275. for (int modPos = 0; modPos < modInfo.length; modPos++)
  276. {
  277. final String moduleName = modInfo[modPos].getModuleClass();
  278. final SortModule reqMod = (SortModule) moduleMap.get(moduleName);
  279. if (reqMod == null)
  280. {
  281. continue;
  282. }
  283. if (reqMod.getPosition() >= position)
  284. {
  285. position = reqMod.getPosition() + 1;
  286. }
  287. }
  288. // check the required modules. Increase our level to at least
  289. // one point over the highest dependent module
  290. // there are no missing modules here (or the package manager
  291. // is invalid)
  292. modInfo = module.getRequiredModules();
  293. for (int modPos = 0; modPos < modInfo.length; modPos++)
  294. {
  295. final String moduleName = modInfo[modPos].getModuleClass();
  296. final SortModule reqMod = (SortModule) moduleMap.get(moduleName);
  297. if (reqMod == null)
  298. {
  299. Log.warn ("Invalid state: Required dependency of '" + moduleName + "' had an error.");
  300. continue;
  301. }
  302. if (reqMod.getPosition() >= position)
  303. {
  304. position = reqMod.getPosition() + 1;
  305. }
  306. }
  307. // check the subsystem dependencies. This way we make sure
  308. // that subsystems are fully initialized before we try to use
  309. // them.
  310. final String subSystem = module.getSubSystem();
  311. final Iterator it = moduleMap.values().iterator();
  312. while (it.hasNext())
  313. {
  314. final SortModule mod = (SortModule) it.next();
  315. // it is evil to compute values on ourself...
  316. if (mod.getState().getModule() == module)
  317. {
  318. // same module ...
  319. continue;
  320. }
  321. final Module subSysMod = mod.getState().getModule();
  322. // if the module we check is part of the same subsystem as
  323. // we are, then we dont do anything. Within the same subsystem
  324. // the dependencies are computed solely by the direct references.
  325. if (subSystem.equals(subSysMod.getSubSystem()))
  326. {
  327. // same subsystem ... ignore
  328. continue;
  329. }
  330. // does the module from the global list <mod> depend on the
  331. // subsystem we are part of?
  332. //
  333. // if yes, we have a relation and may need to adjust the level...
  334. if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))
  335. {
  336. // check whether the module is a base module of the given
  337. // subsystem. We will not adjust our position in that case,
  338. // as this would lead to an infinite loop
  339. if (isBaseModule(subSysMod, module) == false)
  340. {
  341. if (mod.getPosition() >= position)
  342. {
  343. position = mod.getPosition() + 1;
  344. }
  345. }
  346. }
  347. }
  348. return position;
  349. }
  350. /**
  351. * Checks, whether a module is a base module of an given module.
  352. *
  353. * @param mod the module which to check
  354. * @param mi the module info of the suspected base module.
  355. * @return true, if the given module info describes a base module of the
  356. * given module, false otherwise.
  357. */
  358. private static boolean isBaseModule(final Module mod, final ModuleInfo mi)
  359. {
  360. ModuleInfo[] info = mod.getRequiredModules();
  361. for (int i = 0; i < info.length; i++)
  362. {
  363. if (info[i].getModuleClass().equals(mi.getModuleClass()))
  364. {
  365. return true;
  366. }
  367. }
  368. info = mod.getOptionalModules();
  369. for (int i = 0; i < info.length; i++)
  370. {
  371. if (info[i].getModuleClass().equals(mi.getModuleClass()))
  372. {
  373. return true;
  374. }
  375. }
  376. return false;
  377. }
  378. /**
  379. * Collects all directly dependent subsystems.
  380. *
  381. * @param childMod the module which to check
  382. * @param moduleMap the map of all other modules, keyed by module class.
  383. * @return the list of all dependent subsystems.
  384. */
  385. private static ArrayList collectSubsystemModules
  386. (final Module childMod, final HashMap moduleMap)
  387. {
  388. final ArrayList collector = new ArrayList();
  389. ModuleInfo[] info = childMod.getRequiredModules();
  390. for (int i = 0; i < info.length; i++)
  391. {
  392. final SortModule dependentModule = (SortModule)
  393. moduleMap.get(info[i].getModuleClass());
  394. if (dependentModule == null)
  395. {
  396. Log.warn
  397. (new Log.SimpleMessage
  398. ("A dependent module was not found in the list of known modules.",
  399. info[i].getModuleClass()));
  400. continue;
  401. }
  402. collector.add (dependentModule.getState().getModule().getSubSystem());
  403. }
  404. info = childMod.getOptionalModules();
  405. for (int i = 0; i < info.length; i++)
  406. {
  407. final Module dependentModule = (Module)
  408. moduleMap.get(info[i].getModuleClass());
  409. if (dependentModule == null)
  410. {
  411. Log.warn ("A dependent module was not found in the list of known modules.");
  412. continue;
  413. }
  414. collector.add (dependentModule.getSubSystem());
  415. }
  416. return collector;
  417. }
  418. }