1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.exec; 28 29 import com.sun.javatest.TRT_TreeNode; 30 import com.sun.javatest.util.Debug; 31 32 import com.sun.javatest.TestResult; 33 import com.sun.javatest.TestResultTable; 34 import java.util.ArrayList; 35 import java.util.Comparator; 36 import java.util.Enumeration; 37 import java.util.Iterator; 38 import java.util.concurrent.atomic.AtomicBoolean; 39 import javax.swing.event.TreeModelEvent; 40 import javax.swing.tree.TreeNode; 41 42 /** 43 * 44 * Representation of a node in the GUI tree representing the visible test structure. 45 */ 46 public class TT_BasicNode extends TT_TreeNode { 47 48 TT_BasicNode(TT_BasicNode parent, TRT_TreeNode tn, Comparator<String> comp) { 49 this.comp = comp; 50 this.parent = parent; 51 this.tn = tn; 52 } 53 // ------- interface methods -------- 54 public Enumeration<TT_TreeNode> children() { 55 updateNode(); 56 57 ArrayList<TT_TreeNode> copy = null; 58 synchronized (children) { 59 // shallow copy child list? 60 copy = new ArrayList<>(children); 61 } 62 63 final Iterator<TT_TreeNode> it = copy.iterator(); 64 return new Enumeration<TT_TreeNode>() { 65 66 public boolean hasMoreElements() { 67 return it.hasNext(); 68 } 69 70 public TT_TreeNode nextElement() { 71 return it.next(); 72 } 73 }; 74 } 75 76 public boolean getAllowsChildren() { 77 return true; 78 } 79 80 public TreeNode getChildAt(int arg0) { 81 if (children == null) { 82 return null; 83 } 84 updateNode(); 85 synchronized (children) { 86 return children.get(arg0); 87 } 88 } 89 90 public int getChildCount() { 91 updateNode(); 92 93 synchronized (children) { 94 return children.size(); 95 } 96 } 97 98 public int getIndex(TreeNode arg0) { 99 if (children == null) { 100 return -1; 101 } 102 updateNode(); 103 synchronized (children) { 104 return children.indexOf(arg0); 105 } 106 } 107 108 public int getIndex(TT_TestNode arg) { 109 updateNode(); 110 synchronized (children) { 111 return children.indexOf(arg); 112 } 113 } 114 115 public TreeNode getParent() { 116 return parent; 117 } 118 119 public boolean isLeaf() { 120 throw new UnsupportedOperationException("Not supported yet."); 121 } 122 // --------- basic interface ------------- 123 boolean isRoot() { 124 return (parent == null); 125 } 126 127 /** 128 * What should the name of this node be when shown in a user interface? 129 * May or may not be the same as the short name. 130 * @see #getShortName() 131 * @return Shortest possible name of this node when displayed. 132 */ 133 String getDisplayName() { 134 // could allow override of this 135 // maybe translate underscores to spaces? 136 return getShortName(); 137 } 138 139 String getLongDescription() { 140 // should be the long path to the folder, or custom 141 return null; 142 } 143 144 /** 145 * String for use whenever you need a basic name for this node. You can 146 * assume that this name is unique within any node. 147 * @return Short name for this node, containing no forward slashes or 148 * spaces. 149 */ 150 String getShortName() { 151 return tn.getName(); 152 } 153 154 /** 155 * Get the long internal representation of this location. 156 * @return Null if the node is the root, else a forward slash separated 157 * path. 158 */ 159 String getLongPath() { 160 if (parent == null) // root 161 { 162 return null; 163 } 164 StringBuffer sb = new StringBuffer(getShortName()); 165 TT_BasicNode spot = parent; 166 while (spot.parent != null) { 167 sb.insert(0, "/"); 168 sb.insert(0, spot.getShortName()); 169 spot = spot.parent; 170 } 171 return sb.toString(); 172 } 173 174 TT_TreeNode findByName(String name) { 175 updateNode(); 176 177 synchronized (children) { 178 if (children.size() == 0) { 179 return null; 180 } 181 for (TT_TreeNode node : children) { 182 if (name.equals(node.getShortName())) { 183 return node; 184 } 185 } // for 186 } 187 188 return null; 189 } 190 191 TT_TestNode findByName(TestResult tr) { 192 updateNode(); 193 194 synchronized (children) { 195 if (children.size() == 0) { 196 return null; 197 } 198 } 199 200 String name = tr.getTestName(); 201 synchronized (children) { 202 for (TT_TreeNode node : children) { 203 if (name.equals(node.getLongPath())) { 204 return (TT_TestNode) node; 205 } 206 } // for 207 } 208 209 return null; 210 } 211 212 TRT_TreeNode getTableNode() { 213 return tn; 214 } 215 216 TreeModelEvent removeTest(TestResult tr) { 217 if (updateNode()) { 218 return null; 219 } 220 221 TT_TestNode tn = findByName(tr); 222 if (tn == null) 223 return null; 224 225 synchronized (children) { 226 int index = children.indexOf(tn); 227 children.remove(tn); 228 return new TreeModelEvent( 229 this, getNodePath(), new int[]{index}, new Object[]{tn}); 230 //return index; 231 } 232 } 233 234 int removeNode(TT_TreeNode node) { 235 updateNode(); 236 237 synchronized (children) { 238 int index = children.indexOf(node); 239 if (index >= 0) { 240 children.remove(index); 241 return index; 242 } 243 } 244 return -1; 245 } 246 247 TreeModelEvent replaceTest(TestResult tr, boolean insert) { 248 if (updateNode()) { 249 return null; 250 } 251 252 synchronized (children) { 253 TT_TestNode tn = findByName(tr); 254 if (tn != null) { 255 int pos = getIndex(tn); 256 children.remove(pos); 257 children.add(pos, new TT_TestNode(this, tr)); 258 return new TreeModelEvent(this, getNodePath()); 259 } 260 else { 261 if (insert) { 262 return addTest(new TT_TestNode(this, tr), comp); 263 } 264 else 265 return null; 266 } 267 } 268 } 269 270 TreeModelEvent addTest(final TT_TestNode tn, Comparator<String> sortComparator) { 271 if (updateNode()) { 272 //Debug.println("Ignoring add of " + tn.getDisplayName()); 273 return null; 274 } 275 276 // if (sortComparator == null) 277 // synchronized (children) { 278 // children.add(0, tn); 279 // return 0; 280 // } 281 // 282 synchronized (children) { 283 int result = -1; 284 if (children.size() == 0) { 285 children.add(tn); 286 result = 0; 287 } else if (sortComparator == null) { 288 // consider inserting it at the end instead? 289 children.add(0, tn); 290 result = 0; 291 } else { 292 result = recursiveIns(0, children.size()-1, tn, tn.getDisplayName(), 293 sortComparator); 294 } 295 296 return new TreeModelEvent( 297 this, getNodePath(), new int[]{result}, new Object[]{tn}); 298 } 299 } 300 301 // --------- internal methods ------------ 302 private int recursiveIns(final int lPos, final int rPos, 303 final TT_TreeNode tn, 304 final String dispName, 305 final Comparator<String> sortComparator) { 306 synchronized (children) { // should already be locked! 307 int diff = rPos-lPos; 308 int pos = (diff/2) + lPos; 309 String posStr = children.get(pos).getDisplayName(); 310 int res = sortComparator.compare(dispName, posStr); 311 if (res == 0) { 312 children.set(pos, tn); 313 if (debug) { 314 Debug.println("Duplicate test, replaced - " + tn.getDisplayName()); 315 //Exception e = new Exception(); e.printStackTrace(System.err); 316 } 317 return pos; 318 } 319 320 if (diff <= 0) 321 if (res < 0) { 322 children.add(lPos, tn); 323 return lPos; 324 } else if (res > 0) { 325 children.add(lPos+1, tn); 326 return lPos+1; 327 } 328 329 if (res < 0) { 330 return recursiveIns(lPos, pos-1, tn, dispName, sortComparator); 331 } 332 else { 333 return recursiveIns(pos+1, rPos, tn, dispName, sortComparator); 334 } 335 } 336 } 337 338 private boolean updateNode() { 339 // is update needed? 340 synchronized (children) { 341 if (isUpdated.compareAndSet(false, true)) { 342 updateNode0(); 343 return true; 344 } 345 else 346 return false; 347 } // sync 348 } 349 350 /** 351 * If update needed, this method does it by getting info from TRT. 352 */ 353 private void updateNode0() { 354 TestResultTable.TreeNode[] nodes = tn.getTreeNodes(); 355 addNodes(nodes); 356 357 TestResult[] tests = tn.getTestResults(); 358 addTests(tests); 359 } 360 361 int[] addNodes(TestResultTable.TreeNode[] nodes) { 362 if (nodes == null || nodes.length == 0) { 363 return null; 364 } 365 366 updateNode(); 367 368 int[] newPositions = new int[nodes.length]; 369 for (int i = 0; i < nodes.length; i++) { 370 TRT_TreeNode tnode = (TRT_TreeNode)nodes[i]; 371 372 synchronized (children) { 373 newPositions[i] = findDuplicateNode(tnode); 374 if (newPositions[i] >= 0) { 375 continue; // add it 376 } 377 TT_BasicNode newNode = new TT_BasicNode(this, tnode, comp); 378 if (children.size() == 0) { 379 children.add(newNode); 380 newPositions[i] = 0; 381 } 382 else if (comp == null) { 383 children.add(newNode); 384 newPositions[i] = children.size()-1; 385 } 386 else { 387 newPositions[i] = recursiveIns(0, children.size()-1, newNode, 388 newNode.getDisplayName(), comp); 389 } 390 } 391 } // for 392 393 return newPositions; 394 } 395 396 private void addTests(TestResult[] tests) { 397 if (tests == null || tests.length == 0) { 398 return; 399 } 400 for (int i = 0; i < tests.length; i++) { 401 TT_TestNode tn = new TT_TestNode(this, tests[i]); 402 403 synchronized (children) { 404 if (children.size() == 0 || comp == null) { 405 children.add(tn); 406 } else { 407 int result = recursiveIns(0, children.size()-1, tn, 408 tn.getDisplayName(), comp); 409 } 410 } // sync 411 } 412 } 413 414 /** 415 * Assumes that children is already locked (synchronized on). 416 * @param node The node to look for. 417 * @return Position of duplicate, -1 if not found. 418 */ 419 private int findDuplicateNode(TRT_TreeNode node) { 420 Iterator<TT_TreeNode> it = children.iterator(); 421 while (it.hasNext()) { 422 TreeNode tn = it.next(); 423 if (tn instanceof TT_BasicNode) { 424 if (((TT_BasicNode) tn).tn == node) { 425 return children.indexOf(tn); 426 } 427 } 428 // skipping TT_TestNodes 429 } 430 431 return -1; // no dup 432 } 433 434 private int insertNewBranch(TT_BasicNode tn) { 435 // need to sort 436 synchronized (children) { 437 if (children.size() == 0) { 438 children.add(tn); 439 return 0; 440 } 441 else if (comp == null) { 442 children.add(tn); 443 return children.size()-1; 444 } else { 445 int result = recursiveIns(0, children.size()-1, tn, 446 tn.getDisplayName(), comp); 447 // Iterator<TT_TreeNode> it = children.iterator(); 448 // while (it.hasNext()) 449 // System.out.println(" " + it.next().getDisplayName()); 450 451 return result; 452 } 453 } 454 } 455 456 private final ArrayList<TT_TreeNode> children = new ArrayList<TT_TreeNode>(); 457 private TRT_TreeNode tn; 458 private Comparator<String> comp; 459 private final AtomicBoolean isUpdated = new AtomicBoolean(false); 460 private boolean debug = Debug.getBoolean(TT_BasicNode.class); 461 }