Tree.java
上传用户:huihesys
上传日期:2007-01-04
资源大小:3877k
文件大小:18k
源码类别:

WEB邮件程序

开发平台:

C/C++

  1. /*
  2.  * Tree.java
  3.  * Copyright (C) 1999 dog <dog@dog.net.uk>
  4.  * 
  5.  * This library is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU Lesser General Public
  7.  * License as published by the Free Software Foundation; either
  8.  * version 2 of the License, or (at your option) any later version.
  9.  * 
  10.  * This library is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  * Lesser General Public License for more details.
  14.  * 
  15.  * You should have received a copy of the GNU Lesser General Public
  16.  * License along with this library; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  * 
  19.  * You may retrieve the latest version of this library from
  20.  * http://www.dog.net.uk/knife/
  21.  */
  22. package dog.util;
  23. import java.util.Vector;
  24. /**
  25.  * A utility class for manipulating objects in a tree structure.
  26.  * <p>
  27.  * This class provides a way to associate arbitrary objects with one another in a hierarchy.
  28.  *
  29.  * @author dog@dog.net.uk
  30.  * @version 1.0
  31.  */
  32. public final class Tree {
  33. int nparents = 0;
  34. Object[] parents;
  35. int[] nchildren;
  36. Object[][] children;
  37. int nelements = 0;
  38. Object[] elements;
  39. int[] depths;
  40. int increment;
  41. Object LOCK = new Object();
  42. /**
  43.  * Constructs an empty tree.
  44.  */
  45. public Tree() {
  46. this(10);
  47. }
  48. /**
  49.  * Constructs an empty tree with the specified capacity increment.
  50.  * @param increment the amount by which the capacity is increased when an array overflows.
  51.  */
  52. public Tree(int increment) {
  53. super();
  54. this.increment = (increment>0) ? increment : 1;
  55. clear();
  56. }
  57. // Returns the elements index of the specified object, or -1 if no such element exists.
  58. private int elementIndex(Object object) {
  59. for (int i=0; i<nelements; i++)
  60. if (elements[i]==object)
  61. return i;
  62. return -1;
  63. }
  64. // Returns the parents index of the specified object, or -1 if no such parent exists.
  65. private int parentIndex(Object object) {
  66. for (int i=0; i<nparents; i++)
  67. if (parents[i]==object)
  68. return i;
  69. return -1;
  70. }
  71. /**
  72.  * Returns the parent of the specified object,
  73.  * or null if the object is a root or not a child in the tree.
  74.  */
  75. public Object getParent(Object child) {
  76. synchronized (LOCK) {
  77. return parentOf(child);
  78. }
  79. }
  80. private Object parentOf(Object child) {
  81. for (int p = 0; p<nparents; p++) {
  82. for (int c = 0; c<nchildren[p]; c++)
  83. if (children[p][c]==child)
  84. return parents[p];
  85. }
  86. return null;
  87. }
  88. /**
  89.  * Returns an array of children of the specified object,
  90.  * or an empty array if the object is not a parent in the tree.
  91.  */
  92. public Object[] getChildren(Object parent) {
  93. synchronized (LOCK) {
  94. int p = parentIndex(parent);
  95. if (p>-1) {
  96. Object[] c = new Object[nchildren[p]];
  97. System.arraycopy(children[p], 0, c, 0, nchildren[p]);
  98. return c;
  99. }
  100. }
  101. return new Object[0];
  102. }
  103. /**
  104.  * Returns the number of children of the specified object.
  105.  */
  106. public int getChildCount(Object parent) {
  107. synchronized (LOCK) {
  108. int p = parentIndex(parent);
  109. if (p>-1)
  110. return nchildren[p];
  111. }
  112. return 0;
  113. }
  114. /**
  115.  * Indicates whether the specified object is contained in the tree.
  116.  */
  117. public boolean contains(Object object) {
  118. synchronized (LOCK) {
  119. return (elementIndex(object)>-1);
  120. }
  121. }
  122. private void ensureRootCapacity(int amount) {
  123. // calculate the amount by which to increase arrays
  124. int block = ((int)(Math.max(Math.ceil((double)amount/(double)increment), 1)))*increment;
  125. // ensure capacity of elements
  126. if (elements.length<nelements+amount) {
  127. int len = elements.length;
  128. Object[] newelements = new Object[len+block];
  129. System.arraycopy(elements, 0, newelements, 0, nelements);
  130. elements = newelements;
  131. int[] newdepths = new int[len+block];
  132. System.arraycopy(depths, 0, newdepths, 0, nelements);
  133. depths = newdepths;
  134. }
  135. }
  136. private void ensureParentCapacity(int amount) {
  137. // calculate the amount by which to increase arrays
  138. int block = ((int)(Math.max(Math.ceil((double)amount/(double)increment), 1)))*increment;
  139. // ensure capacity of parents (and hence children in parent dimension)
  140. if (parents.length<nparents+1) {
  141. Object[] newparents = new Object[parents.length+increment];
  142. System.arraycopy(parents, 0, newparents, 0, nparents);
  143. parents = newparents;
  144. Object[][] newchildren = new Object[parents.length+increment][];
  145. System.arraycopy(children, 0, newchildren, 0, nparents);
  146. children = newchildren;
  147. int[] newnchildren = new int[parents.length+increment];
  148. System.arraycopy(nchildren, 0, newnchildren, 0, nparents);
  149. nchildren = newnchildren;
  150. }
  151. // ensure capacity of elements
  152. if (elements.length<nelements+amount) {
  153. int len = elements.length;
  154. Object[] newelements = new Object[len+block];
  155. System.arraycopy(elements, 0, newelements, 0, nelements);
  156. elements = newelements;
  157. int[] newdepths = new int[len+block];
  158. System.arraycopy(depths, 0, newdepths, 0, nelements);
  159. depths = newdepths;
  160. }
  161. }
  162. private void ensureChildCapacity(int amount, int parent) {
  163. // calculate the amount by which to increase arrays
  164. int block = ((int)(Math.max(Math.ceil((double)amount/(double)increment), 1)))*increment;
  165. // ensure capacity of children for this parent
  166. if (children[parent].length<nchildren[parent]+amount) {
  167. Object[] newchildren = new Object[children[parent].length+block];
  168. System.arraycopy(children[parent], 0, newchildren, 0, nchildren[parent]);
  169. children[parent] = newchildren;
  170. }
  171. // ensure capacity of elements
  172. if (elements.length<nelements+amount) {
  173. int len = elements.length;
  174. Object[] newelements = new Object[len+block];
  175. System.arraycopy(elements, 0, newelements, 0, nelements);
  176. elements = newelements;
  177. int[] newdepths = new int[len+block];
  178. System.arraycopy(depths, 0, newdepths, 0, nelements);
  179. depths = newdepths;
  180. }
  181. }
  182. /**
  183.  * Adds a root to the tree.
  184.  * Returns the root if it was added.
  185.  */
  186. public void add(Object object) {
  187. if (object==null)
  188. throw new NullPointerException("cannot add null to a tree");
  189. synchronized (LOCK) {
  190. int e;
  191. if ((e = elementIndex(object))>-1) // if we already have the object
  192. removeElement(e); // remove it
  193. ensureRootCapacity(1);
  194. elements[nelements] = object;
  195. depths[nelements] = 1;
  196. nelements++;
  197. }
  198. }
  199. /**
  200.  * Adds roots to the tree.
  201.  */
  202. public void add(Object[] objects) {
  203. synchronized (LOCK) {
  204. for (int i=0; i<objects.length; i++) {
  205. if (objects[i]==null)
  206. throw new NullPointerException("cannot add null to a tree");
  207. int e;
  208. if ((e = elementIndex(objects[i]))>-1) // if we already have the object
  209. removeElement(e); // remove it
  210. }
  211. ensureRootCapacity(objects.length);
  212. System.arraycopy(objects, 0, elements, nelements, objects.length);
  213. for (int i=nelements; i<nelements+objects.length; i++)
  214. depths[i] = 1;
  215. nelements += objects.length;
  216. }
  217. }
  218. /**
  219.  * Adds a child to the specified parent in the tree.
  220.  * @throws IllegalArgumentException if the parent does not exist in the tree or the child is the parent.
  221.  */
  222. public void add(Object parent, Object child) {
  223. if (parent==null)
  224. throw new NullPointerException("null parent specified");
  225. if (child==null)
  226. throw new NullPointerException("cannot add null to a tree");
  227. if (child==parent)
  228. throw new IllegalArgumentException("cannot add child to itself");
  229. synchronized (LOCK) {
  230. int p = parentIndex(parent), pe = -1;
  231. if (p<0) { // does it exist in the tree?
  232. if ((pe = elementIndex(parent))<0)
  233. throw new IllegalArgumentException("parent does not exist");
  234. } // so we'll add it to parents
  235. int e;
  236. if ((e = elementIndex(child))>-1) // if the child is already an element
  237. removeElement(e);
  238. if (p<0) { // add the parent to parents
  239. p = nparents;
  240. ensureParentCapacity(1);
  241. parents[p] = parent;
  242. children[p] = new Object[increment];
  243. nchildren[p] = 0;
  244. nparents++;
  245. }
  246. ensureChildCapacity(1, p);
  247. children[p][nchildren[p]] = child;
  248. // insert the child into elements and set its depth
  249. int depth = depth(parent);
  250. int off = pe+nchildren[p]+1;
  251. int len = nelements-off;
  252. Object[] buffer = new Object[len];
  253. System.arraycopy(elements, off, buffer, 0, len);
  254. elements[off] = child;
  255. System.arraycopy(buffer, 0, elements, off+1, len);
  256. int[] dbuffer = new int[len];
  257. System.arraycopy(depths, off, dbuffer, 0, len);
  258. depths[off] = depth+1;
  259. System.arraycopy(dbuffer, 0, depths, off+1, len);
  260. nelements++;
  261. nchildren[p]++;
  262. }
  263. }
  264. /**
  265.  * Adds children to the specified parent in the tree.
  266.  * @throws IllegalArgumentException if the parent does not exist in the tree or one of the children is the parent.
  267.  */
  268. public void add(Object parent, Object[] children) {
  269. if (parent==null)
  270. throw new NullPointerException("null parent specified");
  271. synchronized (LOCK) {
  272. int p = parentIndex(parent), pe = -1;
  273. if (p<0) { // does it exist in the tree?
  274. if ((pe = elementIndex(parent))<0)
  275. throw new IllegalArgumentException("parent does not exist");
  276. } // so we'll add it to parents
  277. for (int i=0; i<children.length; i++) {
  278. if (children[i]==null)
  279. throw new NullPointerException("cannot add null to a tree");
  280. if (children[i]==parent)
  281. throw new IllegalArgumentException("cannot add child to itself");
  282. int e;
  283. if ((e = elementIndex(children[i]))>-1) // if we already have the object
  284. removeElement(e); // remove it
  285. }
  286. if (p<0) { // add the parent to parents
  287. p = nparents;
  288. ensureParentCapacity(1);
  289. parents[p] = parent;
  290. this.children[p] = new Object[Math.max(increment, (children.length/increment)*increment)];
  291. nchildren[p] = 0;
  292. nparents++;
  293. }
  294. ensureChildCapacity(children.length, p);
  295. System.arraycopy(children, 0, this.children[p], nchildren[p], children.length);
  296. // insert the children into elements and set their depths
  297. int depth = depth(parent);
  298. int off = pe+nchildren[p]+1;
  299. int len = nelements-off;
  300. Object[] buffer = new Object[len];
  301. System.arraycopy(elements, off, buffer, 0, len);
  302. System.arraycopy(children, 0, elements, off, children.length);
  303. System.arraycopy(buffer, 0, elements, off+children.length, len);
  304. int[] dbuffer = new int[len];
  305. System.arraycopy(depths, off, dbuffer, 0, len);
  306. for (int i=off; i<off+children.length; i++)
  307. depths[i] = depth+1;
  308. System.arraycopy(dbuffer, 0, depths, off+children.length, len);
  309. nelements += children.length;
  310. nchildren[p] += children.length;
  311. }
  312. }
  313. // Returns the depth of an object in the tree
  314. private int depth(Object object) {
  315. Object parent = parentOf(object);
  316. if (parent==null)
  317. return 1;
  318. else
  319. return depth(parent)+1;
  320. }
  321. /**
  322.  * Removes the specified object and all its children from the tree.
  323.  * Computationally pretty expensive.
  324.  */
  325. public void remove(Object object) {
  326. if (object==null)
  327. return; // i mean, really...
  328. synchronized (LOCK) {
  329. int e = elementIndex(object);
  330. if (e>-1)
  331. removeElement(e);
  332. }
  333. }
  334. void removeElement(int e) {
  335. int len = 0;
  336.         // does this element have children?
  337. for (int p=0; p<nparents; p++) {
  338. if (parents[p]==elements[e]) {
  339. int nc = nchildren[p];
  340.                 // remove the children of this parent first
  341.                 Object[] pchildren = new Object[nc];
  342. System.arraycopy(children[p], 0, pchildren, 0, nc);
  343. for (int c=0; c<nc; c++) {
  344. int ce = elementIndex(pchildren[c]);
  345. if (ce>-1)
  346. removeElement(ce);
  347. }
  348.                 // remove the parent
  349. if ((len = nparents-p-1)>0) {
  350. System.arraycopy(parents, p+1, parents, p, len);
  351. System.arraycopy(children, p+1, children, p, len);
  352. System.arraycopy(nchildren, p+1, nchildren, p, len);
  353. }
  354. nparents--; p--;
  355. } else {
  356. // is this element a child of another parent?
  357. for (int c=0; c<nchildren[p]; c++) {
  358. if (children[p][c]==elements[e]) {
  359. // remove this element from the children array
  360. if ((len = nchildren[p]-c-1)>0) {
  361. System.arraycopy(children[p], c+1, children[p], c, len);
  362. }
  363. nchildren[p]--; c--;
  364. }
  365. }
  366. }
  367. }
  368. if ((len = nelements-e-1)>0) {
  369. System.arraycopy(elements, e+1, elements, e, len);
  370. System.arraycopy(depths, e+1, depths, e, len);
  371. }
  372. nelements--;
  373. }
  374. /**
  375.  * Removes the specified objects and all their children from the tree.
  376.  * @throws NullPointerException if an object does not exist in the tree.
  377.  */
  378. public void remove(Object[] objects) {
  379. for (int i=0; i<objects.length; i++)
  380. remove(objects[i]);
  381. }
  382. /**
  383.  * Recursively removes the specified object's children from the tree.
  384.  * @throws NullPointerException if the object does not exist in the tree.
  385.  */
  386. public void removeChildren(Object parent) {
  387. synchronized (LOCK) {
  388. removeChildrenImpl(parent);
  389. }
  390. }
  391. void removeChildrenImpl(Object parent) {
  392. int p = parentIndex(parent);
  393. if (p>-1) {
  394. for (int c=0; c<nchildren[p]; c++)
  395. removeChildrenImpl(children[p][c]);
  396. children[p] = new Object[increment];
  397. nchildren[p] = 0;
  398. }
  399. }
  400. /**
  401.  * Removes all objects from the tree.
  402.  */
  403. public void clear() {
  404. synchronized (LOCK) {
  405. parents = new Object[increment];
  406. nparents = 0;
  407. nchildren = new int[increment];
  408. for (int i=0; i<increment; i++)
  409. nchildren[i] = 0;
  410. children = new Object[increment][increment];
  411. elements = new Object[increment];
  412. depths = new int[increment];
  413. nelements = 0;
  414. }
  415. }
  416. /**
  417.  * Returns the depth of the specified object in the tree.
  418.  * Roots have a depth of 1.
  419.  */
  420. public int getDepth(Object object) {
  421. synchronized (LOCK) {
  422. int e = elementIndex(object);
  423. if (e==-1) {
  424. //System.out.println("Warning: "+object+" not found");
  425. //new Throwable().printStackTrace();
  426. return -1;
  427. }
  428. return depths[e];
  429. }
  430. }
  431. /**
  432.  * Returns the roots in this tree.
  433.  */
  434. public Object[] getRoots() {
  435. synchronized (LOCK) {
  436. Vector v = new Vector();
  437. for (int i=0; i<nelements; i++) {
  438. if (depths[i]==1)
  439. v.addElement(elements[i]);
  440. }
  441. Object[] roots = new Object[v.size()];
  442. v.copyInto(roots);
  443. return roots;
  444. }
  445. }
  446. /**
  447.  * Returns the number of roots in this tree.
  448.  */
  449. public int getRootCount() {
  450. synchronized (LOCK) {
  451. Vector v = new Vector();
  452. for (int i=0; i<nelements; i++) {
  453. if (depths[i]==1)
  454. v.addElement(elements[i]);
  455. }
  456. return v.size();
  457. }
  458. }
  459. /**
  460.  * Returns all the objects in this tree in depth-first order.
  461.  */
  462. public Object[] getTree() {
  463. synchronized (LOCK) {
  464. Object[] objects = new Object[nelements];
  465. System.arraycopy(elements, 0, objects, 0, nelements);
  466. return objects;
  467. }
  468. }
  469. /**
  470.  * Returns the number of objects in this tree.
  471.  */
  472. public int getTreeCount() {
  473. return nelements;
  474. }
  475. /**
  476.  * Indicates whether there are any children in this tree.
  477.  */
  478. public boolean hasChildren() {
  479. synchronized (LOCK) {
  480. for (int p=0; p<nparents; p++)
  481. if (nchildren[p]>0)
  482. return true;
  483. }
  484. return false;
  485. }
  486. /**
  487.  * Sorts the objects in the tree according to the specified object collator.
  488.  * This has no effect if a collator has not been defined.
  489.  */
  490. public void sort(ObjectCollator collator) {
  491. if (collator==null) throw new NullPointerException("null collator");
  492. synchronized (LOCK) {
  493. Sorter sorter = this.new Sorter(collator);
  494. elements = sorter.newelements;
  495. depths = sorter.newdepths;
  496. }
  497. }
  498. class Sorter {
  499. Object[] newelements;
  500. int[] newdepths;
  501. int count = 0;
  502. Sorter(ObjectCollator collator) {
  503. // first sort the roots
  504. Vector v = new Vector();
  505. for (int i=0; i<nelements; i++) {
  506. if (depths[i]==1)
  507. v.addElement(elements[i]);
  508. }
  509. Object[] roots = new Object[v.size()];
  510. v.copyInto(roots);
  511. sort(roots, 0, roots.length-1, collator);
  512. // now sort the children of each parent
  513. for (int p=0; p<nparents; p++)
  514. sort(children[p], 0, nchildren[p]-1, collator);
  515. // now create a new elements array positioning the elements according to the new order
  516. newelements = new Object[elements.length];
  517. newdepths = new int[elements.length];
  518. for (int r=0; r<roots.length; r++) {
  519. newelements[count] = roots[r];
  520. newdepths[count] = depths[elementIndex(roots[r])];
  521. count++;
  522. appendChildren(roots[r]);
  523. }
  524. }
  525. void appendChildren(Object parent) {
  526. for (int p=0; p<nparents; p++) {
  527. if (parent==parents[p]) {
  528. for (int c=0; c<nchildren[p]; c++) {
  529. newelements[count] = children[p][c];
  530. newdepths[count] = getDepth(children[p][c]);
  531. count++;
  532. appendChildren(children[p][c]);
  533. }
  534. break;
  535. }
  536. }
  537. }
  538. }
  539. // Quicksort algorithm.
  540. static void sort(Object[] objects, int first, int last, ObjectCollator collator) {
  541. int lo = first, hi = last;
  542. Object mid;
  543. if (last>first) {
  544. mid = objects[(first+last)/2];
  545. while (lo<=hi) {
  546. while (lo<last && collator.compare(objects[lo], mid)<0)
  547. lo += 1;
  548. while (hi>first && collator.compare(objects[hi], mid)>0)
  549. hi -= 1;
  550. if (lo<=hi) { // swap
  551. Object tmp = objects[lo];
  552. objects[lo] = objects[hi];
  553. objects[hi] = tmp;
  554. lo += 1;
  555. hi -= 1;
  556. }
  557. }
  558. if (first<hi)
  559. sort(objects, first, hi, collator);
  560. if (lo<last)
  561. sort(objects, lo, last, collator);
  562. }
  563. }
  564. public void printArray(String name, Object[] array, int count) {
  565. System.out.println(name+": length="+array.length+", count="+count);
  566. for (int i=0; i<count; i++)
  567. System.out.println("t"+array[i]);
  568. }
  569. public void printElements() {
  570.         System.out.println("elements=");
  571. for (int i=0; i<nelements; i++) {
  572. StringBuffer buffer = new StringBuffer();
  573. buffer.append(Integer.toString(depths[i]));
  574. for (int j=0; j<depths[i]; j++)
  575. buffer.append("  ");
  576. buffer.append(elements[i]);
  577. System.out.println(buffer.toString());
  578. }
  579. System.out.println("tree=");
  580. for (int p=0; p<nparents; p++) {
  581. System.out.println(parents[p]);
  582. for (int c=0; c<nchildren[p]; c++)
  583. System.out.println("  "+children[p][c]);
  584. }
  585. }
  586. // -- Utility methods --
  587. public String toString() {
  588. StringBuffer buffer = new StringBuffer();
  589. buffer.append(getClass().getName());
  590. buffer.append("[");
  591. buffer.append("nelements="+nelements);
  592. buffer.append(",elements.length="+elements.length);
  593. buffer.append(",depths.length="+depths.length);
  594. buffer.append(",nparents="+nparents);
  595. buffer.append(",parents.length="+parents.length);
  596. buffer.append("]");
  597. return buffer.toString();
  598. }
  599. }