// package com.wallyflint.one_point_one.collections; // The package statement has been commented out so that every class can reside in the same package. This allows a java 1.1 demo applet to // run without throwing security exceptions. package net.sourceforge.sqlexplorer.sessiontree.model.utility; // import com.wallyflint.util.CharUtility; // The import statement above has been commented out so that every class can reside in the same package. This allows a java 1.1 demo applet to // run without throwing security exceptions. // Author Wally Flint: wally@wallyflint.com // With thanks to Michael Amster of webeasy.com for introducing me to the Ternary Search Tree, and providing some starting code. /** Implementation of ternary search tree. A Ternary Search Tree is a data structure that behaves * in a manner that is very similar to a HashMap. */ class TernarySearchTree { private TSTNode rootNode; private int defaultNumReturnValues = -1; private int lastNumberOfReturnValues; // convenience variable for matchPrefix and sortKeys methods /** Stores value in the TernarySearchTree. The value may be retrieved using key. * @param key A string that indexes the object to be stored. * @param value The object to be stored in the tree. */ public void put(String key, Object value) { getOrCreateNode(key).data = value; } /** * Retrieve the object indexed by key. * @param key A String index. * @return Object The object retrieved from the TernarySearchTree. */ public Object get(String key) { TSTNode node = getNode(key); if(node==null) return null; return node.data; } /** Removes value indexed by key. Also removes all nodes that are rendered unnecessary by the removal of this data. * @param key A string that indexes the object to be removed from the tree. */ public void remove(String key) { deleteNode(getNode(key)); } /** Sets default maximum number of values returned from matchPrefix, matchPrefixString, * matchAlmost, and matchAlmostString methods. * @param num The number of values that will be returned when calling the methods above. Set * this to -1 to get an unlimited number of return values. Note that * the methods mentioned above provide overloaded versions that allow you to specify the maximum number of * return values, in which case this value is temporarily overridden. */ public void setNumReturnValues(int num) { defaultNumReturnValues = (num < 0) ? -1 : num; } private int checkNumberOfReturnValues(int numReturnValues) { return ((numReturnValues < 0) ? -1 : numReturnValues); } /** Returns the number of values returned by the last call to matchAlmostString or matchPrefixString methods. * (This is really just for the purposes of the demo applet.) */ public int getLastNumReturnValues() { return lastNumberOfReturnValues; } /** Returns the Node indexed by key, or null if that node doesn't exist. Search begins at root node. * @param key An index that points to the desired node. * @return TSTNode The node object indexed by key. This object is an instance of an inner class * named TernarySearchTree.TSTNode. */ public TSTNode getNode(String key) { return getNode(key, rootNode); } /** Returns the Node indexed by key, or null if that node doesn't exist. Search begins at root node. * @param key An index that points to the desired node. * @param startNode The top node defining the subtree to be searched. * @return TSTNode The node object indexed by key. This object is an instance of an inner class * named TernarySearchTree.TSTNode. */ protected TSTNode getNode(String key, TSTNode startNode) { if(key == null || startNode == null || key.length() == 0) return null; TSTNode currentNode = startNode; int charIndex = 0; while(true) { if(currentNode == null) return null; int charComp = CharUtility.compareCharsAlphabetically(key.charAt(charIndex), currentNode.splitchar); if (charComp == 0) { charIndex++; if(charIndex == key.length()) return currentNode; currentNode = currentNode.relatives[TSTNode.EQKID]; } else if(charComp < 0) { currentNode = currentNode.relatives[TSTNode.LOKID]; } else { // charComp must be greater than zero currentNode = currentNode.relatives[TSTNode.HIKID]; } } } /** Returns alphabetical list of all keys in the tree that begin with prefix. Only keys for nodes having non-null data * are included in the list. * @param prefix Each key returned from this method will begin with the characters in prefix. * @return DoublyLinkedList An implementation of a LinkedList that is java 1.1 compatible. */ public DoublyLinkedList matchPrefix(String prefix) { return matchPrefix(prefix, defaultNumReturnValues); } /** Returns alphabetical list of all keys in the tree that begin with prefix. Only keys for nodes having non-null data * are included in the list. * @param prefix Each key returned from this method will begin with the characters in prefix. * @param numReturnValues The maximum number of values returned from this method. * @return DoublyLinkedList An implementation of a LinkedList that is java 1.1 compatible. */ public DoublyLinkedList matchPrefix(String prefix, int numReturnValues) { sortKeysNumReturnValues = checkNumberOfReturnValues(numReturnValues); sortKeysResult = new DoublyLinkedList(); TSTNode startNode = getNode(prefix); if(startNode == null) return sortKeysResult; if(startNode.data != null) { sortKeysResult.addLast(getKey(startNode)); sortKeysNumReturnValues--; } sortKeysList = true; sortKeysRecursion(startNode.relatives[TSTNode.EQKID]); return sortKeysResult; } /** Returns alphabetical list of all keys in the tree that begin with prefix. Only keys for nodes having non-null data * are included in the list. * @param prefix Each key returned from this method will begin with the characters in prefix. * @return String A string representation of all keys matching the argument prefix. Keys are delimited with the newline * char ('\n'). */ public String matchPrefixString(String prefix) { return matchPrefixString(prefix, defaultNumReturnValues); } /** Returns alphabetical list of all keys in the tree that begin with prefix. Only keys for nodes having non-null data * are included in the list. * @param prefix Each key returned from this method will begin with the characters in prefix. * @param numReturnValues The maximum number of values returned from this method. * @return String A string representation of all keys matching the argument prefix. Keys are delimited with the newline * char ('\n'). */ public String matchPrefixString(String prefix, int numReturnValues) { TSTNode startNode = getNode(prefix); if(startNode == null) return ""; //$NON-NLS-1$ sortKeysNumReturnValues = checkNumberOfReturnValues(numReturnValues); lastNumberOfReturnValues = sortKeysNumReturnValues; sortKeysBuffer = new StringBuffer(); if(startNode.data != null) { sortKeysBuffer.append(getKey(startNode) + "\n"); //$NON-NLS-1$ sortKeysNumReturnValues--; } sortKeysList = false; sortKeysRecursion(startNode.relatives[TSTNode.EQKID]); int bufferLength = sortKeysBuffer.length(); if(bufferLength > 0) sortKeysBuffer.setLength(bufferLength - 1); // delete the final \n lastNumberOfReturnValues = lastNumberOfReturnValues - sortKeysNumReturnValues; return sortKeysBuffer.toString(); } private DoublyLinkedList sortKeysResult; // convenience variable for matchPrefix and sortKeys methods private boolean sortKeysList; // convenience variable for matchPrefix, matchPrefixString and sortKeys methods private StringBuffer sortKeysBuffer; // convenience variable for matchPrefixString and sortKeys methods private int sortKeysNumReturnValues; // convenience variable for matchPrefix and sortKeys methods /* Returns keys sorted in alphabetical order. Includes currentNode and all nodes connected to currentNode. Sorted keys will * be appended to end of result list. (result may be empty when this method is invoked, but may not be null.) */ private void sortKeysRecursion(TSTNode currentNode) { if(currentNode == null) return; sortKeysRecursion(currentNode.relatives[TSTNode.LOKID]); if(sortKeysNumReturnValues == 0) return; if(currentNode.data != null) { if(sortKeysList) { sortKeysResult.addLast(getKey(currentNode)); } else { sortKeysBuffer.append(getKey(currentNode) + "\n"); //$NON-NLS-1$ } sortKeysNumReturnValues--; } sortKeysRecursion(currentNode.relatives[TSTNode.EQKID]); sortKeysRecursion(currentNode.relatives[TSTNode.HIKID]); } /** Returns keys sorted in alphabetical order. Includes startNode and all nodes connected to startNode. * Number of keys returned is limited to numReturnValues. To get a list that isn't limited in size, * set numReturnValues to -1. * @param startNode The top node defining the subtree to be searched. * @param numReturnValues The maximum number of values returned from this method. * @return DoublyLinkedList An implementation of a LinkedList that is java 1.1 compatible. */ protected DoublyLinkedList sortKeys(TSTNode startNode, int numReturnValues) { sortKeysNumReturnValues = checkNumberOfReturnValues(numReturnValues); sortKeysResult = new DoublyLinkedList(); sortKeysRecursion(startNode); return sortKeysResult; } public String sortKeysString(int numReturnValues) { return sortKeysString(rootNode, numReturnValues); } public String sortKeysString() { return sortKeysString(rootNode, defaultNumReturnValues); } /** Returns keys sorted in alphabetical order, returning a result of type String. Includes startNode * and all nodes connected to startNode. Number of keys returned is limited to numReturnValues. To get * a list that isn't limited in size, set numReturnValues to -1. * @param startNode The top node defining the subtree to be searched. * @param numReturnValues The maximum number of values returned from this method. * @return String A string representation of keys in alphabetical order. */ public String sortKeysString(TSTNode startNode, int numReturnValues) { if(startNode == null) return new String(""); //$NON-NLS-1$ sortKeysNumReturnValues = checkNumberOfReturnValues(numReturnValues); lastNumberOfReturnValues = sortKeysNumReturnValues; sortKeysBuffer = new StringBuffer(); if(startNode.data != null) { sortKeysBuffer.append(getKey(startNode) + "\n"); //$NON-NLS-1$ sortKeysNumReturnValues--; } sortKeysList = false; sortKeysRecursion(startNode); int bufferLength = sortKeysBuffer.length(); if(bufferLength > 0) sortKeysBuffer.setLength(bufferLength - 1); // delete the final \n lastNumberOfReturnValues = lastNumberOfReturnValues - sortKeysNumReturnValues; return sortKeysBuffer.toString(); } private DoublyLinkedList matchAlmostResult; // convenience variable for matchAlmost private StringBuffer matchAlmostBuffer; // convenience variable for matchAlmostString private boolean matchAlmostListAction; // convenience variable for matchAlmost and matchAlmostString private int matchAlmostNumReturnValues; // convenience variable for matchAlmost private String matchAlmostKey; // convenience variable for matchAlmost private int matchAlmostDiff; // convenience variable for matchAlmost private int maxMatchAlmostDiff = 4; // convenience variable for matchAlmost /** Returns a list of keys that almost match argument key. * Keys returned will have exactly diff characters that do not match the target key, * where diff is equal to the last value passed in as an argument to the setMatchAlmostDiff * method. If the matchAlmost method is called before the setMatchAlmostDiff method has been * called for the first time, then diff = 0. * @param key The target key. * @return DoublyLinkedList An implementation of a LinkedList that is java 1.1 compatible. */ public DoublyLinkedList matchAlmost(String key) { return matchAlmost(key, defaultNumReturnValues); } /** Returns a list of keys that almost match argument key. * Keys returned will have exactly diff characters that do not match the target key, * where diff is equal to the last value passed in as an argument to the setMatchAlmostDiff * method. If the matchAlmost method is called before the setMatchAlmostDiff method has been * called for the first time, then diff = 0. * @param key The target key. * @param numReturnValues The maximum number of values returned by this method. * @return DoublyLinkedList An implementation of a LinkedList that is java 1.1 compatible. */ protected DoublyLinkedList matchAlmost(String key, int numReturnValues) { matchAlmostListAction = true; matchAlmostNumReturnValues = checkNumberOfReturnValues(numReturnValues); matchAlmostResult = new DoublyLinkedList(); matchAlmostKey = key; matchAlmostRecursion(rootNode, 0, matchAlmostDiff); return matchAlmostResult; } /** Returns a String representation of keys that almost match argument key. * Keys returned will have exactly diff characters that do not match the target key, * where diff is equal to the last value passed in as an argument to the setMatchAlmostDiff * method. If the matchAlmost method is called before the setMatchAlmostDiff method has been * called for the first time, then diff = 0. * @param key The target key. * @return String A String representation of keys that almost match the target key. Keys are * delimited by the newline char ('\n'). */ public String matchAlmostString(String key) { return matchAlmostString(key, defaultNumReturnValues); } /** Returns a String representation of keys that almost match argument key. * Keys returned will have exactly diff characters that do not match the target key, * where diff is equal to the last value passed in as an argument to the setMatchAlmostDiff * method. If the matchAlmost method is called before the setMatchAlmostDiff method has been * called for the first time, then diff = 0. * @param key The target key. * @param numReturnValues The maximum number of values returned by this method. * @return String A String representation of keys that almost match the target key. Keys are * delimited by the newline char ('\n'). */ protected String matchAlmostString(String key, int numReturnValues) { matchAlmostListAction = false; matchAlmostNumReturnValues = checkNumberOfReturnValues(numReturnValues); lastNumberOfReturnValues = matchAlmostNumReturnValues; matchAlmostBuffer = new StringBuffer(); matchAlmostKey = key; matchAlmostRecursion(rootNode, 0, matchAlmostDiff); int bufferLength = matchAlmostBuffer.length(); if(bufferLength > 0) matchAlmostBuffer.setLength(bufferLength - 1); // delete the final \n lastNumberOfReturnValues = lastNumberOfReturnValues - matchAlmostNumReturnValues; return matchAlmostBuffer.toString(); } private void matchAlmostRecursion(TSTNode currentNode, int charIndex, int d) { if((currentNode == null) || (d < 0) || (matchAlmostNumReturnValues == 0) || (charIndex >= matchAlmostKey.length())) return; int charComp = CharUtility.compareCharsAlphabetically(matchAlmostKey.charAt(charIndex), currentNode.splitchar); // low branch if((d > 0) || (charComp < 0)) matchAlmostRecursion(currentNode.relatives[TSTNode.LOKID], charIndex, d); //equal branch int nextD = (charComp == 0) ? d : d - 1; if((matchAlmostKey.length() == charIndex + 1) && (nextD == 0) && (currentNode.data != null)) { // Note: the condition nextD == 0 causes keys to be included in the result only if they have exactly matchAlmostDiff number // of mismatched letters // If instead the condition nextD >= 0 is used, then all keys having up to and including matchAlmostDiff mismatched letters // will be included in the result (including a key that is exactly the same as the target string). if(matchAlmostListAction) { matchAlmostResult.addLast(getKey(currentNode)); } else { matchAlmostBuffer.append(getKey(currentNode) + "\n"); //$NON-NLS-1$ } matchAlmostNumReturnValues--; } matchAlmostRecursion(currentNode.relatives[TSTNode.EQKID], charIndex + 1, nextD); // hi branch if((d > 0) || (charComp > 0)) matchAlmostRecursion(currentNode.relatives[TSTNode.HIKID], charIndex, d); } /** Sets the number of characters by which words can differ from target word when * calling matchAlmost or matchAlmostString methods. Arguments * less than 1 will set the char difference to 1, and arguments greater than 4 * will set the char difference to 4. * @param diff The number of characters by which words can differ from target word. */ public void setMatchAlmostDiff(int diff) { if(diff < 0) { matchAlmostDiff = 0; } else if (diff > maxMatchAlmostDiff) { matchAlmostDiff = maxMatchAlmostDiff; } else { matchAlmostDiff = diff; } } /** Returns the Node indexed by key, creating that node if it doesn't exist, and creating any required. * intermediate nodes if they don't exist. * @param key A string that indexes the node that is returned. * @return TSTNode The node object indexed by key. This object is an instance of an inner class * named TernarySearchTree.TSTNode. */ protected TSTNode getOrCreateNode(String key) throws NullPointerException, IllegalArgumentException { if(key == null) throw new NullPointerException("attempt to get or create node with null key"); //$NON-NLS-1$ if(key.length() == 0) throw new IllegalArgumentException("attempt to get or create node with key of zero length"); //$NON-NLS-1$ if(rootNode==null) rootNode = new TSTNode(key.charAt(0), null); TSTNode currentNode = rootNode; int charIndex = 0; while(true) { int charComp = CharUtility.compareCharsAlphabetically(key.charAt(charIndex), currentNode.splitchar); if (charComp == 0) { charIndex++; if(charIndex == key.length()) return currentNode; if(currentNode.relatives[TSTNode.EQKID] == null) currentNode.relatives[TSTNode.EQKID] = new TSTNode(key.charAt(charIndex), currentNode); currentNode = currentNode.relatives[TSTNode.EQKID]; } else if(charComp < 0) { if(currentNode.relatives[TSTNode.LOKID] == null) currentNode.relatives[TSTNode.LOKID] = new TSTNode(key.charAt(charIndex), currentNode); currentNode = currentNode.relatives[TSTNode.LOKID]; } else { // charComp must be greater than zero if(currentNode.relatives[TSTNode.HIKID] == null) currentNode.relatives[TSTNode.HIKID] = new TSTNode(key.charAt(charIndex), currentNode); currentNode = currentNode.relatives[TSTNode.HIKID]; } } } /* Deletes the node passed in as an argument to this method. If this node has non-null data, then both the node and the data will be deleted. * Also deletes any other nodes in the tree that are no longer needed after the deletion of the node first passed in as an argument to this method. */ private void deleteNode(TSTNode nodeToDelete) { if(nodeToDelete == null) return; nodeToDelete.data = null; TSTNode n = nodeToDelete; while(n != null) { n = deleteNodeRecursion(n); } } private TSTNode deleteNodeRecursion(TSTNode currentNode) { // To delete a node, first set its data to null, then pass it into this method, then pass the node returned by this method into this method // (make sure you don't delete the data of any of the nodes returned from this method!) // and continue in this fashion until the node returned by this method is null. // The TSTNode instance returned by this method will be next node to be operated on by deleteNodeRecursion. // (This emulates recursive method call while avoiding the JVM overhead normally associated with a recursive method.) if(currentNode == null) return null; if(currentNode.relatives[TSTNode.EQKID] != null || currentNode.data != null) return null; // can't delete this node if it has a non-null eq kid or data TSTNode currentParent = currentNode.relatives[TSTNode.PARENT]; // if we've made it this far, then we know the currentNode isn't null, but its data and equal kid are null, so we can delete the current node // (before deleting the current node, we'll move any lower nodes higher in the tree) boolean lokidNull = currentNode.relatives[TSTNode.LOKID] == null; boolean hikidNull = currentNode.relatives[TSTNode.HIKID] == null; // now find out what kind of child current node is int childType; if(currentParent.relatives[TSTNode.LOKID] == currentNode) { childType = TSTNode.LOKID; } else if(currentParent.relatives[TSTNode.EQKID] == currentNode) { childType = TSTNode.EQKID; } else if(currentParent.relatives[TSTNode.HIKID] == currentNode) { childType = TSTNode.HIKID; } else { // if this executes, then current node is root node rootNode = null; return null; } if(lokidNull && hikidNull) { // if we make it to here, all three kids are null and we can just delete this node currentParent.relatives[childType] = null; return currentParent; } // if we make it this far, we know that EQKID is null, and either HIKID or LOKID is null, or both HIKID and LOKID are NON-null if(lokidNull) { currentParent.relatives[childType] = currentNode.relatives[TSTNode.HIKID]; currentNode.relatives[TSTNode.HIKID].relatives[TSTNode.PARENT] = currentParent; return currentParent; } if(hikidNull) { currentParent.relatives[childType] = currentNode.relatives[TSTNode.LOKID]; currentNode.relatives[TSTNode.LOKID].relatives[TSTNode.PARENT] = currentParent; return currentParent; } int deltaHi = currentNode.relatives[TSTNode.HIKID].splitchar - currentNode.splitchar; int deltaLo = currentNode.splitchar - currentNode.relatives[TSTNode.LOKID].splitchar; int movingKid; TSTNode targetNode; // if deltaHi is equal to deltaLo, then choose one of them at random, and make it "further away" from the current node's splitchar if(deltaHi == deltaLo) { if(Math.random() < 0.5) { deltaHi++; } else { deltaLo++; } } if(deltaHi > deltaLo) { movingKid = TSTNode.HIKID; targetNode = currentNode.relatives[TSTNode.LOKID]; } else { movingKid = TSTNode.LOKID; targetNode = currentNode.relatives[TSTNode.HIKID]; } while(targetNode.relatives[movingKid] != null) targetNode = targetNode.relatives[movingKid]; // now targetNode.relatives[movingKid] is null, and we can put the moving kid into it. targetNode.relatives[movingKid] = currentNode.relatives[movingKid]; // now we need to put the target node where the current node used to be currentParent.relatives[childType] = targetNode; targetNode.relatives[TSTNode.PARENT] = currentParent; if(!lokidNull) currentNode.relatives[TSTNode.LOKID] = null; if(!hikidNull) currentNode.relatives[TSTNode.HIKID] = null; // note that the statements above ensure currentNode is completely dereferenced, and so it will be garbage collected return currentParent; } /** An inner class of TernarySearchTree that represents a node in the tree. */ protected class TSTNode { protected static final int PARENT = 0, LOKID = 1, EQKID = 2, HIKID = 3; // index values for accessing relatives array protected char splitchar; protected TSTNode[] relatives = new TSTNode[4]; protected Object data; protected TSTNode(char splitchar, TSTNode parent) { this.splitchar = splitchar; relatives[PARENT] = parent; } } private StringBuffer getKeyBuffer = new StringBuffer(); // convenience variable for getKey method /** Returns the key that indexes the node argument. * @param node The node whose index is to be calculated. * @return String The string that indexes the node argument. */ protected String getKey(TSTNode node) { getKeyBuffer.setLength(0); getKeyBuffer.append(node.splitchar); TSTNode currentNode, lastNode; currentNode = node.relatives[TSTNode.PARENT]; lastNode = node; while(currentNode != null) { if(currentNode.relatives[TSTNode.EQKID] == lastNode) getKeyBuffer.append(currentNode.splitchar); lastNode = currentNode; currentNode = currentNode.relatives[TSTNode.PARENT]; } getKeyBuffer.reverse(); return getKeyBuffer.toString(); } private int numNodes; // convenience variable for numNodes methods private boolean checkData; // convenience variable for numNodes methods /** Returns the total number of nodes in the tree. Counts nodes whether or not they have data. * @return int The total number of nodes in the tree. */ public int numNodes() { return numNodes(rootNode); } /** Returns the total number of nodes in the subtree below and including startingNode. Counts nodes whether or not they have data. * @param startingNode The top node of the subtree. The node that defines the subtree. * @return int The total number of nodes in the subtree. */ protected int numNodes(TSTNode startingNode) { numNodes = 0; checkData = false; recursiveNodeCalculator(startingNode); return numNodes; } /** Returns the number of nodes in the tree that have non-null data. * @return int The number of nodes in the tree that have non-null data. */ public int numDataNodes() { return numDataNodes(rootNode); } /** Returns the number of nodes in the subtree below and including startingNode. Counts only nodes that have non-null data. * @param startingNode The top node of the subtree. The node that defines the subtree. * @return int The total number of nodes in the subtree. */ protected int numDataNodes(TSTNode startingNode) { numNodes = 0; checkData = true; recursiveNodeCalculator(startingNode); return numNodes; } private void recursiveNodeCalculator(TSTNode currentNode) { if(currentNode == null) return; recursiveNodeCalculator(currentNode.relatives[TSTNode.LOKID]); recursiveNodeCalculator(currentNode.relatives[TSTNode.EQKID]); recursiveNodeCalculator(currentNode.relatives[TSTNode.HIKID]); if(checkData) { if(currentNode.data != null) numNodes++; } else { numNodes++; } } /** Prints entire tree structure to standard output, beginning with the root node and workind down. */ protected void printTree() { System.out.println(""); //$NON-NLS-1$ if(rootNode == null) { System.out.println("tree is empty"); //$NON-NLS-1$ return; } System.out.println("Here's the entire tree structure:"); //$NON-NLS-1$ printNodeRecursion(rootNode); } /** Prints subtree structure to standard output, beginning with startingNode and workind down. */ protected void printTree(TSTNode startingNode) { System.out.println(""); //$NON-NLS-1$ if(rootNode == null) { System.out.println("subtree is empty"); //$NON-NLS-1$ return; } System.out.println("Here's the entire subtree structure:"); //$NON-NLS-1$ printNodeRecursion(startingNode); } /** Recursive method used to print out tree or subtree structure. */ private void printNodeRecursion(TSTNode currentNode) { if(currentNode == null) return; System.out.println(""); //$NON-NLS-1$ System.out.println("( keys are delimited by vertical lines: |example key| )"); //$NON-NLS-1$ System.out.println("info for node |" + getKey(currentNode) + "| node data: " + currentNode.data); //$NON-NLS-1$ //$NON-NLS-2$ if(currentNode.relatives[TSTNode.PARENT] == null) { System.out.println("parent null"); //$NON-NLS-1$ } else { System.out.println("parent key |" + getKey(currentNode.relatives[TSTNode.PARENT]) + "| parent data: " + currentNode.relatives[TSTNode.PARENT].data); //$NON-NLS-1$ //$NON-NLS-2$ } if(currentNode.relatives[TSTNode.LOKID] == null) { System.out.println("lokid null"); //$NON-NLS-1$ } else { System.out.println("lokid key |" + getKey(currentNode.relatives[TSTNode.LOKID]) + "| lo kid data: " + currentNode.relatives[TSTNode.LOKID].data); //$NON-NLS-1$ //$NON-NLS-2$ } if(currentNode.relatives[TSTNode.EQKID] == null) { System.out.println("eqkid null"); //$NON-NLS-1$ } else { System.out.println("eqkid key |" + getKey(currentNode.relatives[TSTNode.EQKID]) + "| equal kid data: " + currentNode.relatives[TSTNode.EQKID].data); //$NON-NLS-1$ //$NON-NLS-2$ } if(currentNode.relatives[TSTNode.HIKID] == null) { System.out.println("hikid null"); //$NON-NLS-1$ } else { System.out.println("hikid key |" + getKey(currentNode.relatives[TSTNode.HIKID]) + "| hi kid data: " + currentNode.relatives[TSTNode.HIKID].data); //$NON-NLS-1$ //$NON-NLS-2$ } printNodeRecursion(currentNode.relatives[TSTNode.LOKID]); printNodeRecursion(currentNode.relatives[TSTNode.EQKID]); printNodeRecursion(currentNode.relatives[TSTNode.HIKID]); } }