gt:                                     done = false;   // jump out to special case
context: /*
   * $Id$
   *
   * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package com.sun.javatest;
  
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.NoSuchElementException;
  import java.util.Stack;
  import java.util.Vector;
  
  import com.sun.javatest.util.Debug;
  import com.sun.javatest.util.I18NResourceBundle;
  
  
  /**
   * This class processes the iteration with the following priorities:
   * <ol>
   * <li>return any tests which were explicitly supplied at construction time
   * <li>serially exhaust all tests underneath each node specified at construction time
   * <li>return tests found by walking the tree in an ``in-order'' manner
   * </ul>
   * The iterator operates by immediately returning explicit tests.  If not explicit
   * tests are available, the ``next'' methods always keeps one ahead of the current
   * request so that it can answer the ``has more?'' question.  On each ``next'' request,
   * it searches for the next test it can distribute.
   */
  class TRT_Iterator implements TestResultTable.TreeIterator {
  /*
   * path[pathIdx] points to the next available TestResult
   * finished indicates that we're done
   */
      /**
       * Initialize the outQueue and result stats.  This is here only to share
       * construction code and does not create a usable object.
       */
      protected TRT_Iterator() {
          outQueue = new LinkedList();
          resultStats = new int[Status.NUM_STATES];
          nodeIndex = -1;
          currFrame = null;
      }
  
      TRT_Iterator(TestResultTable.TreeNode node) {
          this();
          nodes = new TestResultTable.TreeNode[1];
          nodes[0] = node;
          init(nodes);
  
          if (debug)
              Debug.println("Created TreeIterator without filters, one initial node.");
      }
  
      /**
       * Enumerate elements below and including the specified node.
       *
       * @param filters Which filters to apply to the tests found.  May be null.
       */
      TRT_Iterator(TestResultTable.TreeNode node, TestFilter[] filters) {
          this();
          this.filters = filters;
          nodes = new TestResultTable.TreeNode[1];
          nodes[0] = node;
          init(nodes);
  
          if (debug)
              Debug.println("Created TreeIterator with filters and one initial node.");
      }
  
      /**
       * Enumerate elements below and including the specified nodes.
       * This is mainly here to support initial URLs.
       *
       * @param nodes Which nodes to enumerate.  May be null or zero length.  This will be
       *        shallow copied.
       * @param filters Which filters to apply to the tests found.  May be null.
       */
      TRT_Iterator(TestResultTable.TreeNode[] nodes, TestFilter[] filters) {
          this();
          this.filters = filters;
  
          if (nodes != null) {
              this.nodes = new TestResultTable.TreeNode[nodes.length];
              System.arraycopy(nodes, 0, this.nodes, 0, nodes.length);
          }
  
          init(this.nodes);
  
          if (debug)
              Debug.println("Created TreeIterator with filters and initial nodes.");
      }
  
      /**
       * Enumerate elements below and including the specified nodes.
       * This is mainly here to support initial URLs.
       *
       * @param nodes Which nodes to enumerate.  May be null or zero length.  This will be
       *        shallow copied.
       * @param trs   A given set of TestResults which should be enumerated.  These
       *              are sent through the filters.  This set of tests is shallow copied.
       * @param filters Which filters to apply to the tests found.  May be null.
       */
      TRT_Iterator(TestResultTable.TreeNode[] nodes, TestResult[] trs, TestFilter[] filters) {
          this();
          this.filters = filters;
  
          if (trs != null && trs.length != 0) {
              ArrayList names = new ArrayList();
  
              // prime the outqueue with the given tests
              for (int i = 0; i < trs.length; i++) {
                  try {
                      if (wouldAccept(trs[i]) == -1) {
                          outQueue.addLast(trs[i]);
                          names.add(trs[i].getDescription().getRootRelativeURL());
                      }
                      else { }
                  }   // try
                  catch (TestResult.Fault f) {
                  }
              }   // for
  
              initialTests = new String[names.size()];
              names.toArray(initialTests);
          }
  
          if (nodes != null) {
              this.nodes = new TestResultTable.TreeNode[nodes.length];
              System.arraycopy(nodes, 0, this.nodes, 0, nodes.length);
          }
  
          init(this.nodes);
  
          if (debug)
              Debug.println("Created TreeIterator with filters, nodes and initial TR set.");
      }
  
      // --- Enumerator interface  ---
      @Override
      public boolean hasMoreElements() {
          synchronized(outQueueLock) {
              if (!finished) {
                  return true;
              }
              else
                  return false;
          }   // sync
      }
  
      @Override
      public Object nextElement() {
          TestResult val = null;
          synchronized (outQueueLock) {
              if (hasMoreElements()) {
                  val = outQueue.removeFirst();
  
                  findNext();
  
                  // XXX refresh val from TRT?
                  // the test in the TRT may have changed since it was selected by
                  resultStats[val.getStatus().getType()]++;
                  return val;
              }
              else {
                  throw new NoSuchElementException(i18n.getString("trt.noElements"));
              }
          }       // sync
  
      }
  
      // --- Iterator interface ---
      @Override
      public boolean hasNext() {
          return hasMoreElements();
      }
  
      @Override
      public Object next() {
          return nextElement();
      }
  
      /**
       * Do not call this method.
       *
       * @throws UnsupportedOperationException Not available for this iterator.
       */
      @Override
      public void remove() {
          throw new UnsupportedOperationException("Cannot remove from TestResultTable through iterator.  Do not call this method.");
      }
  
      // --- Statistics info ---
      /**
       * Find out how many tests have been processed.
       * This count includes tests which have been filtered out and is an
       * instantaneous snapshot.
       */
      public int getProcessedCount() {
          return absoluteCount;
      }
  
      @Override
      public int getRejectCount() {
          return rejectCount;
      }
  
      /**
       * Determines whether or not filtered out tests are recorded for future use
       * or not.  This does not affect operation of <tt>getRejectCount()</tt>.
       * By default, this setting is false.
       * @param state True if rejected tests should be logged, false otherwise.
       */
      @Override
      public void setRecordRejects(boolean state) {
          recordRejects = state;
  
          if (recordRejects == true) {
              // only create new objects if they don't exist
              // users can turn on and off this feature if they want
              if (filteredTRs == null)
                  filteredTRs = new Hashtable(10);        // not likely to have > 10 filters
  
              if (fo == null)
                  fo = new FilterObserver();
          }
      }
  
      @Override
      public int[] getResultStats() {
          int[] copy = new int[resultStats.length];
          System.arraycopy(resultStats, 0, copy, 0, resultStats.length);
  
          return copy;
      }
  
      /**
       * Find out which filters rejected which tests.
       * The data is valid at any point in time; hasNext() does not have to
       * be false.  Note that filters are evaluated in the order shown in getFilters()
       * and that the statistics only registered the <em>first</em> filter that rejected
       * the test; there may be additional filters which would also reject any given
       * test.
       * <p>
       * The hashtable has keys of TestResults, and values which are TestFilters.
       * Because of CompositeFilters, the set of filters found in the ``values''
       * is not necessarily equivalent to those given by getFilters().
       *
       * @return Array as described or null if no tests have been rejected yet.
       */
      @Override
      public Hashtable getFilterStats() {
          if (filteredTRs == null)
              return null;
          else {
              // create shallow, reformatted version of stored data
              // keys are TRs, values are TestFilters.
              Hashtable out = new Hashtable();
  
              synchronized (rejLock) {
                  Enumeration keys = filteredTRs.keys();
                  // each key is a TestFilter
                  while (keys.hasMoreElements()) {
                      // could cast this to TestFilter, but why?
                      Object thisKey = keys.nextElement();
  
                      // the HT value is a Vector or TRs
                      Iterator it = ((Vector)(filteredTRs.get(thisKey))).iterator();
  
                      while (it.hasNext()) {
                          out.put(it.next(), thisKey);
                      }   // inner while
                  }   // outer while
              }   // sync
  
              return out;
          }
      }
  
      /**
       * Find out what the effective filters are.
       *
       * @return Null if there are no active filters.
       */
      @Override
      public TestFilter[] getFilters() {
          if (filters == null || filters.length == 0)
              return null;
          else {
              // shallow copy it
              TestFilter[] copy = new TestFilter[filters.length];
              System.arraycopy(filters, 0, copy, 0, filters.length);
              return copy;
          }
  
      }
  
      /**
       * Find out what the effective initial URLs for this iterator are.
       * The returned array can be any combination of URLs to individual tests
       * or URLs to directories.  Remember these are URLs, so the paths are not
       * platform specific.
       * <p>
       * Note: this method isn't free, cache the data if you need to call this
       * method repeatedly.  The initial URLs will not change over time for any
       * particular instance of this class.
       *
       * @return Null if no specific nodes or tests were requested.  Any array of the initial
       *         URLs otherwise.
       */
      @Override
      public String[] getInitialURLs() {
          String[] list = null;
          ArrayList urls = new ArrayList();
  
          // can we correctly show the effective urls from here?  what about TCs?
          if (nodes != null) {
              for (int i = 0; i < nodes.length; i++)
                  urls.add(TestResultTable.getRootRelativePath(nodes[i]));
          }
  
          if (initialTests != null) {
              for (int i = 0; i < initialTests.length; i++)
                  urls.add(initialTests[i]);
          }
  
          if (urls.size() > 0) {
              list = new String[urls.size()];
              urls.toArray(list);
          }
  
          return list;
      }
  
      @Override
      public Object peek() {
          if (hasNext())
              synchronized (outQueueLock) {
                  return outQueue.getFirst();
              }   // sync
          else
              return null;
      }
  
      @Override
      public boolean isPending(TestResult node) {
          if (!hasMoreElements())
              return false;
  
          String testName = node.getTestName();
          String partial = testName;
  
          synchronized (outQueueLock) {
              // compare to outgoing test result objects
              if (outQueue.size() > 0)
                  for (int i = 0; i < outQueue.size(); i++) {
                      TestResult tr = outQueue.get(i);
                      if (tr.getTestName().equals(testName)) {
                          return true;
                      }
                  }   // for
          }       // sync
  
          boolean result = false;
          boolean done = false;
          if (nodes != null) {
              for (int i = nodeIndex; i < nodes.length; i++) {
                  String nodePath = TestResultTable.getRootRelativePath(nodes[i]);
  
                  if (testName.startsWith(nodePath)) {
                      if (i > nodeIndex) {    // haven't processed that init. node yet
                          result = true;
                          done = true;
                          break;
                      }
                      else if (i < nodeIndex) { // passed this location already
                          result = false;
                          done = true;
                          break;
                      }
                      else {                  // working at this location
                          // if this is a root iterator, we always end up in this
                          // case
  
                          // remove the "known" part of the location
                          // if we are at node lang/foo, the test name
                          // lang/foo/bar/baz.html becomes bar/baz.html and
                          // we continue...
                          if (nodePath.length() != 0)     // root nodePath is ""
                              partial = testName.substring(nodePath.length()+1);
  
                          // traverse the stack
                          // XXX this could be made more efficient
                          for (int j = 0; j < stack.size(); j++) {
                              String dir = TestResultTable.getDirName(partial);
                              partial = TestResultTable.behead(partial);
  
                              // XXX special cases here...
  
                              // it would be nicer if we could just cast to TRT.TreeNode
                              PseudoFrame frame = (PseudoFrame)(stack.elementAt(j));
                              TRT_TreeNode tn = (TRT_TreeNode)(frame.getNode());
                              int pos = tn.getNodeIndex(dir, false);
                              int currIndex = frame.getCurrentIndex();
  
                              // special case where test is at top of testsuite
                              if (testName.indexOf("/") == -1)
                                  pos = currIndex;
  
                              // possible outcomes:
                              // 1 test in question can not be found
                              // 2 test path indicates that we've passed it's position
                              // 3 we are working along the test's path
                              // 4 the test's path is ahead of our position
  
                              // not present or already past
                              if (pos == -1 || (pos < currIndex)) {   // case 1-2
                                  result = false;
                                  done = true;
                                  break;
                              }
                              else if (pos == frame.getCurrentIndex()) { // case 3
                                  if (partial.indexOf('/') != -1) {
                                      // more depth needed, go around again
                                      continue;
                                  }
                                  else if (j+1 == stack.size()) {     // test should be in currFrame
  