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 }