samer@0: /* samer@0: * Node.java samer@0: * samer@0: * Copyright (c) 2000, Samer Abdallah, King's College London. samer@0: * All rights reserved. samer@0: * samer@0: * This software is provided AS iS and WITHOUT ANY WARRANTY; samer@0: * without even the implied warranty of MERCHANTABILITY or samer@0: * FITNESS FOR A PARTICULAR PURPOSE. samer@0: * samer@0: * Change history: samer@0: * samer@0: * Removed all references to parent nodes - each node now samer@0: * stores its full name. Uses more memory but simpler in the samer@0: * long run, I think. samer@0: */ samer@0: samer@0: package samer.core; samer@0: import java.util.*; samer@0: samer@0: /** samer@0:
A Node is essentially a path in a hierarchical
samer@0: name space. Each node is an independent object.
samer@0: A Node can be constructed either with an explicit
samer@0: parent Node, or with an absolute path, or as a child
samer@0: of the current Node maintained by the current Shell.
samer@0: Names are constructed in parts, concatenated with
samer@0: dots, eg,
samer@0:
samer@0: Node A=new Node("top",null);
samer@0: Node B=new Node("middle",A);
samer@0: Node C=new Node("bottom",B);
samer@0:
samer@0: Then node A is "top", node B is "top.middle" and node
samer@0: C is "top.middle.bottom"
samer@0:
samer@0:
The top level Environment (see Environment)
samer@0: actually uses a nameless top level node: "", so that
samer@0: all fully specified node paths begin with ".", eg
samer@0: ".antelope.gibbon.firebucket"
samer@0: This is so that names that don't start with a
samer@0: dot are treated as relative to the current Environment's node.
samer@0: */
samer@0:
samer@0: public class Node implements java.io.Serializable
samer@0: {
samer@0: private final String name;
samer@0:
samer@0: /** Construct new Node with given name as a child of the
samer@0: current Environment's base node, ie
samer@0: Shell.env().node()
samer@0: */
samer@0: public Node(String name) {
samer@0: if (name==null || name.equals(""))
samer@0: throw new Error("Node with empty name");
samer@0:
samer@0: if (isAbsolute(name)) this.name=name;
samer@0: else this.name=Shell.env().node().fullNameFor(name);
samer@0: }
samer@0:
samer@0:
samer@0: private Node() { name=""; }
samer@0:
samer@0: /** Construct new Node with given name and parent
samer@0: If parent is null, a top level node is created
samer@0: */
samer@0: public Node(String name, Node parent) { this.name = parent.fullName()+"."+name; }
samer@0:
samer@0: /** Returns parent Node, or null if no parent */
samer@0: public Node getParent() { return new Node(name.substring(0,name.lastIndexOf('.'))); }
samer@0: public static String lastPart(String str) { return str.substring(1+str.lastIndexOf('.')); }
samer@0: public String fullName() { return name; }
samer@0: public String shortName() { return lastPart(name); }
samer@0:
samer@0: /** Return full name of hypothetical Node with given name
samer@0: as a child of this Node, ie, this node's full name + "."+ nm.
samer@0: */
samer@0: public String fullNameFor(String nm) { return name+"."+nm; }
samer@0:
samer@0: /** Absolute names start with a . This is only true of the top
samer@0: level Node has an empty name!
samer@0: */
samer@0: public static boolean isAbsolute(String nm) { return (nm.charAt(0)=='.'); }
samer@0:
samer@0: /** Returns true if and only if the given name is a child of this one.
samer@0: This is trivially true if the given name is relative (ie doesn't start
samer@0: with a dot). If it does, then the initial substrings must match.
samer@0: */
samer@0: public boolean isSubnode(String nm) {
samer@0: if (!isAbsolute(nm)) return true;
samer@0: return nm.startsWith(name);
samer@0: }
samer@0:
samer@0: /** Returns the full name of this node. */
samer@0: public String toString() { return name; }
samer@0:
samer@0: /** Returns a NEW top-level node each time it is called. */
samer@0: public static Node root() { return new Node(); }
samer@0: }