/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: MyTreeNode.java
* Written by Eric Kim, Sun Microsystems.
*
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) 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 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.simulation.test;
import javax.swing.tree.TreeNode;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.regex.Pattern;
/**
* Default node class for chip-testing hierarchical data structures. This is the
* superclass of SubchainNode, ChipNode, etc.
*/
public class MyTreeNode implements TreeNode {
/** One level up the MyTreeNode hieararchy */
private MyTreeNode parent;
/** One level down the MyTreeNode hierarchy */
private ArrayList children;
/**
* Node name. E.g., the node path "expC.receive.calibrate" describes a node
* named "calibrate", which is a child of node "receive", which is a child
* of the root scan chain "expC".
*/
private String name;
/** Comment attached to this node */
private String comment;
/** Number of entries in children ArrayList */
private int childCount;
/** Define the blackslash and the period as field separators */
private static final Pattern splitter = Pattern.compile("\\.");
/**
* Default constructor.
*
* @param name
* node name.
* @param comment
* comment attached to this node
*/
public MyTreeNode(String name, String comment) {
setName(name);
this.comment = comment;
this.parent = null;
children = new ArrayList();
childCount = 0;
}
/**
* Get long version of node name. Overides default toString method, and is
* in turn overridden by subclasses.
*/
public String toString() {
return name;
}
/**
* Get short version of node name. Provided to make short version accessible
* after toString() gets overridden.
*/
final public String getName() {
return name;
}
/** Accessor method, sets node name */
public void setName(String name) {
this.name = name;
}
String getComment() {
return comment;
}
/** Add a child to the node */
void addChild(MyTreeNode newNode) {
if (newNode == null) {
return;
}
children.add(newNode);
newNode.parent = this;
childCount++;
}
public Enumeration children() {
return new ChildEnumerator(children);
}
public boolean getAllowsChildren() {
return true;
}
/**
* Return selected child
*
* @param index
* index of the child
* @return the selected child
*/
public MyTreeNode getChildAt(int index) {
if (index < 0 || index >= children.size()) {
return null;
} else {
return (MyTreeNode) children.get(index);
}
}
/** return the number of children */
public int getChildCount() {
return childCount;
}
/**
* Returns MyTreeNode object one level up in hierarchy
*
* @return parent tree node
*/
public MyTreeNode getParent() {
return parent;
}
/**
* return the index of the child
*
* @param child
* node to be found
* @return index of the child
*/
public int getIndex(TreeNode child) {
return children.indexOf(child);
}
public boolean isLeaf() {
if (children.size() > 0) return false;
return true;
}
/**
* Return the node hierarchy from root down to <tt>this</tt>. E.g., if
* <tt>this</tt> is node expC.receive.calibrate, function will return the
* MyTreeNode array {expC, receive, calibrate}.
*
* @return array of MyTreeNode objects in path to "this"
*/
public MyTreeNode[] getHierarchy() {
ArrayList paths = new ArrayList();
for (MyTreeNode node = this; node != null; node = node.getParent()) {
paths.add(0, node);
}
MyTreeNode[] ret = new MyTreeNode[paths.size()];
for (int i = 0; i < paths.size(); i++) {
ret[i] = (MyTreeNode) paths.get(i);
}
return ret;
}
/**
* return part of the string representation of the path. E.g.,
* "expC.receive.calibrate" (startLevel=0) or "receive.calibrate"
* (startLevel=1).
*
* @param startLevel
* start point of the path. 0=root, 1=first level, ..
* @return string representation of the path, starting at startLevel
*/
public String getPathString(int startLevel) {
StringBuffer sb = new StringBuffer();
MyTreeNode[] nodes = getHierarchy();
for (int i = startLevel; i < nodes.length; i++) {
sb.append(nodes[i].getName() + '.');
}
// Delete the trailing "." (lame)
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
/**
* find a node given root node and partial path string. WARNING: Cannot be
* used to find the root node.
*
* @param root
* starting node of the path
* @param path
* path string, starting at level 1 (excludes root node)
* @return node under root that is described by the path string
*/
public static MyTreeNode getNode(MyTreeNode root, String path) {
MyTreeNode node = root, nextNode = null;
// Generate array of node names (excluding root)
String[] pathNames = splitter.split(path);
if (path.equals("")) {
System.out.println("MyTreeNode.getNode() WARNING: cannot use this "
+ "method to find root node");
}
for (int depth = 0; depth < pathNames.length; depth++) {
boolean found = false;
String name = pathNames[depth];
// Loop over children until find one whose name matches the next
// node name in the path
for (int ind = 0; ind < node.getChildCount(); ind++) {
MyTreeNode child = node.getChildAt(ind);
if (child.getName().equals(name)) {
if (found) {
Infrastructure.fatal("Two nodes with name " + name
+ " in path " + path);
}
nextNode = child;
found = true;
}
}
// If none of the children match at this depth, game over
if (found == false) {
return null;
}
node = nextNode;
}
return node;
}
/**
* Recursively add to a list all of the nodes in the hierarchy under the
* requested node.
*
* @param node
* node whose descendents to add to the list
* @param list
* list to add the descendents to
*/
private void addDescendentsToList(MyTreeNode node, java.util.List list) {
int numKids = node.getChildCount();
for (int ind = 0; ind < numKids; ind++) {
MyTreeNode kid = node.getChildAt(ind);
list.add(kid);
addDescendentsToList(kid, list);
}
}
/**
* Returns all nodes below the current node in the hierarchy.
*
* @return array of nodes beneath <code>this</code> node
*/
public MyTreeNode[] getDescendents() {
// Create list of path names for hierarchy starting at the this node,
// then remove the entry for the this node itself
java.util.List kidList = new java.util.ArrayList();
addDescendentsToList(this, kidList);
// Convert the path list to a string array
MyTreeNode[] descendents = new MyTreeNode[kidList.size()];
for (int ind = 0; ind < kidList.size(); ind++) {
descendents[ind] = (MyTreeNode) kidList.get(ind);
}
return descendents;
}
/** Helper for CompareXML */
void compare(MyTreeNode that, String thisFile, String thatFile) {
if (getName().equals(that.getName()) == false) {
System.out.println("**** Node names differ: '" + getPathString(1)
+ "' in " + thisFile + ", but '" + that.getPathString(1)
+ "' in " + thatFile);
}
}
public static class ChildEnumerator implements Enumeration {
private ArrayList list;
private int index = 0;
public ChildEnumerator(ArrayList list) {
this.list = list;
}
public boolean hasMoreElements() {
return index < list.size();
}
public Object nextElement() {
if (index < list.size()) {
Object obj = list.get(index);
index++;
return obj;
}
return null;
}
}
/**
* Unit test. Creates a hierarchy level0 -> {level1a, level1b}; level1b ->
* level2. Uses it to test the getPathString() and getNode() methods.
*/
public static void main(String[] args) throws Exception {
String path;
MyTreeNode tryFind;
MyTreeNode level0 = new MyTreeNode("level0", "frog");
MyTreeNode level1a = new MyTreeNode("level1a", "frog");
MyTreeNode level1b = new MyTreeNode("level1b", "frog");
MyTreeNode level2 = new MyTreeNode("level2", "frog");
level0.addChild(level1a);
level0.addChild(level1b);
level1b.addChild(level2);
path = level0.getPathString(0);
System.out.println("path string, starting at level 0: " + path);
path = level0.getPathString(1);
System.out.println("path string, starting at level 1 (should fail): "
+ path);
tryFind = MyTreeNode.getNode(level0, "");
System.out.println("tryFind = " + tryFind);
tryFind = MyTreeNode.getNode(level0, "level0");
System.out.println("tryFind = " + tryFind + "\n");
path = level1b.getPathString(0);
System.out.println("path string, starting at level 0: " + path);
path = level1b.getPathString(1);
System.out.println("path string, starting at level 1: " + path);
tryFind = MyTreeNode.getNode(level0, path);
System.out.println("tryFind = " + tryFind + "\n");
path = level2.getPathString(0);
System.out.println("path string, starting at level 0: " + path);
path = level2.getPathString(1);
System.out.println("path string, starting at level 1: " + path);
tryFind = MyTreeNode.getNode(level0, path);
System.out.println("tryFind = " + tryFind);
}
}