1// BEGIN LICENSE BLOCK 2// Version: CMPL 1.1 3// 4// The contents of this file are subject to the Cisco-style Mozilla Public 5// License Version 1.1 (the "License"); you may not use this file except 6// in compliance with the License. You may obtain a copy of the License 7// at www.eclipse-clp.org/license. 8// 9// Software distributed under the License is distributed on an "AS IS" 10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11// the License for the specific language governing rights and limitations 12// under the License. 13// 14// The Original Code is The ECLiPSe Constraint Logic Programming System. 15// The Initial Developer of the Original Code is Cisco Systems, Inc. 16// Portions created by the Initial Developer are 17// Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. 18// 19// Contributor(s): 20// 21// END LICENSE BLOCK 22 23package com.parctechnologies.eclipse.visualisation; 24import java.util.Map; 25import java.util.HashMap; 26import java.util.List; 27import java.util.LinkedList; 28import java.util.Iterator; 29import java.io.Serializable; 30import java.lang.ref.*; 31 32/** 33 * A classd designed to act as a Symbolic Reference to objects. 34 * 35 * Intended primarily to be used in the recording and playback of commands. 36 * 37 */ 38public class SymRef implements Serializable { 39 /** defines the root of the symbolic heirarchy */ 40 public static final SymRef ROOT = new SymRef(); 41 42 protected List path; 43 44 /** 45 * Private constructor for the ROOT SymRef 46 */ 47 private SymRef() { 48 this.path = new LinkedList(); 49 this.path.add("ROOT"); 50 } 51 52 53 /** 54 * Private constructor to turn a path into a SymRef 55 */ 56 private SymRef(List path) { 57 this.path = path; 58 } 59 60 /** 61 * Holds the root of the tree 62 */ 63 private static Node root = new Node(); 64 65 66 /** 67 * Constructs a top level SymRef. 68 * This is shorthand for calling 69 * <CODE>SymRef(value, SymRef.ROOT, childName)</CODE> 70 * @param value The object to be refered to by this SymRef 71 * @param childName An object identifying the child 72 */ 73 public SymRef(SymRefable value, Object childName) { 74 this.path = Node.put(childName, value); 75 } 76 77 /** 78 * Constructs a child SymRef 79 * @param value The object to be refered to by this SymRef 80 * @param parentRef The SymRef of the parent object (if applicable) 81 * @param childName An object identifying the child 82 */ 83 public SymRef(SymRefable value, SymRef parentRef, Object childName) { 84 this.path = Node.addChild(parentRef.path, childName, value); 85 } 86 87 88 /** 89 * @return The SymRef of the root top level object 90 */ 91 public SymRef getRoot() { 92 // make a new path with just the top most element of this symref 93 List newPath = new LinkedList(); 94 newPath.add(this.path.get(0)); 95 return new SymRef(newPath); 96 } 97 98 99 /** 100 * Returns the string representation of the symRef 101 */ 102 public String toString() { 103 StringBuffer sb = new StringBuffer(); 104 for(Iterator it = path.iterator(); it.hasNext(); ) { 105 sb.append("/").append(it.next()); 106 } 107 return sb.toString(); 108 } 109 110 /** 111 * Handles the equality testing of symbolic keys 112 */ 113 public boolean equals(Object o) { 114 if (o instanceof SymRef) { 115 return toString().equals(((SymRef)o).toString()); 116 } else { 117 return false; 118 } 119 } 120 121 /** 122 * Handles the hashing of SymRefs 123 */ 124 public int hashCode() { 125 return toString().hashCode(); 126 } 127 128 /** 129 * Looks up the value of a SymRef 130 */ 131 public static synchronized SymRefable get(SymRef key) throws InvalidSymRefException { 132 if ( Node.containsKey(key.path) ) { 133 SymRefable value = (Node.findNode(key.path).get()); 134 if (value != null) { 135 return value; 136 } else { 137 if (DebuggingSupport.logMessages) { 138 DebuggingSupport. 139 logMessage(key, 140 "SymRef has been garbage collected=" + 141 key); 142 } 143 } 144 } 145 throw new InvalidSymRefException(key); 146 } 147 148 149 150 /** 151 * Internal SymRef tree structure 152 */ 153 private static class Node { 154 public Node parent; 155 public Object id; 156 public Reference val_ref; 157 158 public Map children; 159 160 public Node() { 161 this(null, null, null); 162 } 163 164 public Node(Node parent, 165 Object id, 166 Object value) { 167 this.parent = parent; 168 this.id = id; 169 this.val_ref = new WeakReference(value); 170 children = new HashMap(); 171 if (parent != null && parent.children != null) { 172 parent.children.put(id, this); 173 } 174 } 175 176 /** 177 * Returns the object stored in this node 178 */ 179 public final SymRefable get() { 180 return (SymRefable)(val_ref.get()); 181 } 182 183 /** 184 * Insert a value into the root of the tree, possibly overwritting 185 * existing sub-trees 186 */ 187 public static List put(Object id, Object val) { 188 if (DebuggingSupport.logMessages) { 189 DebuggingSupport. 190 logMessage(null, 191 "put id=" + id + 192 " val=" + val); 193 } 194 Node newNode = new Node(root, id, val); 195 List newPath = new LinkedList(); 196 newPath.add(id); 197 return newPath; 198 } 199 200 /** 201 * Add a value as a child of the given path. 202 * This call will not overwrite any existing children 203 * @return The path to the child 204 */ 205 public static List addChild(List parentPath, Object id, Object val) { 206 Node parent = root.findNode(parentPath); 207 if (DebuggingSupport.logMessages) { 208 DebuggingSupport. 209 logMessage(null, 210 "addChild parentPath=" + parentPath + 211 " id=" + id + 212 " val=" + val); 213 } 214 if (parent == null) { 215 if (DebuggingSupport.logMessages) { 216 DebuggingSupport. 217 logMessage(null, 218 "addChild unable to find parent path=" + 219 parentPath); 220 } 221 return null; 222 } 223 String newId = id.toString() + "_" + parent.children.size(); 224 Node newNode = new Node(parent, newId, val); 225 List newPath = new LinkedList(parentPath); 226 newPath.add(newId); 227 return newPath; 228 } 229 230 231 /** 232 * @return true iff the given key(path) exists in the tree 233 */ 234 public static boolean containsKey(List list) { 235 return (root.matchKey(list) == list.size()); 236 } 237 238 /** 239 * returns the number of elements of the path (list) that were 240 * sucessfully matched 241 */ 242 public static int matchKey(List list) { 243 Node node = root; 244 final int size = list.size(); 245 int i = 0 ; 246 for(Iterator it = list.iterator(); it.hasNext(); i++ ) { 247 Object id = it.next(); 248 // check for match in current nodes children 249 if (node.children.containsKey(id)) { 250 node = (Node)node.children.get(id); 251 } else { 252 break; 253 } 254 } 255 return i; 256 } 257 258 /** 259 * returns the node at the end of this path, if it was 260 * sucessfully matched 261 */ 262 public static Node findNode(List list) { 263 Node node = root; 264 final int size = list.size(); 265 for(Iterator it = list.iterator(); it.hasNext(); ) { 266 Object id = it.next(); 267 // check for match in current nodes children 268 if (node.children.containsKey(id)) { 269 node = (Node)node.children.get(id); 270 } else { 271 return null; 272 } 273 } 274 return node; 275 } 276 277 278 279 280 } 281 282} 283 284