Tree.java
上传用户:huihesys
上传日期:2007-01-04
资源大小:3877k
文件大小:18k
- /*
- * Tree.java
- * Copyright (C) 1999 dog <dog@dog.net.uk>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * You may retrieve the latest version of this library from
- * http://www.dog.net.uk/knife/
- */
- package dog.util;
- import java.util.Vector;
- /**
- * A utility class for manipulating objects in a tree structure.
- * <p>
- * This class provides a way to associate arbitrary objects with one another in a hierarchy.
- *
- * @author dog@dog.net.uk
- * @version 1.0
- */
- public final class Tree {
- int nparents = 0;
- Object[] parents;
- int[] nchildren;
- Object[][] children;
- int nelements = 0;
- Object[] elements;
- int[] depths;
- int increment;
- Object LOCK = new Object();
- /**
- * Constructs an empty tree.
- */
- public Tree() {
- this(10);
- }
-
- /**
- * Constructs an empty tree with the specified capacity increment.
- * @param increment the amount by which the capacity is increased when an array overflows.
- */
- public Tree(int increment) {
- super();
- this.increment = (increment>0) ? increment : 1;
- clear();
- }
- // Returns the elements index of the specified object, or -1 if no such element exists.
- private int elementIndex(Object object) {
- for (int i=0; i<nelements; i++)
- if (elements[i]==object)
- return i;
- return -1;
- }
- // Returns the parents index of the specified object, or -1 if no such parent exists.
- private int parentIndex(Object object) {
- for (int i=0; i<nparents; i++)
- if (parents[i]==object)
- return i;
- return -1;
- }
- /**
- * Returns the parent of the specified object,
- * or null if the object is a root or not a child in the tree.
- */
- public Object getParent(Object child) {
- synchronized (LOCK) {
- return parentOf(child);
- }
- }
- private Object parentOf(Object child) {
- for (int p = 0; p<nparents; p++) {
- for (int c = 0; c<nchildren[p]; c++)
- if (children[p][c]==child)
- return parents[p];
- }
- return null;
- }
- /**
- * Returns an array of children of the specified object,
- * or an empty array if the object is not a parent in the tree.
- */
- public Object[] getChildren(Object parent) {
- synchronized (LOCK) {
- int p = parentIndex(parent);
- if (p>-1) {
- Object[] c = new Object[nchildren[p]];
- System.arraycopy(children[p], 0, c, 0, nchildren[p]);
- return c;
- }
- }
- return new Object[0];
- }
- /**
- * Returns the number of children of the specified object.
- */
- public int getChildCount(Object parent) {
- synchronized (LOCK) {
- int p = parentIndex(parent);
- if (p>-1)
- return nchildren[p];
- }
- return 0;
- }
- /**
- * Indicates whether the specified object is contained in the tree.
- */
- public boolean contains(Object object) {
- synchronized (LOCK) {
- return (elementIndex(object)>-1);
- }
- }
- private void ensureRootCapacity(int amount) {
- // calculate the amount by which to increase arrays
- int block = ((int)(Math.max(Math.ceil((double)amount/(double)increment), 1)))*increment;
- // ensure capacity of elements
- if (elements.length<nelements+amount) {
- int len = elements.length;
- Object[] newelements = new Object[len+block];
- System.arraycopy(elements, 0, newelements, 0, nelements);
- elements = newelements;
- int[] newdepths = new int[len+block];
- System.arraycopy(depths, 0, newdepths, 0, nelements);
- depths = newdepths;
- }
- }
- private void ensureParentCapacity(int amount) {
- // calculate the amount by which to increase arrays
- int block = ((int)(Math.max(Math.ceil((double)amount/(double)increment), 1)))*increment;
- // ensure capacity of parents (and hence children in parent dimension)
- if (parents.length<nparents+1) {
- Object[] newparents = new Object[parents.length+increment];
- System.arraycopy(parents, 0, newparents, 0, nparents);
- parents = newparents;
- Object[][] newchildren = new Object[parents.length+increment][];
- System.arraycopy(children, 0, newchildren, 0, nparents);
- children = newchildren;
- int[] newnchildren = new int[parents.length+increment];
- System.arraycopy(nchildren, 0, newnchildren, 0, nparents);
- nchildren = newnchildren;
- }
- // ensure capacity of elements
- if (elements.length<nelements+amount) {
- int len = elements.length;
- Object[] newelements = new Object[len+block];
- System.arraycopy(elements, 0, newelements, 0, nelements);
- elements = newelements;
- int[] newdepths = new int[len+block];
- System.arraycopy(depths, 0, newdepths, 0, nelements);
- depths = newdepths;
- }
- }
- private void ensureChildCapacity(int amount, int parent) {
- // calculate the amount by which to increase arrays
- int block = ((int)(Math.max(Math.ceil((double)amount/(double)increment), 1)))*increment;
- // ensure capacity of children for this parent
- if (children[parent].length<nchildren[parent]+amount) {
- Object[] newchildren = new Object[children[parent].length+block];
- System.arraycopy(children[parent], 0, newchildren, 0, nchildren[parent]);
- children[parent] = newchildren;
- }
- // ensure capacity of elements
- if (elements.length<nelements+amount) {
- int len = elements.length;
- Object[] newelements = new Object[len+block];
- System.arraycopy(elements, 0, newelements, 0, nelements);
- elements = newelements;
- int[] newdepths = new int[len+block];
- System.arraycopy(depths, 0, newdepths, 0, nelements);
- depths = newdepths;
- }
- }
- /**
- * Adds a root to the tree.
- * Returns the root if it was added.
- */
- public void add(Object object) {
- if (object==null)
- throw new NullPointerException("cannot add null to a tree");
- synchronized (LOCK) {
- int e;
- if ((e = elementIndex(object))>-1) // if we already have the object
- removeElement(e); // remove it
- ensureRootCapacity(1);
- elements[nelements] = object;
- depths[nelements] = 1;
- nelements++;
- }
- }
- /**
- * Adds roots to the tree.
- */
- public void add(Object[] objects) {
- synchronized (LOCK) {
- for (int i=0; i<objects.length; i++) {
- if (objects[i]==null)
- throw new NullPointerException("cannot add null to a tree");
- int e;
- if ((e = elementIndex(objects[i]))>-1) // if we already have the object
- removeElement(e); // remove it
- }
- ensureRootCapacity(objects.length);
- System.arraycopy(objects, 0, elements, nelements, objects.length);
- for (int i=nelements; i<nelements+objects.length; i++)
- depths[i] = 1;
- nelements += objects.length;
- }
- }
- /**
- * Adds a child to the specified parent in the tree.
- * @throws IllegalArgumentException if the parent does not exist in the tree or the child is the parent.
- */
- public void add(Object parent, Object child) {
- if (parent==null)
- throw new NullPointerException("null parent specified");
- if (child==null)
- throw new NullPointerException("cannot add null to a tree");
- if (child==parent)
- throw new IllegalArgumentException("cannot add child to itself");
- synchronized (LOCK) {
- int p = parentIndex(parent), pe = -1;
- if (p<0) { // does it exist in the tree?
- if ((pe = elementIndex(parent))<0)
- throw new IllegalArgumentException("parent does not exist");
- } // so we'll add it to parents
- int e;
- if ((e = elementIndex(child))>-1) // if the child is already an element
- removeElement(e);
- if (p<0) { // add the parent to parents
- p = nparents;
- ensureParentCapacity(1);
- parents[p] = parent;
- children[p] = new Object[increment];
- nchildren[p] = 0;
- nparents++;
- }
- ensureChildCapacity(1, p);
- children[p][nchildren[p]] = child;
- // insert the child into elements and set its depth
- int depth = depth(parent);
- int off = pe+nchildren[p]+1;
- int len = nelements-off;
- Object[] buffer = new Object[len];
- System.arraycopy(elements, off, buffer, 0, len);
- elements[off] = child;
- System.arraycopy(buffer, 0, elements, off+1, len);
- int[] dbuffer = new int[len];
- System.arraycopy(depths, off, dbuffer, 0, len);
- depths[off] = depth+1;
- System.arraycopy(dbuffer, 0, depths, off+1, len);
- nelements++;
- nchildren[p]++;
- }
- }
- /**
- * Adds children to the specified parent in the tree.
- * @throws IllegalArgumentException if the parent does not exist in the tree or one of the children is the parent.
- */
- public void add(Object parent, Object[] children) {
- if (parent==null)
- throw new NullPointerException("null parent specified");
- synchronized (LOCK) {
- int p = parentIndex(parent), pe = -1;
- if (p<0) { // does it exist in the tree?
- if ((pe = elementIndex(parent))<0)
- throw new IllegalArgumentException("parent does not exist");
- } // so we'll add it to parents
- for (int i=0; i<children.length; i++) {
- if (children[i]==null)
- throw new NullPointerException("cannot add null to a tree");
- if (children[i]==parent)
- throw new IllegalArgumentException("cannot add child to itself");
- int e;
- if ((e = elementIndex(children[i]))>-1) // if we already have the object
- removeElement(e); // remove it
- }
- if (p<0) { // add the parent to parents
- p = nparents;
- ensureParentCapacity(1);
- parents[p] = parent;
- this.children[p] = new Object[Math.max(increment, (children.length/increment)*increment)];
- nchildren[p] = 0;
- nparents++;
- }
- ensureChildCapacity(children.length, p);
- System.arraycopy(children, 0, this.children[p], nchildren[p], children.length);
- // insert the children into elements and set their depths
- int depth = depth(parent);
- int off = pe+nchildren[p]+1;
- int len = nelements-off;
- Object[] buffer = new Object[len];
- System.arraycopy(elements, off, buffer, 0, len);
- System.arraycopy(children, 0, elements, off, children.length);
- System.arraycopy(buffer, 0, elements, off+children.length, len);
- int[] dbuffer = new int[len];
- System.arraycopy(depths, off, dbuffer, 0, len);
- for (int i=off; i<off+children.length; i++)
- depths[i] = depth+1;
- System.arraycopy(dbuffer, 0, depths, off+children.length, len);
- nelements += children.length;
- nchildren[p] += children.length;
- }
- }
- // Returns the depth of an object in the tree
- private int depth(Object object) {
- Object parent = parentOf(object);
- if (parent==null)
- return 1;
- else
- return depth(parent)+1;
- }
- /**
- * Removes the specified object and all its children from the tree.
- * Computationally pretty expensive.
- */
- public void remove(Object object) {
- if (object==null)
- return; // i mean, really...
- synchronized (LOCK) {
- int e = elementIndex(object);
- if (e>-1)
- removeElement(e);
- }
- }
- void removeElement(int e) {
- int len = 0;
- // does this element have children?
- for (int p=0; p<nparents; p++) {
- if (parents[p]==elements[e]) {
- int nc = nchildren[p];
- // remove the children of this parent first
- Object[] pchildren = new Object[nc];
- System.arraycopy(children[p], 0, pchildren, 0, nc);
- for (int c=0; c<nc; c++) {
- int ce = elementIndex(pchildren[c]);
- if (ce>-1)
- removeElement(ce);
- }
- // remove the parent
- if ((len = nparents-p-1)>0) {
- System.arraycopy(parents, p+1, parents, p, len);
- System.arraycopy(children, p+1, children, p, len);
- System.arraycopy(nchildren, p+1, nchildren, p, len);
- }
- nparents--; p--;
- } else {
- // is this element a child of another parent?
- for (int c=0; c<nchildren[p]; c++) {
- if (children[p][c]==elements[e]) {
- // remove this element from the children array
- if ((len = nchildren[p]-c-1)>0) {
- System.arraycopy(children[p], c+1, children[p], c, len);
- }
- nchildren[p]--; c--;
- }
- }
- }
- }
- if ((len = nelements-e-1)>0) {
- System.arraycopy(elements, e+1, elements, e, len);
- System.arraycopy(depths, e+1, depths, e, len);
- }
- nelements--;
- }
-
- /**
- * Removes the specified objects and all their children from the tree.
- * @throws NullPointerException if an object does not exist in the tree.
- */
- public void remove(Object[] objects) {
- for (int i=0; i<objects.length; i++)
- remove(objects[i]);
- }
-
- /**
- * Recursively removes the specified object's children from the tree.
- * @throws NullPointerException if the object does not exist in the tree.
- */
- public void removeChildren(Object parent) {
- synchronized (LOCK) {
- removeChildrenImpl(parent);
- }
- }
- void removeChildrenImpl(Object parent) {
- int p = parentIndex(parent);
- if (p>-1) {
- for (int c=0; c<nchildren[p]; c++)
- removeChildrenImpl(children[p][c]);
- children[p] = new Object[increment];
- nchildren[p] = 0;
- }
- }
- /**
- * Removes all objects from the tree.
- */
- public void clear() {
- synchronized (LOCK) {
- parents = new Object[increment];
- nparents = 0;
- nchildren = new int[increment];
- for (int i=0; i<increment; i++)
- nchildren[i] = 0;
- children = new Object[increment][increment];
- elements = new Object[increment];
- depths = new int[increment];
- nelements = 0;
- }
- }
- /**
- * Returns the depth of the specified object in the tree.
- * Roots have a depth of 1.
- */
- public int getDepth(Object object) {
- synchronized (LOCK) {
- int e = elementIndex(object);
- if (e==-1) {
- //System.out.println("Warning: "+object+" not found");
- //new Throwable().printStackTrace();
- return -1;
- }
- return depths[e];
- }
- }
- /**
- * Returns the roots in this tree.
- */
- public Object[] getRoots() {
- synchronized (LOCK) {
- Vector v = new Vector();
- for (int i=0; i<nelements; i++) {
- if (depths[i]==1)
- v.addElement(elements[i]);
- }
- Object[] roots = new Object[v.size()];
- v.copyInto(roots);
- return roots;
- }
- }
- /**
- * Returns the number of roots in this tree.
- */
- public int getRootCount() {
- synchronized (LOCK) {
- Vector v = new Vector();
- for (int i=0; i<nelements; i++) {
- if (depths[i]==1)
- v.addElement(elements[i]);
- }
- return v.size();
- }
- }
- /**
- * Returns all the objects in this tree in depth-first order.
- */
- public Object[] getTree() {
- synchronized (LOCK) {
- Object[] objects = new Object[nelements];
- System.arraycopy(elements, 0, objects, 0, nelements);
- return objects;
- }
- }
- /**
- * Returns the number of objects in this tree.
- */
- public int getTreeCount() {
- return nelements;
- }
- /**
- * Indicates whether there are any children in this tree.
- */
- public boolean hasChildren() {
- synchronized (LOCK) {
- for (int p=0; p<nparents; p++)
- if (nchildren[p]>0)
- return true;
- }
- return false;
- }
-
- /**
- * Sorts the objects in the tree according to the specified object collator.
- * This has no effect if a collator has not been defined.
- */
- public void sort(ObjectCollator collator) {
- if (collator==null) throw new NullPointerException("null collator");
- synchronized (LOCK) {
- Sorter sorter = this.new Sorter(collator);
- elements = sorter.newelements;
- depths = sorter.newdepths;
- }
- }
- class Sorter {
- Object[] newelements;
- int[] newdepths;
- int count = 0;
- Sorter(ObjectCollator collator) {
- // first sort the roots
- Vector v = new Vector();
- for (int i=0; i<nelements; i++) {
- if (depths[i]==1)
- v.addElement(elements[i]);
- }
- Object[] roots = new Object[v.size()];
- v.copyInto(roots);
- sort(roots, 0, roots.length-1, collator);
- // now sort the children of each parent
- for (int p=0; p<nparents; p++)
- sort(children[p], 0, nchildren[p]-1, collator);
- // now create a new elements array positioning the elements according to the new order
- newelements = new Object[elements.length];
- newdepths = new int[elements.length];
- for (int r=0; r<roots.length; r++) {
- newelements[count] = roots[r];
- newdepths[count] = depths[elementIndex(roots[r])];
- count++;
- appendChildren(roots[r]);
- }
- }
- void appendChildren(Object parent) {
- for (int p=0; p<nparents; p++) {
- if (parent==parents[p]) {
- for (int c=0; c<nchildren[p]; c++) {
- newelements[count] = children[p][c];
- newdepths[count] = getDepth(children[p][c]);
- count++;
- appendChildren(children[p][c]);
- }
- break;
- }
- }
- }
-
- }
- // Quicksort algorithm.
- static void sort(Object[] objects, int first, int last, ObjectCollator collator) {
- int lo = first, hi = last;
- Object mid;
- if (last>first) {
- mid = objects[(first+last)/2];
- while (lo<=hi) {
- while (lo<last && collator.compare(objects[lo], mid)<0)
- lo += 1;
- while (hi>first && collator.compare(objects[hi], mid)>0)
- hi -= 1;
- if (lo<=hi) { // swap
- Object tmp = objects[lo];
- objects[lo] = objects[hi];
- objects[hi] = tmp;
- lo += 1;
- hi -= 1;
- }
- }
- if (first<hi)
- sort(objects, first, hi, collator);
- if (lo<last)
- sort(objects, lo, last, collator);
- }
- }
- public void printArray(String name, Object[] array, int count) {
- System.out.println(name+": length="+array.length+", count="+count);
- for (int i=0; i<count; i++)
- System.out.println("t"+array[i]);
- }
- public void printElements() {
- System.out.println("elements=");
- for (int i=0; i<nelements; i++) {
- StringBuffer buffer = new StringBuffer();
- buffer.append(Integer.toString(depths[i]));
- for (int j=0; j<depths[i]; j++)
- buffer.append(" ");
- buffer.append(elements[i]);
- System.out.println(buffer.toString());
- }
- System.out.println("tree=");
- for (int p=0; p<nparents; p++) {
- System.out.println(parents[p]);
- for (int c=0; c<nchildren[p]; c++)
- System.out.println(" "+children[p][c]);
- }
- }
- // -- Utility methods --
- public String toString() {
- StringBuffer buffer = new StringBuffer();
- buffer.append(getClass().getName());
- buffer.append("[");
- buffer.append("nelements="+nelements);
- buffer.append(",elements.length="+elements.length);
- buffer.append(",depths.length="+depths.length);
- buffer.append(",nparents="+nparents);
- buffer.append(",parents.length="+parents.length);
- buffer.append("]");
- return buffer.toString();
- }
- }