/* CategoryDump.java This class is intended to serve as a stub to hold basic information about server side categories for the client to process locally. Note that even though this class is implementing a remote interface, it is doing so for the purpose of providing a consistent interface for the client, not for actual remote access. Thus, we are not extending UnicastRemoteObject as we would if we were truly a remote object. Created: 12 February 1998 Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2013 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Web site: http://www.arlut.utexas.edu/gash2 Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package arlut.csd.ganymede.common; import java.rmi.RemoteException; import java.util.Vector; import arlut.csd.ganymede.rmi.Category; import arlut.csd.ganymede.rmi.CategoryNode; /*------------------------------------------------------------------------------ class CategoryDump ------------------------------------------------------------------------------*/ /** * <p>This class is intended to serve as a stub to hold basic * information about server side categories for the client * to process locally.</p> * * <p>Note that even though this class is implementing a remote * interface, it is doing so for the purpose of providing * a consistent interface for the client, not for actual * remote access. Thus, we are not extending UnicastRemoteObject * as we would if we were truly a remote object.</p> */ public class CategoryDump implements Category, CategoryNode { CategoryDump parent; String name; Vector<CategoryNode> contents = new Vector<CategoryNode>(); private int lastIndex = -1; /* -- */ public CategoryDump(CategoryDump parent, char[] src, int index) { String token; CategoryDump catChild; BaseDump baseChild; /* -- */ this.parent = parent; if (parent == null) { // skip the 'cat' chunk if we're the root getChunk(src, index); } else { lastIndex = index; } this.name = getChunk(src, lastIndex); // getChunk() updates lastIndex for us token = getChunk(src, lastIndex); if (token.equals("<")) { // we've got contents token = getChunk(src, lastIndex); while (!token.equals(">")) { if (token.equals("cat")) { catChild = new CategoryDump(this, src, lastIndex); lastIndex = catChild.getLastIndex(); contents.add(catChild); } else if (token.equals("base")) { baseChild = new BaseDump(this, src, lastIndex); lastIndex = baseChild.getLastIndex(); contents.add(baseChild); } else { throw new RuntimeException("parse error, unrecognized chunk: " + token); } // get the next member chunk token = getChunk(src, lastIndex); } } if (!token.equals(">")) { throw new RuntimeException("parse error, couldn't find end of category in dump: " + token); } } public int getLastIndex() { return lastIndex; } /** * Returns the name of this category. */ public String getName() { return name; } /** * Returns the full path to this category, with levels * in the hierarchy separated by '/'s. */ public String getPath() { if (parent != null) { return parent.getPath() + "/" + name; } else { return "/" + name; } } /** * This method returns a vector of BaseDump objects, one for each * base held under this base. */ public synchronized Vector<CategoryNode> getBases() { Vector<CategoryNode> result = new Vector<CategoryNode>(); /* -- */ getBases(result); return result; } private void getBases(Vector<CategoryNode> inout) { for (CategoryNode node: contents) { if (node instanceof BaseDump) { inout.add(node); } else { CategoryDump element = (CategoryDump) node; element.getBases(inout); } } } /** * Sets the name of this node. The name must not include a '/' * character, but all other characters are acceptable. */ public boolean setName(String newName) { throw new IllegalArgumentException("can't call modification methods on CategoryDump."); } /** * This method tells the CategoryNode what it's containing * category is. */ public void setCategory(Category category) { throw new IllegalArgumentException("can't call modification methods on CategoryDump."); } /** * This method returns the category that this * category node belongs to. If this is the DBStore's * root category, this will return null. */ public Category getCategory() { return parent; } /** * Returns child nodes */ public Vector<CategoryNode> getNodes() { return contents; } /** * Returns a subcategory of name name. */ public CategoryNode getNode(String name) { for (CategoryNode candidate: contents) { try { if (candidate.getName().equals(name)) { return candidate; } } catch (RemoteException ex) { throw new RuntimeException("caught remote: " + ex); } } return null; } /** * <p>This method is used to place a Category Node under us. This * method adds a new node into this category, after prevNodeName if * prevNodeName is not null, or at the end of the category if it * is.</p> * * @param node Node to place under this category * @param prevNodeName the name of the node that the new node is to * be added after * * @see arlut.csd.ganymede.rmi.Category */ public void addNodeAfter(CategoryNode node, String prevNodeName) { throw new IllegalArgumentException("can't call modification methods on CategoryDump."); } /** * <p>This method is used to place a Category Node under us. This * method adds a new node into this category, before nextNodeName if * nextNodeName is not null, or at the beginning of the category if * it is.</p> * * @param node Node to place under this category * @param nextNodeName the name of the node that the new node is to * be added before, must not be path-qualified. * * @see arlut.csd.ganymede.rmi.Category */ public void addNodeBefore(CategoryNode node, String nextNodeName) { throw new IllegalArgumentException("can't call modification methods on CategoryDump."); } /** * <p>This method can be used to move a Category from another * Category to this Category, or to move a Category around within * this Category.</p> * * @param catPath category path * @param prevNodeName the name of the node that the new node is to be added after */ public void moveCategoryNode(String catPath, String prevNodeName) { throw new IllegalArgumentException("can't call modification methods on CategoryDump."); } /** * <p>This method is used to remove a Category Node from under * us.</p> * * <p>Note that removeNode assumes that it can recalculate the * displayOrder values for other nodes in this category. This * method should not be called if other nodes with prefixed * displayOrder values are still to be added to this category, as * from the DBStore file.</p> */ public void removeNode(CategoryNode node) { throw new IllegalArgumentException("can't call modification methods on CategoryDump."); } /** * <p>This method is used to remove a Category Node from under * us.</p> * * <p>Note that removeNode assumes that it can recalculate the * displayOrder values for other nodes in this category. This * method should not be called if other nodes with prefixed * displayOrder values are still to be added to this category, as * from the DBStore file.</p> */ public void removeNode(String name) { throw new IllegalArgumentException("can't call modification methods on CategoryDump."); } /** * <p>This creates a new subcategory under this category, with * displayOrder after the last item currently in the category. This * method should only be called when there are no nodes left to be * added to the category with prefixed displayOrder values.</p> */ public Category newSubCategory() { throw new IllegalArgumentException("can't call modification methods on CategoryDump."); } /** * <p>This method returns true if this is a subcategory of cat.</p> */ public boolean isUnder(Category cat) { if (cat == null) { return false; } if (cat.equals(this)) { return true; } if (parent == null) { return false; } else { return parent.isUnder(cat); } } // *** // // private methods // // *** private String getChunk(char[] chars, int startDex) { StringBuilder result = new StringBuilder(); /* -- */ for (lastIndex = startDex; lastIndex < chars.length; lastIndex++) { if (chars[lastIndex] == '|') { lastIndex++; return result.toString(); } else if (chars[lastIndex] == '\\') { result.append(chars[++lastIndex]); } else { result.append(chars[lastIndex]); } } throw new RuntimeException("Ran out of chunk data: " + result.toString()); } }