/* * Tree.java * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is Knife. * * The Initial Developer of the Original Code is dog. * Portions created by dog are Copyright (C) 1998 dog . All Rights Reserved. * * Contributor(s): n/a. * * You may retrieve the latest version of this package at the knife home page, * located at http://www.dog.net.uk/knife/ */ package dog.util; import java.util.Vector; /** * A utility class for manipulating objects in a tree structure. *

* This class provides a way to associate arbitrary objects with one another in a hierarchy. * * @author dog@dog.net.uk * @version 1.0final */ 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-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-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-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-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-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-1) removeElement(e); } } void removeElement(int e) { int len = 0; boolean removed = false; for (int p=0; p0) { 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--; //printArray("parents", parents, nparents); //printArray("elements", elements, nelements); if ((len = nelements-e-1)>0) { System.arraycopy(elements, e+1+nc, elements, e, len-nc); System.arraycopy(depths, e+1+nc, depths, e, len-nc); } nelements-=(nc+1); //printArray("elements", elements, nelements); removed = true; } catch (ArrayIndexOutOfBoundsException x) { System.out.println("arrayindexoutofboundsexception in tree:"); System.out.println("p="+p+": "+parents[p]); System.out.println("e="+e+", nelements="+nelements+", elements.length="+elements.length+", nc="+nc+", len="+len); } break; } } for (int p=0; p0) { System.arraycopy(children[p], c+1, children[p], c, len); } nchildren[p]--; if ((len = nelements-e-1)>0) { System.arraycopy(elements, e+1, elements, e, len); System.arraycopy(depths, e+1, depths, e, len); } nelements--; removed = true; break; } } } if (!removed) { if ((len = nelements-e-1)>0) { System.arraycopy(elements, e+1, elements, e, len); System.arraycopy(depths, e+1, depths, e, len); } nelements--; } //printElements(); } /** * 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-1) { for (int c=0; c0) 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; ifirst) { mid = objects[(first+last)/2]; while (lo<=hi) { while (lofirst && 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