* @param keyOfNodeToDelete nodeKey of BonNode to delete * @return boolean true if deleted, false otherwise */ private boolean doDeleteNodeNodeKey keyOfNodeToDelete {// LATER: this will j
Trang 1* XML document specification.
* ForestHashtable is described fully in the book:
* <i>XML, XSLT, Java and JSP - A Case Study in Developing a Web Application</i>,
* by Westy Rockwell, published by
* <A HREF=”http://www.newriders.com”>New Riders</A>
* Translation to German published by
* <A HREF=”http://www.galileocomputing.de”>Galileo Press</A>
* <p>For further information visit the open source
* <A HREF=”http://www.bonForum.org”>BonForum Project on SourceForge</A>
* @author <A HREF=”mailto://email@bonforum.org”>Westy Rockwell</A>
*/
public class ForestHashtable extends java.util.Hashtable {private NodeNameHashtable nodeNameHashtable;
private PathNameHashtable pathNameHashtable;
private boolean lastRootNodeFound;
private boolean lastChildOfRootNodeFound;
// next one defined further down for recursion// private boolean lastChildOfNonRootNodeFound;
private String currentRootNodeAKey;
private String currentRootNodeBKey;
private String currentRootNodeCKey; // for debug onlyprivate String currentChildOfRootNodeAKey;
private String currentChildOfRootNodeBKey;
private String currentChildOfRootNodeCKey; // for debug only// next 3 defined further down for recursion
// private String currentChildOfNonRootNodeAKey;
// private String currentChildOfNonRootNodeBKey;
// private String currentChildOfNonRootNodeCKey;
private static BonLogger logFH = null;
// Controls logger output
private static String logging = null;
// False until logger ready
private static boolean loggingInitialized = false;
// for logging output, when no sessionId availableprivate static String sessionId = “0000000000”;
private static final int NO_NODEKEY_KEY_PREFIX = 0;
private static final int SESSION_ID = 1;
private static final int SESSION_ID_AND_CREATION_TIME = 2;
private String uniqueNodeKeyKeyList = “message;messageKey”;
/** Sets uniqueNodeKeyKeyList of node names unique per session
* in nodeNameHashtable Determines key prefix choice
* Node names in list can have one key per session
* Node names not in list can have multiple keys per session
* This saves space by not storing keys for unused nodeKey entries
* (Note: applies only adding children to non-root nodes.)
Trang 2* (see set method for details)
*/
public ForestHashtable() {super();
nodeNameHashtable = new NodeNameHashtable();
pathNameHashtable = new PathNameHashtable();
}/** Creates a ForestHashtable of a given capacity
nodeNameHashtable = new NodeNameHashtable();
pathNameHashtable = new PathNameHashtable();
}private void log(String sessionId, String where, String what) {if(logging != null) {
logFH.logWrite(System.currentTimeMillis(), sessionId, where,what);
}}/** Gets logging setting
System.err.println(“ForestHashtable init logging:” +logging);
if(logging != null) {logFH = new BonLogger(“ForestHashtableLog.txt”,logging);
Trang 3System.err.println(“ForestHashtable init logFH:” +logFH);
logFH.setLogging(newLogging);
loggingInitialized = true;
System.err.println(“ForestHashtable initloggingInitialized:” + loggingInitialized);
}}}}/** Gets nodeNameHashtable
* @return NodeNameHashtable nodeNameHashtable
*/
protected NodeNameHashtable getNodeNameHashtable() {return nodeNameHashtable;
}/** Gets pathNameHashtable
* @return PathNameHashtable pathNameHashtable
*/
protected PathNameHashtable getPathNameHashtable() {return pathNameHashtable;
}/** Returns nodeKey from a BonNode, as an object
*
* @param bonNode node whose key is returned
* @return nodeKey of BonNode cast to an Object
*/
protected Object nodeKeyFromBonNode(BonNode bonNode) {return (Object)bonNode.nodeKey;
}/** Provides a useable NodeKey with a unique default root key value
*
* @return NodeKey instance with unique aKey, initialized as a root node key
* (to use for non-root nodes, change bKey andcKey values)
*/
private NodeKey getNextAvailableNodeKey() {long temp = 0;
long lastCurrentTimeMillis = System.currentTimeMillis();
NodeKey nodeKey = new NodeKey();
while (temp <= lastCurrentTimeMillis) {temp = System.currentTimeMillis();
}nodeKey.aKey = Long.toString(temp);
// initialize other keys to first,// that makes node a root node by defaultnodeKey.bKey = nodeKey.aKey;
nodeKey.cKey = nodeKey.aKey;
return nodeKey;
}/** Deletes a BonNode given its nodeKey value
Trang 4* @param keyOfNodeToDelete nodeKey of BonNode to delete
* @return boolean true if deleted, false otherwise
*/
private boolean doDeleteNode(NodeKey keyOfNodeToDelete) {// LATER: this will just mark node as deleted,// and a separate thread will scavenge deleted nodes
if (this.containsKey(keyOfNodeToDelete)) {this.remove(keyOfNodeToDelete);
return true;
}else {return false;
}}/** Deletes a BonNode and its descendant nodes, given a nodeKey value
*
* @param keyOfNodeToDelete nodeKey of BonNode to delete
* @return boolean true if at least one node was deleted, falseotherwise
*/
private boolean doDeleteNodeRecursive(NodeKey keyOfNodeToDelete) {// LATER: this will just mark node as deleted,
// and a separate thread will scavenge deleted nodes
String parentAKey = keyOfNodeToDelete.aKey;
NodeKey nodeKey = new NodeKey();
BonNode bonNode = null;
Enumeration enumeration = this.elements();
if(!(enumeration.hasMoreElements())) {return false; // no elements to delete}
while(enumeration.hasMoreElements()) {bonNode = (BonNode)enumeration.nextElement();
nodeKey = bonNode.nodeKey;
if(nodeKey.bKey.equals(parentAKey)) { // found a childdoDeleteNodeRecursive(nodeKey);
}}bonNode = this.getBonNode(keyOfNodeToDelete);
this.remove(keyOfNodeToDelete);
return true;
}/** Returns true if a BonNode has child nodes
*
* @param parentNodeKey the node being tested for children
* @return boolean true if at least one node was deleted, falseotherwise
*/
protected boolean hasAtLeastOneChild(NodeKey parentNodeKey) {// in a ForestHashtable, children have nodeKey.bKey equal to theparent’s nodeKey.aKey
Trang 5BonNode bonNode = null;
String parentAKey = parentNodeKey.aKey;
Enumeration enumeration = this.elements();
while(enumeration.hasMoreElements()) {bonNode = (BonNode)enumeration.nextElement();
if(bonNode.nodeKey.bKey.equals(parentAKey)) {return true;
}}return false;
}/** Deletes a BonNode and possibly its descendant nodes, given a nodeKeyvalue
*
* @param keyOfNodeToDelete nodeKey of BonNode to delete
* @param leafOnly boolean true to delete only if node has no children
* false to delete node and any descendant nodes
* @return boolean true if at least one node was deleted, false otherwise
if(hasAtLeastOneChild(keyOfNodeToDelete)) {return false; // was not a leaf node, sonot deleted
}}// delete and report success or failurereturn doDeleteNodeRecursive(keyOfNodeToDelete);
}else {return false; // no such node}
}}/** Adds a BonNode (and optionally its nodeKey to another hashtable for fastlookups)
* To add nodes, user only calls addRootNode, addChildNodeToRootNode oraddChildNodeToNonRootNode
* If nodeKeyHashtableName is “nodeNameHashtable” then these nodeKeyKeyPrefixvalues are possible:
Trang 6only to current session
* <LI> SESSION_ID_AND_CREATION_TIME allows multiple nodes with nodeName,and visible only to current session
* </UL>
*
* Note: more values of nodeKeyKeyPrefix will be defined in later versions!
*
* @param nodeName String naming this node
* @param nodeAttributes String containing all attributes concatenated(name=value name=value )
* @param nodeContent String containing text content of node
* @param nodeKey NodeKey uniquely identifying andpositioning node to be added in hierarchy
* @param parentNodeKey NodeKey for parent of node to be added
* @param nodeKeyHashtableName String naming hashtable in which tocache key of added node
* @param nodeKeyKeyPrefix int = NO_NODEKEY_KEY_PREFIX makes addednode global, visible to all HTTP Sessions,
* = SESSION_ID makesnode UNIQUE for nodeName in session, and visible only to current session
* =SESSION_ID_AND_CREATION_TIME allows multiple nodes with nodeName, and visible only
BonNode node = new BonNode();
node.deleted = false;
node.flagged = false;
node.nodeName = nodeName;
if(nodeAttributes != null && nodeAttributes.length() > 0) {
node.nodeAttributes = “nodeKey=\””+ nodeKey + “\” “ +nodeAttributes;
}else {node.nodeAttributes = “nodeKey=\””+ nodeKey + “\””;
}node.nodeContent = nodeContent;
node.nodeKey = nodeKey;
node.parentNodeKey = parentNodeKey;
// put in this ForestHashtable// also optionally put nodeKey in nodeNameHashtable// but not if it is a subject element, etc
if(nodeKeyHashtableName.equals(“nodeNameHashtable”)) {// Hashtable is synchronized, but we need to sync two togetherhere:
String nodeKeyKey = null;
Trang 7synchronized(this) {try {
this.put(nodeKey, node);
}catch(Exception ee) {log(sessionId, “err”, “EXCEPTION in addNode():” +ee.getMessage());
ee.printStackTrace();
}if(nodeKeyKeyPrefix == SESSION_ID) {// allows only one key per session// use this option to reduce size of table// by not storing key to nodeKeys not needed// (examples: message keys, messageKey keys).nodeKeyKey = sessionId + “:” + nodeName;
}else if(nodeKeyKeyPrefix == SESSION_ID_AND_CREATION_TIME){
// the nodeKey.aKey acts as a timestamp// allowing multiple keys per session innodeNameHashtable
// use to find multiple nodes with same name for onesession
// (example: chat keys, guest keys, host keys)nodeKeyKey = sessionId + “_” + nodeKey.aKey +”:” +nodeName;
}else if(nodeKeyKeyPrefix == NO_NODEKEY_KEY_PREFIX) {// use no prefix for elements global to all sessionsnodeKeyKey = nodeName;
}else {nodeKeyKey = nodeName; // unknown arg value, couldcomplain
}// else ifs and/or else can add other prefixes here.// Note: it replaces older entries, if any
this.nodeNameHashtable.put(nodeKeyKey, nodeKey);
}}// else ifs here can add other hashtables laterelse {
// Hashtable is synchronized, so if you change ancestor classfor this,
// be sure to sync addition to this here also
this.put(nodeKey, node);
}return node;
}/** Adds a BonNode as a root node (Names should be unique among siblings, ifnodeKeyHashtable is used)
Trang 8* @param rootNodeName String naming this node
* @param rootNodeAttributes String containing all attributesconcatenated (name=value name=value )
* @param rootNodeContent String containing text content ofnode
* @param nodeKeyHashtableName String naming hashtable in which to cachekey of added node
* @return BonNode that was added
*/
protected BonNode addRootNode(String rootNodeName, StringrootNodeAttributes, String rootNodeContent, String nodeKeyHashtableName) {// Node is an object that points to everything mapped to that node in thetree
// In a table that holds objects, just stick Node in table
// OTW, you can extract info and write to fields in table
NodeKey nodeKey = getNextAvailableNodeKey(); // initially, nodeKey =An.An.An;
// When all three keys are equal, the row is a root node!
// An empty parent node key means no parent, because it is a root node
NodeKey emptyParentNodeKey = new NodeKey();
return addNode(rootNodeName, rootNodeAttributes, rootNodeContent,nodeKey, emptyParentNodeKey, nodeKeyHashtableName, NO_NODEKEY_KEY_PREFIX, “”);
}/** Adds a BonNode as a child of a root node.(Names should be unique amongsiblings, if nodeKeyHashtable is used)
*
* @param childNodeName String naming this node
* @param childNodeAttributes String containing all attributesconcatenated (name=value name=value )
* @param childNodeContent String containing text content ofnode
* @param childNodeKey NodeKey uniquely identifying a rootnode
* @param nodeKeyHashtableName String naming hashtable in which
to cache key of added node
* @return BonNode that was added
*/
protected BonNode addChildNodeToRootNode(String childNodeName, StringchildNodeAttributes, String childNodeContent, NodeKey rootNodeKey, StringnodeKeyHashtableName) {
NodeKey childNodeKey = getNextAvailableNodeKey(); // initially,NodeKey = An.An.An;
childNodeKey.bKey = rootNodeKey.aKey;
childNodeKey.cKey = rootNodeKey.bKey;
// when the second and third key are equal, it is child of a rootreturn addNode(childNodeName, childNodeAttributes, childNodeContent,childNodeKey, rootNodeKey, nodeKeyHashtableName, NO_NODEKEY_KEY_PREFIX, “”);
}/** Adds a BonNode as a child of a non-root node (and optionally its nodeKey
to another hashtable for fast lookups)
Trang 9* If “nodeNameHashtable” nodeKeyHashtable is used, sessionId must be passed
in to create session-related key for nodeKey
* (for now, caller is responsible for that!)
*
* @param childNodeName String naming this node
* @param childNodeAttributes String containing all attributesconcatenated (name=value name=value )
* @param childNodeContent String containing text content ofnode
* @param nonRootNodeKey NodeKey uniquely identifying a root node
non-* @param nodeKeyHashtableName String naming hashtable in which tocache key of added node
* @param sessionId String id of callers session, ifnodeKeyHashtable is used
* @return BonNode that was added
*/
protected BonNode addChildNodeToNonRootNode(String childNodeName, StringchildNodeAttributes, String childNodeContent, NodeKey nonRootNodeKey, StringnodeKeyHashtableName, String sessionId) {
NodeKey childNodeKey = getNextAvailableNodeKey();
// when no keys are equal, its a root grandchild or deeperchildNodeKey.bKey = nonRootNodeKey.aKey;
childNodeKey.cKey = nonRootNodeKey.bKey;
// Assume multiple keys per nodeKey allowed in “nodeNameHashtable”nodeKeyHashtable
int nodeKeyKeyPrefix = SESSION_ID_AND_CREATION_TIME;
// unless node name to be added is in the “list”
if(uniqueNodeKeyKeyList.trim().length() > 0) {if(uniqueNodeKeyKeyList.indexOf(childNodeName) > -1) {nodeKeyKeyPrefix = SESSION_ID;
}}return addNode(childNodeName, childNodeAttributes, childNodeContent,childNodeKey, nonRootNodeKey, nodeKeyHashtableName, nodeKeyKeyPrefix, sessionId);}
/** Counts the children of a BonNode
long counter = 0;
BonNode bonNode = null;
String parentAKey = parentNodeKey.aKey;
Enumeration enumeration = this.elements();
while(enumeration.hasMoreElements()) {bonNode = (BonNode)enumeration.nextElement();
Trang 10if(bonNode.nodeKey.bKey.equals(parentAKey)) {counter++;
}}return counter;
}/** Gets from NodeKey ArrayList the first one whose BonNode has a child withgiven content
*
* @param nodeKeys ArrayList of NodeKeys to will be checked
* @param childContent String content of child node to look for
* @return NodeKey of first BonNode with child node that has childContent ascontent, or null
NodeKey nodeKey = getNodeKeyForString((String)iK.next());
NodeKey childNodeKey = getChildNodeByNameAndContent(nodeKey,childName, childContent);
if(childNodeKey != null) {return nodeKey; // of parent whose child has content}
}return null;
}/** Gets a new NodeKey whose toString() method returns a given String
* Note: If argument string is empty or null method returns an emptyNodeKey
*
* @param nodeKeyString String
* @return NodeKey for the given nodeKeyString
*/
protected NodeKey getNodeKeyForString(String nodeKeyString) {//log(sessionId, “”, “getNodeKeyForString() nodeKeyString” +nodeKeyString);
NodeKey nodeKey = new NodeKey();
int inx;
if((nodeKeyString == null) || (nodeKeyString.equals(“”))) {return nodeKey;
}String keyString = nodeKeyString;
// 984576125127.984576061235.984576061225// 1 2 3// 01234567890123456789012345678901234567inx = keyString.indexOf(“.”);
if(inx > -1) {nodeKey.aKey = keyString.substring(0, inx);
keyString = keyString.substring(inx + 1);
inx = keyString.indexOf(“.”);
Trang 11if(inx > -1) {nodeKey.bKey = keyString.substring(0, inx);
String cKey = keyString.substring(inx + 1);
if(cKey.length() > 0) {nodeKey.cKey = cKey;
}}}if(nodeKey.toString().equals(nodeKeyString)) {return nodeKey;
}return null;
}/** Gets ArrayList with contents of all child nodes with given name
*
* @param parentNodeKey NodeKey of node whose children will be checked
* @param nodeName String name of child nodes to look for
* @return ArrayList of content of all child nodes with the given name
BonNode bonNode = new BonNode();
ArrayList nodeContents = new ArrayList();
if(parentNodeKey != null && nodeName != null) {String parentAKey = parentNodeKey.aKey;
Enumeration enumeration = this.elements();
while(enumeration.hasMoreElements()) {bonNode = (BonNode)enumeration.nextElement();
if(bonNode.nodeKey.bKey.equals(parentAKey)) { // it ischild node
if(nodeName.equals(bonNode.nodeName)) {nodeContents.add(bonNode.nodeContent.toString());
}}}return nodeContents;
}return null;
}/** Gets ArrayList with keys of all child nodes with given name
*
* @param parentNodeKey NodeKey of node whose children will be checked
* @param nodeName String name of child nodes to look for
* @return ArrayList of NodeKeys of child nodes with the given name
Trang 12parent’s nodeKey.aKey
BonNode bonNode = new BonNode();
ArrayList nodeKeys = new ArrayList();
if(parentNodeKey != null && nodeName != null) {String parentAKey = parentNodeKey.aKey;
Enumeration enumeration = this.elements();
while(enumeration.hasMoreElements()) {bonNode = (BonNode)enumeration.nextElement();
if(bonNode.nodeKey.bKey.equals(parentAKey)) { // it ischild node
if(nodeName.equals(bonNode.nodeName)) {nodeKeys.add(bonNode.nodeKey.toString());
}}}return nodeKeys;
}return null;
}/** Gets first child node with given name and/or content
* <pre>
* If nodeName is null, match by content only
* If nodeContent is null, match by name only
* If both null, or no match, or no children, returns null
* </pre>
* @param parentNodeKey NodeKey of node whose children will be checked
* @param nodeName String child name to look for
* @param nodeContent String child content to look for
* @return NodeKey for first (only!) child with the given name and/orcontent
BonNode bonNode = new BonNode();
if(parentNodeKey != null && (nodeContent != null || nodeName != null)){
String parentAKey = parentNodeKey.aKey;
Enumeration enumeration = this.elements();
while(enumeration.hasMoreElements()) {bonNode = (BonNode)enumeration.nextElement();
if(bonNode.nodeKey.bKey.equals(parentAKey)) { // it ischild node
if(nodeName != null && nodeContent != null) { //
Trang 13match name and content
if(nodeName.equals(bonNode.nodeName)) {if(nodeContent.equals(bonNode.nodeContent)) {
return bonNode.nodeKey;
}}}else if(nodeName != null) { // match name onlyif(nodeName.equals(bonNode.nodeName)) {return bonNode.nodeKey;
}}else if(nodeContent != null) { // match content onlyif(nodeContent.equals(bonNode.nodeContent)) {return bonNode.nodeKey;
}}}}}return null;
}/** Gets first child node with given attribute name and value pair
*
* @param parentNodeKey NodeKey of node whose children will be checked
* @param attributeName String name of attribute to look for
* @param attributeValue String value of named attribute to look for
* @return BonNode first child node (only!) with the given attribute name andvalue
*/
protected BonNode getChildNodeFromAttributeValue(NodeKey parentNodeKey,String attributeName, String attributeValue) {
// NOTE: only gets first child with value=name sought!
// It is used when attribute value must be unique among siblingnodes
// It can be easily changed to return a list of nodes instead,when needed
// In a ForestHashtable, children have nodeKey.bKey equal// to the parent’s nodeKey.aKey
BonNode bonNode = new BonNode();
if(parentNodeKey != null && attributeName != null && attributeValue !=null) {
String parentAKey = parentNodeKey.aKey;
Enumeration enumeration = this.elements();
while(enumeration.hasMoreElements()) {bonNode = (BonNode)enumeration.nextElement();
if(bonNode.nodeKey.bKey.equals(parentAKey)) { // it ischild node
if(attributeValue.equals(getAttributeValue(bonNode.nodeAttributes,
Trang 14attributeName))) {
return bonNode;
}}}}return null;
}/** Finds out if a given attribute exists in a nodeAttributes string
}else {return false;
}}/** Gets the value assigned to a given attribute in a nodeAttributes string
* whose value is returned
* @return null if value has no closing quote or if attributeName notfound,
* else value as string
int inx1 = allAttributes.indexOf(attributeName+”=\””);
if(inx1 > -1) { // found nameint inx2 = inx1 + (attributeName+”=\””).length();
Trang 15str1 = allAttributes.substring(inx2); // remove all up throughname, equals and opening quote
String str2 = new String(str1);
// tes\”ti\”ng” itemKey=”961755688708.961755643923.961755643913”dateStamp=”Fri Jun 23 12:21:39 2000”
boolean findingClosingQuote = true;
int inxAcc = 0;
while(findingClosingQuote) {int inx3 = str2.indexOf(“\””); // find next quotation markif(inx3 < 0) {
str1 = null;
break;
}// find next escaped quotation mark (if any) int inx4 = str2.indexOf(“\\\””);
if(inx4 > -1) { // found one// te\”st\”ing” goal=”961772451582”
// |// inx3// |// inx4if(inx3 == inx4 + 1) { // same one again// accumulate an index relative to// beginning of attribute valueinxAcc += inx3 + 1;
// remove all up to and including escapedquote
// | |// inx3 inx4if(inxAcc > 0) {
inx3 = inxAcc + ++inx3;
}str1 = str1.substring(0, inx3);
break; // success}
}else {if(inxAcc > 0) {// ng”
itemKey=”961755688708.961755643923.961755643913” dateStamp=”Fri Jun 23 12:21:392000”
Trang 16// ^inx3 = inxAcc + ++inx3;
}str1 = str1.substring(0, inx3);
break; // success}
}}else {log(sessionId, “err”, “ERROR in getAttributeValue()?
attributeName not found!?”);
}return str1;
}/** Gets a BonNode given its nodeKey
*
* @param nodeKey key of node to return
* @return BonNode for the given key, or null if node non-existent
}else {return null;
}}/** Allows editing name, attributes and/or content of a BonNode given itsnodeKey
* Can use this for cross-HttpSession node edits, so later may have toprevent that?
* If no BonNode exists for nodeKey, silently does nothing
* If all String arguments are null, silently does nothing
*
* @param nodeKey NodeKey of node to edit
* @param newNodeName String name of node after editing(unless null)
* @param newNodeAttributes String attributes of node after
* editing (unless null)
* @param newNodeContent String content of node after editing(unless null)
* @return NodeKey of BonNode edited, or null if no such node or
Trang 17synchronized(this) {BonNode bonNode = getBonNode(nodeKey);
if(bonNode != null) {boolean putNew = false;
if(newNodeName != null) {bonNode.nodeName = newNodeName;
putNew = true;
}if(newNodeAttributes != null) {bonNode.nodeAttributes = newNodeAttributes;
putNew = true;
}if(newNodeContent != null) {bonNode.nodeContent = newNodeContent;
putNew = true;
}if(putNew) {try {doDeleteNode(nodeKey);
}catch(Exception ee) {log(sessionId, “err”, “editBonNode() EXCEPTIONdeleting node!:” + ee.getMessage());
}try {retval = (NodeKey)this.put(nodeKey, bonNode);}
catch(Exception ee) {log(sessionId, “err”, “editBonNode() EXCEPTIONputting node!:” + ee.getMessage());
}} // else silently do nothing} // else silently do nothing}
return retval;
}/** Gets BonNode as an XML element in a string
*
* @param nodeKey NodeKey of node to edit
* @return String containing an XML element, or empty if no such node
String attributes = bonNode.nodeAttributes;
String content = bonNode.nodeContent;
if (attributes != null && attributes.trim().length() > 0) {xml = xml + “<” + name + “ “ + attributes;
}
Trang 18else {xml = xml + “<” + name;
}
if (content != null && content.trim().length() > 0) {xml = xml + “>” + content + “<\\” + name + “>”;
}else {xml = xml + “\\>”;
}}return xml;
}/** Helps with debugging <b>only</b>, applying <value>getXMLNode</value> toall elements
NodeKey nodeKey = bonNode.nodeKey;
outString = outString + this.getXmlNode(nodeKey);
}return outString;
}/** Returns a String containing all the trees in the ForestHashtable
* <pre>
* NOTES: Depending on the application and its current state,
* that can be a large String object!
* More selectivity will be added later
* for extracting XML subsets from the entire content
* This method assumes ForestHashtable includes
* zero or more well-formed XML SubTrees, or,
* more specifically, zero or more elements each
* either a leaf node, or else the root of a
* well-formed tree of elements
Trang 19elementCount = unFlagAllFlaggedElements(); // unhide all hiddenelements
Enumeration enumerationRN = this.elements();
lastRootNodeFound = false;
while (!lastRootNodeFound) {bonNode = getNextRootNode(enumerationRN);
if (bonNode == null) {lastRootNodeFound = true;
break;
}name = bonNode.nodeName;
}
if (content != null && content.trim().length() > 0) {xml = xml + “>” + content;
}else {xml = xml + “>”;
}Enumeration enumerationCRN = this.elements();
lastChildOfRootNodeFound = false;
while (!lastChildOfRootNodeFound) {bonNode = getNextChildOfRootNode(enumerationCRN);
if (bonNode == null) {lastChildOfRootNodeFound = true;
break;
}name = bonNode.nodeName;
nameChildOfRootNode = name;
attributes = bonNode.nodeAttributes;
content = bonNode.nodeContent;
// OUTPUT A CHILD OF A ROOTNODE
if (attributes != null && attributes.trim().length()
> 0) {
xml = xml + “<” + name + “ “ + attributes;}
else {xml = xml + “<” + name;
}
if (content != null && content.trim().length() > 0){
xml = xml + “>” + content;
Trang 20}else {xml = xml + “>”;
}xml = getNextChildOfNonRootNodeRecursively(xml,bonNode.nodeKey);
xml = xml + “</” + nameChildOfRootNode + “>”;
}xml = xml + “</” + nameRootNode + “>”;
}elementCount = unFlagAllFlaggedElements(); // unhide all hiddenelements
}return xml;
}/** Makes <value>flagged</value> member false for all BonNodes
BonNode bonNodeALL = null;
NodeKey nodeKeyALL = null;
nodeKeyALL = bonNodeALL.nodeKey;
if(nodeKeyALL != null) {count++;
bonNodeALL.flagged = false; // unhide each node, so wecan find it
}}return count;
}/** Gets next root node in an enumeration of BonNode instances
* Sets its <value>flagged</value> member true so it will not be foundagain
* Keeps its nodeKey.aKey in <value>currentRootNodeAKey</value>,
* Keeps its nodeKey.bKey in <value>currentRootNodeBKey</value>,
* Keeps its nodeKey.cKey in <value>currentRootNodeCKey</value>
*
* @param enumerationRN Enumeration of nodes
* @return BonNode next root node in <value>enumerationRN</value> or null
*/
protected BonNode getNextRootNode(Enumeration enumerationRN) {// NOTE: This process is an extremely inefficient simulation fordatabase model!
Trang 21BonNode bonNodeRN = null;
NodeKey nodeKeyRN = null;
boolean foundNextRootNode;
foundNextRootNode = false;
while(enumerationRN.hasMoreElements()) {bonNodeRN = (BonNode)enumerationRN.nextElement();
currentRootNodeBKey = nodeKeyRN.bKey;
currentRootNodeCKey = nodeKeyRN.cKey; // not needed,just for debugging
}break;
}}
if (!foundNextRootNode) {lastRootNodeFound = true;
bonNodeRN = null;
}return bonNodeRN;
}/** Gets next child-of-root node in an enumeration of BonNode instances
* Sets its <value>flagged</value> member true so it will not be foundagain,
* Keeps its nodeKey.aKey in <value>currentChildOfRootNodeAKey</value>,
* Keeps its nodeKey.bKey in <value>currentChildOfRootNodeBKey</value>,
* Keeps its nodeKey.cKey in <value>currentChildOfRootNodeCKey</value>
*
* @param enumerationCRN Enumeration of nodes
* @return BonNode next child-of-root node in <value>enumerationCRN</value>
or null
*/
protected BonNode getNextChildOfRootNode(Enumeration enumerationCRN) {// NOTE: This process is an extremely inefficient simulation fordatabase model!
BonNode bonNodeCRN = null;
NodeKey nodeKeyCRN = null;
boolean foundNextChildOfRootNode;
foundNextChildOfRootNode = false;
while(enumerationCRN.hasMoreElements()) {bonNodeCRN = (BonNode)enumerationCRN.nextElement();
nodeKeyCRN = bonNodeCRN.nodeKey;
// this is a test for child of current root node
Trang 22if((!bonNodeCRN.flagged) && (nodeKeyCRN.aKey != nodeKeyCRN.bKey)
&& (nodeKeyCRN.bKey == currentRootNodeAKey) && (nodeKeyCRN.cKey ==
currentChildOfRootNodeBKey = nodeKeyCRN.bKey;
currentChildOfRootNodeCKey = nodeKeyCRN.cKey; // fordebug only
}break;
}}
if (!foundNextChildOfRootNode) {lastChildOfRootNodeFound = true;
bonNodeCRN = null;
}return bonNodeCRN;
}/** Gets all descendants of a non-root node as one XML string
* Applies <value>getNextChildOfNonRootNode</value> recursively
if (bonNode == null) {lastChildOfNonRootNodeFound = true;
break;
}name = bonNode.nodeName;
nameChildOfNonRootNode = name;
Trang 23//log(sessionId, “”, “Child of NonRoot: “ + name);
attributes = bonNode.nodeAttributes;
content = bonNode.nodeContent;
// OUTPUT A CHILD OF A NON-ROOTNODE
if (attributes != null && attributes.trim().length() > 0) {xml = xml + “<” + name + “ “ + attributes;
}else {xml = xml + “<” + name;
}
if (content != null && content.trim().length() > 0) {xml = xml + “>” + content;
}else {xml = xml + “>”;
}xml = getNextChildOfNonRootNodeRecursively(xml,bonNode.nodeKey);
xml = xml + “</” + nameChildOfNonRootNode + “>”;
}return xml;
}/** Gets next child-of-non-root node in an enumeration of BonNode instances
* Sets its <value>flagged</value> member true so it will not be foundagain
*
* @param enumerationCNRN Enumeration of nodes
* @param nonRootNodeKey NodeKey of non-root node whose descendants are
BonNode bonNodeCNRN = null;
NodeKey nodeKeyCNRN = null;
boolean foundNextChildOfNonRootNode;
foundNextChildOfNonRootNode = false;
while(enumerationCNRN.hasMoreElements()) {bonNodeCNRN = (BonNode)enumerationCNRN.nextElement();
nodeKeyCNRN = bonNodeCNRN.nodeKey;
// this is a compound test for child of current non-root nodeString currentChildOfNonRootNodeAKey = nonRootNodeKey.aKey;String currentChildOfNonRootNodeBKey = nonRootNodeKey.bKey;String currentChildOfNonRootNodeCKey = nonRootNodeKey.cKey;boolean isChildOfNonRootNode;
isChildOfNonRootNode = false;
if(currentChildOfNonRootNodeAKey != null &&
currentChildOfNonRootNodeAKey.length() < 1) { // then this is grandchild of a
Trang 24root node
if((!bonNodeCNRN.flagged) && (nodeKeyCNRN.bKey ==
currentChildOfRootNodeAKey) && (nodeKeyCNRN.cKey == currentChildOfRootNodeBKey)) {
isChildOfNonRootNode = true;
}}else { // then this is great-grandchild or greater of a rootnode
if((!bonNodeCNRN.flagged) && (nodeKeyCNRN.bKey ==
currentChildOfNonRootNodeAKey) && (nodeKeyCNRN.cKey ==
currentChildOfNonRootNodeBKey)) {
isChildOfNonRootNode = true;
}}
if (isChildOfNonRootNode) {foundNextChildOfNonRootNode = true;
bonNodeCNRN.flagged = true; // hide this node, so we get
it only once
if(nodeKeyCNRN != null) {currentChildOfNonRootNodeAKey = nodeKeyCNRN.aKey;
currentChildOfNonRootNodeBKey = nodeKeyCNRN.bKey;
// not needed, just for debuggingcurrentChildOfNonRootNodeCKey = nodeKeyCNRN.cKey;
}break;
}}
if (!foundNextChildOfNonRootNode) {bonNodeCNRN = null;
}return bonNodeCNRN;
}}/** NodeNameHashtable only wraps java.util.Hashtable for access from JSP Customtag
* @author Westy Rockwell (wrockwell@tarent.de)
* @author Westy Rockwell (wrockwell@tarent.de)
Trang 25C.23 Filename: Projects\bonForum\src\de\ tarent\forum\NodeKey.java
* Note that in new NodeKey, no null xKey parts exist,
* but any xKey can be set to null later
* Note also that toString returns:
* <pre>
* a | a.b | a.b.c | b | b.c | a c | c
* (where a, b, c can be null in toString return value,
* but no trailing dots are allowed)
* /<pre>
* That allows using NodeKey for a.b.c and a.b type keys,
* while still allowing partially filled keys to be used
*
* <p>For further information visit the open source
* <A HREF=”http://www.bonForum.org”>BonForum Project on SourceForge</A>
* @author <A HREF=”mailto://email@bonforum.org”>Westy Rockwell</A>
this.bKey = “”;
this.cKey = “”;
}/** Converts a NodeKey to a String
* See doc comments for NodeKey class for details
*
*/
public String toString() {String key = this.aKey + “.” + this.bKey + “.” + this.cKey;if(key.equals(“ ”)) {
return(“”);
}