You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by cu...@locus.apache.org on 2000/11/02 00:27:05 UTC

cvs commit: xml-xalan/test/java/src/org/apache/qetest/xslwrapper LotusXSLWrapper.java ProcessorWrapper.java ProcessorWrapper.properties SaxonWrapper.class SaxonWrapper.java TraxWrapper.java XTWrapper.class XTWrapper.java XalanWrapper.java package.html

curcuru     00/11/01 15:27:04

  Added:       test/java/bin ant.jar antRun antRun.bat
               test/java/src/org/apache/qetest/trax LoggingURIResolver.java
                        ProcessorAPITest.java ResultAPITest.java
                        TemplatesAPITest.java TestThreads.java
                        TransformerAPITest.java package.html
               test/java/src/org/apache/qetest/xalanj1 ParamTest.java
                        package.html
               test/java/src/org/apache/qetest/xsl CConformanceTest.java
                        ConformanceDirRules.java
                        ConformanceErrFileRules.java
                        ConformanceFileRules.java ConformanceTest.java
                        LoggingEntityResolver.java
                        LoggingSAXErrorHandler.java PerformanceTest.java
                        XHTComparator.java XHTFileCheckService.java
                        XSLDirectoryIterator.java XSLProcessorTestBase.java
                        XSLTestfileInfo.java package.html
               test/java/src/org/apache/qetest/xslwrapper
                        LotusXSLWrapper.java ProcessorWrapper.java
                        ProcessorWrapper.properties SaxonWrapper.class
                        SaxonWrapper.java TraxWrapper.java XTWrapper.class
                        XTWrapper.java XalanWrapper.java package.html
  Log:
  Xalan Java-based test automation
  
  Revision  Changes    Path
  1.1                  xml-xalan/test/java/bin/ant.jar
  
  	<<Binary file>>
  
  
  1.1                  xml-xalan/test/java/bin/antRun
  
  Index: antRun
  ===================================================================
  #! /bin/sh
  
  # Args: DIR command
  cd $1
  CMD=$2
  shift
  shift
  
  if test -f $CMD.sh; then
    CMD="sh $CMD.sh"
  fi
  
  echo $CMD $@ | sh
  
  
  
  1.1                  xml-xalan/test/java/bin/antRun.bat
  
  Index: antRun.bat
  ===================================================================
  @echo off
  
  cd %1
  set ANT_RUN_CMD=%2
  shift
  shift
  
  %ANT_RUN_CMD% %1 %2 %3 %4 %5 %6 %7 %8 %9
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/trax/LoggingURIResolver.java
  
  Index: LoggingURIResolver.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * LoggingURIResolver.java
   *
   */
  package org.apache.qetest.trax;
  
  import org.apache.qetest.*;
  import org.apache.trax.URIResolver;
  import org.apache.trax.TransformException;
  
  import org.w3c.dom.Node;
  
  import org.xml.sax.InputSource;
  import org.xml.sax.XMLReader;
  
  //-------------------------------------------------------------------------
  
  /**
   * Implementation of URIResolver that logs all calls.
   * Currently just provides default service; returns null.
   * @author shane_curcuru@lotus.com
   * @version $Id: LoggingURIResolver.java,v 1.1 2000/11/01 23:26:54 curcuru Exp $
   */
  public class LoggingURIResolver implements URIResolver
  {
  
      /** No-op ctor since it's often useful to have one. */
      public LoggingURIResolver(){}
  
      /**
       * Ctor that calls setReporter automatically.  
       *
       * NEEDSDOC @param r
       */
      public LoggingURIResolver(Reporter r)
      {
          setReporter(r);
      }
  
      /** Our Reporter, who we tell all our secrets to. */
      private Reporter reporter;
  
      /**
       * Accesor methods for our Reporter.  
       *
       * NEEDSDOC @param r
       */
      public void setReporter(Reporter r)
      {
          if (r != null)
              reporter = r;
      }
  
      /**
       * Accesor methods for our Reporter.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public Reporter getReporter()
      {
          return (reporter);
      }
  
      /** Prefixed to all reporter msg output. */
      private String prefix = "UR:";
  
      /** Counters for how many URIs we've 'resolved'. */
      private int URICtr = 0;
  
      /**
       * Accesor methods for URI counter.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getURICtr()
      {
          return URICtr;
      }
  
      /**
       * Cheap-o string representation of our state.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getCounterString()
      {
          return (prefix + "URIs: " + getURICtr());
      }
  
      /** Cheap-o string representation of last entity we resolved. */
      private String lastURI = null;
  
      /**
       * NEEDSDOC Method setLastURI 
       *
       *
       * NEEDSDOC @param s
       */
      protected void setLastURI(String s)
      {
          lastURI = s;
      }
  
      /**
       * Accessor for string representation of last entity we resolved.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getLastURI()
      {
          return lastURI;
      }
  
      /** What loggingLevel to use for reporter.logMsg(). */
      private int level = Reporter.DEFAULT_LOGGINGLEVEL;
  
      /**
       * Accesor methods; don't think it needs to be synchronized.  
       *
       * NEEDSDOC @param l
       */
      public void setLoggingLevel(int l)
      {
          level = l;
      }
  
      /**
       * Accesor methods; don't think it needs to be synchronized.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getLoggingLevel()
      {
          return level;
      }
  
      /**
       * Cheap-o utility to get a string value.
       * @todo improve string return value
       *
       * NEEDSDOC @param i
       *
       * NEEDSDOC ($objectName$) @return
       */
      private String getString(InputSource i)
      {
          return i.toString();
      }
  
      /**
       * Implement this method: just returns null for now.
       * Also saves the last entity for later retrieval, and counts
       * how many entities we've 'resolved' overall.
       * @todo have a settable property to actually return as the InputSource
       * @param inputSource The value returned from the EntityResolver.
       * @return (null currently) a DOM node that represents the resolution of the URI
       * @exception TransformException never thrown currently
       */
      public Node getDOMNode(InputSource inputSource) throws TransformException
      {
  
          URICtr++;
  
          setLastURI(getString(inputSource));
  
          if (reporter != null)
          {
              reporter.logMsg(level,
                              prefix + getLastURI() + " " + getCounterString());
          }
  
          return null;
      }
  
      /**
       * Implement this method: just returns null for now.
       * Also saves the last entity for later retrieval, and counts
       * how many entities we've 'resolved' overall.
       * @todo have a settable property to actually return as the InputSource
       * @param inputSource The value returned from the EntityResolver.
       * @return (null currently) a SAX2 parser to use with the InputSource.
       * @exception TransformException never thrown currently
       */
      public XMLReader getXMLReader(InputSource inputSource)
              throws TransformException
      {
  
          URICtr++;
  
          setLastURI(getString(inputSource));
  
          if (reporter != null)
          {
              reporter.logMsg(level,
                              prefix + getLastURI() + " " + getCounterString());
          }
  
          return null;
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/trax/ProcessorAPITest.java
  
  Index: ProcessorAPITest.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * ProcessorAPITest.java
   *
   */
  package org.apache.qetest.trax;
  
  import org.apache.qetest.*;
  import org.apache.qetest.xsl.*;
  
  // Just import the whole trax package; note the packaging is likely to change
  import org.apache.trax.*;
  
  // Use Serializer classes from Xalan distro
  import org.apache.serialize.OutputFormat;
  import org.apache.serialize.Serializer;
  import org.apache.serialize.SerializerFactory;
  
  // Needed SAX classes
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.Parser;
  import org.xml.sax.helpers.ParserAdapter;
  import org.xml.sax.helpers.XMLReaderFactory;
  import org.xml.sax.XMLReader;
  import org.xml.sax.ContentHandler;
  
  // Needed DOM classes
  import org.w3c.dom.Node;
  
  // javax JAXP classes for parser pluggability
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  
  // java classes
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.InputStream;
  import java.io.FileReader;
  import java.io.FileWriter;
  import java.io.Reader;
  import java.io.IOException;
  
  import java.util.Properties;
  
  //-------------------------------------------------------------------------
  
  /**
   * Basic API coverage test for the Processor class of TRAX.
   * Currently assumes you're using Xalan 2.x.
   * @author shane_curcuru@lotus.com
   */
  public class ProcessorAPITest extends XSLProcessorTestBase
  {
  
      /**
       * Cheap-o filename for various output files.
       *
       */
      protected OutputNameManager outNames;
  
      /** Cheap-o filename set for both API tests and exampleSimple. */
      protected XSLTestfileInfo simpleTest = new XSLTestfileInfo();
  
      /** Cache the relevant system property. */
      protected String saveXSLTProp = null;
  
      /** NEEDSDOC Field TRAX_PROCESSOR          */
      public static final String TRAX_PROCESSOR = "trax.processor";
  
      /** NEEDSDOC Field XSLT          */
      public static final String XSLT = "xslt";
  
      /** NEEDSDOC Field TRAX_PROCESSOR_XSLT          */
      public static final String TRAX_PROCESSOR_XSLT = TRAX_PROCESSOR + "."
                                                           + XSLT;
  
      /** Allow user to override our default of Xalan 2.x processor classname. */
      public static final String XALAN_CLASSNAME =
          "org.apache.xalan.processor.StylesheetProcessor";
  
      /** NEEDSDOC Field PROCESSOR_CLASSNAME          */
      protected String PROCESSOR_CLASSNAME = "processorClassname";
  
      /** NEEDSDOC Field processorClassname          */
      protected String processorClassname = XALAN_CLASSNAME;
  
      /** NEEDSDOC Field INVALID_NAME          */
      public static final String INVALID_NAME = "invalid.name";
  
      /** NEEDSDOC Field PROPERTY_LEXICAL_HANDLER          */
      public static final String PROPERTY_LEXICAL_HANDLER =
          "http://xml.org/sax/properties/lexical-handler";
  
      /** NEEDSDOC Field FEATURE_DOM_INPUT          */
      public static final String FEATURE_DOM_INPUT =
          "http://xml.org/trax/features/dom/input";
  
      /** NEEDSDOC Field FEATURE_SAX_INPUT          */
      public static final String FEATURE_SAX_INPUT =
          "http://xml.org/trax/features/sax/input";
  
      /** NEEDSDOC Field XAPI          */
      public static final String XAPI = "trax";
  
      /** Default ctor initializes test name, comment, numTestCases. */
      public ProcessorAPITest()
      {
  
          numTestCases = 4;  // REPLACE_num
          testName = "ProcessorAPITest";
          testComment =
              "Basic API coverage test for the Processor class of TRAX";
      }
  
      /**
       * Initialize this test - Set names of xml/xsl test files, cache system property.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileInit(Properties p)
      {
  
          // Used for all tests; just dump files in xapi subdir
          File outSubDir = new File(outputDir + File.separator + XAPI);
  
          if (!outSubDir.mkdirs())
              reporter.logWarningMsg("Could not create output dir: "
                                     + outSubDir);
  
          outNames = new OutputNameManager(outputDir + File.separator + XAPI
                                           + File.separator + testName, ".out");
  
          // Used for API coverage and exampleSimple
          String testBasePath = inputDir + File.separator + XAPI
                                + File.separator;
          String goldBasePath = goldDir + File.separator + XAPI
                                + File.separator;
  
          simpleTest.xmlName = testBasePath + "ProcessorAPITest.xml";
          simpleTest.inputName = testBasePath + "ProcessorAPITest.xsl";
          simpleTest.goldName = goldBasePath + "ProcessorAPITest.out";
  
          // Cache trax system property
          saveXSLTProp = System.getProperty(TRAX_PROCESSOR_XSLT);
  
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + saveXSLTProp);
  
          // Check if user wants to use a processor other than Xalan 2.x
          processorClassname = testProps.getProperty(PROCESSOR_CLASSNAME,
                                                     XALAN_CLASSNAME);
  
          reporter.logInfoMsg(PROCESSOR_CLASSNAME + " property is: "
                              + processorClassname);
  
          return true;
      }
  
      /**
       * Cleanup this test - reset the cached system property trax.processor.xslt.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileClose(Properties p)
      {
  
          if (saveXSLTProp == null)
          {
              System.getProperties().remove(TRAX_PROCESSOR_XSLT);
          }
          else
          {
              System.getProperties().put(TRAX_PROCESSOR_XSLT, saveXSLTProp);
          }
  
          return true;
      }
  
      /**
       * TRAX Processor: cover static methods (creating processors).
       * Simple coverage tests; most should work with any trax processor <b>but</b>
       * we explicitly set the trax.processor.xslt property to the Xalan 2.x class.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase1()
      {
  
          reporter.testCaseInit(
              "TRAX Processor: cover static methods (creating processors)");
  
          // To be thorough, we should call Processor.getPlatformDefaultProcessor() and log it's value
          //  But: it's not in the trax spec yet (Sep-00)
          // Some negative test cases: bad processor types
          boolean bit = true;
  
          try
          {
              Object o = Processor.newInstance(INVALID_NAME);
          }
          catch (ProcessorFactoryException pfe)
          {
              reporter.checkPass("newInstance(" + INVALID_NAME
                                 + ")1 properly threw: " + pfe.toString());
  
              bit = false;
          }
  
          if (bit)
              reporter.checkFail("newInstance(" + INVALID_NAME
                                 + ")1 did not throw exception");
  
          System.getProperties().put(TRAX_PROCESSOR + "." + INVALID_NAME,
                                     "this.class.does.not.exist");
  
          bit = true;
  
          try
          {
              Object o = Processor.newInstance(INVALID_NAME);
          }
          catch (ProcessorFactoryException pfe)
          {
              reporter.checkPass("newInstance(" + INVALID_NAME
                                 + ")2 properly threw: " + pfe.toString());
  
              bit = false;
          }
  
          if (bit)
              reporter.checkFail("newInstance(" + INVALID_NAME
                                 + ")2 did not throw exception");
  
          // Negative test case: default is not valid, property is not valid
          System.getProperties().put(TRAX_PROCESSOR_XSLT,
                                     "this.class.does.not.exist");
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property set to: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
          Processor.setPlatformDefaultProcessor(INVALID_NAME);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(INVALID_NAME)");
  
          bit = true;
  
          try
          {
              Object o = Processor.newInstance(XSLT);
          }
          catch (ProcessorFactoryException pfe)
          {
              reporter.checkPass(
                  "newInstance(xslt)3 of invalid default and prop properly threw: "
                  + pfe.toString());
  
              bit = false;
          }
  
          if (bit)
              reporter.checkFail(
                  "newInstance(xslt)3 of invalid default and prop did not throw exception");
  
          // Negative test case: default is valid, property is not valid
          Processor.setPlatformDefaultProcessor(processorClassname);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(processorClassname)");
  
          bit = true;
  
          try
          {
              Object o = Processor.newInstance(XSLT);
          }
          catch (ProcessorFactoryException pfe)
          {
              reporter.checkPass(
                  "newInstance(xslt)4 of valid default and invalid prop properly threw: "
                  + pfe.toString());
  
              bit = false;
          }
  
          if (bit)
              reporter.checkFail(
                  "newInstance(xslt)4 of valid default and invalid prop did not throw exception");
  
          // Positive test cases: property is valid Xalan 2.x, default is not
          Processor p = null;
  
          System.getProperties().put(TRAX_PROCESSOR_XSLT, processorClassname);
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property set to: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
          Processor.setPlatformDefaultProcessor(INVALID_NAME);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(INVALID_NAME)");
  
          try
          {
              p = Processor.newInstance(XSLT);
  
              reporter.checkBool(
                  (p != null), true,
                  "newInstance(xslt)5 of invalid default but prop:Xalan 2.x");
          }
          catch (ProcessorFactoryException pfe)
          {
              reporter.checkFail(
                  "newInstance(xslt)5 of invalid default but prop:Xalan 2.x threw: "
                  + pfe.toString());
          }
  
          p = null;
  
          // Positive test case: property is null, default is valid
          System.getProperties().remove(TRAX_PROCESSOR_XSLT);
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
          Processor.setPlatformDefaultProcessor(processorClassname);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(processorClassname)");
  
          try
          {
              p = Processor.newInstance(XSLT);
  
              reporter.checkBool(
                  (p != null), true,
                  "newInstance(xslt)6 of default:Xalan 2.x but prop:null");
          }
          catch (ProcessorFactoryException pfe)
          {
              reporter.checkFail(
                  "newInstance(xslt)6 of default:Xalan 2.x but prop:null threw: "
                  + pfe.toString());
          }
  
          // Reset the property to what it was before the testCase started
          if (saveXSLTProp == null)
          {
              System.getProperties().remove(TRAX_PROCESSOR_XSLT);
          }
          else
          {
              System.getProperties().put(TRAX_PROCESSOR_XSLT, saveXSLTProp);
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * TRAX Processor: cover set/get instance methods.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase2()
      {
  
          reporter.testCaseInit(
              "TRAX Processor: cover set/get instance methods");
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
          Processor.setPlatformDefaultProcessor(processorClassname);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(processorClassname)");
  
          Processor p = null;
  
          try
          {
              p = Processor.newInstance(XSLT);
  
              if (p == null)
              {
                  reporter.checkFail(
                      "Processor is null; cannot continue testcase");
  
                  return false;
              }
          }
          catch (ProcessorFactoryException pfe)
          {  // No-op
          }
  
          // Testing SAX get/set features and properties - both negative and positive tests
          boolean bit = true;
  
          try
          {
              p.setFeature(INVALID_NAME, true);
          }
          catch (Exception e)
          {
              reporter.checkPass("setFeature(" + INVALID_NAME
                                 + ", true) properly threw: " + e.toString());
  
              bit = false;
          }
  
          if (bit)
              reporter.checkFail("setFeature(" + INVALID_NAME
                                 + ", true) did not throw exception");
  
          bit = true;
  
          try
          {
              bit = p.getFeature(INVALID_NAME);
          }
          catch (Exception e)
          {
              reporter.checkPass("getFeature(" + INVALID_NAME
                                 + ") properly threw: " + e.toString());
  
              bit = false;
          }
  
          if (bit)
              reporter.checkFail("getFeature(" + INVALID_NAME
                                 + ") did not throw exception");
  
          bit = false;
  
          try
          {
  
              // We presume this is supported, since we use it later on
              bit = p.getFeature(FEATURE_DOM_INPUT);
  
              reporter.check(bit, true,
                             "getFeature(" + FEATURE_DOM_INPUT
                             + ") must be true for Xalan 2.x");
          }
          catch (Exception e)
          {
              reporter.checkFail("getFeature(" + FEATURE_DOM_INPUT
                                 + ") threw: " + e.toString());
          }
  
          bit = false;
  
          try
          {
  
              // We presume this is supported, since we use it later on
              bit = p.getFeature(FEATURE_SAX_INPUT);
  
              reporter.check(bit, true,
                             "getFeature(" + FEATURE_SAX_INPUT
                             + ") must be true for Xalan 2.x");
          }
          catch (Exception e)
          {
              reporter.checkFail("getFeature(" + FEATURE_SAX_INPUT
                                 + ") threw: " + e.toString());
          }
  
          // TODO investigate why setting this feature false throws SAXNotRecognizedException
          //      Maybe we can't set the native features?
          // p.setFeature(FEATURE_SAX_INPUT, false);
          // Test errorHandler set/get api and functionality
          LoggingSAXErrorHandler errHandler =
              new LoggingSAXErrorHandler(reporter);
  
          errHandler.setThrowWhen(errHandler.THROW_ON_FATAL);
          p.setErrorHandler(errHandler);
          reporter.checkObject(p.getErrorHandler(), errHandler,
                               "set/getErrorHandler API test");
  
          // See if we can get the errHandler to have something output
          try
          {
              Templates t = p.process(new InputSource(""));
          }
          catch (Exception e)
          {
              reporter.logStatusMsg("process(blank string) threw: "
                                    + e.toString());
          }
  
          reporter.logStatusMsg("process(blank string) errorHandler :"
                                + errHandler.getLastError() + ": "
                                + errHandler.getCounterString());
  
          try
          {
              Templates t = p.process(new InputSource(INVALID_NAME));
          }
          catch (Exception e)
          {
              reporter.logStatusMsg("process(" + INVALID_NAME + ") threw: "
                                    + e.toString());
          }
  
          reporter.logStatusMsg("process(" + INVALID_NAME + ") errorHandler :"
                                + errHandler.getLastError() + ": "
                                + errHandler.getCounterString());
  
          try
          {
              Templates t = p.process(new InputSource(simpleTest.inputName));
  
              // TODO: cause an error parsing the XML or something
          }
          catch (Exception e)
          {
              reporter.logStatusMsg("process(" + simpleTest.inputName
                                    + ") threw: " + e.toString());
          }
  
          reporter.logStatusMsg("process(" + simpleTest.inputName
                                + ") errorHandler :"
                                + errHandler.getLastError() + ": "
                                + errHandler.getCounterString());
  
          // Cheap set/get test for EntityResolver
          LoggingEntityResolver entRes = new LoggingEntityResolver(reporter);
  
          p.setEntityResolver(entRes);
          reporter.checkObject(p.getEntityResolver(), entRes,
                               "set/getEntityResolver API test");
          reporter.checkAmbiguous(
              "// TODO add validation for set/getEntityResolver");
  
          LoggingURIResolver URIRes = new LoggingURIResolver(reporter);
  
          p.setURIResolver(URIRes);
          reporter.checkObject(p.getURIResolver(), URIRes,
                               "set/getURIResolver API test");
          reporter.checkAmbiguous(
              "// TODO add validation for set/getURIResolver");
  
          try
          {
              XMLReader reader = XMLReaderFactory.createXMLReader();
  
              p.setXMLReader(reader);
              reporter.checkObject(p.getXMLReader(), reader,
                                   "set/getXMLReader API test");
              reporter.checkAmbiguous(
                  "// TODO add validation for set/getXMLReader");
          }
          catch (SAXException se)
          {
              reporter.checkErr("set/getXMLReader API test: " + se.toString());
              reporter.logThrowable(reporter.STATUSMSG, se,
                                    "set/getXMLReader API test threw:");
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * TRAX Processor: cover process* instance methods.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase3()
      {
  
          reporter.testCaseInit(
              "TRAX Processor: cover process* instance methods");
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
          Processor.setPlatformDefaultProcessor(processorClassname);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(processorClassname)");
  
          Processor p = null;
  
          try
          {
              p = Processor.newInstance(XSLT);
  
              if (p == null)
              {
                  reporter.checkFail(
                      "Processor is null; cannot continue testcase");
  
                  return false;
              }
          }
          catch (ProcessorFactoryException pfe)
          {  // No-op
          }
  
          // Processor.process() basic coverage test
          String messageStylesheet = inputDir + File.separator + XAPI
                                     + File.separator
                                     + "ProcessorAPIMessage.xsl";
          LoggingSAXErrorHandler errHandler;
  
          errHandler = new LoggingSAXErrorHandler(reporter);
  
          errHandler.setThrowWhen(errHandler.THROW_ON_FATAL);
  
          try
          {
              p.setErrorHandler(errHandler);
  
              Templates t = p.process(new InputSource(messageStylesheet));
  
              reporter.check((t != null), true,
                             "Created basic Templates object");
  
              Transformer f = t.newTransformer();
  
              reporter.check((f != null), true,
                             "Created basic Transformer object");
              f.transform(new InputSource(simpleTest.xmlName),
                          new Result(new FileWriter(outNames.nextName())));
              reporter.check(((new File(outNames.currentName())).exists()),
                             true, "transform() created an output file");
              reporter.logStatusMsg("messageStylesheet just created output:"
                                    + outNames.currentName());
          }
          catch (Exception e)
          {
              reporter.checkErr("process() basic test threw: " + e.toString());
              reporter.logThrowable(reporter.STATUSMSG, e,
                                    "process() basic test threw:");
          }
  
          reporter.logStatusMsg("process() basic test errorHandler :"
                                + errHandler.getLastError() + ": "
                                + errHandler.getCounterString());
  
          // Templates = Processor.processFromNode(Node) basic coverage test
          reporter.checkAmbiguous(
              "// TODO processFromNode(Node) copy from TraxMinitest");
  
          // Templates = Processor.processFromNode(Node, String) basic coverage test
          // TODO create stylesheet where String systemID is important 
          //      (does this need an import or include, or does other basic stuff matter?)
          reporter.checkAmbiguous(
              "// TODO processFromNode(Node, String) test needs stylesheet written");
  
          // Templates = Processor.processMultiple(InputSource[]) basic coverage test
          // InputSource[] = Processor.getAssociatedStylesheets(InputSource, media, title, charset) basic coverage test
          reporter.checkAmbiguous(
              "// TODO getAssociatedStylesheets test needs stylesheet written");
  
          // TemplatesBuilder = Processor.getTemplatesBuilder() basic coverage test
          reporter.checkAmbiguous("// TODO getTemplatesBuilder() ");
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * Basic negative tests: various illegal stylesheets, etc..
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase4()
      {
  
          reporter.testCaseInit(
              "Basic negative tests: various illegal stylesheets, etc.");
  
          // Set the default, but leave the system property alone
          //  In theory, you could manually set the system property to some other 
          //  TRAX-conformant processor, and we'd test that instead of Xalan 2.x
          Processor.setPlatformDefaultProcessor(processorClassname);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(processorClassname)");
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
  
          // Some invalid stylesheets to test for parsing/processing errors
          // use simpleTest.xmlName as an XML file without a xsl:stylesheet element
          Processor p = null;
          LoggingSAXErrorHandler errHandler;
  
          errHandler = new LoggingSAXErrorHandler(reporter);
  
          errHandler.setThrowWhen(errHandler.THROW_ON_FATAL);
  
          try
          {
              p = Processor.newInstance(XSLT);
  
              p.setErrorHandler(errHandler);
  
              Templates t = p.process(new InputSource(simpleTest.xmlName));
          }
          catch (Exception e)
          {
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "process of an xmlFile threw:");
          }
  
          String basePath = outputDir + File.separator + XAPI + File.separator;
          String illegalStylesheets[][] =
          {
              { basePath + "badXMLData.xsl", "This is not your parents XML!" },
              { basePath + "badClosingTag.xsl",
                "<?xml version=\"1.0\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">" },
              { basePath + "badNamespace.xsl",
                "<?xml version=\"1.0\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/blah/blah/blah\" version=\"1.0\">\n</xsl:stylesheet>" },
  
              /**
               * // For some reason, this still produces output
               *
               * {
               * basePath + "badVersion.xsl",
               * "<?xml version=\"1.0\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"abc\">\n</xsl:stylesheet>"
               * },
               */
              { basePath + "badTag.xsl",
                "<?xml version=\"1.0\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n<xsl:bad-tag/></xsl:stylesheet>" },
              { basePath + "badEntity.xsl",
                "<?xml version=\"1.0\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999XSL/Transform\" version=\"1.0\">\n&not-an-entity;</xsl:stylesheet>" }
          };
  
          // Cheap-o validation; should be improved to validate each case separately
          int numErrs = 0;
  
          for (int i = 0; i < illegalStylesheets.length; i++)
          {
              if (!writeFile(illegalStylesheets[i][0],
                             illegalStylesheets[i][1]))
                  continue;
  
              errHandler = new LoggingSAXErrorHandler(reporter);
  
              errHandler.setThrowWhen(errHandler.THROW_ON_FATAL);
  
              String tmp = "(0)";  // Marker for when exception is thrown
  
              reporter.logTraceMsg("about to process: "
                                   + illegalStylesheets[i][0]);
  
              try
              {
                  p = Processor.newInstance(XSLT);
  
                  // Should we actually expect anything to get passed to the errHandler?
                  p.setErrorHandler(errHandler);
  
                  tmp = "(1)";
  
                  Templates t =
                      p.process(new InputSource(illegalStylesheets[i][0]));
  
                  tmp = "(2)";
  
                  Transformer f = t.newTransformer();
  
                  tmp = "(3)";
  
                  f.transform(new InputSource(simpleTest.xmlName),
                              new Result(new FileWriter(outNames.nextName())));
                  reporter.checkFail(illegalStylesheets[i][0]
                                     + " should not have created output:"
                                     + outNames.currentName());
              }
              catch (Exception e)
              {
                  reporter.logThrowable(reporter.ERRORMSG, e,
                                        "process" + tmp + " of "
                                        + illegalStylesheets[i][0] + " threw:");
  
                  numErrs++;
              }
  
              reporter.logStatusMsg("process(" + tmp + " of "
                                    + illegalStylesheets[i][0] + "):"
                                    + errHandler.getLastError() + ": "
                                    + errHandler.getCounterString());
          }  // end of for...
  
          reporter.check((numErrs > 0), true,
                         "Some bad stylesheets correctly threw exceptions");
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * Cheap-o utility to write a test file; no validation performed.
       *
       * NEEDSDOC @param fileName
       * NEEDSDOC @param contents
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean writeFile(String fileName, String contents)
      {
  
          reporter.logTraceMsg("writeFile(" + fileName + ",...)");
  
          try
          {
              FileWriter fw = new FileWriter(fileName);
  
              fw.write(contents);
              fw.close();
  
              return true;
          }
          catch (IOException ioe)
          {
              reporter.logThrowable(reporter.ERRORMSG, ioe, "writeFile threw:");
  
              return false;
          }
      }
  
      /**
       * Convenience method to print out usage information - update if needed.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
  
          return ("Common [optional] options supported by ProcessorAPITest:\n"
                  + "(Note: assumes inputDir=.\\prod)\n"
                  + "-processorClassname classname.of.processor  (to override setPlatformDefaultProcessor to Xalan 2.x)\n"
                  + super.usage());
      }
  
      /**
       * Main method to run test from the command line - can be left alone.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          ProcessorAPITest app = new ProcessorAPITest();
  
          app.doMain(args);
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/trax/ResultAPITest.java
  
  Index: ResultAPITest.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * ResultAPITest.java
   *
   */
  package org.apache.qetest.trax;
  
  import org.apache.qetest.*;
  import org.apache.qetest.xsl.*;
  
  // Just import the whole trax package; note the packaging is likely to change
  import org.apache.trax.*;
  
  // Use Serializer classes from Xalan distro
  import org.apache.serialize.OutputFormat;
  import org.apache.serialize.Serializer;
  import org.apache.serialize.SerializerFactory;
  
  // Needed SAX classes
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.Parser;
  import org.xml.sax.ContentHandler;
  
  // Needed DOM classes
  import org.w3c.dom.Node;
  import org.w3c.dom.Document;
  
  // javax JAXP classes for parser pluggability
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  
  // java classes
  import java.io.File;
  import java.io.ByteArrayOutputStream;
  import java.io.FileOutputStream;
  import java.io.InputStream;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.StringWriter;
  
  import java.util.Properties;
  
  //-------------------------------------------------------------------------
  
  /**
   * Basic API coverage test for the Result class of TRAX.
   * @author shane_curcuru@lotus.com
   */
  public class ResultAPITest extends XSLProcessorTestBase
  {
  
      /**
       * Cheap-o filename for various output files.
       *
       */
      protected OutputNameManager outNames;
  
      /** Cheap-o filename set for both API tests and exampleSimple. */
      protected XSLTestfileInfo simpleTest = new XSLTestfileInfo();
  
      /** Cache the relevant system property. */
      protected String saveXSLTProp = null;
  
      /** Allow user to override our default of Xalan 2.x processor classname. */
      public static final String XALAN_CLASSNAME =
          "org.apache.xalan.processor.StylesheetProcessor";
  
      /** NEEDSDOC Field PROCESSOR_CLASSNAME          */
      protected String PROCESSOR_CLASSNAME = "processorClassname";
  
      /** NEEDSDOC Field processorClassname          */
      protected String processorClassname = XALAN_CLASSNAME;
  
      /** NEEDSDOC Field TRAX_PROCESSOR_XSLT          */
      public static final String TRAX_PROCESSOR_XSLT = "trax.processor.xslt";
  
      /** NEEDSDOC Field XSLT          */
      public static final String XSLT = "xslt";
  
      /** NEEDSDOC Field PROPERTY_LEXICAL_HANDLER          */
      public static final String PROPERTY_LEXICAL_HANDLER =
          "http://xml.org/sax/properties/lexical-handler";
  
      /** NEEDSDOC Field FEATURE_DOM_INPUT          */
      public static final String FEATURE_DOM_INPUT =
          "http://xml.org/trax/features/dom/input";
  
      /** NEEDSDOC Field FEATURE_SAX_INPUT          */
      public static final String FEATURE_SAX_INPUT =
          "http://xml.org/trax/features/sax/input";
  
      /** NEEDSDOC Field XAPI          */
      public static final String XAPI = "trax";
  
      /** Default ctor initializes test name, comment, numTestCases. */
      public ResultAPITest()
      {
  
          numTestCases = 2;  // REPLACE_num
          testName = "ResultAPITest";
          testComment = "Basic API coverage test for the Result class of TRAX";
      }
  
      /**
       * Initialize this test - Set names of xml/xsl test files, cache system property.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileInit(Properties p)
      {
  
          // Used for all tests; just dump files in xapi subdir
          File outSubDir = new File(outputDir + File.separator + XAPI);
  
          if (!outSubDir.mkdirs())
              reporter.logWarningMsg("Could not create output dir: "
                                     + outSubDir);
  
          outNames = new OutputNameManager(outputDir + File.separator + XAPI
                                           + File.separator + testName, ".out");
  
          // Used for API coverage and exampleSimple
          String testBasePath = inputDir + File.separator + XAPI
                                + File.separator;
          String goldBasePath = goldDir + File.separator + XAPI
                                + File.separator;
  
          simpleTest.xmlName = testBasePath + "APIMinitest.xml";
          simpleTest.inputName = testBasePath + "APIMinitest.xsl";
          simpleTest.goldName = goldBasePath + "APIMinitest.out";
  
          // Cache trax system property
          saveXSLTProp = System.getProperty(TRAX_PROCESSOR_XSLT);
  
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + saveXSLTProp);
  
          // Check if user wants to use a processor other than Xalan 2.x
          processorClassname = testProps.getProperty(PROCESSOR_CLASSNAME,
                                                     XALAN_CLASSNAME);
  
          reporter.logInfoMsg(PROCESSOR_CLASSNAME + " property is: "
                              + processorClassname);
  
          return true;
      }
  
      /**
       * Cleanup this test - reset the cached system property trax.processor.xslt.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileClose(Properties p)
      {
  
          if (saveXSLTProp == null)
          {
              System.getProperties().remove(TRAX_PROCESSOR_XSLT);
          }
          else
          {
              System.getProperties().put(TRAX_PROCESSOR_XSLT, saveXSLTProp);
          }
  
          return true;
      }
  
      /**
       * TRAX Result: cover APIs.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase1()
      {
  
          reporter.testCaseInit("TRAX Result: cover APIs");
  
          // Ctor(OutputStream)
          // set/getByteStream (OutputStream)
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          Result resultBytes = new Result(baos);
  
          reporter.checkObject(resultBytes.getByteStream(), baos,
                               "ctor()/getByteStream()");
  
          // Verify other items are not set
          if ((resultBytes.getCharacterStream() == null)
                  && (resultBytes.getNode() == null))
          {
              reporter.checkPass("resultBytes should not have Writer/Node");
          }
          else
          {
              reporter.checkFail("resultBytes should not have Writer/Node");
          }
  
          ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
  
          resultBytes.setByteStream(baos2);
          reporter.checkObject(resultBytes.getByteStream(), baos2,
                               "set/getByteStream()");
  
          // Ctor(Writer)
          // set/getCharacterStream (Writer)
          StringWriter sw = new StringWriter();
          Result resultWriter = new Result(sw);
  
          reporter.checkObject(resultWriter.getCharacterStream(), sw,
                               "ctor()/getCharacterStream()");
  
          // Verify other items are not set
          if ((resultWriter.getByteStream() == null)
                  && (resultWriter.getNode() == null))
          {
              reporter.checkPass(
                  "resultWriter should not have ByteStream/Node");
          }
          else
          {
              reporter.checkFail(
                  "resultWriter should not have ByteStream/Node");
          }
  
          StringWriter sw2 = new StringWriter();
  
          resultWriter.setCharacterStream(sw2);
          reporter.checkObject(resultWriter.getCharacterStream(), sw2,
                               "set/getCharacterStream()");
  
          // Ctor(Node)
          // set/getNode (Node)
          // What's the easiest way to just create a Node?
          try
          {
              DocumentBuilderFactory dfactory =
                  DocumentBuilderFactory.newInstance();
              DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
              Node n = docBuilder.newDocument();
              Result resultNode = new Result(n);
  
              reporter.checkObject(resultNode.getNode(), n, "ctor()/getNode()");
  
              // Verify other items are not set
              if ((resultNode.getByteStream() == null)
                      && (resultNode.getCharacterStream() == null))
              {
                  reporter.checkPass(
                      "resultNode should not have ByteStream/CharacterStream");
              }
              else
              {
                  reporter.checkFail(
                      "resultNode should not have ByteStream/CharacterStream");
              }
  
              Node n2 = docBuilder.newDocument();
  
              resultNode.setNode(n2);
              reporter.checkObject(resultNode.getNode(), n2, "set/getNode()");
          }
          catch (ParserConfigurationException pce)
          {
              reporter.checkFail("Creating Node threw: " + pce.toString());
              reporter.logThrowable(reporter.ERRORMSG, pce,
                                    "Creating Node threw:");
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * TRAX Result: validate basic Result functionality.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase2()
      {
  
          reporter.testCaseInit(
              "TRAX Result: validate basic Results functionality");
  
          // Actually use each type of Results to get some output
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
          Processor.setPlatformDefaultProcessor(processorClassname);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(processorClassname)");
  
          // Setup inputs and get a stylesheet for use by all tests
          InputSource xsl = new InputSource(simpleTest.inputName);
          InputSource xml = new InputSource(simpleTest.xmlName);
          Templates templates = null;
          Transformer transformer = null;
  
          try
          {
              Processor p = Processor.newInstance(XSLT);
  
              // Setup inputs and get a stylesheet for use by all tests
              templates = p.process(xsl);
          }
          catch (Exception e)
          {
              reporter.checkFail(
                  "Problem creating Templates; cannot continue testcase");
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "Problem creating Templates");
  
              return true;
          }
  
          try
          {
  
              // Test Results(ByteStream)
              FileOutputStream fos = new FileOutputStream(outNames.nextName());
              Result resultBytes = new Result(fos);
  
              transformer = templates.newTransformer();
  
              transformer.transform(xml, resultBytes);
  
              // Ensure the stream is flushed and closed
              fos.close();
              fileChecker.check(reporter, new File(outNames.currentName()),
                                new File(simpleTest.goldName),
                                "Result(ByteStream) into "
                                + outNames.currentName());
          }
          catch (Exception e)
          {
              reporter.checkFail("Testing Results(ByteStream) threw: "
                                 + e.toString());
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "Testing Results(ByteStream) threw:");
          }
  
          try
          {
  
              // Test Results(CharacterStream)
              FileWriter fw = new FileWriter(outNames.nextName());
              Result resultWriter = new Result(fw);
  
              transformer = templates.newTransformer();
  
              transformer.transform(xml, resultWriter);
  
              // Ensure the stream is flushed and closed
              fw.close();
              fileChecker.check(reporter, new File(outNames.currentName()),
                                new File(simpleTest.goldName),
                                "Result(CharacterStream) into "
                                + outNames.currentName());
          }
          catch (Exception e)
          {
              reporter.checkFail("Testing Results(CharacterStream) threw: "
                                 + e.toString());
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "Testing Results(CharacterStream) threw:");
          }
  
          try
          {
  
              // Test Result(Node)
              DocumentBuilderFactory dfactory =
                  DocumentBuilderFactory.newInstance();
              DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
              Document node = docBuilder.newDocument();
              Result resultNode = new Result(node);
  
              transformer = templates.newTransformer();
  
              transformer.transform(xml, resultNode);
  
              // Use the serializers to output the result to disk
              OutputFormat format = templates.getOutputFormat();
              Serializer serializer = SerializerFactory.getSerializer(format);
  
              serializer.setOutputStream(
                  new FileOutputStream(outNames.nextName()));
              serializer.asDOMSerializer().serialize(node);
              fileChecker.check(reporter, new File(outNames.currentName()),
                                new File(simpleTest.goldName),
                                "Result(Node) into " + outNames.currentName());
          }
          catch (Exception e)
          {
              reporter.checkFail("Testing Results(Node) threw: "
                                 + e.toString());
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "Testing Results(Node) threw:");
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * Convenience method to print out usage information - update if needed.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
  
          return ("Common [optional] options supported by ResultAPITest:\n"
                  + "(Note: assumes inputDir=.\\prod)\n"
                  + "-processorClassname classname.of.processor  (to override setPlatformDefaultProcessor to Xalan 2.x)\n"
                  + super.usage());
      }
  
      /**
       * Main method to run test from the command line - can be left alone.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          ResultAPITest app = new ResultAPITest();
  
          app.doMain(args);
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/trax/TemplatesAPITest.java
  
  Index: TemplatesAPITest.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * TemplatesAPITest.java
   *
   */
  package org.apache.qetest.trax;
  
  import org.apache.qetest.*;
  import org.apache.qetest.xsl.*;
  
  // Just import the whole trax package; note the packaging is likely to change
  import org.apache.trax.*;
  
  // Use Serializer classes from Xalan distro
  import org.apache.serialize.Method;
  import org.apache.serialize.OutputFormat;
  import org.apache.serialize.Serializer;
  import org.apache.serialize.SerializerFactory;
  
  // Needed SAX classes
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.Parser;
  import org.xml.sax.ContentHandler;
  import org.xml.sax.helpers.XMLReaderFactory;
  import org.xml.sax.XMLReader;
  
  // Needed DOM classes
  import org.w3c.dom.Node;
  import org.w3c.dom.Document;
  
  // javax JAXP classes for parser pluggability
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  
  // java classes
  import java.io.File;
  import java.io.ByteArrayOutputStream;
  import java.io.FileOutputStream;
  import java.io.InputStream;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.StringWriter;
  
  import java.util.Properties;
  
  //-------------------------------------------------------------------------
  
  /**
   * Basic API coverage test for the Templates and TemplatesBuilder classes of TRAX.
   * @author shane_curcuru@lotus.com
   */
  public class TemplatesAPITest extends XSLProcessorTestBase
  {
  
      /**
       * Cheap-o filename for various output files.
       *
       */
      protected OutputNameManager outNames;
  
      /** Cheap-o filename set for both API tests and exampleSimple. */
      protected XSLTestfileInfo simpleTest = new XSLTestfileInfo();
  
      /** Name of a stylesheet with xsl:output HTML. */
      protected String htmlStylesheet = null;
  
      /** Cache the relevant system property. */
      protected String saveXSLTProp = null;
  
      /** Allow user to override our default of Xalan 2.x processor classname. */
      public static final String XALAN_CLASSNAME =
          "org.apache.xalan.processor.StylesheetProcessor";
  
      /** NEEDSDOC Field PROCESSOR_CLASSNAME          */
      protected String PROCESSOR_CLASSNAME = "processorClassname";
  
      /** NEEDSDOC Field processorClassname          */
      protected String processorClassname = XALAN_CLASSNAME;
  
      /** NEEDSDOC Field TRAX_PROCESSOR_XSLT          */
      public static final String TRAX_PROCESSOR_XSLT = "trax.processor.xslt";
  
      /** NEEDSDOC Field XSLT          */
      public static final String XSLT = "xslt";
  
      // http://xml.org/sax/features/namespace-prefixes feature
      // http://xml.org/sax/features/namespaces feature
      // http://xml.org/sax/features/external-general-entities property
      // http://xml.org/sax/features/external-parameter-entities property
      // http://xml.apache.org/xslt/sourcebase property in TransformerImpl
  
      /** NEEDSDOC Field PROPERTY_LEXICAL_HANDLER          */
      public static final String PROPERTY_LEXICAL_HANDLER =
          "http://xml.org/sax/properties/lexical-handler";
  
      /** NEEDSDOC Field FEATURE_DOM_INPUT          */
      public static final String FEATURE_DOM_INPUT =
          "http://xml.org/trax/features/dom/input";
  
      /** NEEDSDOC Field FEATURE_SAX_INPUT          */
      public static final String FEATURE_SAX_INPUT =
          "http://xml.org/trax/features/sax/input";
  
      /** NEEDSDOC Field XAPI          */
      public static final String XAPI = "trax";
  
      /** Default ctor initializes test name, comment, numTestCases. */
      public TemplatesAPITest()
      {
  
          numTestCases = 2;  // REPLACE_num
          testName = "TemplatesAPITest";
          testComment =
              "Basic API coverage test for the Templates and TemplatesBuilder classes of TRAX";
      }
  
      /**
       * Initialize this test - Set names of xml/xsl test files, cache system property.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileInit(Properties p)
      {
  
          // Used for all tests; just dump files in xapi subdir
          File outSubDir = new File(outputDir + File.separator + XAPI);
  
          if (!outSubDir.mkdirs())
              reporter.logWarningMsg("Could not create output dir: "
                                     + outSubDir);
  
          outNames = new OutputNameManager(outputDir + File.separator + XAPI
                                           + File.separator + testName, ".out");
  
          // Used for API coverage and exampleSimple
          String testBasePath = inputDir + File.separator + XAPI
                                + File.separator;
          String goldBasePath = goldDir + File.separator + XAPI
                                + File.separator;
  
          simpleTest.xmlName = testBasePath + "APIMinitest.xml";
          simpleTest.inputName = testBasePath + "APIMinitest.xsl";
          simpleTest.goldName = goldBasePath + "APIMinitest.out";
          htmlStylesheet = inputDir + File.separator + XAPI + File.separator
                           + "TemplatesAPIHTML.xsl";
  
          // Cache trax system property
          saveXSLTProp = System.getProperty(TRAX_PROCESSOR_XSLT);
  
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + saveXSLTProp);
  
          // Check if user wants to use a processor other than Xalan 2.x
          processorClassname = testProps.getProperty(PROCESSOR_CLASSNAME,
                                                     XALAN_CLASSNAME);
  
          reporter.logInfoMsg(PROCESSOR_CLASSNAME + " property is: "
                              + processorClassname);
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
  
          // Just call this static method once for the whole test
          // TODO will this ever affect other tests run through a harness?
          Processor.setPlatformDefaultProcessor(processorClassname);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(processorClassname)");
  
          return true;
      }
  
      /**
       * Cleanup this test - reset the cached system property trax.processor.xslt.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileClose(Properties p)
      {
  
          if (saveXSLTProp == null)
          {
              System.getProperties().remove(TRAX_PROCESSOR_XSLT);
          }
          else
          {
              System.getProperties().put(TRAX_PROCESSOR_XSLT, saveXSLTProp);
          }
  
          return true;
      }
  
      /**
       * TRAX Templates: cover APIs and functionality.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase1()
      {
  
          reporter.testCaseInit("TRAX Templates: cover APIs and functionality");
  
          Processor p = null;
  
          try
          {
              p = Processor.newInstance(XSLT);
          }
          catch (Exception e)
          {
              reporter.checkFail(
                  "Problem creating Processor; cannot continue testcase");
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "Problem creating Processor");
  
              return true;
          }
  
          try
          {
  
              // Cover APIs newTransformer(), getOutputFormat()
              Templates templates =
                  p.process(new InputSource(simpleTest.inputName));
              Transformer transformer = templates.newTransformer();
  
              reporter.check((transformer != null), true,
                             "newTransformer() is non-null for "
                             + simpleTest.inputName);
  
              OutputFormat outputFormat = templates.getOutputFormat();
  
              reporter.check((outputFormat != null), true,
                             "getOutputFormat() is non-null for "
                             + simpleTest.inputName);
              reporter.check(outputFormat.getMethod(), Method.XML,
                             "outputFormat.getMethod() is xml for "
                             + simpleTest.inputName);
          }
          catch (Exception e)
          {
              reporter.checkErr("newTransformer/getOutputFormat threw: "
                                + e.toString());
              reporter.logThrowable(reporter.STATUSMSG, e,
                                    "newTransformer/getOutputFormat threw:");
          }
  
          try
          {
              Templates templatesHTML =
                  p.process(new InputSource(htmlStylesheet));
              OutputFormat outputFormatHTML = templatesHTML.getOutputFormat();
  
              reporter.check(outputFormatHTML.getMethod(), Method.HTML,
                             "outputFormat() is html for " + htmlStylesheet);
          }
          catch (Exception e)
          {
              reporter.checkErr("outputFormat() is html... threw: "
                                + e.toString());
              reporter.logThrowable(reporter.STATUSMSG, e,
                                    "outputFormat() is html... threw:");
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * TRAX TemplatesBuilder: cover APIs and functionality.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase2()
      {
  
          reporter.testCaseInit(
              "TRAX TemplatesBuilder: cover APIs and functionality");
  
          try
          {
              Processor p = Processor.newInstance(XSLT);
              TemplatesBuilder templatesBuilder = p.getTemplatesBuilder();
  
              reporter.check((templatesBuilder != null), true,
                             "getTemplatesBuilder() is non-null");
  
              XMLReader reader = XMLReaderFactory.createXMLReader();
  
              reader.setContentHandler(templatesBuilder);
  
              if (templatesBuilder instanceof org.xml.sax.ext.LexicalHandler)
              {
                  reader.setProperty(PROPERTY_LEXICAL_HANDLER,
                                     templatesBuilder);
              }
  
              reader.parse(new InputSource(htmlStylesheet));
  
              Templates templates = templatesBuilder.getTemplates();
  
              reporter.check((templates != null), true,
                             "templates from SAX build is non-null");
  
              // Cheap-o verification it's the right stylesheet
              OutputFormat outputFormatHTML = templates.getOutputFormat();
  
              reporter.check(outputFormatHTML.getMethod(), Method.HTML,
                             "outputFormat() is html for " + htmlStylesheet);
              reporter.checkAmbiguous(
                  "//TODO create stylesheet both ways (t=process() and via SAX build) and compare outputs");
              reporter.checkAmbiguous(
                  "//TODO setBaseID() and then create different stylesheet");
          }
          catch (Exception e)
          {
              reporter.checkFail("TestCase threw: " + e.toString());
              reporter.logThrowable(reporter.ERRORMSG, e, "TestCase threw:");
  
              return true;
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * Convenience method to print out usage information - update if needed.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
  
          return ("Common [optional] options supported by TemplatesAPITest:\n"
                  + "(Note: assumes inputDir=.\\prod)\n"
                  + "-processorClassname classname.of.processor  (to override setPlatformDefaultProcessor to Xalan 2.x)\n"
                  + super.usage());
      }
  
      /**
       * Main method to run test from the command line - can be left alone.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          TemplatesAPITest app = new TemplatesAPITest();
  
          app.doMain(args);
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/trax/TestThreads.java
  
  Index: TestThreads.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * TestThreads.java
   *
   */
  package org.apache.qetest.trax;
  
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileReader;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.io.Reader;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.OutputStream;
  
  import java.util.Properties;
  import java.util.StringTokenizer;
  
  // Needed SAX classes
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  
  // For optional URI/URLs instead of string filenames
  import java.net.URL;
  import java.net.MalformedURLException;
  
  // Note that not all imports are listed here
  import org.apache.trax.*;  // TRAX package name will change
  
  //-------------------------------------------------------------------------
  
  /**
   * Testing multiple simultaneous processors on different threads with TRAX.
   * <p>No validation of output files is currently done!  You must manually
   * inspect any logfiles.  Most options can be passed in with a Properties file.</p>
   * @author shane_curcuru@lotus.com
   */
  public class TestThreads
  {
  
      /**
       * Convenience method to print out usage information.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public static String usage()
      {
  
          return ("Usage: TestThreads file.properties :\n"
                  + "    where the properties file can set:,\n"
                  + "    testDir=e:\\builds\\xsl-test\n"
                  + "    outDir=e:\\builds\\xsl-test\\results\n"
                  + "    logFile=e:\\builds\\xsl-test\\results\\TestThreads.xml\n"
                  + "    numRunners=5\n" + "    numRunnerCalls=10\n"
                  + "    setOneFile=bool01\n" + "    setTwoFile=expr01\n"
                  + "    setThreeFile=numb01\n" + "    paramName=SomeParam\n"
                  + "    paramVal=TheValue\n");
      }
  
      /** NEEDSDOC Field debug          */
      public boolean debug = true;  // for adhoc debugging
  
      /**
       * Number of sets of worker threads to create and loops per runner.
       * <p>'numRunners=xx', default is 10; 'numRunnerCalls=xx', default is 50.</p>
       */
      protected int numRunners = 10;
  
      /** NEEDSDOC Field numRunnerCalls          */
      protected int numRunnerCalls = 50;
  
      /**
       * Root input filenames that certain runners should use, in the testDir.
       * <p>'setOneFile=File'; 'setTwoFile=File'; 'setThreeFile=File'
       * in .prop file to set; default is TestThreads1, TestThreads2, TestThreads3.</p>
       * <p>Files are found in 'testDir=c:\bar\baz' from .prop file.</p>
       */
      protected String testDir = null;
  
      /** NEEDSDOC Field setOneFilenameRoot          */
      protected String setOneFilenameRoot = "TestThreads1";
  
      /** NEEDSDOC Field setTwoFilenameRoot          */
      protected String setTwoFilenameRoot = "TestThreads2";
  
      /** NEEDSDOC Field setThreeFilenameRoot          */
      protected String setThreeFilenameRoot = "TestThreads3";
  
      /**
       * All output logs and files get put in the outDir.
       */
      protected String outDir = null;
  
      /**
       * Sample PARAM name that certain runners should use.
       * <p>Use 'paramName=xx' in .prop file to set, default is test1.</p>
       */
      protected String paramName = "test1";
  
      /**
       * Sample PARAM value that certain runners should use.
       * <p>Use 'paramVal=xx' in .prop file to set, default is bar.</p>
       */
      protected String paramVal = "bar";
  
      /**
       * liaisonClassName that just the *second* set of runners should use.
       * <p>Use 'liaison=xx' in .prop file to set, default is null (whatever the processor's default is).</p>
       */
      protected String liaison = null;  // TRAX unused
  
      // Used to pass info to runners; simpler to update than changing ctors
  
      /** NEEDSDOC Field ID          */
      public static final int ID = 0;
  
      /** NEEDSDOC Field XMLNAME          */
      public static final int XMLNAME = 1;
  
      /** NEEDSDOC Field XSLNAME          */
      public static final int XSLNAME = 2;
  
      /** NEEDSDOC Field OUTNAME          */
      public static final int OUTNAME = 3;
  
      /** NEEDSDOC Field PARAMNAME          */
      public static final int PARAMNAME = 4;
  
      /** NEEDSDOC Field PARAMVAL          */
      public static final int PARAMVAL = 5;
  
      /** NEEDSDOC Field OPTIONS          */
      public static final int OPTIONS = 6;
  
      /** NEEDSDOC Field LIAISON          */
      public static final int LIAISON = 7;
  
      /** NEEDSDOC Field FUTUREUSE          */
      public static final int FUTUREUSE = 8;
  
      /**
       * Name of main file's output logging; each runner also has separate output.
       */
      protected String logFileName = "TestThreads.xml";
  
      /**
       * Construct multiple threads with processors and run them all.
       * @author Shane Curcuru & Scott Boag
       * <p>Preprocesses some stylesheets, then creates lots of worker threads.</p>
       */
      public void runTest()
      {
  
          // Prepare a log file and dump out some basic info
          createLogFile(logFileName);
          println("<?xml version=\"1.0\"?>");
          println("<resultsfile fileName=\"" + logFileName + "\">");
          println("<message desc=\"threads=" + (3 * numRunners)
                  + " iterations=" + numRunnerCalls + "\"/>");
          println("<message desc=\"oneF=" + setOneFilenameRoot + " twof="
                  + setTwoFilenameRoot + " threef=" + setThreeFilenameRoot
                  + "\"/>");
          println("<message desc=\"param=" + paramName + " val=" + paramVal
                  + " liaison=" + liaison + "\"/>");
  
          // Preprocess some stylesheets for use by the runners
          String errStr = "Create processor threw: ";
          Templates stylesheet1, stylesheet2, stylesheet3;
  
          try
          {
              String setOneURL =
                  getURLFromString(testDir + setOneFilenameRoot + ".xsl",
                                   null).toExternalForm();
              String setTwoURL =
                  getURLFromString(testDir + setTwoFilenameRoot + ".xsl",
                                   null).toExternalForm();
              String setThreeURL =
                  getURLFromString(testDir + setThreeFilenameRoot + ".xsl",
                                   null).toExternalForm();
              Processor stylesheetProcessor = Processor.newInstance("xslt");
  
              errStr = "Processing stylesheet1 threw: ";
              stylesheet1 =
                  stylesheetProcessor.process(new InputSource(setOneURL));
              errStr = "Processing stylesheet2 threw: ";
              stylesheet2 =
                  stylesheetProcessor.process(new InputSource(setTwoURL));
              errStr = "Processing stylesheet3 threw: ";
              stylesheet3 =
                  stylesheetProcessor.process(new InputSource(setThreeURL));
          }
          catch (Exception e)
          {
              println("<arbitrary desc=\"" + errStr + e.toString() + "\">");
  
              if (pWriter != null)
              {
                  e.printStackTrace(pWriter);
              }
  
              e.printStackTrace();
              println("</arbitrary>");
  
              return;
          }
  
          errStr = "PreCreating runners threw: ";
  
          try
          {
              String[] rValues = new String[FUTUREUSE];
  
              // Create a whole bunch of worker threads and run them
              for (int i = 0; i < numRunners; i++)
              {
                  TestThreadsRunner r1, r2, r3;
                  Thread t1, t2, t3;
  
                  // First set of runners reports on memory usage periodically
                  rValues[ID] = "one-" + i;
                  rValues[XMLNAME] = "file:" + testDir + setOneFilenameRoot
                                     + ".xml";
                  rValues[XSLNAME] = testDir + setOneFilenameRoot + ".xsl";
                  rValues[OUTNAME] = outDir + setOneFilenameRoot + "r" + i;
                  rValues[PARAMNAME] = paramName;
                  rValues[PARAMVAL] = paramVal;
                  rValues[OPTIONS] = "memory;param";
                  errStr = "Creating runnerone-" + i + " threw: ";
                  r1 = new TestThreadsRunner(rValues, stylesheet1,
                                             numRunnerCalls);
                  t1 = new Thread(r1);
  
                  t1.start();
  
                  // Second set of runners is polite; uses optional liaison
                  rValues[ID] = "two-" + i;
                  rValues[XMLNAME] = "file:" + testDir + setTwoFilenameRoot
                                     + ".xml";
                  rValues[XSLNAME] = testDir + setTwoFilenameRoot + ".xsl";
                  rValues[OUTNAME] = outDir + setTwoFilenameRoot + "r" + i;
                  rValues[PARAMNAME] = paramName;
                  rValues[PARAMVAL] = paramVal;
                  rValues[OPTIONS] = "polite;param";
  
                  if ((liaison != null) &&!(liaison.equals("")))
                      rValues[LIAISON] = liaison;
  
                  errStr = "Creating runnertwo-" + i + " threw: ";
                  r2 = new TestThreadsRunner(rValues, stylesheet2,
                                             numRunnerCalls);
                  t2 = new Thread(r2);
  
                  t2.start();
  
                  rValues[LIAISON] = null;
  
                  // Third set of runners will recreate it's processor each time
                  // and report memory usage; but not set the param
                  // Note: this causes lots of calls to System.gc
                  rValues[ID] = "thr-" + i;
                  rValues[XMLNAME] = "file:" + testDir + setThreeFilenameRoot
                                     + ".xml";
                  rValues[XSLNAME] = testDir + setThreeFilenameRoot + ".xsl";
                  rValues[OUTNAME] = outDir + setThreeFilenameRoot + "r" + i;
                  rValues[PARAMNAME] = paramName;
                  rValues[PARAMVAL] = paramVal;
                  rValues[OPTIONS] = "recreate;memory";
                  errStr = "Creating runnerthree-" + i + " threw: ";
                  r3 = new TestThreadsRunner(rValues, stylesheet3,
                                             numRunnerCalls);
                  t3 = new Thread(r3);
  
                  t3.start();
                  println("<message desc=\"Created " + i
                          + "th set of runners.\"/>");
              }
          }
          catch (Exception e)
          {
              println("<arbitrary desc=\"" + errStr + e.toString() + "\">");
  
              if (pWriter != null)
              {
                  e.printStackTrace(pWriter);
              }
  
              e.printStackTrace();
              println("</arbitrary>");
          }
  
          // Clean up our own references, just for completeness
          stylesheet1 = null;
          stylesheet2 = null;
          stylesheet3 = null;
          errStr = null;
  
          println("<message desc=\"Created all our runners!\"/>");
          println("<message desc=\"TestThreads main thread now complete\"/>");
          println("</resultsfile>");
  
          if (pWriter != null)
              pWriter.flush();
      }
  
      /**
       * Read in properties file and set instance variables.  
       *
       * NEEDSDOC @param fName
       *
       * NEEDSDOC ($objectName$) @return
       */
      protected boolean initPropFile(String fName)
      {
  
          Properties p = new Properties();
  
          try
          {
  
              // Load named file into our properties block
              FileInputStream fIS = new FileInputStream(fName);
  
              p.load(fIS);
  
              // Parse out any values that match our internal convenience variables
              outDir = p.getProperty("outputDir", outDir);
  
              // Validate the outDir and use it to reset the logFileName
              File oDir = new File(outDir);
  
              if (!oDir.exists())
              {
                  if (!oDir.mkdirs())
                  {
  
                      // Error, we can't create the outDir, default to current dir
                      println("<message desc=\"outputDir(" + outDir
                              + ") does not exist, defaulting to .\"/>");
  
                      outDir = ".";
                  }
              }
  
              // Verify testDir as well
              testDir = p.getProperty("inputDir", testDir);
  
              File tDir = new File(testDir);
  
              if (!tDir.exists())
              {
                  if (!tDir.mkdirs())
                  {
  
                      // Error, we can't create the testDir, abort
                      println("<message desc=\"inputDir(" + testDir
                              + ") does not exist, terminating test\"/>");
  
                      return false;
                  }
              }
  
              // Add on separators
              testDir += File.separator;
              outDir += File.separator;
  
              // Each defaults to variable initializers            
              logFileName = p.getProperty("logFile", logFileName);
              setOneFilenameRoot = p.getProperty("setOneFile",
                                                 setOneFilenameRoot);
              setTwoFilenameRoot = p.getProperty("setTwoFile",
                                                 setTwoFilenameRoot);
              setThreeFilenameRoot = p.getProperty("setThreeFile",
                                                   setThreeFilenameRoot);
              paramName = p.getProperty("paramName", paramName);
              paramVal = p.getProperty("paramVal", paramVal);
              liaison = p.getProperty("liaison", liaison);
  
              String numb;
  
              numb = p.getProperty("numRunners");
  
              if (numb != null)
              {
                  try
                  {
                      numRunners = Integer.parseInt(numb);
                  }
                  catch (NumberFormatException numEx)
                  {
  
                      // no-op, leave set as default
                      println("<message desc=\"numRunners threw: "
                              + numEx.toString() + "\"/>");
                  }
              }
  
              numb = p.getProperty("numRunnerCalls");
  
              if (numb != null)
              {
                  try
                  {
                      numRunnerCalls = Integer.parseInt(numb);
                  }
                  catch (NumberFormatException numEx)
                  {
  
                      // no-op, leave set as default
                      println("<message desc=\"numRunnerCalls threw: "
                              + numEx.toString() + "\"/>");
                  }
              }
          }
          catch (Exception e)
          {
              println("<arbitrary=\"initPropFile: " + fName + " threw: "
                      + e.toString() + "\">");
  
              if (pWriter != null)
              {
                  e.printStackTrace(pWriter);
              }
  
              e.printStackTrace();
              println("</arbitrary>");
  
              return false;
          }
  
          return true;
      }
  
      /**
       * Bottleneck output; goes to System.out and main's pWriter.  
       *
       * NEEDSDOC @param s
       */
      protected void println(String s)
      {
  
          System.out.println(s);
  
          if (pWriter != null)
              pWriter.println(s);
      }
  
      /** A simple log output file for the main thread; each runner also has it's own. */
      protected PrintWriter pWriter = null;
  
      /**
       * Worker method to setup a simple log output file.  
       *
       * NEEDSDOC @param n
       */
      protected void createLogFile(String n)
      {
  
          try
          {
              pWriter = new PrintWriter(new FileWriter(n, true));
          }
          catch (Exception e)
          {
              System.err.println("<message desc=\"createLogFile threw: "
                                 + e.toString() + "\"/>");
              e.printStackTrace();
          }
      }
  
      /**
       * Startup the test from the command line.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          if (args.length != 1)
          {
              System.err.println("ERROR! Must have one argument\n" + usage());
  
              return;  // Don't System.exit, it's not polite
          }
  
          TestThreads app = new TestThreads();
  
          if (!app.initPropFile(args[0]))  // Side effect: creates pWriter for logging
          {
              System.err.println("ERROR! Could not read properties file: "
                                 + args[0]);
  
              return;
          }
  
          app.runTest();
      }
  
      // /////////////////// HACK - added from Xalan1 org.apache.xalan.xslt.Process /////////////////////
  
      /**
       * Take a user string and try and parse XML, and also return the url.
       *
       * NEEDSDOC @param urlString
       * NEEDSDOC @param base
       *
       * NEEDSDOC ($objectName$) @return
       * @exception SAXException thrown if we really really can't create the URL
       */
      public static URL getURLFromString(String urlString, String base)
              throws SAXException
      {
  
          String origURLString = urlString;
          String origBase = base;
  
          // System.out.println("getURLFromString - urlString: "+urlString+", base: "+base);
          Object doc;
          URL url = null;
          int fileStartType = 0;
  
          try
          {
              if (null != base)
              {
                  if (base.toLowerCase().startsWith("file:/"))
                  {
                      fileStartType = 1;
                  }
                  else if (base.toLowerCase().startsWith("file:"))
                  {
                      fileStartType = 2;
                  }
              }
  
              boolean isAbsoluteURL;
  
              // From http://www.ics.uci.edu/pub/ietf/uri/rfc1630.txt
              // A partial form can be distinguished from an absolute form in that the
              // latter must have a colon and that colon must occur before any slash
              // characters. Systems not requiring partial forms should not use any
              // unencoded slashes in their naming schemes.  If they do, absolute URIs
              // will still work, but confusion may result.
              int indexOfColon = urlString.indexOf(':');
              int indexOfSlash = urlString.indexOf('/');
  
              if ((indexOfColon != -1) && (indexOfSlash != -1)
                      && (indexOfColon < indexOfSlash))
              {
  
                  // The url (or filename, for that matter) is absolute.
                  isAbsoluteURL = true;
              }
              else
              {
                  isAbsoluteURL = false;
              }
  
              if (isAbsoluteURL || (null == base) || (base.length() == 0))
              {
                  try
                  {
                      url = new URL(urlString);
                  }
                  catch (MalformedURLException e){}
              }
  
              // The Java URL handling doesn't seem to handle relative file names.
              else if (!((urlString.charAt(0) == '.') || (fileStartType > 0)))
              {
                  try
                  {
                      URL baseUrl = new URL(base);
  
                      url = new URL(baseUrl, urlString);
                  }
                  catch (MalformedURLException e){}
              }
  
              if (null == url)
              {
  
                  // Then we're going to try and make a file URL below, so strip 
                  // off the protocol header.
                  if (urlString.toLowerCase().startsWith("file:/"))
                  {
                      urlString = urlString.substring(6);
                  }
                  else if (urlString.toLowerCase().startsWith("file:"))
                  {
                      urlString = urlString.substring(5);
                  }
              }
  
              if ((null == url) && ((null == base) || (fileStartType > 0)))
              {
                  if (1 == fileStartType)
                  {
                      if (null != base)
                          base = base.substring(6);
  
                      fileStartType = 1;
                  }
                  else if (2 == fileStartType)
                  {
                      if (null != base)
                          base = base.substring(5);
  
                      fileStartType = 2;
                  }
  
                  File f = new File(urlString);
  
                  if (!f.isAbsolute() && (null != base))
                  {
  
                      // String dir = f.isDirectory() ? f.getAbsolutePath() : f.getParent();
                      // System.out.println("prebuiltUrlString (1): "+base);
                      StringTokenizer tokenizer = new StringTokenizer(base,
                                                      "\\/");
                      String fixedBase = null;
  
                      while (tokenizer.hasMoreTokens())
                      {
                          String token = tokenizer.nextToken();
  
                          if (null == fixedBase)
                          {
  
                              // Thanks to Rick Maddy for the bug fix for UNIX here.
                              if (base.charAt(0) == '\\'
                                      || base.charAt(0) == '/')
                              {
                                  fixedBase = File.separator + token;
                              }
                              else
                              {
                                  fixedBase = token;
                              }
                          }
                          else
                          {
                              fixedBase += File.separator + token;
                          }
                      }
  
                      // System.out.println("rebuiltUrlString (1): "+fixedBase);
                      f = new File(fixedBase);
  
                      String dir = f.isDirectory()
                                   ? f.getAbsolutePath() : f.getParent();
  
                      // System.out.println("dir: "+dir);
                      // System.out.println("urlString: "+urlString);
                      // f = new File(dir, urlString);
                      // System.out.println("f (1): "+f.toString());
                      // urlString = f.getAbsolutePath();
                      f = new File(urlString);
  
                      boolean isAbsolute = f.isAbsolute()
                                           || (urlString.charAt(0) == '\\')
                                           || (urlString.charAt(0) == '/');
  
                      if (!isAbsolute)
                      {
  
                          // Getting more and more ugly...
                          if (dir.charAt(dir.length() - 1)
                                  != File.separator.charAt(0)
                                  && urlString.charAt(0)
                                     != File.separator.charAt(0))
                          {
                              urlString = dir + File.separator + urlString;
                          }
                          else
                          {
                              urlString = dir + urlString;
                          }
  
                          // System.out.println("prebuiltUrlString (2): "+urlString);
                          tokenizer = new StringTokenizer(urlString, "\\/");
  
                          String rebuiltUrlString = null;
  
                          while (tokenizer.hasMoreTokens())
                          {
                              String token = tokenizer.nextToken();
  
                              if (null == rebuiltUrlString)
                              {
  
                                  // Thanks to Rick Maddy for the bug fix for UNIX here.
                                  if (urlString.charAt(0) == '\\'
                                          || urlString.charAt(0) == '/')
                                  {
                                      rebuiltUrlString = File.separator + token;
                                  }
                                  else
                                  {
                                      rebuiltUrlString = token;
                                  }
                              }
                              else
                              {
                                  rebuiltUrlString += File.separator + token;
                              }
                          }
  
                          // System.out.println("rebuiltUrlString (2): "+rebuiltUrlString);
                          if (null != rebuiltUrlString)
                              urlString = rebuiltUrlString;
                      }
  
                      // System.out.println("fileStartType: "+fileStartType);
                      if (1 == fileStartType)
                      {
                          if (urlString.charAt(0) == '/')
                          {
                              urlString = "file://" + urlString;
                          }
                          else
                          {
                              urlString = "file:/" + urlString;
                          }
                      }
                      else if (2 == fileStartType)
                      {
                          urlString = "file:" + urlString;
                      }
  
                      try
                      {
  
                          // System.out.println("Final before try: "+urlString);
                          url = new URL(urlString);
                      }
                      catch (MalformedURLException e)
                      {
  
                          // System.out.println("Error trying to make URL from "+urlString);
                      }
                  }
              }
  
              if (null == url)
              {
  
                  // The sun java VM doesn't do this correctly, but I'll 
                  // try it here as a second-to-last resort.
                  if ((null != origBase) && (origBase.length() > 0))
                  {
                      try
                      {
                          URL baseURL = new URL(origBase);
  
                          // System.out.println("Trying to make URL from "+origBase+" and "+origURLString);
                          url = new URL(baseURL, origURLString);
  
                          // System.out.println("Success! New URL is: "+url.toString());
                      }
                      catch (MalformedURLException e)
                      {
  
                          // System.out.println("Error trying to make URL from "+origBase+" and "+origURLString);
                      }
                  }
  
                  if (null == url)
                  {
                      try
                      {
                          String lastPart;
  
                          if (null != origBase)
                          {
                              File baseFile = new File(origBase);
  
                              if (baseFile.isDirectory())
                              {
                                  lastPart =
                                      new File(baseFile,
                                               urlString).getAbsolutePath();
                              }
                              else
                              {
                                  String parentDir = baseFile.getParent();
  
                                  lastPart =
                                      new File(parentDir,
                                               urlString).getAbsolutePath();
                              }
                          }
                          else
                          {
                              lastPart = new File(urlString).getAbsolutePath();
                          }
  
                          // Hack
                          // if((lastPart.charAt(0) == '/') && (lastPart.charAt(2) == ':'))
                          //   lastPart = lastPart.substring(1, lastPart.length() - 1);
                          String fullpath;
  
                          if (lastPart.charAt(0) == '\\'
                                  || lastPart.charAt(0) == '/')
                          {
                              fullpath = "file://" + lastPart;
                          }
                          else
                          {
                              fullpath = "file:" + lastPart;
                          }
  
                          url = new URL(fullpath);
                      }
                      catch (MalformedURLException e2)
                      {
                          throw new SAXException("Cannot create url for: "
                                                 + urlString, e2);
  
                          //XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_CREATE_URL, new Object[]{urlString}),e2); //"Cannot create url for: " + urlString, e2 );
                      }
                  }
              }
          }
          catch (SecurityException se)
          {
              try
              {
                  url = new URL("http://xml.apache.org/xslt/"
                                + java.lang.Math.random());  // dummy
              }
              catch (MalformedURLException e2)
              {
  
                  // I give up
              }
          }
  
          // System.out.println("url: "+url.toString());
          return url;
      }
  }  // end of class TestThreads
  
  /**
   * Worker class to run a processor on a separate thread.
   * <p>Currently, no automated validation is done, however most
   * output files and all error logs are saved to disk allowing for
   * later manual verification.</p>
   */
  class TestThreadsRunner implements Runnable
  {
  
      /** NEEDSDOC Field xslStylesheet          */
      Templates xslStylesheet;
  
      /** NEEDSDOC Field numProcesses          */
      int numProcesses;
  
      /** NEEDSDOC Field runnerID          */
      String runnerID;
  
      /** NEEDSDOC Field xmlName          */
      String xmlName;
  
      /** NEEDSDOC Field xslName          */
      String xslName;
  
      /** NEEDSDOC Field outName          */
      String outName;
  
      /** NEEDSDOC Field paramName          */
      String paramName;
  
      /** NEEDSDOC Field paramVal          */
      String paramVal;
  
      /** NEEDSDOC Field liaison          */
      String liaison;
  
      /** NEEDSDOC Field polite          */
      boolean polite = false;  // if we should yield each loop
  
      /** NEEDSDOC Field recreate          */
      boolean recreate = false;  // if we should re-create a new processor each time
  
      /** NEEDSDOC Field validate          */
      boolean validate = false;  // if we should attempt to validate output files (FUTUREWORK)
  
      /** NEEDSDOC Field reportMem          */
      boolean reportMem = false;  // if we should report memory usage periodically
  
      /** NEEDSDOC Field setParam          */
      boolean setParam = false;  // if we should set our parameter or not
  
      /**
       * Constructor TestThreadsRunner
       *
       *
       * NEEDSDOC @param params
       * NEEDSDOC @param xslStylesheet
       * NEEDSDOC @param numProcesses
       */
      TestThreadsRunner(String[] params, Templates xslStylesheet,
                        int numProcesses)
      {
  
          this.xslStylesheet = xslStylesheet;
          this.numProcesses = numProcesses;
          this.runnerID = params[TestThreads.ID];
          this.xmlName = params[TestThreads.XMLNAME];
          this.xslName = params[TestThreads.XSLNAME];
          this.outName = params[TestThreads.OUTNAME];
          this.paramName = params[TestThreads.PARAMNAME];
          this.paramVal = params[TestThreads.PARAMVAL];
  
          if (params[TestThreads.OPTIONS].indexOf("polite") > 0)
              polite = true;
  
          if (params[TestThreads.OPTIONS].indexOf("recreate") > 0)
              recreate = true;
  
          if (params[TestThreads.OPTIONS].indexOf("validate") > 0)
              validate = true;
  
          if (params[TestThreads.OPTIONS].indexOf("memory") > 0)
              reportMem = true;
  
          if (params[TestThreads.OPTIONS].indexOf("param") > 0)
              setParam = true;
  
          if (params[TestThreads.LIAISON] != null)  // TRAX unused
              liaison = params[TestThreads.LIAISON];
      }
  
      /**
       * Bottleneck output; both to System.out and to our private errWriter.  
       *
       * NEEDSDOC @param s
       */
      protected void println(String s)
      {
  
          System.out.println(s);
  
          if (errWriter != null)
              errWriter.println(s);
      }
  
      /**
       * Bottleneck output; both to System.out and to our private errWriter.  
       *
       * NEEDSDOC @param s
       */
      protected void print(String s)
      {
  
          System.out.print(s);
  
          if (errWriter != null)
              errWriter.print(s);
      }
  
      /** NEEDSDOC Field errWriter          */
      PrintWriter errWriter = null;
  
      /**
       * NEEDSDOC Method createErrWriter 
       *
       */
      protected void createErrWriter()
      {
  
          try
          {
              errWriter = new PrintWriter(new FileWriter(outName + ".log"),
                                          true);
          }
          catch (Exception e)
          {
              System.err.println("<message desc=\"" + runnerID + ":threw: "
                                 + e.toString() + "\"/>");
          }
      }
  
      /** Main entrypoint; loop and perform lots of processes. */
      public void run()
      {
  
          int i = 0;  // loop counter; used for error reporting
  
          createErrWriter();
          println("<?xml version=\"1.0\"?>");
          println("<testrunner desc=\"" + runnerID + ":started\" fileName=\""
                  + xslName + "\">");
  
          Processor p = null;
  
          try
          {
  
              // Each runner creates it's own processor for use and it's own error log
              p = Processor.newInstance("xslt");
  
              // Munge the input filenames to be URLs
              xmlName = TestThreads.getURLFromString(xmlName,
                                                     null).toExternalForm();
              xslName = TestThreads.getURLFromString(xslName,
                                                     null).toExternalForm();
  
              println("<arbitrary desc=\"" + runnerID + ":processing\">");
          }
          catch (Throwable ex)
          {  // If we got here, just log it and bail, no sense continuing
              println("<throwable desc=\"" + ex.toString() + "\"><![CDATA[");
              ex.printStackTrace(errWriter);
              println("\n</throwable>");
              println("<message desc=\"" + runnerID + ":complete-ERROR:after:"
                      + i + "\"/>");
              println("</testrunner>");
  
              if (errWriter != null)
                  errWriter.close();
  
              return;
          }
  
          try
          {
  
              // Loop away...
              for (i = 0; i < numProcesses; i++)
              {
  
                  // Run a process using the pre-compiled stylesheet we were construced with
                  {
                      Transformer transformer1 = xslStylesheet.newTransformer();
                      FileOutputStream resultStream1 =
                          new FileOutputStream(outName + ".out");
                      Result result1 = new Result(resultStream1);
  
                      if (setParam)
                          transformer1.setParameter(paramName, null, paramVal);
  
                      print(".");  // Note presence of this in logs shows which process threw an exception
                      transformer1.transform(new InputSource(xmlName), result1);
                      resultStream1.close();
  
                      // Temporary vars go out of scope for cleanup here
                  }
  
                  // Now process something with a newly-processed stylesheet
                  {
                      Templates templates2 =
                          p.process(new InputSource(xslName));
                      Transformer transformer2 = templates2.newTransformer();
                      FileOutputStream resultStream2 =
                          new FileOutputStream(outName + "_.out");
                      Result result2 = new Result(resultStream2);
  
                      if (setParam)
                          transformer2.setParameter(paramName, null, paramVal);
  
                      print("*");  // Note presence of this in logs shows which process threw an exception
                      transformer2.transform(new InputSource(xmlName), result2);
                      resultStream2.close();
                  }
  
                  // if asked, report memory statistics
                  if (reportMem)
                  {
                      Runtime r = Runtime.getRuntime();
  
                      r.gc();
  
                      long freeMemory = r.freeMemory();
                      long totalMemory = r.totalMemory();
  
                      println("<statistic desc=\"" + runnerID
                              + ":memory:longval-free:doubleval-total\">");
                      println("<longval>" + freeMemory + "</longval>");
                      println("<doubleval>" + totalMemory + "</doubleval>");
                      println("</statistic>");
                  }
  
                  // if we're polite, let others play for a bit
                  if (polite)
                      java.lang.Thread.yield();
              }
  
              // IF we get here, we worked without exceptions (presumably successfully)
              println("</arbitrary>");
              println("<message desc=\"" + runnerID + ":complete-OK:after:"
                      + numProcesses + "\"/>");
          }
  
          // Separate messages for each kind of exception
          catch (TransformException te)
          {
              println("\n<transformexception desc=\"" + te.toString() + "\">");
              logStackTrace(te, errWriter);
              logContainedException(te, errWriter);
              println("</transformexception>");
              println("</arbitrary>");
              println("<message desc=\"" + runnerID + ":complete-ERROR:after:"
                      + i + "\"/>");
          }
          catch (SAXException se)
          {
              println("\n<saxexception desc=\"" + se.toString() + "\">");
              logStackTrace(se, errWriter);
              logContainedException(se, errWriter);
              println("</saxexception>");
              println("</arbitrary>");
              println("<message desc=\"" + runnerID + ":complete-ERROR:after:"
                      + i + "\"/>");
          }
          catch (Throwable ex)
          {
              logThrowable(ex, errWriter);
              println("</arbitrary>");
              println("<message desc=\"" + runnerID + ":complete-ERROR:after:"
                      + i + "\"/>");
          }
          finally
          {
  
              // Cleanup our references, etc.
              println("</testrunner>");
  
              if (errWriter != null)
                  errWriter.close();
  
              runnerID = null;
              xmlName = null;
              xslName = null;
              xslStylesheet = null;
              outName = null;
          }
      }  // end of run()...
  
      /**
       * NEEDSDOC Method logContainedException 
       *
       *
       * NEEDSDOC @param parent
       * NEEDSDOC @param p
       */
      private void logContainedException(SAXException parent, PrintWriter p)
      {
  
          Exception containedException = parent.getException();
  
          if (null != containedException)
          {
              println("<containedexception desc=\""
                      + containedException.toString() + "\">");
              logStackTrace(containedException, p);
              println("</containedexception>");
          }
      }
  
      /**
       * NEEDSDOC Method logThrowable 
       *
       *
       * NEEDSDOC @param t
       * NEEDSDOC @param p
       */
      private void logThrowable(Throwable t, PrintWriter p)
      {
  
          println("\n<throwable desc=\"" + t.toString() + "\">");
          logStackTrace(t, p);
          println("</throwable>");
      }
  
      /**
       * NEEDSDOC Method logStackTrace 
       *
       *
       * NEEDSDOC @param t
       * NEEDSDOC @param p
       */
      private void logStackTrace(Throwable t, PrintWriter p)
      {
  
          // Should check if (errWriter == null)
          println("<stacktrace><![CDATA[");
          t.printStackTrace(p);
  
          // Could also echo to stdout, but not really worth it
          println("]]></stacktrace>");
      }
  }  // end of class TestThreadsRunner...
  
  // END OF FILE
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/trax/TransformerAPITest.java
  
  Index: TransformerAPITest.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * TransformerAPITest.java
   *
   */
  package org.apache.qetest.trax;
  
  import org.apache.qetest.*;
  import org.apache.qetest.xsl.*;
  
  // Just import the whole trax package; note the packaging is likely to change
  import org.apache.trax.*;
  
  // Use Serializer classes from Xalan distro
  import org.apache.serialize.Method;
  import org.apache.serialize.OutputFormat;
  
  // java classes
  import java.io.File;
  import java.io.FileOutputStream;
  
  import java.util.Properties;
  
  // Needed SAX classes
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.Parser;
  import org.xml.sax.ContentHandler;
  import org.xml.sax.helpers.XMLReaderFactory;
  import org.xml.sax.XMLReader;
  
  // Needed DOM classes
  import org.w3c.dom.Node;
  import org.w3c.dom.Document;
  
  // javax JAXP classes for parser pluggability
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  
  //-------------------------------------------------------------------------
  
  /**
   * Basic API coverage test for the Transformer class of TRAX.
   * @author shane_curcuru@lotus.com
   */
  public class TransformerAPITest extends XSLProcessorTestBase
  {
  
      /**
       * Cheap-o filename for various output files.
       *
       */
      protected OutputNameManager outNames;
  
      /** Cheap-o filename set for both API tests and exampleSimple. */
      protected XSLTestfileInfo simpleTest = new XSLTestfileInfo();
  
      /** NEEDSDOC Field paramTest          */
      protected XSLTestfileInfo paramTest = new XSLTestfileInfo();
  
      /** Cache the relevant system property. */
      protected String saveXSLTProp = null;
  
      /** Allow user to override our default of Xalan 2.x processor classname. */
      public static final String XALAN_CLASSNAME =
          "org.apache.xalan.processor.StylesheetProcessor";
  
      /** NEEDSDOC Field PROCESSOR_CLASSNAME          */
      protected String PROCESSOR_CLASSNAME = "processorClassname";
  
      /** NEEDSDOC Field processorClassname          */
      protected String processorClassname = XALAN_CLASSNAME;
  
      /** NEEDSDOC Field TRAX_PROCESSOR_XSLT          */
      public static final String TRAX_PROCESSOR_XSLT = "trax.processor.xslt";
  
      /** NEEDSDOC Field XSLT          */
      public static final String XSLT = "xslt";
  
      /** NEEDSDOC Field PROPERTY_LEXICAL_HANDLER          */
      public static final String PROPERTY_LEXICAL_HANDLER =
          "http://xml.org/sax/properties/lexical-handler";
  
      /** NEEDSDOC Field FEATURE_DOM_INPUT          */
      public static final String FEATURE_DOM_INPUT =
          "http://xml.org/trax/features/dom/input";
  
      /** NEEDSDOC Field FEATURE_SAX_INPUT          */
      public static final String FEATURE_SAX_INPUT =
          "http://xml.org/trax/features/sax/input";
  
      /** NEEDSDOC Field XAPI          */
      public static final String XAPI = "trax";
  
      /** Default ctor initializes test name, comment, numTestCases. */
      public TransformerAPITest()
      {
  
          numTestCases = 2;  // REPLACE_num
          testName = "TransformerAPITest";
          testComment = "Basic API coverage test for the class of TRAX";
      }
  
      /**
       * Initialize this test - Set names of xml/xsl test files, cache system property.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileInit(Properties p)
      {
  
          // Used for all tests; just dump files in xapi subdir
          File outSubDir = new File(outputDir + File.separator + XAPI);
  
          if (!outSubDir.mkdirs())
              reporter.logWarningMsg("Could not create output dir: "
                                     + outSubDir);
  
          outNames = new OutputNameManager(outputDir + File.separator + XAPI
                                           + File.separator + testName, ".out");
  
          // Used for API coverage and exampleSimple
          String testBasePath = inputDir + File.separator + XAPI
                                + File.separator;
          String goldBasePath = goldDir + File.separator + XAPI
                                + File.separator;
  
          simpleTest.xmlName = testBasePath + "APIMinitest.xml";
          simpleTest.inputName = testBasePath + "APIMinitest.xsl";
          simpleTest.goldName = goldBasePath + "APIMinitest.out";
          paramTest.xmlName = testBasePath + "TraxMinitestParam.xml";
          paramTest.inputName = testBasePath + "TraxMinitestParam.xsl";
          paramTest.goldName = goldBasePath + "TraxMinitestParam.out";
  
          // Cache trax system property
          saveXSLTProp = System.getProperty(TRAX_PROCESSOR_XSLT);
  
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + saveXSLTProp);
  
          // Check if user wants to use a processor other than Xalan 2.x
          processorClassname = testProps.getProperty(PROCESSOR_CLASSNAME,
                                                     XALAN_CLASSNAME);
  
          reporter.logInfoMsg(PROCESSOR_CLASSNAME + " property is: "
                              + processorClassname);
          reporter.logInfoMsg(TRAX_PROCESSOR_XSLT + " property is: "
                              + System.getProperty(TRAX_PROCESSOR_XSLT));
  
          // Just call this static method once for the whole test
          // TODO will this ever affect other tests run through a harness?
          Processor.setPlatformDefaultProcessor(processorClassname);
          reporter.logTraceMsg(
              "Processor.setPlatformDefaultProcessor(processorClassname)");
  
          return true;
      }
  
      /**
       * Cleanup this test - reset the cached system property trax.processor.xslt.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileClose(Properties p)
      {
  
          if (saveXSLTProp == null)
          {
              System.getProperties().remove(TRAX_PROCESSOR_XSLT);
          }
          else
          {
              System.getProperties().put(TRAX_PROCESSOR_XSLT, saveXSLTProp);
          }
  
          return true;
      }
  
      /**
       * TRAX Transformer: cover other APIs and functionality.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase1()
      {
  
          reporter.testCaseInit(
              "TRAX Transformer: cover other APIs and functionality");
  
          Processor p = null;
          Templates templates = null;
  
          try
          {
              p = Processor.newInstance(XSLT);
  
              // Use paramTest for later testing of setParameter()/resetParameters()
              templates = p.process(new InputSource(paramTest.inputName));
          }
          catch (Exception e)
          {
              reporter.checkFail(
                  "Problem creating Processor; cannot continue testcase");
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "Problem creating Processor");
  
              return true;
          }
  
          Transformer transformer = templates.newTransformer();
  
          try
          {
  
              // Cover APIs get*Handler()
              if (p.getFeature(FEATURE_SAX_INPUT))
              {
  
                  // Validate simply by checking for null
                  reporter.check((transformer.getInputContentHandler() != null),
                                 true, "getInputContentHandler() is non-null");
                  reporter.check((transformer.getInputDeclHandler() != null),
                                 true, "getInputDeclHandler() is non-null");
                  reporter.check((transformer.getInputLexicalHandler() != null),
                                 true, "getInputLexicalHandler() is non-null");
              }
              else
              {
  
                  // Can't validate, just print out values
                  reporter.logWarningMsg(
                      "getInputContentHandler is: "
                      + transformer.getInputContentHandler());
                  reporter.logWarningMsg("getInputDeclHandler is: "
                                         + transformer.getInputDeclHandler());
                  reporter.logWarningMsg(
                      "getInputLexicalHandler is: "
                      + transformer.getInputLexicalHandler());
              }
          }
          catch (Exception e)
          {
              reporter.checkErr("get*Handler() test threw: " + e.toString());
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "get*Handler() test threw:");
          }
  
          try
          {
              transformer = templates.newTransformer();
  
              // Basic setOutputFormat() test
              String fName1 = outNames.nextName();
  
              transformer.transform(new InputSource(paramTest.xmlName),
                                    new Result(new FileOutputStream(fName1)));
              reporter.logStatusMsg("Created default output: " + fName1);
  
              // Force output type to be different
              OutputFormat outputFormat = templates.getOutputFormat();
  
              outputFormat.setMethod(Method.Text);  // TODO better switch
              transformer.setOutputFormat(outputFormat);
              transformer.transform(
                  new InputSource(paramTest.xmlName),
                  new Result(new FileOutputStream(outNames.nextName())));
              reporter.checkAmbiguous("// TODO validate: Created TEXT output: "
                                      + outNames.currentName());
          }
          catch (Exception e)
          {
              reporter.checkErr("setOutputFormat() test threw: "
                                + e.toString());
              reporter.logThrowable(reporter.ERRORMSG, e,
                                    "setOutputFormat() test threw:");
          }
  
          try
          {
              transformer = templates.newTransformer();
  
              // Basic setParameter()/resetParameters() test
              String noParams = outNames.nextName();
              String withParams = outNames.nextName();
              String resetParams = outNames.nextName();
              String neverParams = outNames.nextName();
  
              transformer.transform(new InputSource(paramTest.xmlName),
                                    new Result(new FileOutputStream(noParams)));
              reporter.logStatusMsg("Created noParams output: " + noParams);
  
              String paramName = "paramName";
              String paramNamespace = null;  // TODO write stylesheet that uses the namespaces
              String paramValue = "paramValue";
  
              transformer.setParameter(paramName, paramNamespace, paramValue);
              transformer.transform(
                  new InputSource(paramTest.xmlName),
                  new Result(new FileOutputStream(withParams)));
              reporter.logStatusMsg("Created withParams output: " + withParams);
              transformer.resetParameters();
              transformer.transform(
                  new InputSource(paramTest.xmlName),
                  new Result(new FileOutputStream(resetParams)));
              reporter.checkAmbiguous(
                  "// TODO validate Created resetParams output: "
                  + resetParams);
  
              // Also check what happens when you use a new transformer
              transformer = templates.newTransformer();
  
              transformer.transform(
                  new InputSource(paramTest.xmlName),
                  new Result(new FileOutputStream(neverParams)));
              reporter.checkAmbiguous(
                  "// TODO validate Created neverParams output: "
                  + neverParams);
          }
          catch (Exception e)
          {
              reporter.checkErr("setParameter()/resetParameters() test threw: "
                                + e.toString());
              reporter.logThrowable(
                  reporter.ERRORMSG, e,
                  "setParameter()/resetParameters() test threw:");
          }
  
          reporter.checkAmbiguous(
              "// TODO Cover setURIResolver() API and functionality");
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * TRAX Transformer: cover transform*() APIs and functionality.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase2()
      {
  
          reporter.testCaseInit(
              "TRAX Transformer: cover transform*() APIs and functionality");
  
          try
          {
              Processor p = Processor.newInstance(XSLT);
  
              // These tests are already covered in TraxWrapper.java
              reporter.checkAmbiguous("// TODO transform(InputSource) -> SAX");
              reporter.checkAmbiguous("// TODO transform(InputSource, Result)");
  
              if (p.getFeature(FEATURE_DOM_INPUT))
              {
                  reporter.checkAmbiguous(
                      "// TODO transformNode(Node, Result)  if dom/input");
                  reporter.checkAmbiguous(
                      "// TODO transformNode(Node) -> SAX  if dom/input");
              }
              else
              {
                  reporter.logWarningMsg("Skipping transformNode(*) tests, "
                                         + FEATURE_DOM_INPUT
                                         + " not supported");
              }
          }
          catch (Exception e)
          {
              reporter.checkFail("TestCase threw: " + e.toString());
              reporter.logThrowable(reporter.ERRORMSG, e, "TestCase threw:");
  
              return true;
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * Convenience method to print out usage information - update if needed.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
  
          return ("Common [optional] options supported by TransformerAPITest:\n"
                  + "(Note: assumes inputDir=.\\prod)\n"
                  + "-processorClassname classname.of.processor  (to override setPlatformDefaultProcessor to Xalan 2.x)\n"
                  + super.usage());
      }
  
      /**
       * Main method to run test from the command line - can be left alone.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          TransformerAPITest app = new TransformerAPITest();
  
          app.doMain(args);
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/trax/package.html
  
  Index: package.html
  ===================================================================
  <html>
    <title>XSL-TEST TRAX testing package.</title>
    <body>
      <p>This package is for TRAX-interface API tests.<p>
      <dl>
        <dt><b>Author: </b></dt><dd><a href="mailto:shane_curcuru@lotus.com">Shane_Curcuru@lotus.com</a></dd>
        <dt><b>Program(s) Under Test: </b></dt>
        <dd><a href="http://xml.apache.org/xalan-j" target="_top">Xalan-J 2.x XSLT Processor</a></dd>
      </dl>
      <p>Most tests are completely generic to the TRAX interface, although they 
      do default System properties to use the Xalan-J 2.x implementation.<p>
      <ul>Current tests are primarily focused on covering the API's and include:
      <li>ProcessorAPITest - basic coverage of both factory methods and instance methods</li>
      <li>ResultAPITest</li>
      <li>TransformerAPITest</li>
      <li>TemplatesAPITest - also covers TemplatesBuilder class</li>
      <li>TestThreads - a semi-automated test for multithreaded testing, 
      results must be manually analyzed currently</li>
      </ul>
    </body>
  </html>
  
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xalanj1/ParamTest.java
  
  Index: ParamTest.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * ParamTest.java
   *
   */
  package org.apache.qetest.xalanj1;
  
  import org.apache.qetest.*;
  import org.apache.qetest.xsl.*;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FileReader;
  import java.io.IOException;
  
  import java.util.Properties;
  
  // Specific imports for testing Xalan
  import org.xml.sax.SAXException;
  
  import org.apache.xalan.xslt.XSLTProcessor;
  import org.apache.xalan.xslt.XSLTProcessorFactory;
  import org.apache.xalan.xslt.XSLTInputSource;
  import org.apache.xalan.xslt.XSLTResultTarget;
  
  //-------------------------------------------------------------------------
  
  /**
   * Testing setStylesheetParam with XSL files.
   */
  public class ParamTest extends XSLProcessorTestBase
  {
  
      /** Our version of the processor. */
      protected org.apache.xalan.xslt.XSLTProcessor processor;
  
      // Strings to hold names of our files
  
      /** NEEDSDOC Field xmlFilename          */
      protected String xmlFilename;
  
      /** NEEDSDOC Field xslFilename          */
      protected String xslFilename;
  
      /** NEEDSDOC Field outNames          */
      protected OutputNameManager outNames;
  
      /**
       * Default constructor - initialize testName, Comment.
       */
      public ParamTest()
      {
  
          numTestCases = 2;  // REPLACE_num
          testName = "ParamTest";
          testComment = "Testing Xalan-J 1.x setStylesheetParam with XSL files";
      }
  
      /**
       * Initialize this test - Update with your tests's data.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileInit(Properties p)
      {
  
          outNames = new OutputNameManager(outputDir + File.separator
                                           + testName, ".out");
  
          // inputDir should be parent of xapi directory
          xmlFilename = inputDir + File.separator + "xalanj1" + File.separator
                        + "ParamTest1.xml";
          xslFilename = inputDir + File.separator + "xalanj1" + File.separator
                        + "ParamTest1.xsl";
  
          try
          {
              if ((liaison == null) || ("".equals(liaison)))
              {
                  processor = XSLTProcessorFactory.getProcessor();
              }
              else
              {
                  processor = XSLTProcessorFactory.getProcessorUsingLiaisonName(
                      liaison);
              }
          }
          catch (Exception e)
          {
              reporter.checkFail("Could not create processor, threw: "
                                 + e.toString());
              e.printStackTrace();
              setAbortTest(true);
  
              return false;
          }
  
          return true;
      }
  
      /**
       * Write some test cases!  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase1()
      {
  
          reporter.testCaseInit("Testing setStylesheetParam");
  
          try
          {
              org.apache.xalan.xslt.XSLTInputSource xmlSource =
                  new XSLTInputSource(xmlFilename);
              org.apache.xalan.xslt.XSLTInputSource xslStylesheet =
                  new XSLTInputSource(xslFilename);
  
              // Process the file as-is
              org.apache.xalan.xslt.XSLTResultTarget xmlOutput1 =
                  new XSLTResultTarget(outNames.nextName());
  
              processor.process(xmlSource, xslStylesheet, xmlOutput1);
              processor.reset();
              checkFileContains(outNames.currentName(), "ABC,<B>ABC</B>;",
                                "out(" + outNames.currentCounter()
                                + ") Stylesheet with default param value");
  
              // Also verify that $t1 tests are correct
              checkFileContains(
                  outNames.currentName(),
                  "<outt>true,false,false,false,notset</outt>",
                  "out(" + outNames.currentCounter()
                  + ") ... also with default param value in select expr");
  
              // Test setting the value and checking it in a select expr
              processor.setStylesheetParam("t1", "''");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "<outt>false,true,false,false,</outt>",
                                "out(" + outNames.currentCounter()
                                + ") Select expr of a param blank string");
              processor.setStylesheetParam("t1", "'a'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "<outt>false,false,true,false,a</outt>",
                                "out(" + outNames.currentCounter()
                                + ") Select expr of a param string");
              processor.setStylesheetParam("t1", "'1'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "<outt>false,false,false,true,1</outt>",
                                "out(" + outNames.currentCounter()
                                + ") Select expr of a param number");
  
              // Now re-set the value of the element-value params in the xsl file
              processor.setStylesheetParam("p1", "'foo'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(), "foo,foo;",
                                "out(" + outNames.currentCounter()
                                + ") Stylesheet with literal param value");
  
              // Test resetting the value of a parameter with the same processor instance
              processor.setStylesheetParam("p1", "'bar'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(
                  outNames.currentName(), "bar,bar;",
                  "out(" + outNames.currentCounter()
                  + ") Stylesheet with replaced literal param value");
  
              // Test putting other nodes in the value
              processor.setStylesheetParam("p2",
                                           "'&lt;item&gt;bar&lt;/item&gt;'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(
                  outNames.currentName(),
                  "&amp;lt;item&amp;gt;bar&amp;lt;/item&amp;gt;,&amp;lt;item&amp;gt;bar&amp;lt;/item&amp;gt;;",
                  "out(" + outNames.currentCounter()
                  + ") Stylesheet with param value with nodes(?)");
  
              // Param within a template
              processor.setStylesheetParam("p3", "'foo3'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(
                  outNames.currentName(), "GHI,<B>GHI</B>;",
                  "out(" + outNames.currentCounter()
                  + ") Stylesheet with literal param value in a template, is not passed");
  
              // Now test the value of the select-value params in the xsl file
              processor.setStylesheetParam("s1", "'foos'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(), "foos,foos;",
                                "out(" + outNames.currentCounter()
                                + ") Stylesheet with literal param select");
              processor.setStylesheetParam("s1", "'bars'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(
                  outNames.currentName(), "bars,bars;",
                  "out(" + outNames.currentCounter()
                  + ") Stylesheet with replaced literal param select");
              processor.setStylesheetParam("s2", "'&lt;item/&gt;'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "&amp;lt;item/&amp;gt;,&amp;lt;item/&amp;gt;;",
                                "out(" + outNames.currentCounter()
                                + ") Stylesheet with nodes(?) param select");
              processor.setStylesheetParam("s3", "'foos3'");
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(
                  outNames.currentName(), "s3val,s3val;",
                  "out(" + outNames.currentCounter()
                  + ") Stylesheet with literal param select in a template, is not passed");
          }
          catch (Exception e)
          {
              reporter.logErrorMsg("Testcase threw: " + e.toString());
              reporter.testCaseClose();  //- Required -
  
              return false;
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * Write some test cases!  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean testCase2()
      {
  
          reporter.testCaseInit("Testing setStylesheetParam with XObjects");
  
          try
          {
  
              // Note we may be implicitly using the same processor as before!
              org.apache.xalan.xslt.XSLTInputSource xmlSource =
                  new XSLTInputSource(xmlFilename);
              org.apache.xalan.xslt.XSLTInputSource xslStylesheet =
                  new XSLTInputSource(xslFilename);
  
              // Create some XObjects to use
              org.apache.xalan.xpath.XBoolean myBoolean =
                  processor.createXBoolean(true);
              org.apache.xalan.xpath.XNull myNull = processor.createXNull();
              org.apache.xalan.xpath.XNumber myNumber =
                  processor.createXNumber(1);
              org.apache.xalan.xpath.XObject myObject =
                  processor.createXObject("a");
              org.apache.xalan.xpath.XString myString =
                  processor.createXString("a");
  
              // Test setting the value and checking it in a select expr
              processor.setStylesheetParam("t1", myNull);
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "<outt>false,false,false,false,</outt>",
                                "out(" + outNames.currentCounter()
                                + ") Select expr with XNull");
              processor.setStylesheetParam("t1", myBoolean);
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "<outt>true,false,true,true,true</outt>",
                                "out(" + outNames.currentCounter()
                                + ") Select expr with XBoolean");
              processor.setStylesheetParam("t1", myString);  // XString = "a"
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "<outt>false,false,true,false,a</outt>",
                                "out(" + outNames.currentCounter()
                                + ") Select expr with XString");
              processor.setStylesheetParam("t1", myObject);  // XObject = (string)"a"
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "<outt>false,false,true,false,a</outt>",
                                "out(" + outNames.currentCounter()
                                + ") Select expr with xObject(string)");
              processor.setStylesheetParam("t1", myNumber);
              processor.process(xmlSource, xslStylesheet,
                                new XSLTResultTarget(outNames.nextName()));
              processor.reset();
              checkFileContains(outNames.currentName(),
                                "<outt>false,false,false,true,1</outt>",
                                "out(" + outNames.currentCounter()
                                + ") Select expr with XNumber");
          }
          catch (Exception e)
          {
              reporter.checkErr("Testcase threw: " + e.toString());
          }
  
          reporter.testCaseClose();
  
          return true;
      }
  
      /**
       * Checks and reports if a file contains a certain string (within one line).
       * <P>We should really validate the entire output file, but this will do for now.</P>
       * @todo update to use new CheckServices!
       *
       * NEEDSDOC @param fName
       * NEEDSDOC @param checkStr
       * NEEDSDOC @param comment
       *
       * NEEDSDOC ($objectName$) @return
       */
      protected boolean checkFileContains(String fName, String checkStr,
                                          String comment)
      {
  
          boolean passFail = false;
          File f = new File(fName);
  
          if (!f.exists())
          {
              reporter.checkFail("checkFileContains(" + fName
                                 + ") does not exist: " + comment);
  
              return false;
          }
  
          try
          {
              FileReader fr = new FileReader(f);
              BufferedReader br = new BufferedReader(fr);
  
              for (;;)
              {
                  String inbuf = br.readLine();
  
                  if (inbuf == null)
                      break;
  
                  if (inbuf.indexOf(checkStr) > 0)
                  {
                      passFail = true;
  
                      reporter.logTraceMsg(
                          "checkFileContains passes with line: " + inbuf);
  
                      break;
                  }
              }
          }
          catch (IOException ioe)
          {
              reporter.checkFail("checkFileContains(" + fName + ") threw: "
                                 + ioe.toString() + " for: " + comment);
  
              return false;
          }
  
          reporter.check(passFail, true, comment);
  
          return passFail;
      }
  
      /**
       * Convenience method to print out usage information - update if needed.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
          return ("Common [optional] options supported by ParamTest:\n"
                  + "(Note: assumes inputDir=.\\prod\\conf)\n" + super.usage());
      }
  
      /**
       * Main method to run test from the command line - can be left alone.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          ParamTest app = new ParamTest();
  
          app.doMain(args);
      }
  }  // end of class ParamTest
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xalanj1/package.html
  
  Index: package.html
  ===================================================================
  <html>
    <title>XSL-TEST TRAX testing package.</title>
    <body>
      <p>This package is for Xalan-J 1.x API tests.<p>
      <dl>
        <dt><b>Author: </b></dt><dd><a href="mailto:shane_curcuru@lotus.com">Shane_Curcuru@lotus.com</a></dd>
        <dt><b>Program(s) Under Test: </b></dt>
        <dd><a href="http://xml.apache.org/xalan" target="_top">Xalan-J 1.x XSLT Processor</a></dd>
      </dl>
      <p>To be filled in!.<p>
      <ul>Current tests are primarily focused on covering the API's and include:
      <li>ParamTest - basic setParameter functionality.</li>
      <li>More tests are needed, although I may be spending more of 
      my time writing TRAX or Xalan-J 2.x tests.</li>
      </ul>
    </body>
  </html>
  
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/CConformanceTest.java
  
  Index: CConformanceTest.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * CConformanceTest.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.*;
  import org.apache.qetest.xslwrapper.ProcessorWrapper;  // Merely for the ERROR constant
  
  import java.io.BufferedInputStream;
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FilenameFilter;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.IOException;
  
  import java.util.Properties;
  import java.util.Vector;
  
  //-------------------------------------------------------------------------
  
  /**
   * New! CConformanceTest for Xalan-C's TestXSLT.exe program.
   * <p>Iterates over all conformance tests using common functionality,
   * then shells out a process to TestXSLT.exe for each test.
   * Automatically validates output files against golds, but may not
   * validate error or exception conditions yet.</p>
   * @author shane_curcuru@lotus.com
   */
  public class CConformanceTest extends XSLDirectoryIterator
  {
  
      /**
       * Convenience method to print out usage information - update if needed.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
  
          return ("Common [optional] options supported by CConformanceTest:\n"
                  + "    -progName   <name.exe>\n"
                  + "    -progPath   <d:\\path\\to\\prog>\n" + super.usage());
      }
  
      /** Array of 'command line' args for Process.main(args) calls. */
      String[] pargs = null;
  
      /**
       * Parameter: Actual name of external program to call.
       * <p>Default: TestXSLT</p>
       */
      String progName = "TestXSLT";
  
      /**
       * Parameter: Actual name of external program to call.
       * <p>Default: TestXSLT</p>
       */
      public static final String OPT_PROGNAME = "progName";
  
      /**
       * Path to external program to call.
       * <p>Default: blank string</p>
       */
      String progPath = "";
  
      /**
       * Path to external program to call.
       * <p>Default: blank string</p>
       */
      public static final String OPT_PROGPATH = "progPath";
  
      /**
       * Parameter: -precompile If we should precompile (and
       * serialize) stylesheets separately first.
       * <p>Default: false.  Note: Likely not supported in early C++ releases.</p>
       */
      boolean precompile = false;
  
      /**
       * Default constructor - initialize testName, Comment.
       */
      public CConformanceTest()
      {
  
          testName = "CConformanceTest";
          testComment =
              "Iterates over all conf test dirs and validates outputs using Xalan-C";
      }
  
      /**
       * Initialize this test - process our input args and construct
       * array of command line args for processor.
       * @todo make this table-driven for the argument names
       * @todo update to include all supported args
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileInit(Properties p)
      {
  
          // Validate/setup progName, progPath
          progName = testProps.getProperty(OPT_PROGNAME, progName);
          progPath = testProps.getProperty(OPT_PROGPATH, progPath);
  
          reporter.logTraceMsg("progPath\\progName= " + progPath
                               + File.separator + progName);
  
          // Attempt to check if it exists (but only in certain cases)
          if ((progPath != null) && (progPath.length() > 0))
          {
              File progF = new File(progPath + File.separator + progName);
  
              if (!progF.exists())
              {
                  reporter.logErrorMsg("Program may not exist! " + progPath
                                       + File.separator + progName);
              }
          }
          else
          {
  
              // No-op: can't easily validate programs (presumably) found on the PATH
          }
  
          Vector vec = new Vector();
  
          // Set up pargs with global args from our inputs; used in every process
          // Standard args that XLTest supports:
          if ((liaison == null) || ("".equals(liaison)))
          {
  
              // no-op, don't use null or blank liaison
          }
          else
          {
  
              // setup args for liaison - note must be fully qualified classname
              vec.addElement("-PARSER");
              vec.addElement(liaison);
          }
  
          // Only pass the indent if it was explicitly set
          if (indentLevel > NO_INDENT)
          {
              vec.addElement("-INDENT");
              vec.addElement(Integer.toString(indentLevel));
          }
  
          // precompile is supported in processSingleFile
          // Unsupported: flavor - not very useful!
          // Unsupported: diagName
          // Unsupported: noReuse
          // Gadzillions of args that Process supports:
          setSingleArg("E", testProps, vec);  //"   [-E (Do not expand entity refs)]");
          setSingleArg("V", testProps, vec);  //"   [-V (Version info)]");
          setSingleArg("QC", testProps, vec);  //"   [-QC (Quiet Pattern Conflicts Warnings)]");
          setSingleArg("Q", testProps, vec);  //"   [-Q  (Quiet Mode)]");
          setSingleArg("LF", testProps, vec);  //"   [-LF (Use linefeeds only on output {default is CR/LF})]");
          setSingleArg("CR", testProps, vec);  //"   [-CR (Use carriage returns only on output {default is CR/LF})]");
          setSingleArg("TT", testProps, vec);  //"   [-TT (Trace the templates as they are being called.)]");
          setSingleArg("TG", testProps, vec);  //"   [-TG (Trace each generation event.)]");
          setSingleArg("TS", testProps, vec);  //"   [-TS (Trace each selection event.)]");
          setSingleArg("TTC", testProps, vec);  //"   [-TTC (Trace the template children as they are being processed.)]");
          setSingleArg("VALIDATE", testProps, vec);  //"   [-VALIDATE (Set whether validation occurs.  Validation is off by default.)]");
          setSingleArg("XML", testProps, vec);  //"   [-XML (Use XML formatter and add XML header.)]");
          setSingleArg("TEXT", testProps, vec);  //"   [-TEXT (Use simple Text formatter.)]");
          setSingleArg("HTML", testProps, vec);  //"   [-HTML (Use HTML formatter.)]");
          setSingleArg("SX", testProps, vec);  //"   [-SX (Xerces serializers)]");
  
          // FIXME: Args not yet tested    
          //"   [-ESCAPE (Which characters to escape {default is <>&\"\'\\r\\n}]");
          //"   [-TCLASS (TraceListener class for trace extensions.)]");
          //"   [-EDUMP {optional filename} (Do stackdump on error.)]");
          //"   [-PARAM name expression (Set a stylesheet parameter)]");
          // Copy the vector into array
          pargs = new String[vec.size()];
  
          vec.copyInto(pargs);
          reporter.logTraceMsg("Default arguments vector: " + vec.toString());
  
          return true;
      }
  
      /**
       * Worker method to add a single "-"arg if found in properties block.  
       *
       * NEEDSDOC @param name
       * NEEDSDOC @param p
       * NEEDSDOC @param v
       */
      protected void setSingleArg(String name, Properties p, Vector v)
      {
  
          final String tmp = (String) p.get(name);
  
          if (tmp != null)  // Note: adds it even if it's blank!
          {
              v.addElement("-" + name);
          }
      }
  
      /**
       * Subclassed callback to provide info about the processor you're testing.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getProcessorDescription()
      {
          return "ProcessorVersion;command line TestXSLT.exe:Xalan only";
      }
  
      /**
       * Run through the directory given to us and run tests found in subdirs.
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean runTestCases(Properties p)
      {
  
          // Use all the default directory processing methods 
          // from our parent XLDirectoryIterator
          processTestDir();
  
          return true;
      }
  
      /**
       * Run one xsl/xml file pair through the processor.
       * <p>We simply call Process.main() with a constructed series
       * of args[] using Runtime.exec().</p>
       * <p>Note that timings are in no way comparable to the normal
       * ConformanceTest since this is C++, not Java, and since we have
       * the overhead of shelling out the process and processing
       * the command line.</p>
       *
       * NEEDSDOC @param XMLName
       * NEEDSDOC @param XSLName
       * NEEDSDOC @param OutName
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int processSingleFile(String XMLName, String XSLName,
                                   String OutName)
      {
  
          long fileTime = ProcessorWrapper.ERROR;
  
          try
          {
              if (precompile)
              {
                  reporter.checkFail(
                      "Sorry, precompile option not supported yet!");
  
                  return UNEXPECTED_EXCEPTION;
              }  // of if (precompile)
  
              String args[] = new String[pargs.length + 7];  // progName -in XML -xsl XSL -out OUT
  
              if ((progPath != null) && (progPath.length() > 0))
              {
                  args[0] = progPath + File.separator + progName;
              }
              else
              {
  
                  // Pesume the program is on the PATH already...
                  args[0] = progName;
              }
  
              args[1] = "-in";
              args[2] = translateInputName(XMLName);
              args[3] = "-xsl";
              args[4] = translateInputName(XSLName);
              args[5] = "-out";
              args[6] = translateOutputName(OutName);
  
              System.arraycopy(pargs, 0, args, 7, pargs.length);
  
              if (debug)
              {
                  for (int x = 0; x < args.length; x++)
                  {
                      reporter.logTraceMsg("arg[" + x + "]=" + args[x]);
                  }
              }
  
              // Declare variables ahead of time to minimize latency
              long startTime = 0;
              long endTime = 0;
              int returnVal = 0;
  
              startTime = System.currentTimeMillis();
  
              // Use our worker method to execute the process
              returnVal = execProcess(args, null);
              endTime = System.currentTimeMillis();
              fileTime = endTime - startTime;
  
              // if the execution of the process was basically OK, 
              //  then add this file to overall timing info
              if (returnVal != ProcessorWrapper.ERROR)
              {
                  dirTime += fileTime;
  
                  dirFilesProcessed++;
  
                  reporter.logTraceMsg("processSingleFile(" + XSLName
                                       + ") no exceptions; time " + fileTime);
                  reporter.logInfoMsg("processSingleFile(" + XSLName
                                      + ") returnValue =  " + returnVal);
              }
              else
              {
  
                  // Do not increment performance counters if there's an error
                  // Note: can this code ever really be executed?
                  reporter.logWarningMsg("processSingleFile(" + XSLName
                                         + ") returned ERROR code!");
              }
          }
  
          // Catch general Exceptions, check if they're expected, and restart
          catch (Exception e)
          {
              reporter.logStatusMsg("processSingleFile(" + XSLName
                                    + ") threw: " + e.toString());
  
              int retVal = checkExpectedException(e, XSLName, OutName);
  
              return retVal;
          }
  
          // Catch any Throwable, check if they're expected, and restart
          catch (Throwable t)
          {
              reporter.logStatusMsg("processSingleFile(" + XSLName
                                    + ") threw: " + t.toString());
  
              int retVal = checkExpectedException(t, XSLName, OutName);
  
              return retVal;
          }
  
          return PROCESS_OK;
      }
  
      /**
       * Worker method to translate filenames into appropriate format for C program.
       * <p>Always returns a string - will be blank if the file does not exist
       * or if we can't figure out the appropriate name to use</p>
       * @param name of file
       * @return appropriate translation into format that the .exe expects for -in, -xsl
       */
      public String translateInputName(String name)
      {
  
          StringBuffer ret = new StringBuffer("file:///");
          File f = new File(name);
  
          try
          {
  
              // This gives the best path, I think, but may throw exceptions
              ret.append(f.getCanonicalPath());
          }
          catch (Exception e)
          {
  
              // This should work in most cases
              ret.append(f.getAbsolutePath());
          }
  
          return ret.toString();
      }
  
      /**
       * Worker method to translate filenames into appropriate format for C program.
       * <p>Always returns a string - will be blank if the file does not exist
       * or if we can't figure out the appropriate name to use</p>
       * @param name of file
       * @return appropriate translation into format that the .exe expects for -out
       */
      public String translateOutputName(String name)
      {
  
          // Hopefully, we don't need translation on the output name
          return name;
      }
  
      /**
       * Simple child thread for reading an InputStream.
       * Used to capture the System.err and System.out streams
       * from the executed process - without hanging or blocking.
       */
      public class ThreadedStreamReader extends Thread
      {  // Begin of inner class
  
          /** NEEDSDOC Field is          */
          BufferedReader is = null;
  
          /** NEEDSDOC Field sb          */
          StringBuffer sb = null;
  
          /**
           * NEEDSDOC Method setInputStream 
           *
           *
           * NEEDSDOC @param set
           */
          public void setInputStream(BufferedReader set)
          {
              is = set;
          }
  
          /**
           * NEEDSDOC Method run 
           *
           */
          public void run()
          {
  
              sb = new StringBuffer();
  
              // Note that reporters may not be threadsafe, so we should avoid there use herein
              if (is == null)
              {
                  sb.append(
                      "ERROR! ThreadedStreamReader.run() with setInputStream(null)");
  
                  return;
              }
  
              sb.append("<tsrbuf>");
  
              String i = null;
  
              try
              {
                  i = is.readLine();
              }
              catch (IOException ioe1)
              {  // Presumably the stream is bad, so just bag out
                  i = null;
              }
  
              while (i != null)
              {
                  sb.append(i);
  
                  try
                  {
                      i = is.readLine();
                  }
                  catch (IOException ioe2)
                  {  // Presumably the stream is bad, so just bag out
                      i = null;
                  }
              }
  
              sb.append("</tsrbuf>");
          }
  
          /**
           * NEEDSDOC Method getBuffer 
           *
           *
           * NEEDSDOC (getBuffer) @return
           */
          public StringBuffer getBuffer()
          {
              return sb;
          }
      }
      ;  // End of inner class
  
      /**
       * Worker method to shell out an external process.
       * <p>Does a simple capturing of the out and err streams from
       * the process.  Inherits the same environment that the current
       * JVM is in.</p>
       * @param cmdLine actual command line to run, including program name
       * NEEDSDOC @param environment
       * @return return value from program
       * @exception Exception may be thrown by Runtime.exec
       */
      public int execProcess(String[] cmdLine, String[] environment)
              throws Exception
      {
  
          // @todo check the logic here: will '-1' be a likely 
          //  return value from the process anyways?
          // this is needed in our caller to see if they should 
          //  use this process as part of timing data
          int retVal = (new Long(ProcessorWrapper.ERROR)).intValue();
  
          if ((cmdLine == null) || (cmdLine.length < 1))
          {
              reporter.logErrorMsg(
                  "execProcess called with null/blank arguments!");
  
              return retVal;
          }
  
          int bufSize = 2048;
          ThreadedStreamReader outReader = new ThreadedStreamReader();
          ThreadedStreamReader errReader = new ThreadedStreamReader();
          Runtime r = Runtime.getRuntime();
          java.lang.Process proc = null;  // Fully declare to not conflict with org.apache.xalan.xslt.Process
  
          // Actually begin executing the program
          reporter.logTraceMsg("execProcess starting " + cmdLine[0]);
  
          proc = r.exec(cmdLine, environment);
  
          // Immediately begin capturing any output therefrom
          outReader.setInputStream(
              new BufferedReader(
                  new InputStreamReader(proc.getInputStream()), bufSize));
          errReader.setInputStream(
              new BufferedReader(
                  new InputStreamReader(proc.getErrorStream()), bufSize));
  
          // Start two thread off on reading the System.out and System.err from proc
          outReader.start();
          errReader.start();
  
          try
          {
  
              // Wait for the process to exit normally
              retVal = proc.waitFor();
          }
          catch (InterruptedException ie1)
          {
              reporter.logWarningMsg("execProcess proc.waitFor() threw: "
                                     + ie1.toString());
          }
  
          // Now that we're done, presumably the Readers are also done
          String sysOut = "No System.out captured";
          String sysErr = "No System.err captured";
  
          try
          {
              outReader.join();
  
              sysOut = outReader.getBuffer().toString();
          }
          catch (InterruptedException ie2)
          {
              reporter.logWarningMsg("Joining outReader threw: "
                                     + ie2.toString());
          }
  
          try
          {
              errReader.join();
  
              sysErr = errReader.getBuffer().toString();
          }
          catch (InterruptedException ie3)
          {
              reporter.logWarningMsg("Joining errReader threw: "
                                     + ie3.toString());
          }
  
          reporter.logArbitrary(reporter.INFOMSG,
                                "proc.System.out was: " + sysOut);
          reporter.logArbitrary(reporter.INFOMSG,
                                "proc.System.err was: " + sysErr);
          reporter.logTraceMsg("execProcess exitVal=" + retVal);
  
          return retVal;
      }
  
      /**
       * Main method to run test from the command line - can be left alone.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          CConformanceTest app = new CConformanceTest();
  
          app.doMain(args);
      }
  }  // end of class CConformanceTest
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/ConformanceDirRules.java
  
  Index: ConformanceDirRules.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.qetest.xsl;
  
  import java.io.FilenameFilter;
  import java.io.File;
  
  import java.util.Hashtable;
  
  /**
   * Returns directories that are either on an inclusion list, or
   * just ones that don't begin with [x|X], or are 'CVS'.
   * @author shane_curcuru@lotus.com
   * @version $Id: ConformanceDirRules.java,v 1.1 2000/11/01 23:26:56 curcuru Exp $
   */
  public class ConformanceDirRules implements FilenameFilter
  {
  
      /** Initialize for defaults (not using inclusion list) no-op. */
      public ConformanceDirRules(){}
  
      /**
       * Initialize with a case-sensitive Hash of directory names to include.  
       *
       * NEEDSDOC @param iDirs
       */
      public ConformanceDirRules(Hashtable iDirs)
      {
          setIncludeDirs(iDirs);
      }
  
      /**
       * Initialize with a case-insensitive String directory name to include.  
       *
       * NEEDSDOC @param incDir
       */
      public ConformanceDirRules(String incDir)
      {
          setIncludeDirs(incDir);
      }
  
      /**
       * Hash of directory names to include.
       * <p>Keys are dir names, values in hash are ignored. Note that
       * directory names are case-sensitive.  If list is not set somehow,
       * then we return all dirs that don't begin with [x|X].</p>
       */
      protected Hashtable includeDirs = null;
  
      /** Exclude CVS repository dirs always. */
      public static final String CVS = "CVS";
  
      /**
       * Accessor methods for case-sensitive Hash of directory names to include.  
       *
       * NEEDSDOC @param iDirs
       */
      public void setIncludeDirs(Hashtable iDirs)
      {
  
          if (iDirs != null)
              includeDirs = (Hashtable) iDirs.clone();
          else
              includeDirs = null;
      }
  
      /**
       * Accessor methods for case-sensitive Hash of directory names to include.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public Hashtable getIncludeDirs()
      {
  
          if (includeDirs != null)
          {
              Hashtable tempHash = (Hashtable) includeDirs.clone();
  
              return (tempHash);
          }
          else
          {
              return null;
          }
      }
  
      /**
       * Accessor method to set a case-insensitive String directory name to include.  
       *
       * NEEDSDOC @param incDir
       */
      public void setIncludeDirs(String incDir)
      {
          setIncludeDirs(incDir, false);
      }
  
      /**
       * Accessor method to set an optionally case-sensitive String directory name to include.
       * <p><b>Note:</b> simply uses .toUpperCase() and .toLowerCase() on the input string;
       * does not do full case-checking on the entire string!</p>
       *
       * NEEDSDOC @param incDir
       * NEEDSDOC @param caseSensitive
       */
      public void setIncludeDirs(String incDir, boolean caseSensitive)
      {
  
          if ((incDir != null) && (incDir != ""))
          {
              includeDirs = null;
              includeDirs = new Hashtable();
  
              includeDirs.put(incDir, "");
  
              if (!caseSensitive)
              {
                  includeDirs.put(incDir.toUpperCase(), "");
                  includeDirs.put(incDir.toLowerCase(), "");
              }
          }
          else
          {
              includeDirs = null;
          }
      }
  
      /**
       * Tests if a specified file should be included in a file list.
       * Returns only directories that are on our inclusion list.
       * Currently may be case-sensitive or insensitive, depending on
       * how/if our inclusion list was set.
       * @param dir the directory in which the file was found.
       * @param name the name of the file.
       * @return <code>true</code> if the name should be included in the file list; <code>false</code> otherwise.
       * @since JDK1.0
       */
      public boolean accept(File dir, String name)
      {
  
          // Shortcut to only look at directories
          File file = new File(dir, name);
  
          if (!file.isDirectory())
              return (false);
  
          // If we have an inclusion list, just look at that
          if (includeDirs != null)
          {
              if (includeDirs.containsKey(name))
                  return (true);
              else
                  return (false);
          }
  
          // Otherwise, exclude any other names that begin with [x|X]
          char firstChar = name.charAt(0);
  
          if ((firstChar == 'x') || (firstChar == 'X'))
              return (false);
          else if (CVS.equals(name))  // ALSO: exclude "CVS" dirs from our source control
              return (false);
          else
              return (true);
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/ConformanceErrFileRules.java
  
  Index: ConformanceErrFileRules.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.qetest.xsl;
  
  import java.io.FilenameFilter;
  import java.io.File;
  
  /**
   * Simple file filter; returns *.xsl non-dir files that match a parent + directory name.
   * @author shane_curcuru@lotus.com
   * @version $Id: ConformanceErrFileRules.java,v 1.1 2000/11/01 23:26:56 curcuru Exp $
   */
  public class ConformanceErrFileRules implements FilenameFilter
  {
  
      /**
       * Initialize with a case-insensitive name of parent directory.  
       *
       * NEEDSDOC @param p
       */
      public ConformanceErrFileRules(String p)
      {
          if (p != null)
              parentName = p;
      }
  
      /** Case-insensitive name of parent directory; used in {@link accept(java.io.File, java.lang.String)}. */
      protected String parentName = "";
  
      /**
       * Tests if a specified file should be included in a file list.
       * <p>Returns true only for *.xsl files whose names start with
       * the name of the parent + name of the directory, case-insensitive (uses .toLowerCase()).</p>
       * @param dir the directory in which the file was found.
       * @param name the name of the file.
       * @return <code>true</code> if the name should be included in the file list; <code>false</code> otherwise.
       * @since JDK1.0
       */
      public boolean accept(File dir, String name)
      {
  
          if (name == null || dir == null)
              return (false);
  
          File file = new File(dir, name);
  
          return (!file.isDirectory()) && name.toLowerCase().endsWith("xsl")
                 && name.toLowerCase().startsWith(
                     parentName.toLowerCase() + dir.getName().toLowerCase());
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/ConformanceFileRules.java
  
  Index: ConformanceFileRules.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.qetest.xsl;
  
  import java.io.FilenameFilter;
  import java.io.File;
  
  import java.util.Hashtable;
  import java.util.StringTokenizer;
  
  /**
   * Simple file filter; returns *.xsl non-dir files that start with the directory name.
   * Has crude support for an excludes list of filename bases.
   * @author shane_curcuru@lotus.com
   * @version $Id: ConformanceFileRules.java,v 1.1 2000/11/01 23:26:56 curcuru Exp $
   */
  public class ConformanceFileRules implements FilenameFilter
  {
  
      /** Initialize for defaults (not using inclusion list) no-op. */
      public ConformanceFileRules(){}
  
      /**
       * Initialize with a case-sensitive Hash of file names to exclude.  
       *
       * NEEDSDOC @param excludesHash
       */
      public ConformanceFileRules(Hashtable excludesHash)
      {
          setExcludes(excludesHash);
      }
  
      /**
       * Initialize with a case-insensitive semicolon-delimited String of file names to exclude.  
       *
       * NEEDSDOC @param excludesStr
       */
      public ConformanceFileRules(String excludesStr)
      {
          setExcludes(excludesStr);
      }
  
      /**
       * Hash of file name portions to exclude.
       * <p>Keys are base file names, values in hash are ignored. Note that
       * file names may be case-sensitive.</p>
       * <p>Note that we will exclude any filename in our excludes.</p>
       */
      protected Hashtable excludeFiles = null;
  
      /**
       * Accessor methods to set a case-sensitive Hash of file names to exclude.  
       *
       * NEEDSDOC @param exFiles
       */
      public void setExcludes(Hashtable exFiles)
      {
  
          if (exFiles != null)
              excludeFiles = (Hashtable) exFiles.clone();
          else
              excludeFiles = null;
      }
  
      /**
       * Accessor methods to set a case-sensitive Hash of file names to exclude.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public Hashtable getExcludes()
      {
  
          if (excludeFiles != null)
          {
              Hashtable tempHash = (Hashtable) excludeFiles.clone();
  
              return tempHash;
          }
          else
          {
              return null;
          }
      }
  
      /**
       * Accessor method to set a list of case-insensitive String
       * directory name(s) to exclude.
       * Names should be separated by {@link #SEPARATOR semicolon}.
       *
       * NEEDSDOC @param exFiles
       */
      public void setExcludes(String exFiles)
      {
          setExcludes(exFiles, false);
      }
  
      /** Semicolon separator for {@link #setExcludes(java.lang.String)}. */
      public static final String SEPARATOR = ";";
  
      /**
       * Accessor method to set an optionally case-sensitive String file name(s) to exclude.
       * <p><b>Note:</b> simply uses .toUpperCase() and .toLowerCase() on the input string(s);
       * does not do full case-checking on the entire string!</p>
       *
       * NEEDSDOC @param exFiles
       * NEEDSDOC @param caseSensitive
       */
      public void setExcludes(String exFiles, boolean caseSensitive)
      {
  
          StringTokenizer st = new StringTokenizer(exFiles, SEPARATOR);
  
          excludeFiles = null;
          excludeFiles = new Hashtable();
  
          for (int i = 0; st.hasMoreTokens(); i++)
          {
              String fName = st.nextToken();
  
              excludeFiles.put(fName, "");
  
              if (!caseSensitive)
              {
                  excludeFiles.put(fName.toUpperCase(), "");
                  excludeFiles.put(fName.toLowerCase(), "");
              }
          }
      }
  
      /**
       * Tests if a specified file should be included in a file list.
       * <p>Returns true only for *.xsl files whose names start with
       * the name of the directory, case-insensitive (uses .toLowerCase()).
       * <b>Except:</b> if any filenames contain an item in excludeFiles.</p>
       * @param dir the directory in which the file was found.
       * @param name the name of the file.
       * @return <code>true</code> if the name should be included in the file list; <code>false</code> otherwise.
       * @since JDK1.0
       */
      public boolean accept(File dir, String name)
      {
  
          // Shortcuts for bogus filenames and dirs
          if (name == null || dir == null)
              return false;
  
          // Exclude any files that match an exclude rule
          if ((excludeFiles != null) && (excludeFiles.containsKey(name)))
              return false;
  
          File file = new File(dir, name);
  
          return (!file.isDirectory()) && name.toLowerCase().endsWith("xsl")
                 && name.toLowerCase().startsWith(dir.getName().toLowerCase());
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/ConformanceTest.java
  
  Index: ConformanceTest.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * ConformanceTest.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.*;
  
  import java.io.File;
  import java.io.FilenameFilter;
  
  import java.util.Properties;
  
  //-------------------------------------------------------------------------
  
  /**
   * New, Improved! ConformanceTest.
   * <p>Note that other than setting up our name and comment, and determining
   * the processor's description, we simply use all the default implementations
   * of methods from XSLDirectoryIterator.</p>
   * @author shane_curcuru@lotus.com
   */
  public class ConformanceTest extends XSLDirectoryIterator
  {
  
      /**
       * Default constructor - initialize testName, Comment.
       */
      public ConformanceTest()
      {
  
          testName = "ConformanceTest";
          testComment =
              "Iterates over all conf test dirs and validates outputs";
      }
  
      /**
       * Initialize this test - setup description and createNewProcessor.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileInit(Properties p)
      {
  
          // Create a processor with our appropriate flavor, etc.
          if (!createNewProcessor())
          {
              reporter.logErrorMsg(
                  "Could not createNewProcessor before testing; it won't work!");
  
              return false;
          }
  
          return true;
      }
  
      /**
       * Run through the directory given to us and run tests found
       * in subdirs; or run through our fileList.
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean runTestCases(Properties p)
      {
  
          // The ConformanceTest simply uses all the default processing methods 
          //  from our parent XSLDirectoryIterator
          // Note that we skip executeTests and the number of 
          //  test cases we have, since we just iterate over 
          //  fileLists or directories
          processTestDir();
  
          return true;
      }
  
      /**
       * Main method to run test from the command line - can be left alone.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          ConformanceTest app = new ConformanceTest();
  
          app.doMain(args);
      }
  }  // end of class ConformanceTest
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/LoggingEntityResolver.java
  
  Index: LoggingEntityResolver.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * LoggingEntityResolver.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.*;
  
  import java.io.IOException;
  
  import org.xml.sax.EntityResolver;
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  
  //-------------------------------------------------------------------------
  
  /**
   * Implementation of EntityResolver that logs all calls.
   * Currently just provides default service; returns null.
   * @author shane_curcuru@lotus.com
   * @version $Id: LoggingEntityResolver.java,v 1.1 2000/11/01 23:26:56 curcuru Exp $
   */
  public class LoggingEntityResolver implements EntityResolver
  {
  
      /** No-op ctor since it's often useful to have one. */
      public LoggingEntityResolver(){}
  
      /**
       * Ctor that calls setReporter automatically.  
       *
       * NEEDSDOC @param r
       */
      public LoggingEntityResolver(Reporter r)
      {
          setReporter(r);
      }
  
      /** Our Reporter, who we tell all our secrets to. */
      private Reporter reporter;
  
      /**
       * Accesor methods for our Reporter.  
       *
       * NEEDSDOC @param r
       */
      public void setReporter(Reporter r)
      {
          if (r != null)
              reporter = r;
      }
  
      /**
       * Accesor methods for our Reporter.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public Reporter getReporter()
      {
          return (reporter);
      }
  
      /** Prefixed to all reporter msg output. */
      private String prefix = "ER:";
  
      /** Counters for how many entities we've 'resolved'. */
      private int entityCtr = 0;
  
      /**
       * Accesor methods for entity counter.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getEntityCtr()
      {
          return entityCtr;
      }
  
      /**
       * Cheap-o string representation of our state.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getCounterString()
      {
          return (prefix + "Entities: " + getEntityCtr());
      }
  
      /** Cheap-o string representation of last entity we resolved. */
      private String lastEntity = null;
  
      /**
       * NEEDSDOC Method setLastEntity 
       *
       *
       * NEEDSDOC @param s
       */
      protected void setLastEntity(String s)
      {
          lastEntity = s;
      }
  
      /**
       * Accessor for string representation of last entity we resolved.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getLastEntity()
      {
          return lastEntity;
      }
  
      /** What loggingLevel to use for reporter.logMsg(). */
      private int level = Reporter.DEFAULT_LOGGINGLEVEL;
  
      /**
       * Accesor methods; don't think it needs to be synchronized.  
       *
       * NEEDSDOC @param l
       */
      public void setLoggingLevel(int l)
      {
          level = l;
      }
  
      /**
       * Accesor methods; don't think it needs to be synchronized.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getLoggingLevel()
      {
          return level;
      }
  
      /**
       * Implement this method: just returns null for now.
       * Also saves the last entity for later retrieval, and counts
       * how many entities we've 'resolved' overall.
       * @todo have a settable property to actually return as the InputSource
       *
       * NEEDSDOC @param publicId
       * NEEDSDOC @param systemId
       *
       * NEEDSDOC ($objectName$) @return
       * @exception SAXException never thrown
       * @exception IOException never thrown
       */
      public InputSource resolveEntity(String publicId, String systemId)
              throws SAXException, IOException
      {
  
          entityCtr++;
  
          setLastEntity(publicId + ";" + systemId);
  
          if (reporter != null)
          {
              reporter.logMsg(level,
                              prefix + getLastEntity() + " "
                              + getCounterString());
          }
  
          return null;
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/LoggingSAXErrorHandler.java
  
  Index: LoggingSAXErrorHandler.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * LoggingSAXErrorHandler.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.*;
  
  import org.xml.sax.ErrorHandler;
  import org.xml.sax.SAXException;
  import org.xml.sax.SAXParseException;
  
  //-------------------------------------------------------------------------
  
  /**
   * Cheap-o ErrorHandler for use by API tests.
   * <p>Implements org.xml.sax.ErrorHandler and dumps everything to a Reporter.</p>
   * @author shane_curcuru@lotus.com
   * @version $Id: LoggingSAXErrorHandler.java,v 1.1 2000/11/01 23:26:57 curcuru Exp $
   */
  public class LoggingSAXErrorHandler implements ErrorHandler
  {
  
      /** No-op ctor seems useful. */
      public LoggingSAXErrorHandler(){}
  
      /**
       * Ctor that calls setReporter automatically.  
       *
       * NEEDSDOC @param r
       */
      public LoggingSAXErrorHandler(Reporter r)
      {
          setReporter(r);
      }
  
      /** Our Reporter, who we tell all our secrets to. */
      private Reporter reporter;
  
      /**
       * Accesor methods for our Reporter.  
       *
       * NEEDSDOC @param r
       */
      public void setReporter(Reporter r)
      {
          if (r != null)
              reporter = r;
      }
  
      /**
       * Accesor methods for our Reporter.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public Reporter getReporter()
      {
          return (reporter);
      }
  
      /** Prefixed to all reporter msg output. */
      private String prefix = "SEH:";
  
      /** Constants determining when we should throw exceptions. */
      public static final int THROW_NEVER = 0;
  
      /** NEEDSDOC Field THROW_ON_WARNING          */
      public static final int THROW_ON_WARNING = 1;
  
      /** NEEDSDOC Field THROW_ON_ERROR          */
      public static final int THROW_ON_ERROR = 2;
  
      /** NEEDSDOC Field THROW_ON_FATAL          */
      public static final int THROW_ON_FATAL = 4;
  
      /** NEEDSDOC Field THROW_ALWAYS          */
      public static final int THROW_ALWAYS = THROW_ON_WARNING & THROW_ON_ERROR
                                             & THROW_ON_FATAL;
  
      /** If we should throw an exception for each message type. */
      private int throwWhen = THROW_ON_FATAL;
  
      /**
       * Accesor methods; don't think it needs to be synchronized.  
       *
       * NEEDSDOC @param t
       */
      public void setThrowWhen(int t)
      {
          throwWhen = t;
      }
  
      /**
       * Accesor methods; don't think it needs to be synchronized.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getThrowWhen()
      {
          return throwWhen;
      }
  
      /** Counters for how many problems or messages we've processed. */
      private int warningCtr = 0;
  
      /** NEEDSDOC Field errorCtr          */
      private int errorCtr = 0;
  
      /** NEEDSDOC Field fatalCtr          */
      private int fatalCtr = 0;
  
      /**
       * Accesor methods for counters.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getWarningCtr()
      {
          return warningCtr;
      }
  
      /**
       * Accesor methods for counters.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getErrorCtr()
      {
          return errorCtr;
      }
  
      /**
       * Accesor methods for counters.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getFatalCtr()
      {
          return fatalCtr;
      }
  
      /**
       * Cheap-o string representation of our state.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getCounterString()
      {
          return (prefix + " Warnings: " + getWarningCtr() + ", Errors: "
                  + getErrorCtr() + ", FatalErrors: " + getFatalCtr());
      }
  
      /** Cheap-o string representation of last warn/error/fatal we got. */
      private String lastError = null;
  
      /**
       * NEEDSDOC Method setLastError 
       *
       *
       * NEEDSDOC @param s
       */
      protected void setLastError(String s)
      {
          lastError = s;
      }
  
      /**
       * Accessor for string representation of last warn/error/fatal we got.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getLastError()
      {
          return lastError;
      }
  
      /** What loggingLevel to use for reporter.logMsg(). */
      private int level = Reporter.DEFAULT_LOGGINGLEVEL;
  
      /**
       * Accesor methods; don't think it needs to be synchronized.  
       *
       * NEEDSDOC @param l
       */
      public void setLoggingLevel(int l)
      {
          level = l;
      }
  
      /**
       * Accesor methods; don't think it needs to be synchronized.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public int getLoggingLevel()
      {
          return level;
      }
  
      /**
       * Grab basic info out of a SAXParseException.
       *
       * NEEDSDOC @param exception
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getParseExceptionInfo(SAXParseException exception)
      {
  
          if (exception == null)
              return null;
  
          String retVal = new String("");
          String tmp;
  
          tmp = exception.getPublicId();
  
          if (tmp != null)
              retVal += " publicID:" + tmp;
  
          tmp = exception.getSystemId();
  
          if (tmp != null)
              retVal += " systemId:" + tmp;
  
          try
          {
              tmp = Integer.toString(exception.getLineNumber());
          }
          catch (NumberFormatException nfe)
          {
              tmp = null;
          }
  
          if (tmp != null)
              retVal += " lineNumber:" + tmp;
  
          try
          {
              tmp = Integer.toString(exception.getColumnNumber());
          }
          catch (NumberFormatException nfe)
          {
              tmp = null;
          }
  
          if (tmp != null)
              retVal += " columnNumber:" + tmp;
  
          tmp = exception.getMessage();  // Will grab inner message if needed
  
          if (tmp != null)
              retVal += " message:" + tmp;
  
          return retVal;
      }
  
      /**
       * Implementation of warning; calls logMsg with info contained in exception.
       *
       * NEEDSDOC @param exception
       * @exception SAXException thrown only if asked to or if reporters are bad
       */
      public void warning(SAXParseException exception) throws SAXException
      {
  
          // Increment counter, save the exception, and log what we got
          warningCtr++;
  
          String exInfo = getParseExceptionInfo(exception);
  
          setLastError(exInfo);
  
          if (reporter != null)
          {
              reporter.logMsg(level, prefix + " warning threw: " + exInfo);
              reporter.logMsg(level, getCounterString());
          }
  
          if ((throwWhen & THROW_ON_WARNING) == THROW_ON_WARNING)
          {
              throw new SAXException(exception);
          }
      }
  
      /**
       * Implementation of error; calls logMsg with info contained in exception.
       * Only ever throws an exception itself if asked to or if reporters are bad.
       *
       * NEEDSDOC @param exception
       * @exception SAXException thrown only if asked to or if reporters are bad
       */
      public void error(SAXParseException exception) throws SAXException
      {
  
          // Increment counter, save the exception, and log what we got
          errorCtr++;
  
          String exInfo = getParseExceptionInfo(exception);
  
          setLastError(exInfo);
  
          if (reporter != null)
          {
              reporter.logMsg(level, prefix + " error threw: " + exInfo);
              reporter.logMsg(level, getCounterString());
          }
  
          if ((throwWhen & THROW_ON_ERROR) == THROW_ON_ERROR)
          {
              throw new SAXException(exception);
          }
      }
  
      /**
       * Implementation of error; calls logMsg with info contained in exception.
       * Only ever throws an exception itself if asked to or if reporters are bad.
       * Note that this may cause unusual behavior since we may not actually
       * re-throw the exception, even though it was 'fatal'.
       *
       * NEEDSDOC @param exception
       * @exception SAXException thrown only if asked to or if reporters are bad
       */
      public void fatalError(SAXParseException exception) throws SAXException
      {
  
          // Increment counter, save the exception, and log what we got
          fatalCtr++;
  
          String exInfo = getParseExceptionInfo(exception);
  
          setLastError(exInfo);
  
          if (reporter != null)
          {
              reporter.logMsg(level, prefix + " fatal threw: " + exInfo);
              reporter.logMsg(level, getCounterString());
          }
  
          if ((throwWhen & THROW_ON_FATAL) == THROW_ON_FATAL)
          {
              throw new SAXException(exception);
          }
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/PerformanceTest.java
  
  Index: PerformanceTest.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * PerformanceTest.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.*;
  import org.apache.qetest.xslwrapper.*;
  
  import java.io.File;
  import java.io.FilenameFilter;
  
  import java.util.Properties;
  
  //-------------------------------------------------------------------------
  
  /**
   * New, Improved! PerformanceTest - benchmark repetitive timings of various processors.
   * <p>While the test harness does add a small amount of overhead to the benchmark
   * process, this makes reporting and running the test much easier.</p>
   * @author shane_curcuru@lotus.com
   * @todo improve verification; make calling fileChecker conditional,
   * perhaps only every 10 iterations or something
   */
  public class PerformanceTest extends XSLDirectoryIterator
  {
  
      /**
       * Parameter: Should we preload a single process before timing iterations?
       * <p>Default: false. Format: -preload true|false</p>
       */
      public static final String OPT_PRELOAD = "preload";
  
      /** NEEDSDOC Field preload          */
      protected boolean preload = false;
  
      /**
       * Parameter: How many iterations should we make for each file?
       * <p>Default: 3. Format: -iterations int</p>
       */
      public static final String OPT_ITERATIONS = "iterations";
  
      /** NEEDSDOC Field iterations          */
      protected int iterations = 3;
  
      /** Markers for performance logging - Preload time. */
      public static final String PERF_PRELOAD = "UPre";
  
      /** Markers for performance logging - single iteration time. */
      public static final String PERF_ITERATION = "UItr";
  
      /** Markers for performance logging - average of iteration times. */
      public static final String PERF_AVERAGE = "UAvg";
  
      /** Markers for memory logging. */
      public static final String PERF_MEMORY = "UMem";
  
      /** Default ctor initializes test name, comment. */
      public PerformanceTest()
      {
  
          testName = "PerformanceTest";
          testComment =
              "Iterates over all perf test dirs and writes timing info";
      }
  
      /**
       * Initialize this test - setup description and createNewProcessor.  
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean doTestFileInit(Properties p)
      {
  
          // HACK Explicitly set diagnostics to null
          //  If we leave it set, then this will affect the processorWrapper
          //  and that may affect different processors differently
          diagnostics = null;
  
          // Read in our additional options from properties block (XLTest should have set this)
          String tmp;
  
          tmp = testProps.getProperty(OPT_PRELOAD, null);
  
          if ((tmp != null) && tmp.equalsIgnoreCase("true"))
          {
              preload = true;
          }
  
          tmp = null;
          tmp = testProps.getProperty(OPT_ITERATIONS, null);
  
          try
          {
              iterations = Integer.parseInt(tmp);
          }
          catch (NumberFormatException numEx)
          {
  
              // no-op; leave as default
          }
  
          // Create a processor with our appropriate flavor, etc.
          // This is really only done to let XLDirectoryIterator later on 
          //  print out the flavor & version of the processor
          // Note: This processor will get destroyed later on anyway:
          //  for the performance test, we re-create for each file
          if (!createNewProcessor())
          {
              reporter.logErrorMsg(
                  "Could not createNewProcessor before testing; it may not work!");
          }
  
          return true;
      }
  
      /**
       * Run through the directory given to us and run tests found in subdirs.
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean runTestCases(Properties p)
      {
  
          // The PerformanceTest simply uses all the default processing methods 
          // from our parent XLDirectoryIterator
          logMemory(true);  // dumps Runtime.freeMemory/totalMemory
          processTestDir();
          logMemory(true);  // dumps Runtime.freeMemory/totalMemory
  
          return true;
      }
  
      /**
       * Run one xsl/xml file pair through the processor; looping and capturing timing info.
       * @author Shane Curcuru
       *
       * NEEDSDOC @param XMLName
       * NEEDSDOC @param XSLName
       * NEEDSDOC @param OutName
       * @return int status - pass, fail, expected exception or unexpected exception
       * <p>processSingleFile should normally do all processing required to transform the XMLName,
       * via the XSLName, into an OutName file on disk.  It returns a status denoting
       * what happend during the processing.</p>
       * <p>For the PerformanceTest, we actually do a lot more:.</p>
       * <UL>
       * <LI>Create a new processorWrapper (and hence a new processor)</LI>
       * <LI>If preload, run one process through first and report timing</LI>
       * <LI>Iterate and perform a number of processes, timing for each one</LI>
       * <LI>Cleanup what we can to not interfere with other tests later</LI>
       * </UL>
       * <p>Futurework: should we strip off just the filename and output just that
       * in a specific format in the logPerfMsg calls to make it simpler to write reports?</p>
       */
      public int processSingleFile(String XMLName, String XSLName,
                                   String OutName)
      {
  
          long fileTime = ProcessorWrapper.ERROR;
  
          try
          {
  
              // Force filerefs to be URI's if needed; Note that this may affect other processors besides xerces badly
              if (useURI)
              {
  
                  // Use this static convenience method; returns a URL; convert to String via toExternalForm()
                  // Note: we should consider caching the original strings first, 
                  //  in case we later on have a use for them instead of the URI'd format
                  XMLName = getURLFromString(XMLName, null).toExternalForm();
                  XSLName = getURLFromString(XSLName, null).toExternalForm();
  
                  // Note: Currently 28-Jun-00, the output of files is handled differently, so 
                  //  we do NOT want to convert those.  Subject to change, however.
                  // OutName = getURLFromString(OutName, null).toExternalForm();
                  reporter.logTraceMsg("processSingleFile() useURI: "
                                       + XSLName);
              }
  
              // TODO replicate ProcessorBenchmark.benchmarkPreprocess()
              // TODO cleanup outName - delete the file on disk
              File outFile = new File(OutName);
  
              try
              {
                  boolean btmp = outFile.delete();
  
                  reporter.logTraceMsg("Deleting OutFile of::" + OutName
                                       + " status: " + btmp);
              }
              catch (SecurityException se)
              {
                  reporter.logWarningMsg("Deleting OutFile of::" + OutName
                                         + " threw: " + se.toString());
  
                  // But continue anyways...
              }
  
              // Equivalent to ProcessorBenchmark.benchmark()
              // Create a new wrapper & processor each time; cleanup stuff
              processorW = null;
  
              logMemory(true);  // dumps Runtime.freeMemory/totalMemory
  
              // Ask our utility routine for a new specific wrapper
              //  (Will automatically create a new wrapper and appropriate processor
              if (!createNewProcessor())
              {
                  reporter.logErrorMsg(
                      "ERROR: could not create processorWrapper, aborting.");
  
                  return UNEXPECTED_EXCEPTION;
              }
  
              // Prime the pump, so to speak, if desired
              if (preload)
              {
                  fileTime = processorW.processToFile(XMLName, XSLName,
                                                      OutName);
  
                  if (fileTime == ProcessorWrapper.ERROR)
                  {
                      reporter.logErrorMsg(
                          "ERROR: Preload process had a problem of::"
                          + XSLName);
  
                      return UNEXPECTED_EXCEPTION;
                  }
  
                  reporter.logPerfMsg(PERF_PRELOAD, fileTime,
                                      "Preload process of::" + XSLName);
              }
  
              logMemory(true);  // dumps Runtime.freeMemory/totalMemory
  
              long aggregate = 0L;
              int j;
  
              for (j = 1; j <= iterations; j++)
              {
                  long retVal;
  
                  // Note: We re-write the same output file each time, so 
                  //       I suppose in theory that could affect future iterations
                  retVal = processorW.processToFile(XMLName, XSLName, OutName);
  
                  if (retVal == ProcessorWrapper.ERROR)
                  {
                      reporter.logErrorMsg(
                          "ERROR: processToFile problem on iteration(" + j
                          + ") of::" + XSLName);
  
                      return UNEXPECTED_EXCEPTION;
                  }
  
                  aggregate += retVal;
  
                  reporter.logPerfMsg(PERF_ITERATION, retVal,
                                      "processToFile(" + j + ") of::"
                                      + XSLName);
                  logMemory(true);
              }
  
              reporter.logPerfMsg(PERF_AVERAGE, (aggregate / iterations),
                                  "Average of (" + iterations
                                  + ") iterations of::" + XSLName);
          }
  
          // Catch any throwable and log an error
          catch (Throwable t)
          {
              reporter.logErrorMsg("processSingleFile of::" + XSLName
                                   + " threw: " + t.toString());
  
              return UNEXPECTED_EXCEPTION;
          }
  
          return PROCESS_OK;
      }
  
      /**
       * Cheap-o memory logger - just reports Runtime.totalMemory/freeMemory.  
       *
       * NEEDSDOC @param total
       */
      protected void logMemory(boolean total)
      {
  
          Runtime r = Runtime.getRuntime();
  
          r.gc();
          reporter.logPerfMsg(PERF_MEMORY, r.freeMemory(), "freeMemory");
  
          if (total)
          {
              reporter.logPerfMsg(PERF_MEMORY, r.totalMemory(), "totalMemory");
          }
      }
  
      /**
       * Convenience method to print out usage information - update if needed.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
  
          return ("Common [optional] options supported by PerformanceTest:\n"
                  + "    -" + OPT_PRELOAD
                  + "   run an extra process first, before looping\n" + "    -"
                  + OPT_ITERATIONS
                  + "   how many times to loop over each file\n"
                  + super.usage());
      }
  
      /**
       * Main method to run test from the command line - can be left alone.  
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          PerformanceTest app = new PerformanceTest();
  
          app.doMain(args);
      }
  }  // end of class PerformanceTest
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/XHTComparator.java
  
  Index: XHTComparator.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * XHTComparator.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.Reporter;  // Only for PASS_RESULT, etc.
  
  import java.io.PrintWriter;
  import java.io.File;
  import java.io.FileReader;
  import java.io.BufferedReader;
  
  import java.net.URL;
  import java.net.MalformedURLException;
  
  import java.util.StringTokenizer;
  
  // DOM imports from Xerces
  import org.w3c.dom.Attr;
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NamedNodeMap;
  import org.w3c.dom.Text;
  
  // Xerces imports
  import org.apache.xerces.parsers.DOMParser;
  import org.apache.xerces.dom.DOMImplementationImpl;
  import org.apache.html.dom.HTMLBuilder;
  
  // SAX2 imports from Xerces
  import org.xml.sax.InputSource;
  
  /**
   * Uses an XML/HTML/Text diff comparator to check or diff two files.
   * <p>Given two files, an actual test result and a known good or 'gold'
   * test result, diff the two files to see if they are equal; if not, provide
   * some very basic info on where they differ.</p>
   * <p>Attempts to parse each file as an XML document using Xerces;
   * if that fails, attempt to parse each as an HTML document using
   * <i>NEED NEW HTML PARSER</i>; if that fails, pretend to parse each
   * doc as a single text node.</p>
   * @todo document whitespace difference handling better -sc
   * @todo check how namespaces are handled and diff'd -sc
   * @todo check how XML decls are handled (or not) -sc
   * @todo check how files of different encodings are handled in each parse type -sc
   * @todo Allow param to define the type of parse we do (i.e. if a
   * testwriter knows their output file will be XML, we should only
   * attempt to parse it as XML, not other types)
   * @author Scott_Boag@lotus.com
   * @author Shane_Curcuru@lotus.com
   * @version $Id: XHTComparator.java,v 1.1 2000/11/01 23:26:57 curcuru Exp $
   */
  public class XHTComparator
  {
  
      /** NEEDSDOC Field MAX_VALUE_DISPLAY_LEN          */
      static final int MAX_VALUE_DISPLAY_LEN = 511;  // arbitrary length, for convenience
  
      /** NEEDSDOC Field maxDisplayLen          */
      private int maxDisplayLen = MAX_VALUE_DISPLAY_LEN;
  
      /**
       * NEEDSDOC Method setMaxDisplayLen 
       *
       *
       * NEEDSDOC @param i
       */
      public void setMaxDisplayLen(int i)
      {
          if (i > 0)
              maxDisplayLen = i;
      }
  
      /** Constants for reporting out reason for failed diffs. */
      public static final String SEPARATOR = ";";
  
      /** NEEDSDOC Field LBRACKET          */
      public static final String LBRACKET = "[";
  
      /** NEEDSDOC Field RBRACKET          */
      public static final String RBRACKET = "]";
  
      /** NEEDSDOC Field TEST          */
      public static final String TEST = "test";
  
      /** NEEDSDOC Field GOLD          */
      public static final String GOLD = "gold";
  
      /** NEEDSDOC Field PARSE_TYPE          */
      public static final String PARSE_TYPE = "-parse-type" + SEPARATOR;  // postpended to TEST or GOLD
  
      /** NEEDSDOC Field OTHER_ERROR          */
      public static final String OTHER_ERROR = "other-error" + SEPARATOR;
  
      /** NEEDSDOC Field WARNING          */
      public static final String WARNING = "warning" + SEPARATOR;
  
      /** NEEDSDOC Field MISMATCH_NODE          */
      public static final String MISMATCH_NODE = "mismatch-node" + SEPARATOR;
  
      /** NEEDSDOC Field MISSING_TEST_NODE          */
      public static final String MISSING_TEST_NODE = "missing-node-" + TEST
                                                         + SEPARATOR;
  
      /** NEEDSDOC Field MISSING_GOLD_NODE          */
      public static final String MISSING_GOLD_NODE = "missing-node-" + GOLD
                                                         + SEPARATOR;
  
      /** NEEDSDOC Field MISMATCH_ATTRIBUTE          */
      public static final String MISMATCH_ATTRIBUTE = "mismatch-attribute"
                                                          + SEPARATOR;
  
      /** NEEDSDOC Field MISMATCH_VALUE          */
      public static final String MISMATCH_VALUE = "mismatch-value" + SEPARATOR;
  
      /** NEEDSDOC Field MISSING_TEST_VALUE          */
      public static final String MISSING_TEST_VALUE = "missing-value-" + TEST
                                                          + SEPARATOR;
  
      /** NEEDSDOC Field MISSING_GOLD_VALUE          */
      public static final String MISSING_GOLD_VALUE = "missing-value-" + GOLD
                                                          + SEPARATOR;
  
      /** NEEDSDOC Field WHITESPACE_DIFF          */
      public static final String WHITESPACE_DIFF = "whitespace-diff;";
  
      /**
       * Compare two files by parsing into DOMs and comparing trees.
       * @param goldFileName expected file
       * @param testFileName actual file
       * @param reporter PrintWriter to dump status info to
       * @param array of warning flags (for whitespace diffs, I think?)
       * NEEDSDOC @param warning
       * @return true if they match, false otherwise
       */
      public boolean compare(String goldFileName, String testFileName,
                             PrintWriter reporter, boolean[] warning)
      {
  
          // parse the gold doc
          Document goldDoc = parse(goldFileName, reporter, GOLD);
  
          // parse the test doc only if gold doc was parsed OK
          Document testDoc = (null != goldDoc)
                             ? parse(testFileName, reporter, TEST) : null;
  
          if (null == goldDoc)
          {
              reporter.println(OTHER_ERROR + GOLD + SEPARATOR
                               + "document null");
  
              return false;
          }
          else if (null == testDoc)
          {
              reporter.println(OTHER_ERROR + TEST + SEPARATOR
                               + "document null");
  
              return false;
          }
  
          return diff(goldDoc, testDoc, reporter, warning);
      }
  
      // Reporter format:
      // REASON_CONSTANT;gold val;test val;reason description
  
      /**
       * The contract is: when you enter here the gold and test nodes are the same type,
       * both non-null, and both in the same basic position in the tree.
       * @todo verify caller really performs for the contract -sc
       *
       * NEEDSDOC @param gold
       * NEEDSDOC @param test
       * NEEDSDOC @param reporter
       * NEEDSDOC @param warning
       *
       * NEEDSDOC ($objectName$) @return
       */
      boolean diff(Node gold, Node test, PrintWriter reporter,
                   boolean[] warning)
      {
  
          String name1 = gold.getNodeName();
          String name2 = test.getNodeName();
  
          // If both there but not equal, fail
          if ((null != name1) && (null != name2) &&!name1.equals(name2))
          {
              reporter.print(MISMATCH_NODE + nodeTypeString(gold) + SEPARATOR
                             + nodeTypeString(test) + SEPARATOR
                             + "name does not equal test node");
  
              return false;
          }
          else if ((null != name1) && (null == name2))
          {
              reporter.print(MISSING_TEST_NODE + nodeTypeString(gold)
                             + SEPARATOR + nodeTypeString(test) + SEPARATOR
                             + "name missing on test");
  
              return false;
          }
          else if ((null == name1) && (null != name2))
          {
              reporter.print(MISSING_GOLD_NODE + nodeTypeString(gold)
                             + SEPARATOR + nodeTypeString(test) + SEPARATOR
                             + "name missing on gold");
  
              return false;
          }
  
          String value1 = gold.getNodeValue();
          String value2 = test.getNodeValue();
  
          if ((null != value1) && (null != value2) &&!value1.equals(value2))
          {
              System.err.println("value1:");
              System.err.println(value1);
              System.err.println("value2:");
              System.err.println(value2);
              reporter.print(MISMATCH_VALUE + nodeTypeString(gold) + "-"
                             + value1.length() + SEPARATOR
                             + nodeTypeString(test) + "-" + value2.length()
                             + SEPARATOR + "lengths do not match");
  
              // Ignore old conditional printing; always print out values, even with newlines -sc
  
              /**
               *     // semi-HACK: only report out stuff that's short enough
               *     // TODO This should be a settable property for the test harness! -sc
               *     if((value1.length() < maxDisplayLen)
               *        && (value2.length() < maxDisplayLen))
               *     {
               *       boolean doPrintValue = true;
               *       // TODO wouldn't it be more efficient to do indexOf's? -sc
               *       for(int i = 0; i < value1.length(); i++)
               *       {
               *         char c = value1.charAt(i);
               *         if((c == 0x0A) || (c == 0x0D) || (c == '\n'))
               *         {
               *           doPrintValue = false;
               *           break;
               *         }
               *       }
               *       if(doPrintValue)
               *       {
               *         for(int i = 0; i < value2.length(); i++)
               *         {
               *           char c = value2.charAt(i);
               *           if((c == 0x0A) || (c == 0x0D) || (c == '\n'))
               *           {
               *             doPrintValue = false;
               *             break;
               *           }
               *         }
               *       }
               *       if(doPrintValue)
               *       {
               * / Ignore old conditional printing; always print out values, even with newlines -sc
               */
              reporter.println();
              reporter.print(MISMATCH_VALUE + value1 + SEPARATOR + value2
                             + SEPARATOR + "values do not match");
  
              return false;
          }
          else if ((null != value1) && (null == value2))
          {
              reporter.print(MISSING_TEST_VALUE + nodeTypeString(gold) + "-"
                             + value1 + SEPARATOR + nodeTypeString(test)
                             + SEPARATOR + "test no value");
  
              return false;
          }
          else if ((null == value1) && (null != value2))
          {
              reporter.print(MISSING_GOLD_VALUE + nodeTypeString(gold)
                             + SEPARATOR + nodeTypeString(test) + "-" + value2
                             + SEPARATOR + "gold no value");
  
              return false;
          }
  
          switch (gold.getNodeType())
          {
          case Node.DOCUMENT_NODE :
          {
  
              // Why don't we do anything here? -sc
          }
          break;
          case Node.ELEMENT_NODE :
          {
  
              // Explicitly ignore attribute ordering
              // TODO do we need to make this settable for testing purposes? -sc
              NamedNodeMap goldAttrs = gold.getAttributes();
              NamedNodeMap testAttrs = test.getAttributes();
  
              if ((null != goldAttrs) && (null == testAttrs))
              {
                  reporter.print(MISMATCH_ATTRIBUTE + nodeTypeString(gold)
                                 + SEPARATOR + nodeTypeString(test) + SEPARATOR
                                 + "test no attrs");
  
                  return false;
              }
              else if ((null == goldAttrs) && (null != testAttrs))
              {
                  reporter.print(MISMATCH_ATTRIBUTE + nodeTypeString(gold)
                                 + SEPARATOR + nodeTypeString(test) + SEPARATOR
                                 + "gold no attrs");
  
                  return false;
              }
  
              int gn = goldAttrs.getLength();
              int tn = testAttrs.getLength();
  
              if (gn != tn)
              {
                  reporter.print(MISMATCH_ATTRIBUTE + nodeTypeString(gold)
                                 + "-" + gn + SEPARATOR + nodeTypeString(test)
                                 + "-" + tn + SEPARATOR
                                 + "attribte count mismatch");
  
                  // TODO: add output of each set of attrs for comparisons
                  return false;
              }
  
              // TODO verify this checks the full list of attributes both ways, 
              //      from gold->test and from test->gold -sc
              for (int i = 0; i < gn; i++)
              {
                  Attr goldAttr = (Attr) goldAttrs.item(i);
                  String goldAttrName = goldAttr.getName();
                  Node testAttr = testAttrs.getNamedItem(goldAttrName);
  
                  if (null == testAttr)
                  {
                      reporter.print(MISMATCH_ATTRIBUTE + nodeTypeString(gold)
                                     + "-" + goldAttrName + SEPARATOR
                                     + nodeTypeString(test) + SEPARATOR
                                     + "missing attribute on test");
  
                      return false;
                  }
  
                  if (!diff(goldAttr, testAttr, reporter, warning))
                  {
                      return false;
                  }
              }
          }
          break;
          case Node.CDATA_SECTION_NODE :{}
          break;
          case Node.ENTITY_REFERENCE_NODE :{}
          break;
          case Node.ATTRIBUTE_NODE :{}
          break;
          case Node.COMMENT_NODE :{}
          break;
          case Node.ENTITY_NODE :{}
          break;
          case Node.NOTATION_NODE :{}
          break;
          case Node.PROCESSING_INSTRUCTION_NODE :{}
          break;
          case Node.TEXT_NODE :{}
          break;
          default :{}
          }
  
          Node try2[] = new Node[2];
          Node goldChild = gold.getFirstChild();
          Node testChild = test.getFirstChild();
  
          if (!basicChildCompare(goldChild, testChild, reporter, warning, try2))
              return false;
  
          goldChild = try2[0];
          testChild = try2[1];
  
          while (null != goldChild)
          {
              if (!diff(goldChild, testChild, reporter, warning))
                  return false;
  
              goldChild = goldChild.getNextSibling();
              testChild = testChild.getNextSibling();
  
              if (!basicChildCompare(goldChild, testChild, reporter, warning,
                                     try2))
                  return false;
  
              goldChild = try2[0];
              testChild = try2[1];
          }
  
          return true;
      }  // end of diff()
  
      /**
       * NEEDSDOC Method isWhiteSpace 
       *
       *
       * NEEDSDOC @param s
       *
       * NEEDSDOC (isWhiteSpace) @return
       */
      boolean isWhiteSpace(String s)
      {
  
          int n = s.length();
  
          for (int i = 0; i < n; i++)
          {
              if (!Character.isWhitespace(s.charAt(i)))
                  return false;
          }
  
          return true;
      }  // end of isWhiteSpace()
  
      /**
       * NEEDSDOC Method tryToAdvancePastWhitespace 
       *
       *
       * NEEDSDOC @param n
       * NEEDSDOC @param reporter
       * NEEDSDOC @param warning
       * NEEDSDOC @param next
       * NEEDSDOC @param which
       *
       * NEEDSDOC (tryToAdvancePastWhitespace) @return
       */
      Node tryToAdvancePastWhitespace(Node n, PrintWriter reporter,
                                      boolean[] warning, Node next[], int which)
      {
  
          if (n.getNodeType() == Node.TEXT_NODE)
          {
              String data = n.getNodeValue();
  
              if (null != data)
              {
                  if (isWhiteSpace(data))
                  {
                      warning[0] = true;
  
                      reporter.print(WHITESPACE_DIFF + " ");  // TODO check the format of this; maybe use println -sc
  
                      n = n.getNextSibling();
                      next[which] = n;
                  }
              }
          }
  
          return n;
      }  // end of tryToAdvancePastWhitespace()
  
      /**
       * NEEDSDOC Method basicChildCompare 
       *
       *
       * NEEDSDOC @param gold
       * NEEDSDOC @param test
       * NEEDSDOC @param reporter
       * NEEDSDOC @param warning
       * NEEDSDOC @param next
       *
       * NEEDSDOC (basicChildCompare) @return
       */
      boolean basicChildCompare(Node gold, Node test, PrintWriter reporter,
                                boolean[] warning, Node next[])
      {
  
          next[0] = gold;
          next[1] = test;
  
          boolean alreadyTriedToAdvance = false;
  
          if ((null != gold) && (null == test))
          {
              gold = tryToAdvancePastWhitespace(gold, reporter, warning, next,
                                                0);
              alreadyTriedToAdvance = true;
  
              if ((null != gold) && (null == test))
              {
                  reporter.print(MISSING_TEST_NODE + nodeTypeString(gold)
                                 + SEPARATOR + SEPARATOR
                                 + "missing node on test");
  
                  return false;
              }
          }
          else if ((null == gold) && (null != test))
          {
              test = tryToAdvancePastWhitespace(test, reporter, warning, next,
                                                1);
              alreadyTriedToAdvance = true;
  
              if ((null == gold) && (null != test))
              {
                  reporter.print(MISSING_GOLD_NODE + SEPARATOR
                                 + nodeTypeString(test) + SEPARATOR
                                 + "missing node on gold");
  
                  return false;
              }
          }
  
          if ((null != gold) && (gold.getNodeType() != test.getNodeType()))
          {
              Node savedGold = gold;
              Node savedTest = test;
  
              if (!alreadyTriedToAdvance)
              {
                  gold = tryToAdvancePastWhitespace(gold, reporter, warning,
                                                    next, 0);
  
                  if (gold == savedGold)
                  {
                      test = tryToAdvancePastWhitespace(test, reporter,
                                                        warning, next, 1);
                  }
              }
  
              if ((null != gold) && (gold.getNodeType() != test.getNodeType()))
              {
                  gold = savedGold;
                  test = savedTest;
  
                  reporter.print(MISMATCH_NODE + nodeTypeString(gold)
                                 + SEPARATOR + nodeTypeString(test) + SEPARATOR
                                 + "node type mismatch");
  
                  return false;
              }
          }
  
          return true;
      }  // end of basicChildCompare()
  
      /**
       * Cheap-o text printout of a node.  By Scott.  
       *
       * NEEDSDOC @param n
       *
       * NEEDSDOC ($objectName$) @return
       */
      String nodeTypeString(Node n)
      {
  
          String s;
  
          switch (n.getNodeType())
          {
          case Node.DOCUMENT_NODE :
              s = "DOCUMENT_NODE(" + n.getNodeName() + ")";
              break;
          case Node.ELEMENT_NODE :
              s = "ELEMENT_NODE(" + n.getNodeName() + ")";
              break;
          case Node.CDATA_SECTION_NODE :
              s = "CDATA_SECTION_NODE(" + n.getNodeName() + ")";
              break;
          case Node.ENTITY_REFERENCE_NODE :
              s = "ENTITY_REFERENCE_NODE(" + n.getNodeName() + ")";
              break;
          case Node.ATTRIBUTE_NODE :
              s = "ATTRIBUTE_NODE(" + n.getNodeName() + ")";
              break;
          case Node.COMMENT_NODE :
              s = "COMMENT_NODE(" + n.getNodeName() + ")";
              break;
          case Node.ENTITY_NODE :
              s = "ENTITY_NODE(" + n.getNodeName() + ")";
              break;
          case Node.NOTATION_NODE :
              s = "NOTATION_NODE(" + n.getNodeName() + ")";
              break;
          case Node.PROCESSING_INSTRUCTION_NODE :
              s = "PROCESSING_INSTRUCTION_NODE(" + n.getNodeName() + ")";
              break;
          case Node.TEXT_NODE :
              s = "TEXT_NODE(" + n.getNodeName() + ")";
              break;
          default :
              s = "UNKNOWN_NODE(" + n.getNodeName() + ")";
          }
  
          return s;
      }  // end of nodeTypeString()
  
      /**
       * Parameter: force use of URI's for Xerces 1.1.2 or leave filenames alone?
       */
      protected boolean useURI = true;
  
      /**
       * NEEDSDOC Method parse 
       *
       *
       * NEEDSDOC @param filename
       * NEEDSDOC @param reporter
       * NEEDSDOC @param which
       *
       * NEEDSDOC (parse) @return
       */
      Document parse(String filename, PrintWriter reporter, String which)
      {
  
          // Force filerefs to be URI's if needed: note this is independent of any other files
          // Remember: this only applies to the wacky Xerces parser, which we're presumably
          //  using as our default DOMParser() below
          String xercesFilename = filename;
  
          if (useURI)
          {
              try
              {
  
                  // Use static worker method to get the correct format
                  // Note: this is copied straight from Xalan 1.x's org.apache.xalan.xslt.Process
                  // TODO verify this is the most correct and simplest way to munge the filename
                  xercesFilename = getURLFromString(filename,
                                                    null).toExternalForm();
              }
              catch (Exception e)
              {
                  reporter.print(WARNING + e.toString() + "\n");
              }
          }
  
          String parseType = which + PARSE_TYPE + "[xml];";
          DOMParser parser = new DOMParser();
          Document doc = null;
  
          try
          {
  
              // Use the Xerces-munged name specifically here!
              parser.parse(new InputSource(xercesFilename));
  
              doc = parser.getDocument();
          }
          catch (Throwable se)
          {
              reporter.print(WARNING + se.toString() + "\n");
  
              parseType = which + PARSE_TYPE + "[html];";
  
              try
              {
  
                  // @todo need to find an HTML to DOM parser we can use!!!
                  // doc = someHTMLParser.parse(new InputSource(filename));
                  throw new RuntimeException("We need an HTML to DOM parser!");
              }
              catch (Exception e)
              {
                  try
                  {
                      reporter.print(WARNING + e.toString() + "\n");
  
                      parseType = which + PARSE_TYPE + "[text];";
  
                      // Safer not to rely directly upon the DocumentImpl classes.
                      //   DocumentImpl docImpl = new DocumentImpl();
                      //   docImpl.appendChild(docElem);
                      //   DocumentImpl docImpl =
                      // Instead, use the DOM Level 2 createDocument call.
                      // Obtaining the DOMImplementation is still nonportable;
                      // DOM Level 3 may address that.
                      Document docImpl =
                          DOMImplementationImpl.getDOMImplementation().createDocument(
                              null, "out", null);
                      Element docElem = docImpl.getDocumentElement();
  
                      // Parse as text, line by line
                      //   Since we already know it should be text, this should 
                      //   work better than parsing by bytes.
                      FileReader fr = new FileReader(filename);
                      BufferedReader br = new BufferedReader(fr);
                      StringBuffer buffer = new StringBuffer();
  
                      for (;;)
                      {
                          String tmp = br.readLine();
  
                          if (tmp == null)
                          {
                              break;
                          }
  
                          buffer.append(tmp);
                          buffer.append("\n");  // Put in the newlines as well
                      }
  
                      Text tx = docImpl.createTextNode(buffer.toString());
  
                      // Note: will this always be a valid node?  If we're parsing 
                      //    in as text, will there ever be cases where the diff that's 
                      //    done later on will fail becuase some really garbage-like 
                      //    text has been put into a node?
                      docElem.appendChild(tx);
  
                      doc = docImpl;
                  }
                  catch (Throwable throwable)
                  {
                      reporter.print(OTHER_ERROR + filename + SEPARATOR
                                     + "threw:" + throwable.toString() + "\n");
                  }
              }
          }
  
          // Output a newline here for readability
          reporter.print(parseType + "\n");
  
          return doc;
      }  // end of parse()
  
      /**
       * Take a user string and try and parse XML, and also return the url.
       * <p>Note: this needs to be revisited.  It is only used to convert Strings
       * that represent filenames into the proper URL format for Xerces 1.1x parser.</p>
       * Note: this is copied straight from Xalan 1.x's org.apache.xalan.xslt.Process
       *
       * NEEDSDOC @param urlString
       * NEEDSDOC @param base
       *
       * NEEDSDOC ($objectName$) @return
       * @exception Exception thrown if we really really can't create the URL
       */
      public static URL getURLFromString(String urlString, String base)
              throws Exception
      {
  
          String origURLString = urlString;
          String origBase = base;
  
          // System.out.println("getURLFromString - urlString: "+urlString+", base: "+base);
          Object doc;
          URL url = null;
          int fileStartType = 0;
  
          try
          {
              if (null != base)
              {
                  if (base.toLowerCase().startsWith("file:/"))
                  {
                      fileStartType = 1;
                  }
                  else if (base.toLowerCase().startsWith("file:"))
                  {
                      fileStartType = 2;
                  }
              }
  
              boolean isAbsoluteURL;
  
              // From http://www.ics.uci.edu/pub/ietf/uri/rfc1630.txt
              // A partial form can be distinguished from an absolute form in that the
              // latter must have a colon and that colon must occur before any slash
              // characters. Systems not requiring partial forms should not use any
              // unencoded slashes in their naming schemes.  If they do, absolute URIs
              // will still work, but confusion may result.
              int indexOfColon = urlString.indexOf(':');
              int indexOfSlash = urlString.indexOf('/');
  
              if ((indexOfColon != -1) && (indexOfSlash != -1)
                      && (indexOfColon < indexOfSlash))
              {
  
                  // The url (or filename, for that matter) is absolute.
                  isAbsoluteURL = true;
              }
              else
              {
                  isAbsoluteURL = false;
              }
  
              if (isAbsoluteURL || (null == base) || (base.length() == 0))
              {
                  try
                  {
                      url = new URL(urlString);
                  }
                  catch (MalformedURLException e){}
              }
  
              // The Java URL handling doesn't seem to handle relative file names.
              else if (!((urlString.charAt(0) == '.') || (fileStartType > 0)))
              {
                  try
                  {
                      URL baseUrl = new URL(base);
  
                      url = new URL(baseUrl, urlString);
                  }
                  catch (MalformedURLException e){}
              }
  
              if (null == url)
              {
  
                  // Then we're going to try and make a file URL below, so strip 
                  // off the protocol header.
                  if (urlString.toLowerCase().startsWith("file:/"))
                  {
                      urlString = urlString.substring(6);
                  }
                  else if (urlString.toLowerCase().startsWith("file:"))
                  {
                      urlString = urlString.substring(5);
                  }
              }
  
              if ((null == url) && ((null == base) || (fileStartType > 0)))
              {
                  if (1 == fileStartType)
                  {
                      if (null != base)
                          base = base.substring(6);
  
                      fileStartType = 1;
                  }
                  else if (2 == fileStartType)
                  {
                      if (null != base)
                          base = base.substring(5);
  
                      fileStartType = 2;
                  }
  
                  File f = new File(urlString);
  
                  if (!f.isAbsolute() && (null != base))
                  {
  
                      // String dir = f.isDirectory() ? f.getAbsolutePath() : f.getParent();
                      // System.out.println("prebuiltUrlString (1): "+base);
                      StringTokenizer tokenizer = new StringTokenizer(base,
                                                      "\\/");
                      String fixedBase = null;
  
                      while (tokenizer.hasMoreTokens())
                      {
                          String token = tokenizer.nextToken();
  
                          if (null == fixedBase)
                          {
  
                              // Thanks to Rick Maddy for the bug fix for UNIX here.
                              if (base.charAt(0) == '\\'
                                      || base.charAt(0) == '/')
                              {
                                  fixedBase = File.separator + token;
                              }
                              else
                              {
                                  fixedBase = token;
                              }
                          }
                          else
                          {
                              fixedBase += File.separator + token;
                          }
                      }
  
                      // System.out.println("rebuiltUrlString (1): "+fixedBase);
                      f = new File(fixedBase);
  
                      String dir = f.isDirectory()
                                   ? f.getAbsolutePath() : f.getParent();
  
                      // System.out.println("dir: "+dir);
                      // System.out.println("urlString: "+urlString);
                      // f = new File(dir, urlString);
                      // System.out.println("f (1): "+f.toString());
                      // urlString = f.getAbsolutePath();
                      f = new File(urlString);
  
                      boolean isAbsolute = f.isAbsolute()
                                           || (urlString.charAt(0) == '\\')
                                           || (urlString.charAt(0) == '/');
  
                      if (!isAbsolute)
                      {
  
                          // Getting more and more ugly...
                          if (dir.charAt(dir.length() - 1)
                                  != File.separator.charAt(0)
                                  && urlString.charAt(0)
                                     != File.separator.charAt(0))
                          {
                              urlString = dir + File.separator + urlString;
                          }
                          else
                          {
                              urlString = dir + urlString;
                          }
  
                          // System.out.println("prebuiltUrlString (2): "+urlString);
                          tokenizer = new StringTokenizer(urlString, "\\/");
  
                          String rebuiltUrlString = null;
  
                          while (tokenizer.hasMoreTokens())
                          {
                              String token = tokenizer.nextToken();
  
                              if (null == rebuiltUrlString)
                              {
  
                                  // Thanks to Rick Maddy for the bug fix for UNIX here.
                                  if (urlString.charAt(0) == '\\'
                                          || urlString.charAt(0) == '/')
                                  {
                                      rebuiltUrlString = File.separator + token;
                                  }
                                  else
                                  {
                                      rebuiltUrlString = token;
                                  }
                              }
                              else
                              {
                                  rebuiltUrlString += File.separator + token;
                              }
                          }
  
                          // System.out.println("rebuiltUrlString (2): "+rebuiltUrlString);
                          if (null != rebuiltUrlString)
                              urlString = rebuiltUrlString;
                      }
  
                      // System.out.println("fileStartType: "+fileStartType);
                      if (1 == fileStartType)
                      {
                          if (urlString.charAt(0) == '/')
                          {
                              urlString = "file://" + urlString;
                          }
                          else
                          {
                              urlString = "file:/" + urlString;
                          }
                      }
                      else if (2 == fileStartType)
                      {
                          urlString = "file:" + urlString;
                      }
  
                      try
                      {
  
                          // System.out.println("Final before try: "+urlString);
                          url = new URL(urlString);
                      }
                      catch (MalformedURLException e)
                      {
  
                          // System.out.println("Error trying to make URL from "+urlString);
                      }
                  }
              }
  
              if (null == url)
              {
  
                  // The sun java VM doesn't do this correctly, but I'll 
                  // try it here as a second-to-last resort.
                  if ((null != origBase) && (origBase.length() > 0))
                  {
                      try
                      {
                          URL baseURL = new URL(origBase);
  
                          // System.out.println("Trying to make URL from "+origBase+" and "+origURLString);
                          url = new URL(baseURL, origURLString);
  
                          // System.out.println("Success! New URL is: "+url.toString());
                      }
                      catch (MalformedURLException e)
                      {
  
                          // System.out.println("Error trying to make URL from "+origBase+" and "+origURLString);
                      }
                  }
  
                  if (null == url)
                  {
                      try
                      {
                          String lastPart;
  
                          if (null != origBase)
                          {
                              File baseFile = new File(origBase);
  
                              if (baseFile.isDirectory())
                              {
                                  lastPart =
                                      new File(baseFile,
                                               urlString).getAbsolutePath();
                              }
                              else
                              {
                                  String parentDir = baseFile.getParent();
  
                                  lastPart =
                                      new File(parentDir,
                                               urlString).getAbsolutePath();
                              }
                          }
                          else
                          {
                              lastPart = new File(urlString).getAbsolutePath();
                          }
  
                          // Hack
                          // if((lastPart.charAt(0) == '/') && (lastPart.charAt(2) == ':'))
                          //   lastPart = lastPart.substring(1, lastPart.length() - 1);
                          String fullpath;
  
                          if (lastPart.charAt(0) == '\\'
                                  || lastPart.charAt(0) == '/')
                          {
                              fullpath = "file://" + lastPart;
                          }
                          else
                          {
                              fullpath = "file:" + lastPart;
                          }
  
                          url = new URL(fullpath);
                      }
                      catch (MalformedURLException e2)
                      {
  
                          // Below throw modified from original version
                          throw new Exception("Cannot create url for: "
                                              + urlString + " threw: "
                                              + e2.toString());
  
                          //XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_CREATE_URL, new Object[]{urlString}),e2); //"Cannot create url for: " + urlString, e2 );
                      }
                  }
              }
          }
          catch (SecurityException se)
          {
              try
              {
                  url = new URL("http://xml.apache.org/xslt/"
                                + java.lang.Math.random());  // dummy
              }
              catch (MalformedURLException e2)
              {
  
                  // I give up
              }
          }
  
          // System.out.println("url: "+url.toString());
          return url;
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/XHTFileCheckService.java
  
  Index: XHTFileCheckService.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * XHTFileCheckService.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.*;
  
  import java.io.BufferedReader;
  import java.io.FileReader;
  import java.io.File;
  import java.io.PrintWriter;
  import java.io.StringWriter;
  
  /**
   * Uses an XML/HTML/Text diff comparator to check or diff two files.
   * @author Shane_Curcuru@lotus.com
   * @version $Id: XHTFileCheckService.java,v 1.1 2000/11/01 23:26:57 curcuru Exp $
   */
  public class XHTFileCheckService implements CheckService
  {
  
      /** XHTComparator tool to diff two files. */
      protected XHTComparator comparator = new XHTComparator();
  
      /** Stores last checkFile calls printed output. */
      private StringWriter sw = null;
  
      /**
       * Compare two objects for equivalence, and return appropriate result.
       * Implementers should provide the details of their "equals"
       * algorithim in getCheckMethod().
       * Note that the order of actual, reference is usually important
       * important in determining the result.
       * <li>Typically:
       * <ul>any unexpected Exceptions thrown -> ERRR_RESULT</ul>
       * <ul>actual does not exist -> FAIL_RESULT</ul>
       * <ul>reference does not exist -> AMBG_RESULT</ul>
       * <ul>actual is equivalent to reference -> PASS_RESULT</ul>
       * <ul>actual is not equivalent to reference -> FAIL_RESULT</ul>
       * </li>
       *
       * @param reporter to dump any output messages to
       * @param actual (current) Object to check
       * @param reference (gold, or expected) Object to check against
       * @param description of what you're checking
       * NEEDSDOC @param msg
       * @return Reporter.*_RESULT code denoting status; each method may define
       * it's own meanings for pass, fail, ambiguous, etc.
       */
      public int check(Reporter reporter, Object actual, Object reference,
                       String msg)
      {
  
          if (!((actual instanceof File) & (reference instanceof File)))
          {
  
              // Must have File objects to continue
              reporter.checkErr("XHTFileCheckService only takes files, with: "
                                + msg);
  
              return reporter.ERRR_RESULT;
          }
  
          File actualFile = (File) actual;
          File referenceFile = (File) reference;
  
          // Fail if Actual file doesn't exist or is 0 len
          if ((!actualFile.exists()) || (actualFile.length() == 0))
          {
              reporter.checkFail(msg);
  
              return reporter.FAIL_RESULT;
          }
  
          // Ambiguous if gold file doesn't exist or is 0 len
          if ((!referenceFile.exists()) || (referenceFile.length() == 0))
          {
              reporter.checkAmbiguous(msg);
  
              return Reporter.AMBG_RESULT;
          }
  
          sw = new StringWriter();
  
          PrintWriter pw = new PrintWriter(sw);
          boolean warning[] = new boolean[1];  // TODO: should use this!
  
          warning[0] = false;
  
          boolean isEqual = false;
  
          try
          {
  
              // Note calling order (gold, act) is different than checkFiles()
              isEqual = comparator.compare(referenceFile.getCanonicalPath(),
                                           actualFile.getCanonicalPath(), pw,
                                           warning);
  
              // Side effect: fills in sw with info about the comparison
          }
          catch (Exception e)
          {
              pw.println("comparator threw: " + e.toString());
  
              isEqual = false;
          }
  
          pw.flush();
  
          if (!isEqual)
          {
  
              // We fail, obviously!
              reporter.checkFail(msg);
  
              return Reporter.FAIL_RESULT;
          }
          else if (warning[0])
          {
              pw.println("comparator whitespace diff warning!");
              pw.flush();
              reporter.checkFail(msg);
  
              return Reporter.FAIL_RESULT;
          }
          else
          {
              reporter.checkPass(msg);
  
              return Reporter.PASS_RESULT;
          }
      }
  
      /**
       * Gets extended information about the last checkFiles call.
       * @return String describing any additional info about the last two files that were checked
       */
      public String getExtendedInfo()
      {
  
          if (sw != null)
              return sw.toString();
          else
              return "XHTFileCheckService-no-info-available";
      }
  
      /**
       * Description of algorithim used to check file equivalence.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String getDescription()
      {
          return ("Uses an XML/HTML/Text diff comparator to check or diff two files.");
      }
  }  // end of class XHTFileCheckService
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/XSLDirectoryIterator.java
  
  Index: XSLDirectoryIterator.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * XSLDirectoryIterator.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.*;
  import org.apache.qetest.xslwrapper.*;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FilenameFilter;
  import java.io.FileReader;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.PrintWriter;
  
  import java.net.URL;  // For optional URI/URLs instead of string filenames
  
  import java.util.Hashtable;
  import java.util.StringTokenizer;
  import java.util.Vector;
  
  // Try to catch this specific exception from the ProcessorWrapper, if it is thrown
  import org.xml.sax.SAXException;
  
  // /////////////////// HACK - added from Xalan1 org.apache.xalan.xslt.Process /////////////////////
  import java.net.MalformedURLException;
  
  //-------------------------------------------------------------------------
  
  /**
   * Sample Xalan test driver base that iterates over directories
   * and xml/xsl file pairs.
   * <p>This class provides generic support for iterating over directories full
   * of xsl/xml file pairs, and sending them through the processor.  Subclasses
   * may override processSingleFile to call different variations of .process(...).</p>
   * <p>Alternately, users may specify -fileList file.lst that contains listings
   * of specific files to run (instead of iterating over the directories by following
   * the appropriate Conformance rules).</p>
   * <ul>initializes all members from XSLProcessorTestBase, plus
   * <li>fileList</li>
   * </ul>
   * @todo improve file discovery - file execution paradigm
   * @author Shane Curcuru
   * @version $Id: XSLDirectoryIterator.java,v 1.1 2000/11/01 23:26:57 curcuru Exp $
   */
  public class XSLDirectoryIterator extends XSLProcessorTestBase
  {
  
      /**
       * Convenience method to print out usage information.
       * @author Shane Curcuru
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
  
          return ("Common options supported by XSLDirectoryIterator:\n"
                  + "    -" + OPT_FILELIST
                  + "  <name of listfile of tests to run>\n" + super.usage());
      }
  
      //-----------------------------------------------------
      //-------- Constants for common input params --------
      //-----------------------------------------------------
  
      /**
       * Parameter: Run a specific list of files, instead of iterating in dir.
       * <p>Default: null, do normal ConformanceDir iteration.</p>
       */
      public static final String OPT_FILELIST = "fileList";
  
      /** NEEDSDOC Field fileList          */
      protected String fileList = "name_of_file.txt";
  
      /**
       * Token in xsl files signifying that an exception is expected.
       * </p>&lt;!-- ExpectedException: 'put the .toString() representation of the exception here' --&gt;</p>
       */
      public static final String EXPECTED_EXCEPTION = "ExpectedException:";
  
      /**
       * Returned from processSingleFile; signifies process completed without exceptions.
       * <p>No other validation is performed (i.e. user must validate expected
       * outputs themselves after this call returns).</p>
       */
      public static final int PROCESS_OK = 1;
  
      /**
       * Returned from processSingleFile; signifies process threw an EXPECTED_EXCEPTION.
       * <p>No other validation is performed (i.e. user must validate expected
       * outputs, if any, themselves after this call returns).</p>
       */
      public static final int GOT_EXPECTED_EXCEPTION = 2;
  
      /**
       * Returned from processSingleFile; signifies process threw a non-EXPECTED_EXCEPTION.
       * <p>No other validation is performed (i.e. user must validate expected
       * outputs, if any, themselves after this call returns, as well as ensuring
       * that the test is still valid to continue).</p>
       */
      public static final int UNEXPECTED_EXCEPTION = 3;
  
      /** Convenience constant: .xml extension for input data file. */
      protected static final String xmlExtension = ".xml";
  
      /** Convenience constant: .xsl extension for stylesheet file. */
      protected static final String xslExtension = ".xsl";
  
      /** Convenience constant: .out extension for output result file. */
      protected static final String outExtension = ".out";
  
      /** Convenience constant: .log extension for logs and reporting files. */
      protected static final String diagExtension = ".log";
  
      /** Cheap-o way to set an indent on the processor. */
      protected static final int NO_INDENT = -1;  // an illegal value specifying 'default indent'
  
      /** NEEDSDOC Field indentLevel          */
      protected int indentLevel = NO_INDENT;
  
      /** Cheap-o diagnotic name manager. */
      protected OutputNameManager diagNameMgr = null;
  
      //-----------------------------------------------------
      //-------- Worker methods to iterate over directories --------
      //-----------------------------------------------------
  
      /**
       * Accesor for a processor wrapper.
       * <ul>Wrapper must support the following operations:
       * <li>Object createNewProcessor (String liaisonClassName) throws Exception</li>
       * <li>long processToFile (String xmlSource, String xslStylesheet, String resultTree) throws Exception</li>
       * <li>void reset()</li>
       * <li>void setDiagnosticsOutput(java.io.PrintWriter pw)</li>
       * </ul>
       * <p>This allows tests to be agnostic to different flavors of processors.</p>
       * @todo Update to new calls everywhere!
       */
      protected org.apache.qetest.xslwrapper.ProcessorWrapper processorW;
  
      /** Cheap-o benchmarking: total time spent in all process calls. */
      long overallTime = 0;
  
      /** Cheap-o benchmarking: total number of files processed. */
      long overallFilesProcessed = 0;
  
      /** Cheap-o benchmarking: total time spent in all process calls per directory. */
      long dirTime = 0;
  
      /** Cheap-o benchmarking: total number of files processed per directory. */
      long dirFilesProcessed = 0;
  
      // We also use XLTest's fields: reporter, flavor, liaison, diagnostics
      // These would have to be parameterized to turn this class into an interface
  
      /**
       * Subclassed callback to provide info about the processor you're testing.
       * <p>Override to return a string describing the specific version/flavor/etc.
       * of the processor you're testing.  Provides a default implementation that
       * can be used if you wish.  Called from logTestProps
       * (which is called from processTestDir).</p>
       * @return 'ProcessorVersion;' + getDescription()
       */
      public String getDescription()
      {
  
          if (processorW != null)
          {
  
              // Auto-create a ProcessorWrapper as needed
              createNewProcessor();
          }
  
          return "ProcessorVersion;" + processorW.getDescription();
      }
  
      /**
       * Log out any version or system info.
       * <p>Logs System.getProperties(), the testProps block, plus the provided string
       * indicating the version info from the processor.  Called from processTestDir.</p>
       */
      public void logTestProps()
      {
  
          // Log out system/build info - use crit msg so it's always reported
          reporter.logCriticalMsg(getDescription());
          reporter.logHashtable(reporter.CRITICALMSG, System.getProperties(),
                                "System.getProperties");
          reporter.logHashtable(reporter.CRITICALMSG, testProps, "testProps");
      }
  
      /**
       * Iterate through our inputDir, going into subdirs and processing xml/xsl file pairs therein.
       * <p>.</p>
       * <p><B>NOTE:</B> It is the responsibility of the subclassed test to call
       * createNewProcessor (or it's equivalent) before calling processTestDir!</p>
       */
      public void processTestDir()  // TODO change to processTests()
      {
  
          // NOTE: This method simply directly uses our/our parents instance variables:
          //      inputDir, reporter, category, outDir, goldDir, noErrTest, 
          //      plus each of the benchmarking variables: 
          //      overallTime, overallFilesProcessed, dirTime, dirFilesProcessed 
          // report out version info, properties, etc.
          logTestProps();
  
          // Ensure the inputDir is there - we must have a valid location for input files
          File testDirectory = new File(inputDir);
  
          if (!testDirectory.exists())
          {
              if (!lookForTestDir())  // attempt self-discovery of dirs
              {
  
                  // Note: This might want to be inside of a testCase
                  reporter.checkErr("inputDir(" + testDirectory.getPath()
                                    + ") does not exist, aborting!");
  
                  return;
              }
          }
  
          // We process tests in one of two different ways:
          //  - from a user specified -fileList
          //  - by iterating over all subdirs that follow the Conformance rules
          String fileListFileName = testProps.getProperty(OPT_FILELIST);
  
          if (fileListFileName != null)
          {
  
              // We're going to grab the list of files the user specified
              //  This should be a vector of XSLTestfileInfo objects, each one a test to run
              // Currently, the readFileListFile method forces all files to be 
              //  specified absolutely by the time it's done
              reporter.logWarningMsg(
                  "-fileList property implies we ignore category and excludes!");
  
              Vector list = readFileListFile(fileListFileName);
  
              reporter.testCaseInit("Conformance Test of: fileList "
                                    + fileListFileName);
              processFileList(list);
              reporter.testCaseClose();
          }
  
          // TODO make the dir iteration below follow the same discovery - execution
          //  model as the fileList case above - requires re-writing the rest of this 
          //  method as well as processSingleDir
          else
          {
  
              // We're going to scan the test directory for subdirectories to iterate over
              // Then for each subdir, we'll scan for files according to the 
              reporter.logInfoMsg("inputDir(" + testDirectory.getPath()
                                  + ") looking for valid subdirs...");
  
              FilenameFilter dirFilter;
  
              if ((category != null) && (category != ""))
              {
  
                  // Filter on the directories we were asked for
                  // semi HACK - should implement a full list of categories, not just one
                  dirFilter = new ConformanceDirRules(category);
              }
              else
              {  // Just use default filtering (skip dirs beginning with [x|X])
                  dirFilter = new ConformanceDirRules();
              }
  
              String dirNames[] = testDirectory.list(dirFilter);
  
              if ((null == dirNames) || (dirNames.length <= 0))
              {
  
                  // TODO should this be wrapped in a testCase?
                  reporter.checkFail("inputDir(" + testDirectory.getPath()
                                     + ") no valid subdirs found!");
  
                  return;
              }
  
              int nDirs = dirNames.length;
  
              // For every subdirectory, check if we should run tests in it
              for (int i = 0; i < nDirs; i++)
              {
                  File subTestDir = new File(testDirectory, dirNames[i]);
  
                  // Convert all dir references to absolute ones, to get around 
                  //  potential problems with relative paths and test harnesses
                  //  that change the current directory
                  // TODO: Check if we should use getCanonicalPath instead!
                  subTestDir = new File(subTestDir.getAbsolutePath());
  
                  if (null == subTestDir)
                  {
                      reporter.logWarningMsg("Could not find subdir: "
                                             + testDirectory.getPath()
                                             + File.pathSeparator
                                             + dirNames[i]);
  
                      continue;
                  }
  
                  File subOutDir = new File(outputDir, dirNames[i]);
  
                  subOutDir = new File(subOutDir.getAbsolutePath());
  
                  File subGoldDir = new File(goldDir, dirNames[i]);
  
                  subGoldDir = new File(subGoldDir.getAbsolutePath());
  
                  // Run our positive case tests
                  reporter.testCaseInit("Conformance Test of: " + dirNames[i]);
                  processSingleDir(subTestDir, subOutDir, subGoldDir, null);  // using default file filter
  
                  if (dirFilesProcessed > 0)
                  {
  
                      // Only log these stats if perfLogging is set to true
                      String tmp =
                          testProps.getProperty(Reporter.OPT_PERFLOGGING);
  
                      if ((tmp != null) && tmp.equalsIgnoreCase("true"))
                      {
                          reporter.logStatistic(
                              reporter.STATUSMSG,
                              (dirTime / dirFilesProcessed), 0,
                              "Dir average time to process files in this dir");
                          reporter.logStatistic(
                              reporter.STATUSMSG,
                              (overallTime / overallFilesProcessed), 0,
                              "Running average time of dirs so far");
                      }
                  }
  
                  reporter.testCaseClose();
  
                  if (!noErrTest)  // Note the double negative
                  {
  
                      // Run our negative, or 'err' case tests within this subdir
                      final String ERRDIRNAME = "err";
                      FilenameFilter errDirFilter =
                          new ConformanceDirRules(ERRDIRNAME);
                      String errDirNames[] = subTestDir.list(errDirFilter);
  
                      if ((null != errDirNames) && (errDirNames.length > 0))
                      {
  
                          // Hopefully, there's only the one 'err' directory!
                          for (int k = 0; k < errDirNames.length; k++)
                          {
                              File errTestDir = new File(subTestDir,
                                                         errDirNames[k]);
  
                              if (null == errTestDir)
                              {
                                  reporter.logWarningMsg(
                                      "Could not find errDir: "
                                      + subTestDir.getPath()
                                      + File.pathSeparator + errDirNames[i]);
  
                                  continue;
                              }
  
                              // Optimization: if there are no files in the errDir, don't bother going there
                              FilenameFilter errFileFilter =
                                  new ConformanceErrFileRules(dirNames[i]);
                              String anyErrNames[] =
                                  errTestDir.list(errFileFilter);
  
                              if ((anyErrNames != null)
                                      && (anyErrNames.length > 0))
                              {
                                  File errOutDir = new File(subOutDir,
                                                            errDirNames[k]);
                                  File errGoldDir = new File(subGoldDir,
                                                             errDirNames[k]);
  
                                  // Use special filter to look for parentName+"err"
                                  reporter.testCaseInit(
                                      "Conformance Test of errDir: "
                                      + errDirNames[k]);
                                  processSingleDir(errTestDir, errOutDir,
                                                   errGoldDir, errFileFilter);
  
                                  if (dirFilesProcessed > 0)
                                  {
                                      reporter.logStatistic(
                                          reporter.STATUSMSG,
                                          (dirTime / dirFilesProcessed), 0,
                                          "Dir average time to process files in this dir");
                                      reporter.logStatistic(
                                          reporter.STATUSMSG,
                                          (overallTime / overallFilesProcessed),
                                          0, "Running average time of dirs so far");
                                  }
  
                                  reporter.testCaseClose();
                              }  // of if ((anyErrNames != null).. Optimization
                              else
                              {
                                  reporter.logStatusMsg(
                                      "inputDir(" + errTestDir.getPath()
                                      + ") no 'err' files found");
                              }
                          }  // of for (int k = 0...
                      }  // of if (null != errDirNames)
                      else
                      {
                          reporter.logInfoMsg("inputDir("
                                              + testDirectory.getPath()
                                              + ") no 'err' subdirs found");
                      }  // of if...else (null != errDirNames)
                  }  // of if (runErrTests)
              }  // of for (int i = 0... 'For every subdirectory'
  
              // Done with all dirs, report last statistic
              if (overallFilesProcessed > 0)
              {
  
                  // Only log these stats if perfLogging is set to true
                  String tmp = testProps.getProperty(Reporter.OPT_PERFLOGGING);
  
                  if ((tmp != null) && tmp.equalsIgnoreCase("true"))
                  {
                      reporter.logStatistic(
                          reporter.STATUSMSG,
                          (overallTime / overallFilesProcessed), 0,
                          "Overall average process time");
                  }
              }
          }  // of if (fileListFileName != null)
      }
  
      /**
       * Run one directory full of xsl/xml file pairs through the processor; virtually stateless.
       * <p>This assumes the roots of each of test and out already exist.
       * This then takes an existing test subdir, creates corresponding out and gold
       * subdirs, and then processes all xsl/xml file pairs using processSingleFile().</p>
       * <p>Default processing is provided for Conformance-style tests.</p>
       * <p>Each subdirectory is treated like a test case.</p>
       * <p>This method does not access the processorWrapper (or it's processor)
       * in any way - thus is suitable for use in subclasses with overriden
       * createNewProcessor() methods.</p>
       * @param testDirectory specific dir with xml/xsl file pairs to iterate through
       * @param outDirectory dir to put any output files in
       * @param goldDirectory dir to look for expected gold files in
       * @param filter filter for filenames to look for (null=default, name starts-with parent)
       */
      public void processSingleDir(File testDirectory, File outDirectory,
                                   File goldDirectory, FilenameFilter filter)
      {
  
          // Reset performance stuff first, so we don't double-report if we fail to run any tests here
          dirTime = 0;
          dirFilesProcessed = 0;
  
          File[] requiredDirs = { testDirectory, outDirectory };
          File[] otherDirs = { goldDirectory };
  
          // testDirectory = 0, outDirectory = 1, goldDirectory = 2
          File[] dirs = validateDirs(requiredDirs, otherDirs);
  
          if (dirs == null)  // also ensures that dirs[0] is non-null
          {
              reporter.checkFail(
                  "testDir or outDir do not exist, aborting test!");
  
              return;
          }
  
          // Set a default filter (<name>*.xsl where <name>=<dir>) only if not already set
          if (filter == null)
          {
              if ((excludes != null) && (excludes.length() > 1))  // Arbitrary check for non-null, non-blank string
              {
                  filter = new ConformanceFileRules(excludes);  // NOTE: changes caller's copy as well
  
                  reporter.logTraceMsg(
                      "Using default ConformanceFileRules with excludes: "
                      + excludes);
              }
              else
              {
                  filter = new ConformanceFileRules();  // NOTE: changes caller's copy as well
  
                  reporter.logTraceMsg("Using default ConformanceFileRules");
              }
          }
          else
          {  // NEEDSWORK: should only print this if it's also not already a ConformanceFileRules
              reporter.logInfoMsg("Using custom fileFilter: "
                                  + filter.toString());
          }
  
          // Use the fileFilter to get only the names we want to test
          String xslnames[] = dirs[0].list(filter);
  
          if (null != xslnames)
          {
              int numXSLFiles = xslnames.length;
  
              reporter.logInfoMsg("processOneDir(" + dirs[0].getPath()
                                  + ") with " + numXSLFiles
                                  + " potential .xsl files to test");
  
              // Iterate over every xsl file found and check if we should test it
              for (int k = 0; k < numXSLFiles; k++)
              {
                  String xslName = xslnames[k];
  
                  // Find the root of the filename, less extension, and create .xml and .out versions of the name
                  // OPTIMIZATION? ConformanceDirRules probably ensure that we have a good name, so we may not need this.
                  int dotIndex = xslName.indexOf('.');
  
                  if (dotIndex > 0)
                  {
  
                      // Construct the various inputs to the LotusXSL processor, based on .xsl filename
                      String rootFileName = xslName.substring(0, dotIndex);
  
                      // Switch to CanonicalPaths which seem to be safer SC 28-Jun-00
                      String absXSLName = null;
  
                      try
                      {
                          absXSLName = dirs[0].getCanonicalPath()
                                       + File.separatorChar + xslName;
                      }
                      catch (IOException ioe1)
                      {
                          absXSLName = dirs[0].getAbsolutePath()
                                       + File.separatorChar + xslName;
                      }
  
                      String absXMLName = null;
  
                      try
                      {
                          absXMLName = dirs[0].getCanonicalPath()
                                       + File.separatorChar + rootFileName
                                       + xmlExtension;
                      }
                      catch (IOException ioe2)
                      {
                          absXMLName = dirs[0].getAbsolutePath()
                                       + File.separatorChar + rootFileName
                                       + xmlExtension;
                      }
  
                      if (dirs[1] == null)
                      {
  
                          // Provide some sort of default TODO is this the best cross-platform default?
                          dirs[1] = new File("\badOutDir");
  
                          reporter.logErrorMsg(
                              "outDir(" + outDirectory
                              + ") is bad - resetting to \badOutDir");
                      }
  
                      String absOutName = null;
  
                      try
                      {
                          absOutName = dirs[1].getCanonicalPath()
                                       + File.separatorChar + rootFileName
                                       + outExtension;
                      }
                      catch (IOException ioe3)
                      {
                          absOutName = dirs[1].getAbsolutePath()
                                       + File.separatorChar + rootFileName
                                       + outExtension;
                      }
  
                      if (dirs[2] == null)
                      {
  
                          // Provide some sort of default
                          dirs[2] = new File("\badGoldDir");
  
                          reporter.logErrorMsg(
                              "goldDir(" + goldDirectory
                              + ") is bad - resetting to \badGoldDir");
                      }
  
                      String absGoldName = null;
  
                      try
                      {
                          absGoldName = dirs[2].getCanonicalPath()
                                        + File.separatorChar + rootFileName
                                        + outExtension;
                      }
                      catch (IOException ioe3)
                      {
                          absGoldName = dirs[2].getAbsolutePath()
                                        + File.separatorChar + rootFileName
                                        + outExtension;
                      }
  
                      // Sanity check - see if files exist (not strictly needed?)
                      File xslF = new File(absXSLName);
                      File xmlF = new File(absXMLName);
  
                      if (xslF.exists() && xmlF.exists())
                      {
                          reporter.logTraceMsg("About to test:"
                                               + xmlF.getName() + " with:"
                                               + xslF.getName() + " into:"
                                               + absOutName);
                      }
                      else
                      {
                          reporter.logWarningMsg("Files may not exist:"
                                                 + absXMLName + " or:"
                                                 + absXSLName);
                      }
  
                      // Actually run the xsl/xml file pair through the processor
                      // Note subclasses may override this to do different kinds of processing
                      // FUTUREWORK: put this switch in a method, so subclasses can ovverride
                      //       to also do different kinds of validation!
                      switch (processSingleFile(absXMLName, absXSLName,
                                                absOutName))
                      {
                      case PROCESS_OK :
                          int res = fileChecker.check(reporter,
                                                      new File(absOutName),
                                                      new File(absGoldName),
                                                      xslF.getName()
                                                      + " output comparison");
  
                          // ((XLTestReporter)reporter).checkFiles(new File(absOutName), new File(absGoldName), xslF.getName() + " output comparison");
                          // If we're using an appropriate fileChecker, 
                          //  get extra info on a fail
                          if (res == reporter.FAIL_RESULT)
                          {
                              String tmp = fileChecker.getExtendedInfo();
  
                              if (tmp != null)
                                  reporter.logArbitrary(reporter.INFOMSG, tmp);
                              else
                                  reporter.logTraceMsg(
                                      "getFileChecker().getExtendedInfo() not available");
                          }
                          break;
                      case GOT_EXPECTED_EXCEPTION :
                          reporter.checkPass(xslF.getName()
                                             + " got expected exception");
                          break;
                      case UNEXPECTED_EXCEPTION :
                          reporter.checkFail(xslF.getName()
                                             + " got unexpected exception");
                          break;
                      }
                  }  // of if (dotIndex > 0)
                  else
                  {
                      reporter.logWarningMsg("Problem with xsl filename: "
                                             + xslName);
                  }
              }  // of for (int k = 0...
          }  // of if (null != xslnames)
          else
          {
              reporter.logWarningMsg("Could not find any xsl files in: "
                                     + dirs[0].getPath());
          }
  
          // update overall statistics
          overallTime += dirTime;
          overallFilesProcessed += dirFilesProcessed;
      }
  
      // HEY - need a quintuple: xml, xsl, out, gold, options
  
      /**
       * Run a list of specific files through the processor; virtually stateless.
       * <p>The file names are assumed to be fully specified, and we assume
       * the corresponding directories exist.</p>
       * <p>This method does not access the processorWrapper (or it's processor)
       * in any way - thus is suitable for use in subclasses with overriden
       * createNewProcessor() methods.</p>
       * @param testDirectory specific dir with xml/xsl file pairs to iterate through
       * @param outDirectory dir to put any output files in
       * @param goldDirectory dir to look for expected gold files in
       * @param vector of XSLTestfileInfo objects of (xslFile, xmlFile, outFile, goldFile, options)
       *
       * NEEDSDOC @param fileList
       */
      public void processFileList(Vector fileList)
      {
  
          // Reset performance stuff first, so we don't double-report if we fail to run any tests here
          dirTime = 0;
          dirFilesProcessed = 0;
  
          // String xslnames[] = dirs[0].list(filter);
          if (null == fileList)
          {
  
              // No files, no testing - report an error and return
              reporter.logErrorMsg("Blank fileList - nothing to test!");
  
              return;
          }
  
          // Now just go through the list and process each set
          int numXSLFiles = fileList.size();
  
          reporter.logInfoMsg("processFileList() with " + numXSLFiles
                              + " potential .xsl files to test");
  
          // Iterate over every xsl file found and check if we should test it
          for (int ctr = 0; ctr < numXSLFiles; ctr++)
          {
  
              // If the object is not an XSLTestfileInfo, assume it's a string to create one with
              XSLTestfileInfo fileSet = null;
  
              try
              {
                  fileSet = (XSLTestfileInfo) fileList.elementAt(ctr);
              }
              catch (ClassCastException cce)
              {
                  fileSet =
                      new XSLTestfileInfo((String) fileList.elementAt(ctr), "");
  
                  // If this line fails, too bad!
              }
  
              // Sanity check - see if files exist (not strictly needed?)
              File xslF = new File(fileSet.inputName);
              File xmlF = new File(fileSet.xmlName);
  
              if (xslF.exists() && xmlF.exists())
              {
                  reporter.logTraceMsg("About to test:" + fileSet.xmlName
                                       + " with:" + fileSet.inputName
                                       + " into:" + fileSet.outputName);
              }
              else
              {
                  reporter.logWarningMsg("Files may not exist:"
                                         + fileSet.xmlName + " or:"
                                         + fileSet.inputName);
              }
  
              // Actually run the xsl/xml file pair through the processor
              // Note subclasses may override this to do different kinds of processing
              // FUTUREWORK: put this switch in a method, so subclasses can ovverride
              //       to also do different kinds of validation!
              switch (processSingleFile(fileSet.xmlName, fileSet.inputName,
                                        fileSet.outputName))
              {
              case PROCESS_OK :
                  int res = fileChecker.check(reporter,
                                              new File(fileSet.outputName),
                                              new File(fileSet.goldName),
                                              xslF.getName()
                                              + " output comparison");
  
                  // ((XLTestReporter)reporter).checkFiles(new File(fileSet.outputName), new File(fileSet.goldName), xslF.getName() + " output comparison");
                  // If we're using an appropriate fileChecker, 
                  //  get extra info on a fail
                  if (res == reporter.FAIL_RESULT)
                  {
                      String tmp = fileChecker.getExtendedInfo();
  
                      if (tmp != null)
                          reporter.logArbitrary(reporter.INFOMSG, tmp);
                      else
                          reporter.logTraceMsg(
                              "getFileChecker().getExtendedInfo() not available");
                  }
                  break;
              case GOT_EXPECTED_EXCEPTION :
                  reporter.checkPass(xslF.getName()
                                     + " got expected exception");
                  break;
              case UNEXPECTED_EXCEPTION :
                  reporter.checkFail(xslF.getName()
                                     + " got unexpected exception");
                  break;
              }
          }  // of while...
  
          // update overall statistics
          overallTime += dirTime;
          overallFilesProcessed += dirFilesProcessed;
      }
  
      /**
       * Run one xsl/xml file pair through the processor.
       * <p>May be overriden by subclasses to do different kinds of processing -
       * mainly variations of the process(...) APIs.</p>
       * <p>XSLDirectoryIterator provides a default implementation that
       * is appropriate for use with Conformance tests.</p>
       * <p>processSingleFile should do all processing required to transform the XMLName,
       * via the XSLName, into an OutName file on disk.  It returns a status denoting
       * what happend during the processing.</p>
       * <p>Should increment dirTotalTime and dirFilesProcessed.</p>
       * <p>processSingleFile() and createNewProcessor() both rely on using the
       * same semantics to use the processor (or processorWrapper); thus if a
       * subclass overrides one, you should probably override the other.</p>
       * <p>Due to changes Jun-00 in Xerces 1.1.2; checks XLTest.useURI to see
       * if we can use unadulterated filenames or have to use URI's.</p>
       * @author Shane Curcuru
       * @todo update to use XSLTestfileInfo objects instead
       * @param XMLName path\filename of XML data file
       * @param XSLName path\filename of XSL Stylesheet file
       * @param OutName path\filename of desired output file
       * @return int status - pass, fail, expected exception or unexpected exception
       */
      public int processSingleFile(String XMLName, String XSLName,
                                   String OutName)
      {
  
          long fileTime = ProcessorWrapper.ERROR;
  
          try
          {
  
              // Reset the indent level each time, to ensure the process uses it (it may get reset() below)
              if (indentLevel > NO_INDENT)
              {
                  reporter.logTraceMsg("processSingleFile() set indent "
                                       + indentLevel);
                  processorW.setIndent(indentLevel);
              }
  
              // Force filerefs to be URI's if needed
              if (useURI)
              {
  
                  // Use this static convenience method; returns a URL; convert to String via toExternalForm()
                  XMLName = getURLFromString(XMLName, null).toExternalForm();
                  XSLName = getURLFromString(XSLName, null).toExternalForm();
  
                  // HACK (end)- replicate this code locally, since we may test Xalan2 which doesn't have this!
                  // Note: Currently 28-Jun-00, the output of files is handled differently, so 
                  //  we do NOT want to convert those.  Subject to change, however.
                  reporter.logTraceMsg("processSingleFile() useURI: "
                                       + XSLName);
              }
  
              fileTime = processorW.processToFile(XMLName, XSLName, OutName);
  
              if (fileTime != ProcessorWrapper.ERROR)
              {
                  dirTime += fileTime;
  
                  dirFilesProcessed++;
  
                  reporter.logTraceMsg("processSingleFile(" + XSLName
                                       + ") no exceptions; time " + fileTime);
              }
              else
              {
  
                  // Do not increment performance counters if there's an error
                  reporter.logWarningMsg("processSingleFile(" + XSLName
                                         + ") returned ERROR code!");
              }
  
              processorW.reset();
          }
  
          // Catch SAXExceptions and check if they're expected; restart to be safe
          catch (SAXException sax)
          {
              reporter.logStatusMsg("processSingleFile(" + XSLName
                                    + ") threw: " + sax.toString());
  
              int retVal = checkExpectedException(sax, XSLName, OutName);
  
              createNewProcessor();  // Should be configurable!
  
              return retVal;
          }
  
          // Catch general Exceptions, check if they're expected, and restart
          catch (Exception e)
          {
              reporter.logStatusMsg("processSingleFile(" + XSLName
                                    + ") threw: " + e.toString());
  
              int retVal = checkExpectedException(e, XSLName, OutName);
  
              createNewProcessor();
  
              return retVal;
          }
  
          // Catch any Throwable, check if they're expected, and restart
          catch (Throwable t)
          {
              reporter.logStatusMsg("processSingleFile(" + XSLName
                                    + ") threw: " + t.toString());
  
              int retVal = checkExpectedException(t, XSLName, OutName);
  
              createNewProcessor();
  
              return retVal;
          }
  
          return PROCESS_OK;
      }
  
      /**
       * Worker method to validate expected exception text.
       * <p>Logs message and creates new processor if useDefaultProcessor;
       * also performs basic validation of .toString() of exception.</p>
       * <p>Does not store any state; suitable for use in interface.</p>
       * @author Shane Curcuru
       * @todo update to use XSLTestfileInfo objects instead
       * @param expected exception that was caught above
       * @param filename of XSL test file where 'ExpectedException:' may be
       * @param filename of output file , optional for future use
       *
       * NEEDSDOC @param ee
       * NEEDSDOC @param testName
       * NEEDSDOC @param outName
       * @return status, as one of PROCESS_OK, GOT_EXPECTED_EXCEPTION, UNEXPECTED_EXCEPTION
       */
      protected int checkExpectedException(Throwable ee, String testName,
                                           String outName)
      {
  
          // Is this exception the one we expected? Assume not
          boolean gotExpected = false;
  
          // Read in the testName file to see if it's expecting something        
          try
          {
              FileReader fr = new FileReader(testName);
              BufferedReader br = new BufferedReader(fr);
  
              // NEEDSWORK: should we optimize and only check the first xx num lines?
              for (;;)
              {
                  String inbuf = br.readLine();
  
                  if (inbuf == null)
                      break;  // end of file, break out and return default (false)
  
                  int idx = inbuf.indexOf(EXPECTED_EXCEPTION);
  
                  if (idx <= 0)
                      continue;  // not on this line, keep going
  
                  // The expected exception.getMessage is the rest of the line, trimmed
                  //   Note that there's actually a " -->" at the end of the line probably,
                  //   but we'll ignore that by using .startsWith()
                  String expExc =
                      inbuf.substring((idx + EXPECTED_EXCEPTION.length()),
                                      inbuf.length()).trim();
  
                  if (expExc.startsWith(ee.toString()))
                  {
                      gotExpected = true;  // equal, they pass
  
                      break;
                  }
                  else
                  {
  
                      // Not equal, so keep looking for another flavor
                      continue;
                  }
              }  // end for (;;)
          }
          catch (java.io.IOException ioe)
          {
              reporter.logWarningMsg("checkExpectedException() threw: "
                                     + ioe.toString());
  
              gotExpected = false;
          }
  
          return (gotExpected ? GOT_EXPECTED_EXCEPTION : UNEXPECTED_EXCEPTION);
      }
  
      /**
       * Ask the wrapper to construct a processor, with optional diagnostic logs.
       * <p>this worker method is called from within processSingleFile as well as
       * various places within subclassed tests.</p>
       * <p>processSingleFile() and createNewProcessor() both rely on using the
       * same semantics to use the processor (or processorWrapper); thus if a
       * subclass overrides one, you should probably override the other.</p>
       * @return status - OK if successfull, false if some error occoured
       */
      protected boolean createNewProcessor()
      {
  
          if ((flavor == null) || (flavor.equals("")))
          {
              return false;
          }
  
          if (processorW == null)
          {
  
              // Create the specific subclass for desired flavor
              processorW = ProcessorWrapper.getWrapper(flavor);
  
              if (processorW == null)
              {
  
                  // Something must have gone wrong with the wrapper
                  reporter.logErrorMsg(
                      "createNewProcessor() failed with flavor: " + flavor);
              }
          }
  
          try
          {
              Object temp = processorW.createNewProcessor(liaison);
  
              if (temp == null)
              {
                  reporter.logErrorMsg(
                      "createNewProcessor() got null for processor");
              }
          }
          catch (Exception e)
          {
              reporter.logErrorMsg("createNewProcessor() threw: "
                                   + e.toString());
              reporter.logThrowable(reporter.CRITICALMSG, e,
                                    "createNewProcessor() threw: ");
  
              return false;
          }
  
          if (diagnostics != null)
          {
              if (diagNameMgr == null)
              {
  
                  // Create a new name manager, hardcode the .log extension just because
                  diagNameMgr = new OutputNameManager(diagnostics, ".log");
              }
  
              try
              {
                  processorW.setDiagnosticsOutput(
                      new PrintWriter(new FileWriter(diagNameMgr.nextName())));
                  reporter.logInfoMsg("createNewProcessor() new diagnostics = "
                                      + diagNameMgr.currentName());
              }
              catch (IOException ioe)
              {
                  reporter.logWarningMsg(
                      "createNewProcessor() could not create new diagnostics = "
                      + diagNameMgr.currentName());
  
                  return false;
              }
          }
  
          // Apply an optional indent if asked; this needs to be documented!
          // @todo this name conflicts with Logger.OPT_INDENT: we should 
          //       consider changing this one (which isn't a great property 
          //       anyway with new serializer models)
          String idt = testProps.getProperty("indent", null);
  
          // Only attempt to apply the indent if we think there's really a value there
          if ((idt != null) && (!idt.equals("")))
          {
              int temp = NO_INDENT;
  
              try
              {
                  temp = Integer.parseInt(idt);
              }
              catch (NumberFormatException numEx)
              {
                  temp = NO_INDENT;  // Make sure we don't try to use an illegal value
  
                  reporter.logWarningMsg(
                      "createNewProcessor() failed to set indent " + idt);
              }
  
              if (temp > NO_INDENT)
              {
                  indentLevel = temp;
  
                  processorW.setIndent(indentLevel);
                  reporter.logTraceMsg("createNewProcessor() set indent "
                                       + idt);
              }
              else
              {
                  reporter.logTraceMsg(
                      "createNewProcessor() did not set indent " + idt);
              }
          }
          else
          {
              reporter.logTraceMsg(
                  "createNewProcessor() skipping blank/null indent, now "
                  + indentLevel);
          }
  
          return true;
      }
  
      /**
       * Read in a line-oriented file specifying a list of files to test.
       * <p>File format is pretty simple:</p>
       * <ul>
       * <li># beginning a line is a comment</li>
       * <li># if <b>first</b> line in file has 'relative' anywhere, paths are
       * relative to testDir, outDir, etc.</li>
       * <li># (default is assuming all paths are absolute)</li>
       * <li># rest of lines are space delimited filenames and options</li>
       * <li>xslName xmlName outName goldName options options options...</li>
       * <li><b>Note:</b> see {@link XSLTestfileInfo XSLTestfileInfo} for
       * details on how the file lines are parsed!</li>
       * </ul>
       * <p>Most items are optional, but not having them may result in validation oddities</p>
       * @param String; name of the file
       *
       * NEEDSDOC @param fileName
       * @return Vector of XSLTestfileInfo objects, null if any error
       */
      public Vector readFileListFile(String fileName)
      {
  
          final String COMMENT_CHAR = "#";
          final String ABSOLUTE = "absolute";
          final String RELATIVE = "relative";
          final String UNSPECIFIED_FILENAME = "UNSPECIFIED_FILENAME";
  
          // Verify the file is there and we can open it
          File f = new File(fileName);
  
          if (!f.exists())
          {
              reporter.logErrorMsg("FileListFile: " + fileName
                                   + " does not exist!");
  
              return null;
          }
  
          FileReader fr = null;
          BufferedReader br = null;
          Vector vec = new Vector();
          String line = null;
  
          try
          {
              br = new BufferedReader(new FileReader(f));
              line = br.readLine();
          }
          catch (IOException ioe)
          {
              reporter.logErrorMsg("FileListFile: " + fileName + " threw: "
                                   + ioe.toString());
  
              return null;
          }
  
          // Read in the file line by line
          if (line == null)
          {
              reporter.logErrorMsg("FileListFile: " + fileName
                                   + " appears to be blank!");
  
              return null;
          }
  
          // Check if the first line is a comment *and* has 'relative'
          //  Note default is relative paths to testDir/testDir/outDir/goldDir
          boolean isRelative = false;
  
          if ((line.startsWith(COMMENT_CHAR)) && (line.indexOf(RELATIVE) > 0))
          {
              isRelative = true;
          }
  
          // Load each line into an XSLTestfileInfo object
          for (;;)
          {
  
              // Skip any lines beginning with # comment char or that are blank
              if ((!line.startsWith(COMMENT_CHAR)) && (line.length() > 0))
              {
  
                  // Create a testInfo object and initialize it with the line's contents
                  XSLTestfileInfo testInfo = new XSLTestfileInfo(line,
                                                 UNSPECIFIED_FILENAME);
  
                  // Avoid spurious passes when output & gold both not specified
                  if (testInfo.goldName.equals(UNSPECIFIED_FILENAME))
                  {
                      testInfo.goldName += "-gold";
                  }
  
                  // Add it to our vector
                  vec.addElement(testInfo);
              }
  
              // Read next line and loop
              try
              {
                  line = br.readLine();
              }
              catch (IOException ioe2)
              {
  
                  // Just force us out of the loop; if we've already read part of the file, fine
                  reporter.logWarningMsg("FileListFile: " + fileName
                                         + " threw: " + ioe2.toString());
  
                  break;
              }
  
              if (line == null)
                  break;
          }
  
          if (vec.size() == 0)
          {
              reporter.logErrorMsg("FileListFile: " + fileName
                                   + " did not have any non-comment lines!");
  
              return null;
          }
  
          // Now, munge the paths if asked to
          if (isRelative)
          {
              XSLTestfileInfo basePaths = new XSLTestfileInfo();
  
              // xml,xsl are in testDir, out is in outDir, gold is in goldDir
              StringTokenizer st = new StringTokenizer(inputDir + "\t"
                                                       + inputDir + "\t"
                                                       + outputDir + "\t"
                                                       + goldDir, "\t");
  
              basePaths.load(st, "undefined");
              mungeListPaths(basePaths, vec);
          }
  
          if (debug)
          {
  
              // Cheap-o debugging for this stuff
              for (int ctr = 0; ctr < vec.size(); ctr++)
              {
                  reporter.logTraceMsg(
                      "vec[" + ctr + "] = "
                      + ((XSLTestfileInfo) vec.elementAt(ctr)).dump());
              }
          }
  
          reporter.logTraceMsg("readFileListFile(" + fileName + ") isRelative="
                               + isRelative + ", size=" + vec.size());
  
          return vec;
      }
  
      /**
       * Munge relative paths to start from testDir,testDir,outDir,goldDir.
       * <p>Changes caller's copy of the vector.  options field is left untouched</p>
       * @param XSLTestfileInfo base directories for each member
       * @param Vector of XSLTestfileInfos to munge
       *
       * NEEDSDOC @param base
       * NEEDSDOC @param vec
       */
      public void mungeListPaths(XSLTestfileInfo base, Vector vec)
      {
  
          // TODO!!!
          reporter.logCriticalMsg(
              "mungeListPaths NOT IMPLEMENTED! Only absolute ones are done");
      }
  
      /**
       * Validate our test, output, and gold dirs; return test dir.
       * <p>If any optional dir cannot be created, it's array entry will be null.</p>
       * @author Shane Curcuru
       * @param array of directories that must previously exist
       * @param array of optional directories; if they do not exist they'll be created
       *
       * NEEDSDOC @param requiredDirs
       * NEEDSDOC @param optionalDirs
       * @return array of file objects, null if any error
       */
      public File[] validateDirs(File[] requiredDirs, File[] optionalDirs)
      {
  
          if ((requiredDirs == null) || (optionalDirs == null))
          {
              return null;
          }
  
          File[] dirs = new File[(requiredDirs.length + optionalDirs.length)];
          int ctr = 0;
  
          try
          {
              for (int ir = 0; ir < requiredDirs.length; ir++)
              {
                  reporter.logTraceMsg("validateDirs(" + requiredDirs[ir]
                                       + ") loop");
  
                  if (!requiredDirs[ir].exists())
                  {
                      if (!requiredDirs[ir].mkdirs())
                      {
                          reporter.logErrorMsg("validateDirs("
                                               + requiredDirs[ir]
                                               + ") does not exist!");
  
                          return null;
                      }
                      else
                      {
                          reporter.logTraceMsg("validateDirs("
                                               + requiredDirs[ir]
                                               + ") was created");
                      }
                  }
  
                  dirs[ctr] = requiredDirs[ir];
  
                  ctr++;
              }
  
              for (int iopt = 0; iopt < optionalDirs.length; iopt++)
              {
                  reporter.logTraceMsg("validateDirs(" + optionalDirs[iopt]
                                       + ") loop");
  
                  if (!optionalDirs[iopt].exists())
                  {
                      if (!optionalDirs[iopt].mkdirs())
                      {
                          reporter.logWarningMsg("validateDirs("
                                                 + optionalDirs[iopt]
                                                 + ") could not be created");
  
                          dirs[ctr] = null;
                      }
                      else
                      {
                          reporter.logTraceMsg("validateDirs("
                                               + optionalDirs[iopt]
                                               + ") was created");
  
                          dirs[ctr] = optionalDirs[iopt];
                      }
                  }
                  else
                  {  // It does previously exist, so copy it over
                      dirs[ctr] = optionalDirs[iopt];
                  }
  
                  ctr++;
              }
          }
          catch (Exception e)
          {
              reporter.logErrorMsg("validateDirs threw: " + e.toString());
              e.printStackTrace();  // NEEDSWORK
  
              return null;
          }
  
          return (dirs);
      }
  
      /**
       * Attempt to find a testDir if provided one doesn't exist.
       * Presumably if we have a known directory structure, we can
       * implement a simple search that will look for the
       * 'conformancetest' directory, whether it's below or above
       * us by one directory level.  For the terminally lazy who
       * don't bother specifying inputDir.
       * @return status - OK if we found it, false otherwise
       */
      public boolean lookForTestDir()
      {
  
          // Not implemented
          return false;
      }
  
      // /////////////////// HACK - added from Xalan1 org.apache.xalan.xslt.Process /////////////////////
  
      /**
       * Take a user string and try and parse XML, and also return
       * the url.  Needed for Xerces 1.2 builds.
       * @todo replace this with simpler code, better logging
       *
       * NEEDSDOC @param urlString
       * NEEDSDOC @param base
       *
       * NEEDSDOC ($objectName$) @return
       * @exception SAXException thrown if we really really can't create the URL
       */
      public static URL getURLFromString(String urlString, String base)
              throws SAXException
      {
  
          String origURLString = urlString;
          String origBase = base;
  
          // System.out.println("getURLFromString - urlString: "+urlString+", base: "+base);
          Object doc;
          URL url = null;
          int fileStartType = 0;
  
          try
          {
              if (null != base)
              {
                  if (base.toLowerCase().startsWith("file:/"))
                  {
                      fileStartType = 1;
                  }
                  else if (base.toLowerCase().startsWith("file:"))
                  {
                      fileStartType = 2;
                  }
              }
  
              boolean isAbsoluteURL;
  
              // From http://www.ics.uci.edu/pub/ietf/uri/rfc1630.txt
              // A partial form can be distinguished from an absolute form in that the
              // latter must have a colon and that colon must occur before any slash
              // characters. Systems not requiring partial forms should not use any
              // unencoded slashes in their naming schemes.  If they do, absolute URIs
              // will still work, but confusion may result.
              int indexOfColon = urlString.indexOf(':');
              int indexOfSlash = urlString.indexOf('/');
  
              if ((indexOfColon != -1) && (indexOfSlash != -1)
                      && (indexOfColon < indexOfSlash))
              {
  
                  // The url (or filename, for that matter) is absolute.
                  isAbsoluteURL = true;
              }
              else
              {
                  isAbsoluteURL = false;
              }
  
              if (isAbsoluteURL || (null == base) || (base.length() == 0))
              {
                  try
                  {
                      url = new URL(urlString);
                  }
                  catch (MalformedURLException e){}
              }
  
              // The Java URL handling doesn't seem to handle relative file names.
              else if (!((urlString.charAt(0) == '.') || (fileStartType > 0)))
              {
                  try
                  {
                      URL baseUrl = new URL(base);
  
                      url = new URL(baseUrl, urlString);
                  }
                  catch (MalformedURLException e){}
              }
  
              if (null == url)
              {
  
                  // Then we're going to try and make a file URL below, so strip 
                  // off the protocol header.
                  if (urlString.toLowerCase().startsWith("file:/"))
                  {
                      urlString = urlString.substring(6);
                  }
                  else if (urlString.toLowerCase().startsWith("file:"))
                  {
                      urlString = urlString.substring(5);
                  }
              }
  
              if ((null == url) && ((null == base) || (fileStartType > 0)))
              {
                  if (1 == fileStartType)
                  {
                      if (null != base)
                          base = base.substring(6);
  
                      fileStartType = 1;
                  }
                  else if (2 == fileStartType)
                  {
                      if (null != base)
                          base = base.substring(5);
  
                      fileStartType = 2;
                  }
  
                  File f = new File(urlString);
  
                  if (!f.isAbsolute() && (null != base))
                  {
  
                      // String dir = f.isDirectory() ? f.getAbsolutePath() : f.getParent();
                      // System.out.println("prebuiltUrlString (1): "+base);
                      StringTokenizer tokenizer = new StringTokenizer(base,
                                                      "\\/");
                      String fixedBase = null;
  
                      while (tokenizer.hasMoreTokens())
                      {
                          String token = tokenizer.nextToken();
  
                          if (null == fixedBase)
                          {
  
                              // Thanks to Rick Maddy for the bug fix for UNIX here.
                              if (base.charAt(0) == '\\'
                                      || base.charAt(0) == '/')
                              {
                                  fixedBase = File.separator + token;
                              }
                              else
                              {
                                  fixedBase = token;
                              }
                          }
                          else
                          {
                              fixedBase += File.separator + token;
                          }
                      }
  
                      // System.out.println("rebuiltUrlString (1): "+fixedBase);
                      f = new File(fixedBase);
  
                      String dir = f.isDirectory()
                                   ? f.getAbsolutePath() : f.getParent();
  
                      // System.out.println("dir: "+dir);
                      // System.out.println("urlString: "+urlString);
                      // f = new File(dir, urlString);
                      // System.out.println("f (1): "+f.toString());
                      // urlString = f.getAbsolutePath();
                      f = new File(urlString);
  
                      boolean isAbsolute = f.isAbsolute()
                                           || (urlString.charAt(0) == '\\')
                                           || (urlString.charAt(0) == '/');
  
                      if (!isAbsolute)
                      {
  
                          // Getting more and more ugly...
                          if (dir.charAt(dir.length() - 1)
                                  != File.separator.charAt(0)
                                  && urlString.charAt(0)
                                     != File.separator.charAt(0))
                          {
                              urlString = dir + File.separator + urlString;
                          }
                          else
                          {
                              urlString = dir + urlString;
                          }
  
                          // System.out.println("prebuiltUrlString (2): "+urlString);
                          tokenizer = new StringTokenizer(urlString, "\\/");
  
                          String rebuiltUrlString = null;
  
                          while (tokenizer.hasMoreTokens())
                          {
                              String token = tokenizer.nextToken();
  
                              if (null == rebuiltUrlString)
                              {
  
                                  // Thanks to Rick Maddy for the bug fix for UNIX here.
                                  if (urlString.charAt(0) == '\\'
                                          || urlString.charAt(0) == '/')
                                  {
                                      rebuiltUrlString = File.separator + token;
                                  }
                                  else
                                  {
                                      rebuiltUrlString = token;
                                  }
                              }
                              else
                              {
                                  rebuiltUrlString += File.separator + token;
                              }
                          }
  
                          // System.out.println("rebuiltUrlString (2): "+rebuiltUrlString);
                          if (null != rebuiltUrlString)
                              urlString = rebuiltUrlString;
                      }
  
                      // System.out.println("fileStartType: "+fileStartType);
                      if (1 == fileStartType)
                      {
                          if (urlString.charAt(0) == '/')
                          {
                              urlString = "file://" + urlString;
                          }
                          else
                          {
                              urlString = "file:/" + urlString;
                          }
                      }
                      else if (2 == fileStartType)
                      {
                          urlString = "file:" + urlString;
                      }
  
                      try
                      {
  
                          // System.out.println("Final before try: "+urlString);
                          url = new URL(urlString);
                      }
                      catch (MalformedURLException e)
                      {
  
                          // System.out.println("Error trying to make URL from "+urlString);
                      }
                  }
              }
  
              if (null == url)
              {
  
                  // The sun java VM doesn't do this correctly, but I'll 
                  // try it here as a second-to-last resort.
                  if ((null != origBase) && (origBase.length() > 0))
                  {
                      try
                      {
                          URL baseURL = new URL(origBase);
  
                          // System.out.println("Trying to make URL from "+origBase+" and "+origURLString);
                          url = new URL(baseURL, origURLString);
  
                          // System.out.println("Success! New URL is: "+url.toString());
                      }
                      catch (MalformedURLException e)
                      {
  
                          // System.out.println("Error trying to make URL from "+origBase+" and "+origURLString);
                      }
                  }
  
                  if (null == url)
                  {
                      try
                      {
                          String lastPart;
  
                          if (null != origBase)
                          {
                              File baseFile = new File(origBase);
  
                              if (baseFile.isDirectory())
                              {
                                  lastPart =
                                      new File(baseFile,
                                               urlString).getAbsolutePath();
                              }
                              else
                              {
                                  String parentDir = baseFile.getParent();
  
                                  lastPart =
                                      new File(parentDir,
                                               urlString).getAbsolutePath();
                              }
                          }
                          else
                          {
                              lastPart = new File(urlString).getAbsolutePath();
                          }
  
                          // Hack
                          // if((lastPart.charAt(0) == '/') && (lastPart.charAt(2) == ':'))
                          //   lastPart = lastPart.substring(1, lastPart.length() - 1);
                          String fullpath;
  
                          if (lastPart.charAt(0) == '\\'
                                  || lastPart.charAt(0) == '/')
                          {
                              fullpath = "file://" + lastPart;
                          }
                          else
                          {
                              fullpath = "file:" + lastPart;
                          }
  
                          url = new URL(fullpath);
                      }
                      catch (MalformedURLException e2)
                      {
                          throw new SAXException("Cannot create url for: "
                                                 + urlString, e2);
  
                          //XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_CREATE_URL, new Object[]{urlString}),e2); //"Cannot create url for: " + urlString, e2 );
                      }
                  }
              }
          }
          catch (SecurityException se)
          {
              try
              {
                  url = new URL("http://xml.apache.org/xslt/"
                                + java.lang.Math.random());  // dummy
              }
              catch (MalformedURLException e2)
              {
  
                  // I give up
              }
          }
  
          // System.out.println("url: "+url.toString());
          return url;
      }
  }  // end of class XSLDirectoryIterator
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/XSLProcessorTestBase.java
  
  Index: XSLProcessorTestBase.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * XSLProcessorTestBase.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.*;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  
  import java.util.Properties;
  import java.util.Vector;
  
  //------------------------------------------------------------------------
  
  /**
   * Base class for all Xalan tests.
   * <p>XSLProcessorTestBase defines a number of common fields
   * that most tests will use in addition to FileBasedTest.  It
   * also defaults to using an XMLFileLogger as well as a
   * ConsoleLogger, if none have been specified.</p>
   * <ul>initializes all members from FileBasedTest, plus
   * <li>category</li>
   * <li>excludes</li>
   * <li>liaison</li>
   * <li>flavor</li>
   * <li>diagnostics</li>
   * <li>noReuse</li>
   * <li>precompile</li>
   * <li>runErr</li>
   * </ul>
   * @author Shane_Curcuru@lotus.com
   * @version $Id: XSLProcessorTestBase.java,v 1.1 2000/11/01 23:26:57 curcuru Exp $
   */
  public class XSLProcessorTestBase extends FileBasedTest
  {
  
      /**
       * Convenience method to print out usage information.
       * @author Shane Curcuru
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String usage()
      {
  
          return ("Common options supported by XSLProcessorTestBase:\n"
                  + "    -" + OPT_CATEGORY
                  + "  <name of single subdir within testDir to run>\n"
                  + "    -" + OPT_EXCLUDES
                  + "  <list;of;specific file.xsl tests to skip>\n" + "    -"
                  + OPT_LIAISON + "   <liaisonClassName>\n" + "    -"
                  + OPT_FLAVOR
                  + "    <xalan|lotusxsl|xt|etc... - which kind of Processor to test>\n"
                  + "    -" + OPT_DIAGNOSTICS
                  + "  <root filename for diagnostics output>\n" + "    -"
                  + OPT_NOREUSE
                  + "   (will force recreate processor each time)\n" + "    -"
                  + OPT_PRECOMPILE
                  + " (will use precompiled stylesheet, if applicable)\n"
                  + "    -" + OPT_NOERRTEST
                  + "  (will skip running 'err' tests, if applicable)\n"
                  + super.usage());
      }
  
      //-----------------------------------------------------
      //-------- Constants for common input params --------
      //-----------------------------------------------------
  
      /**
       * Parameter: Only run a single subcategory of the tests.
       * <p>Default: blank, runs all tests - supply the directory name
       * of a subcategory to run just that set.</p>
       */
      public static final String OPT_CATEGORY = "category";
  
      /** NEEDSDOC Field category          */
      protected String category = null;
  
      /**
       * Parameter: Reuse/reset processor between cases or create a new one each time?
       * <p>Default: false - create one processor and call reset between tests.</p>
       */
  
      // TODO: Move to directoryiterator
      public static final String OPT_NOREUSE = "noReuse";
  
      /** NEEDSDOC Field noReuse          */
      protected boolean noReuse = true;
  
      /**
       * Parameter: What parser liaison or option to use?
       * <p>Default: null, Whichever default your processor uses.</p>
       */
  
      // TODO: Move to directoryiterator
      public static final String OPT_LIAISON = "liaison";
  
      /** NEEDSDOC Field liaison          */
      protected String liaison = null;
  
      /**
       * Parameter: What flavor of ProcessorWrapper to use: trax|xalan1|other?
       * <p>Default: trax.</p>
       */
      public static final String OPT_FLAVOR = "flavor";
  
      /** NEEDSDOC Field flavor          */
      protected String flavor = "trax";
  
      /**
       * Parameter: Name of output file for diagnostics/error logs?
       * <p>Default: null, do not use one</p>
       */
  
      // TODO: Move to directoryiterator
      public static final String OPT_DIAGNOSTICS = "diagnostics";
  
      /** NEEDSDOC Field diagnostics          */
      protected String diagnostics = null;
  
      /**
       * Parameter: Should we try to use a precompiled stylesheet?
       * <p>Default: false, no (not applicable in all cases).</p>
       */
      public static final String OPT_PRECOMPILE = "precompile";
  
      /** NEEDSDOC Field precompile          */
      protected boolean precompile = false;
  
      /**
       * Parameter: Should we run any 'err' subdir tests or not?
       * <p>Default: false (i.e. <b>do</B> run error tests by default).</p>
       * @todo update, this is clumsy (reverse double negative option)
       */
      public static final String OPT_NOERRTEST = "noErrTest";
  
      /** NEEDSDOC Field noErrTest          */
      protected boolean noErrTest = false;
  
      /**
       * Parameter: force use of URI's for Xerces 1.1.2 or leave filenames alone?
       * <p>Default: true, use URI's</p>
       * @todo update, this is clumsy
       */
      public static final String OPT_USEURI = "useURI";
  
      /** NEEDSDOC Field useURI          */
      protected boolean useURI = true;
  
      /**
       * Parameter: Which CheckService should we use for XML output Files?
       * <p>Default: org.apache.qetest.SimpleFileCheckService.</p>
       */
      public static final String OPT_FILECHECKER = "fileChecker";
  
      /** NEEDSDOC Field fileCheckerName          */
      protected String fileCheckerName =
          "org.apache.qetest.SimpleFileCheckService";
  
      /** NEEDSDOC Field fileChecker          */
      protected CheckService fileChecker = null;
  
      /**
       * Parameter: Should we exclude any specific test files?
       * <p>Default: null (no excludes; otherwise specify semicolon delimited list like 'axes01.xsl;bool99.xsl').</p>
       */
      public static final String OPT_EXCLUDES = "excludes";
  
      /** NEEDSDOC Field excludes          */
      protected String excludes = null;
  
      /**
       * Default constructor - initialize testName, Comment.
       */
      public XSLProcessorTestBase()
      {
  
          // Only set them if they're not set
          if (testName == null)
              testName = "XSLProcessorTestBase.defaultName";
  
          if (testComment == null)
              testComment = "XSLProcessorTestBase.defaultComment";
      }
  
      //-----------------------------------------------------
      //-------- Implement Test/TestImpl methods --------
      //----------------------------------------------------
  
      /**
       * Initialize this test - called once before running testcases.
       * <p>Use the loggers field to create some loggers in a Reporter.</p>
       * @author Shane_Curcuru@lotus.com
       *
       * NEEDSDOC @param p
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean preTestFileInit(Properties p)
      {
  
          // Ensure we have an XMLFileLogger if we have a logName
          String logF = testProps.getProperty(Logger.OPT_LOGFILE);
  
          if ((logF != null) && (!logF.equals("")))
          {
  
              // We should ensure there's an XMLFileReporter
              String r = testProps.getProperty(Reporter.OPT_LOGGERS);
  
              if (r == null)
              {
                  testProps.put(Reporter.OPT_LOGGERS,
                                "org.apache.qetest.XMLFileLogger");
              }
              else if (r.indexOf("XMLFileLogger") <= 0)
              {
                  testProps.put(Reporter.OPT_LOGGERS,
                                r + Reporter.LOGGER_SEPARATOR
                                + "org.apache.qetest.XMLFileLogger");
              }
          }
  
          // Ensure we have a ConsoleLogger unless asked not to
          // @todo improve and document this feature
          String noDefault = testProps.getProperty("noDefaultReporter");
  
          if (noDefault == null)
          {
  
              // We should ensure there's an XMLFileReporter
              String r = testProps.getProperty(Reporter.OPT_LOGGERS);
  
              if (r == null)
              {
                  testProps.put(Reporter.OPT_LOGGERS,
                                "org.apache.qetest.ConsoleLogger");
              }
              else if (r.indexOf("ConsoleLogger") <= 0)
              {
                  testProps.put(Reporter.OPT_LOGGERS,
                                r + Reporter.LOGGER_SEPARATOR
                                + "org.apache.qetest.ConsoleLogger");
              }
          }
  
          // Pass our properties block directly to the reporter
          //  so it can use the same values in initialization
          // A Reporter will auto-initialize from the values
          //  in the properties block
          setReporter(new Reporter(p));
          reporter.addDefaultLogger();  // add default logger if needed
          reporter.testFileInit(testName, testComment);
  
          // Fixup some paths to be absolute, see method comments for @todo
          fixupPaths();
  
          // Create a file-based CheckService for later use
          if (fileChecker == null)
          {
              try
              {
                  Class fClass = Class.forName(fileCheckerName);
  
                  fileChecker = (CheckService) fClass.newInstance();
  
                  reporter.logTraceMsg("Using file-based CheckService: "
                                       + fileChecker);
              }
              catch (Exception e)
              {
                  reporter.logWarningMsg("Failed to create an instance of "
                                         + fileCheckerName + ": "
                                         + e.toString());
  
                  // This could cause NullPointerExceptions for those 
                  //  tests that rely on our fileChecker...
              }
          }
  
          return true;
      }
  
      // use other implementations from FileBasedTest
      //-----------------------------------------------------
      //-------- Initialize our common input params --------
      //-----------------------------------------------------
  
      /**
       * Set our instance variables from a Properties file.
       * Calls super.initializeFromProperties() to get defaults.
       * @author Shane Curcuru
       * @param Properties block to set name=value pairs from
       *
       * NEEDSDOC @param props
       * @return status - true if OK, false if error.
       * @todo improve error checking, if needed
       */
      public boolean initializeFromProperties(Properties props)
      {
  
          debugPrintln("XSLProcessorTestBase.initializeFromProperties(" + props
                       + ")");
  
          boolean b = super.initializeFromProperties(props);
  
          category = props.getProperty(OPT_CATEGORY, category);
  
          if (category != null)
              testProps.put(OPT_CATEGORY, category);
  
          liaison = props.getProperty(OPT_LIAISON, liaison);
  
          if (liaison != null)
              testProps.put(OPT_LIAISON, liaison);
  
          flavor = props.getProperty(OPT_FLAVOR, flavor);
  
          if (flavor != null)
              testProps.put(OPT_FLAVOR, flavor);
  
          fileCheckerName = props.getProperty(OPT_FILECHECKER, fileCheckerName);
  
          if (fileCheckerName != null)
              testProps.put(OPT_FILECHECKER, fileCheckerName);
  
          excludes = props.getProperty(OPT_EXCLUDES, excludes);
  
          if (excludes != null)
              testProps.put(OPT_EXCLUDES, excludes);
  
          diagnostics = props.getProperty(OPT_EXCLUDES, diagnostics);
  
          if (diagnostics != null)
              testProps.put(OPT_EXCLUDES, diagnostics);
  
          String prec = props.getProperty(OPT_PRECOMPILE);
  
          if ((prec != null) && prec.equalsIgnoreCase("true"))
          {
              precompile = true;
  
              testProps.put(OPT_PRECOMPILE, "true");
          }
  
          String noet = props.getProperty(OPT_NOERRTEST);
  
          if ((noet != null) && noet.equalsIgnoreCase("true"))
          {
              noErrTest = true;
  
              testProps.put(OPT_NOERRTEST, "true");
          }
  
          String uURI = props.getProperty(OPT_USEURI);
  
          if ((uURI != null) && uURI.equalsIgnoreCase("false"))
          {
              useURI = false;
  
              testProps.put(OPT_USEURI, "false");
          }
  
          return b;
      }
  
      /**
       * Sets the provided fields with data from an array, presumably
       * from the command line.
       * <p>Overridden from FileBasedTest, but calls super.() to
       * get default properties.</p>
       * @author Shane Curcuru
       *
       * NEEDSDOC @param args
       * @return status - true if OK, false if error.
       */
      public boolean initializeFromArray(String[] args)
      {
  
          debugPrintln("XSLProcessorTestBase.initializeFromArray(" + args
                       + ")");
  
          // Read in command line args and setup internal variables
          String optPrefix = "-";
          int nArgs = args.length;
  
          if (nArgs == 0)
          {
              System.out.println("ERROR: you must supply required arguments!");
  
              return false;
          }
  
          // Our parent class already read in the properties file
          //  override values from properties file
          for (int k = 0; k < nArgs; k++)
          {
              if (args[k].equalsIgnoreCase(optPrefix + OPT_LOAD))
              {
                  if (++k >= nArgs)
                  {
                      System.out.println(
                          "ERROR: must supply properties filename for: "
                          + optPrefix + OPT_LOAD);
  
                      return false;
                  }
  
                  load = args[k];
  
                  try
                  {
  
                      // Load named file into our properties block
                      FileInputStream fIS = new FileInputStream(load);
                      Properties p = new Properties();
  
                      p.load(fIS);
                      initializeFromProperties(p);
                  }
                  catch (Exception e)
                  {
                      System.out.println(
                          "ERROR: loading properties file failed: " + load);
                      e.printStackTrace();
  
                      return false;
                  }
  
                  break;
              }
          }
  
          // Now read in the rest of the command line
          // @todo cleanup loop to be more table-driven
          for (int i = 0; i < nArgs; i++)
          {
              if (args[i].equalsIgnoreCase(optPrefix + OPT_CATEGORY))
              {
                  if (++i >= nArgs)
                  {
                      System.out.println("ERROR: must supply arg for: "
                                         + optPrefix + OPT_CATEGORY);
  
                      return false;
                  }
  
                  category = args[i];
  
                  testProps.put(OPT_CATEGORY, category);
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_LIAISON))
              {
                  if (++i >= nArgs)
                  {
                      System.out.println("ERROR: must supply arg for: "
                                         + optPrefix + OPT_LIAISON);
  
                      return false;
                  }
  
                  liaison = args[i];
  
                  testProps.put(OPT_LIAISON, liaison);
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_FLAVOR))
              {
                  if (++i >= nArgs)
                  {
                      System.out.println("ERROR: must supply arg for: "
                                         + optPrefix + OPT_FLAVOR);
  
                      return false;
                  }
  
                  flavor = args[i];
  
                  testProps.put(OPT_FLAVOR, flavor);
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_DIAGNOSTICS))
              {
                  if (++i >= nArgs)
                  {
                      System.out.println("ERROR: must supply arg for: "
                                         + optPrefix + OPT_DIAGNOSTICS);
  
                      return false;
                  }
  
                  diagnostics = args[i];
  
                  testProps.put(OPT_DIAGNOSTICS, diagnostics);
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_FILECHECKER))
              {
                  if (++i >= nArgs)
                  {
                      System.out.println("ERROR: must supply arg for: "
                                         + optPrefix + OPT_FILECHECKER);
  
                      return false;
                  }
  
                  fileCheckerName = args[i];
  
                  testProps.put(OPT_FILECHECKER, fileCheckerName);
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_EXCLUDES))
              {
                  if (++i >= nArgs)
                  {
                      System.out.println("ERROR: must supply arg for: "
                                         + optPrefix + OPT_EXCLUDES);
  
                      return false;
                  }
  
                  excludes = args[i];
  
                  testProps.put(OPT_EXCLUDES, excludes);
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_NOREUSE))
              {
                  noReuse = false;
  
                  testProps.put(OPT_NOREUSE, "false");
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_PRECOMPILE))
              {
                  precompile = true;
  
                  testProps.put(OPT_PRECOMPILE, "true");
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_NOERRTEST))
              {
                  noErrTest = true;
  
                  testProps.put(OPT_NOERRTEST, "true");
  
                  continue;
              }
  
              if (args[i].equalsIgnoreCase(optPrefix + OPT_USEURI))
              {
                  useURI = false;  // Toggle from default of true; ugly but I'm in a hurry
  
                  testProps.put(OPT_USEURI, "false");
  
                  continue;
              }
          }
  
          // Be sure to ask our superclass to read it's options as well!
          return super.initializeFromArray(args, false);
      }
  
      //-----------------------------------------------------
      //-------- Other useful and utility methods --------
      //-----------------------------------------------------
  
      /**
       * Worker method to fixup pathing for diagnostics.
       * @todo - this is a hack, we may not even need it
       * @author Shane Curcuru
       */
      public void fixupPaths()
      {
  
          // Convert all dir references to absolute ones, to get around 
          //  potential problems with relative paths and test harnesses
          //  that change the current directory
          // Try getCanonicalPath first; otherwise default to getAbsolutePath()
          // @todo also implement logFile
          if (diagnostics != null)
          {
              File tempF = new File(diagnostics);
  
              try
              {
                  diagnostics = tempF.getCanonicalPath();
              }
              catch (IOException ioe1)
              {
                  diagnostics = tempF.getAbsolutePath();
              }
  
              reporter.logTraceMsg(OPT_DIAGNOSTICS + " reset to absolute: "
                                   + diagnostics);
          }
      }
  
      /**
       * Main worker method to run test from the command line.
       * Test subclasses generally need not override.
       * <p>This is primarily provided to make subclasses implementations
       * of the main method as simple as possible: in general, they
       * should simply do:
       * <code>
       *   public static void main (String[] args)
       *   {
       *       TestSubClass app = new TestSubClass();
       *       app.doMain(args);
       *   }
       * </code>
       * @author Shane Curcuru
       *
       * NEEDSDOC @param args
       */
      public void doMain(String[] args)
      {
  
          debugPrintln("XSLProcessorTestBase.doMain(" + args + ")");
  
          // Initialize any instance variables from the command line 
          //  OR specified properties block
          if (!initializeFromArray(args))
          {
              System.err.println("ERROR in usage:");
              System.err.println(usage());
  
              // Don't use System.exit, since that will blow away any containing harnesses
              return;
          }
  
          // Also pass along the command line, in case someone has 
          //  specific code that's counting on this
          testProps.put(MAIN_CMDLINE, args);
          runTest(testProps);
      }
  
      /**
       * Main method to run test from the command line.
       * @author Shane Curcuru
       * <p>Test subclasses <b>must</b> override, obviously.
       * Only provided here for debugging.</p>
       *
       * NEEDSDOC @param args
       */
      public static void main(String[] args)
      {
  
          XSLProcessorTestBase app = new XSLProcessorTestBase();
  
          app.debug = true;  // HACK
  
          app.doMain(args);
      }
  }  // end of class XSLProcessorTestBase
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/XSLTestfileInfo.java
  
  Index: XSLTestfileInfo.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * XSLTestfileInfo.java
   *
   */
  package org.apache.qetest.xsl;
  
  import org.apache.qetest.TestfileInfo;
  
  import java.util.Properties;
  import java.util.StringTokenizer;
  
  /**
   * Simple data-holding class specifying info about one 'testfile'.
   * <p>Convenience class for XSL Processor testing, includes
   * additional fields and overrides some methods to change order
   * of initializer strings.</p>
   * <ul>Fields read in new order:
   * <li>inputName - xsl stylesheet</li>
   * <li>xmlName - xml data file <b>new</b></li>
   * <li>outputName - for results of transform</li>
   * <li>goldName</li>
   * <li>description</li>
   * <li>author</li>
   * <li>options</li>
   * </ul>
   * @author Shane Curcuru
   * @version $Id: XSLTestfileInfo.java,v 1.1 2000/11/01 23:26:57 curcuru Exp $
   */
  public class XSLTestfileInfo extends TestfileInfo
  {
  
      /** Name of the input XML data file. */
      public String xmlName = null;
  
      /** NEEDSDOC Field XMLNAME          */
      public static final String XMLNAME = "xmlName";
  
      /** No-arg constructor leaves everything null. */
      public XSLTestfileInfo(){}
  
      /**
       * Initialize members from name=value pairs in Properties block.
       * Default value for each field is null.
       * @param Properties block to initialize from
       *
       * NEEDSDOC @param p
       */
      public XSLTestfileInfo(Properties p)
      {
          load(p);
      }
  
      /**
       * Pass in a StringTokenizer-default-delimited string to initialize members.
       * <p>Members are read in order: inputName <i>xmlName</i> outputName goldName
       * author description options...
       * default value for each field is null</p>
       * @param String to initialize from
       *
       * NEEDSDOC @param inputStr
       */
      public XSLTestfileInfo(String inputStr)
      {
  
          StringTokenizer st = new StringTokenizer(inputStr);
  
          load(st, null);
      }
  
      /**
       * Pass in a StringTokenizer-default-delimited string to initialize members.
       * <p>Members are read in order: inputName <i>xmlName</i> outputName goldName
       * author description options...
       * default value for each field is user-specified String</p>
       * @param String to initialize from
       * @param String to use as default for any un-specified value
       *
       * NEEDSDOC @param inputStr
       * NEEDSDOC @param defaultVal
       */
      public XSLTestfileInfo(String inputStr, String defaultVal)
      {
  
          StringTokenizer st = new StringTokenizer(inputStr);
  
          load(st, defaultVal);
      }
  
      /**
       * Pass in a specified-delimited string to initialize members.
       * <p>Members are read in order: inputName <i>xmlName</i> outputName goldName
       * author description options...
       * default value for each field is user-specified String</p>
       * @param String to initialize from
       * @param String to use as default for any un-specified value
       * @param String to use as separator for StringTokenizer
       *
       * NEEDSDOC @param inputStr
       * NEEDSDOC @param defaultVal
       * NEEDSDOC @param separator
       */
      public XSLTestfileInfo(String inputStr, String defaultVal,
                             String separator)
      {
  
          StringTokenizer st = new StringTokenizer(inputStr, separator);
  
          load(st, defaultVal);
      }
  
      /**
       * Worker method to initialize members.
       *
       * NEEDSDOC @param st
       * NEEDSDOC @param defaultVal
       */
      public void load(StringTokenizer st, String defaultVal)
      {
  
          // Fill in as many items as are available; default the value otherwise
          // Note that order is important!
          if (st.hasMoreTokens())
              inputName = st.nextToken();
          else
              inputName = defaultVal;
  
          // Override from base class: put the xmlName next
          if (st.hasMoreTokens())
              xmlName = st.nextToken();
          else
              xmlName = defaultVal;
  
          if (st.hasMoreTokens())
              outputName = st.nextToken();
          else
              outputName = defaultVal;
  
          if (st.hasMoreTokens())
              goldName = st.nextToken();
          else
              goldName = defaultVal;
  
          if (st.hasMoreTokens())
              author = st.nextToken();
          else
              author = defaultVal;
  
          if (st.hasMoreTokens())
              description = st.nextToken();
          else
              description = defaultVal;
  
          if (st.hasMoreTokens())
          {
              options = st.nextToken();
  
              // For now, simply glom all additional tokens into the options, until the end of string
              // Leave separated with a single space char for readability
              while (st.hasMoreTokens())
              {
                  options += " " + st.nextToken();
              }
          }
          else
              options = defaultVal;
      }
  
      /**
       * Initialize members from name=value pairs in Properties block.
       * Default value for each field is null.
       * @param Properties block to initialize from
       *
       * NEEDSDOC @param p
       */
      public void load(Properties p)
      {
  
          inputName = p.getProperty(INPUTNAME);
          xmlName = p.getProperty(XMLNAME);  // Override from base class
          outputName = p.getProperty(OUTPUTNAME);
          goldName = p.getProperty(GOLDNAME);
          author = p.getProperty(AUTHOR);
          description = p.getProperty(DESCRIPTION);
          options = p.getProperty(OPTIONS);
      }
  
      /**
       * Cheap-o debugging: return tab-delimited String of all our values.
       *
       * NEEDSDOC ($objectName$) @return
       */
      public String dump()
      {
  
          return (inputName + '\t' + xmlName  // Override from base class
                  + '\t' + outputName + '\t' + goldName + '\t' + author + '\t'
                  + description + '\t' + options);
      }
  }
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xsl/package.html
  
  Index: package.html
  ===================================================================
  <html>
    <title>XSL-TEST general XSLT package.</title>
    <body>
      <p>This package contains XSLT testing base classes and utilities, and a generic test driver.</p>
      <dl>
        <dt><b>Author: </b></dt><dd><a href="mailto:shane_curcuru@lotus.com">Shane_Curcuru@lotus.com</a></dd>
        <dt><b>Program(s) Under Test: </b></dt>
        <dd><a href="http://xml.apache.org/xalan-j" target="_top">Xalan-J 2.x XSLT Processor</a></dd>
        <dd><a href="http://xml.apache.org/xalan" target="_top">Xalan-J 1.x XSLT Processor</a></dd>
        <dd><a href="http://xml.apache.org/xalan-c" target="_top">Xalan-C 1.x XSLT Processor</a></dd>
      </dl>
      <p>This package includes base classes and utilities, as well as several 
      generic test drivers that use {@link org.apache.qetest.xslwrapper.ProcessorWrapper ProcessorWrappers} 
      to process stylesheets.  Classes in this package must not depend directly 
      on any Xalan interfaces, only on the ProcessorWrapper interface.<p>
      <ul>Current utilities include:
      <li>Logging* - implementations of XSLT-related listeners 
      and the like that log to our Reporter.</li>
      <li>XHTFileCheckService, XHTComparator - checks equivalence of 
      two File objects by parsing either as XML, HTML, or text.  Note 
      the HTML parsing is unimplemented - we need a good HTML->DOM 
      parser we can use in Apache - any suggestions?</li>
      <li>XSLTestfileInfo - simple extension of TestfileInfo to 
      add xmlName member.</li>
      <li>XSLDirectoryIterator - simple implementation that processes 
      xsl/xml file pairs from a fileList or over a directory tree, 
      automatically comparing the result files with a known good 
      or 'gold' reference tree of outputs.</li>
      <li>XSLProcessorTestBase - adds useful XSLT processing 
      utilities, etc. from FileTestBase: including flags like 
      -preprocess, -flavor, -category, etc.  Most xsl and trax 
      package Test scripts derive from this class.</li>
      </ul>
      <ul>Current tests include:
      <li>ConformanceTest - generic test driver, including some 
      FilenameFilters.  This essentially uses the XSLDirectoryIterator 
      for everything.</li>
      <li>PerformanceTest - test driver that iterates repeatedly and 
      reports memory and timing info.</li>
      <li>CConformanceTest - generic test driver that constructs a 
      command line and then shells out to execute TestXSLT.exe, which 
      is from the Xalan-C build.  This allows basic comparisons of 
      results from the Java versions with the C++ versions.</li>
      </ul>
    </body>
  </html>
  
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/LotusXSLWrapper.java
  
  Index: LotusXSLWrapper.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * LotusXSLWrapper.java
   *
   */
  package org.apache.qetest.xslwrapper;
  
  import java.util.Vector;
  
  import java.io.PrintWriter;
  
  // The LotusXSL implementation
  import com.lotus.xsl.XSLProcessor;
  import com.lotus.xsl.XSLTInputSource;
  import com.lotus.xsl.XSLTResultTarget;
  
  // For the versioning string from Xalan
  import org.apache.xalan.xslt.XSLProcessorVersion;
  
  /**
   * Implementation of a ProcessorWrapper for LotusXSL.
   * @author Shane Curcuru
   * @version $Id: LotusXSLWrapper.java,v 1.1 2000/11/01 23:27:00 curcuru Exp $
   */
  public class LotusXSLWrapper extends ProcessorWrapper
  {
  
      /** No-op Ctor for the LotusXSL-only wrapper. */
      public LotusXSLWrapper(){}
  
      /** Reference to current processor - LotusXSL flavor - convenience method. */
      protected com.lotus.xsl.XSLProcessor processor = null;
  
      /**
       * NEEDSDOC Method getLotusXSLProcessor 
       *
       *
       * NEEDSDOC (getLotusXSLProcessor) @return
       */
      public com.lotus.xsl.XSLProcessor getLotusXSLProcessor()
      {
          return processor;
      }
  
      /**
       * Construct a processor of the appropriate flavor, optionally specifying a liaison.
       * <p>May throw exceptions related to the creating of a new processor.</p>
       * <ul>LotusXSL supports five liaisons:
       * <li>org.apache.xalan.xpath.dtm.DTMLiaison (from Xalan)</li>
       * <li>org.apache.xalan.xpath.xdom.XercesLiaison (from Xalan)</li>
       * <li>com.lotus.xml.xml4j2dom.XML4JLiaison4dom (from XML4J 2.0.15)</li>
       * <li>com.lotus.xml.xml4j2tx.XML4JLiaison (from XML4J 2.0.15)</li>
       * <li>com.lotus.xml.xml4j.ProcessXSL (from XML4J 1.1.16)</li>
       * </ul>
       * @param liaisonClassName [optional] if non-null & non-blank, classname of an XML liaison
       * @return (Object)processor as a side effect; null if error
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public Object createNewProcessor(String liaisonClassName)
              throws java.lang.Exception  // Cover all exception cases
      {
  
          // Cleanup any prior objects 
          cleanup();
  
          if (liaisonClassName != null)
              processor = new XSLProcessor(liaisonClassName);
          else
              processor = new XSLProcessor();
  
          p = (Object) processor;
  
          // Return here; will be null if error or exception raised
          return (p);
      }
  
      /**
       * Get a description of the wrappered processor.
       * @return info-string describing the processor and possibly it's common options
       */
      public String getDescription()
      {
  
          if (processor == null)
          {
              return ("ERROR: must call createNewProcessor first from: "
                      + getDescription());
          }
          else
          {
  
              // StringBuffer buf = new StringBuffer(XSLProcessorVersion.PRODUCT);
              StringBuffer buf = new StringBuffer("LotusXSL");  // Note that LotusXSL does not override the PRODUCT field
  
              buf.append(";");
              buf.append(XSLProcessorVersion.LANGUAGE);
              buf.append(";");
              buf.append(XSLProcessorVersion.S_VERSION);
              buf.append(";");
              buf.append(processor.getXMLProcessorLiaison());
  
              return buf.toString();
          }
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * <p>May throw exceptions related to asking the processor to perform the process.</p>
       * <p>Attempts to ask each processor to accomplish the task in the simplest
       * and most obvious manner.  Often copied from various processor's samples.</p>
       * @param xmlSource name of source XML file
       * @param xslStylesheet name of stylesheet XSL file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(
              String xmlSource, String xslStylesheet, String resultFile)
                  throws java.lang.Exception  // Cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create xalan-specific sources
          com.lotus.xsl.XSLTInputSource xml =
              new com.lotus.xsl.XSLTInputSource(xmlSource);
          com.lotus.xsl.XSLTInputSource xsl =
              new com.lotus.xsl.XSLTInputSource(xslStylesheet);
          com.lotus.xsl.XSLTResultTarget result =
              new com.lotus.xsl.XSLTResultTarget(resultFile);
  
          // Begin timing the whole process
          startTime = System.currentTimeMillis();
  
          processor.process(xml, xsl, result);
  
          endTime = System.currentTimeMillis();
  
          return (endTime - startTime);
      }
  
      /**
       * Preprocess a stylesheet and set it into the processor, based on string inputs.
       * @param xslStylesheet name of stylesheet XSL file
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long preProcessStylesheet(String xslStylesheet)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create xalan-specific sources
          com.lotus.xsl.StylesheetRoot sRoot;
          com.lotus.xsl.XSLTInputSource xsl =
              new com.lotus.xsl.XSLTInputSource(xslStylesheet);
  
          // Begin timing loading the stylesheet
          startTime = System.currentTimeMillis();
          sRoot = processor.processStylesheet(xsl);  // side effect: also sets the stylesheet
          endTime = System.currentTimeMillis();
          stylesheetReady = true;
  
          return (endTime - startTime);
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * @param xmlSource name of source XML file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(String xmlSource, String resultFile)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Ensure we (apparently) have already processed a stylesheet
          if (!stylesheetReady)
              throw new java.lang.IllegalStateException(
                  "You must call preProcessStylesheet first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create xalan-specific sources
          com.lotus.xsl.XSLTInputSource xml =
              new com.lotus.xsl.XSLTInputSource(xmlSource);
          com.lotus.xsl.XSLTResultTarget result =
              new com.lotus.xsl.XSLTResultTarget(resultFile);
  
          // Begin timing the whole process
          startTime = System.currentTimeMillis();
  
          processor.process(xml, null, result);
  
          endTime = System.currentTimeMillis();
  
          return (endTime - startTime);
      }
  
      /**
       * Reset the state.
       * <p>This needs to be called after a process() call is invoked,
       * if the processor is to be used again; at least for xalan and lotusxsl flavors.</p>
       */
      public void reset()
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.reset();
  
          stylesheetReady = false;
      }
  
      /**
       * Set diagnostics output PrintWriter.  
       *
       * NEEDSDOC @param pw
       */
      public void setDiagnosticsOutput(java.io.PrintWriter pw)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.setDiagnosticsOutput(pw);
      }
  
      /**
       * Set the indent level of the processor.  
       *
       * NEEDSDOC @param i
       */
      public void setIndent(int i)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.getXMLProcessorLiaison().setIndent(i);
      }
  
      /**
       * Set a String name=value param in the processor, if applicable.  
       *
       * NEEDSDOC @param key
       * NEEDSDOC @param expression
       */
      public void setStylesheetParam(String key, String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.setStylesheetParam(key, expression);
      }
  
      /**
       * Set a String namespace:name=value param in the processor, if applicable.
       * @todo Needs Implementation: namespace is currently <b>ignored!</b>
       * @param namespace of the param
       * @param key name of the param
       * @param expression value of the param
       */
      public void setStylesheetParam(String namespace, String key,
                                     String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.setStylesheetParam(key, expression);
      }
  
      /** Worker method to cleanup any internal state. */
      private void cleanup()
      {
  
          processor = null;
          p = null;
          stylesheetReady = false;
      }
  }  // end of class LotusXSLWrapper
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/ProcessorWrapper.java
  
  Index: ProcessorWrapper.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * ProcessorWrapper.java
   *
   */
  package org.apache.qetest.xslwrapper;
  
  import java.io.InputStream;
  import java.io.IOException;
  
  import java.util.Properties;
  
  /**
   * Helper class to adapt or wrap instances of xsl processors.
   * <p>Abstract class defining basic processor actions and a
   * static method to create specific flavors of wrappers.  This
   * serves as a simple way to abstract the act of processing an XML
   * data file with an XSL stylesheet into an output file.  Thus
   * test classes can be written that can operate on any type of
   * wrappered processor.</p>
   * <p>Basic API's for processing also return a long denoting the
   * number of milliseconds taken during the 'interesting portions'
   * of that operation.  Note that it is <b>very</b> important that
   * implementing classes clearly document exactly what is being
   * timed in their implementations, and the ways that they are
   * implementing the process (SAX, DOM, etc.)</p>
   * @author Shane Curcuru
   * @version $Id: ProcessorWrapper.java,v 1.1 2000/11/01 23:27:00 curcuru Exp $
   */
  public abstract class ProcessorWrapper
  {
  
      //-----------------------------------------------------
      //-------- Worker methods implemented by subclassed wrappers --------
      //-----------------------------------------------------
  
      /**
       * Construct a processor of the appropriate flavor, optionally
       * specifying a liaison or other option.
       * <p>May throw exceptions related to the creating of a new processor.
       * Subclasses must set the value of (Object)p in this method and return it.</p>
       * <p>Note that not all processors may use liaisons in the same manner.</p>
       * @param liaisonClassName [optional] if non-null & non-blank,
       * classname of an XML liaison
       * @return (Object)processor as a side effect; null if error
       * @exception Exception may be thrown by underlying operation
       * @todo revisit: see if liaisonClassName should be genericized
       *
       * @throws java.lang.Exception
       */
      public abstract Object createNewProcessor(String liaisonClassName)
          throws java.lang.Exception;  // Cover all exception cases
  
      /**
       * Get a description of the wrappered processor.
       * @return info-string describing the processor
       */
      public abstract String getDescription();
  
      /** Reference to actual current processor object. */
      protected Object p = null;
  
      /**
       * Reference to actual current processor object.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public Object getProcessor()
      {
          return p;
      }
  
      /** If we think the current processor has a preprocessed stylesheet ready. */
      protected boolean stylesheetReady = false;
  
      /**
       * If we think the current processor has a preprocessed stylesheet ready.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public boolean getStylesheetReady()
      {
          return stylesheetReady;
      }
  
      /** Return value when an error occours from process* methods. */
      public static final long ERROR = -1L;
  
      //-----------------------------------------------------
      //-------- Processing methods implemented by subclassed wrappers --------
      //-----------------------------------------------------
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * <p>May throw exceptions related to asking the processor to perform the process.</p>
       * <p>Attempts to ask each processor to accomplish the task in the simplest
       * and most obvious manner.  Often copied from various processor's samples.</p>
       * @param xmlSource name of source XML file
       * @param xslStylesheet name of stylesheet XSL file
       * @param resultFile name of output file
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public abstract long processToFile(
          String xmlSource, String xslStylesheet, String resultFile)
              throws java.lang.Exception;  // Cover all exception cases
  
      /**
       * Preprocess a stylesheet and set it into the processor.
       * @param xslStylesheet name of stylesheet XSL file
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public abstract long preProcessStylesheet(String xslStylesheet)
          throws java.lang.Exception;  // should cover all exception cases
  
      /**
       * Process the xmlSource using a preProcessStylesheet to produce the resultFile.
       * @param xmlSource name of source XML file
       * @param resultFile name of output file
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public abstract long processToFile(String xmlSource, String resultFile)
          throws java.lang.Exception;  // should cover all exception cases
  
      /**
       * Set a String name=value param in the processor, if applicable.
       * @param key name of the param
       * @param expression value of the param
       */
      public abstract void setStylesheetParam(String key, String expression);
  
      /**
       * Set a String namespace:name=value param in the processor, if applicable.
       * @param namespace of the param
       * @param key name of the param
       * @param expression value of the param
       */
      public abstract void setStylesheetParam(String namespace, String key,
                                              String expression);
  
      /** Reset the state of the processor, if applicable. */
      public abstract void reset();
  
      /**
       * Set a diagnostics output PrintWriter, if applicable.
       * @param pw PrintWriter to dump diagnostic or error output
       */
      public abstract void setDiagnosticsOutput(java.io.PrintWriter pw);
  
      /**
       * Set the indent level of the processor, if applicable.
       * <p>Note that the default is to let the processor decide the indent
       * level (and/or if it should indent or not).  If you set it explicitly,
       * then 0 == no indent or equivalent, >=1 == indent to that level.</p>
       * @param i indent level
       */
      public abstract void setIndent(int i);
  
      //-----------------------------------------------------
      //-------- Factory static class methods --------
      //-----------------------------------------------------
  
      /**
       * Currently known 'good' list of implemented wrappers.
       * <p>Allows specification of a simple name for each wrapper,
       * so clients don't necessarily have to know the full classname.</p>
       * <ul>Where:
       * <li>key = String simple name = xalan|trax|etc...</li>
       * <li>value = String full classname</li>
       * <li>Or, optionally:</li>
       * <li>value = String full classname;property=value</li>
       * <li>Supports: See ProcessorWrapper.properties</li>
       * </ul>
       * <p>As a hack, some values can be 'overloaded' by specifying
       * an optional system property name = value pair after a semicolon
       * in the value.  This will be stripped off and set into the System
       * properties before creating the wrapper.</p>
       */
      protected static Properties wrapperMapper = null;
  
      /** Static initializer for wrapperMapper. */
      static
      {
          wrapperMapper = new Properties();
  
          try
          {
              InputStream is = ProcessorWrapper.class.getResourceAsStream(
                  "ProcessorWrapper.properties");
  
              wrapperMapper.load(is);
              is.close();
          }
          catch (IOException ioe)
          {
              System.err.println("Loading ProcessorWrapper.properties threw: "
                                 + ioe.toString());
              ioe.printStackTrace();
          }
      }
      ;
  
      /**
       *  Accessor for our wrapperMapper.  
       *
       * NEEDSDOC ($objectName$) @return
       */
      public static final Properties getDescriptions()
      {
          return wrapperMapper;
      }
  
      /** Separators for specially wrapped classes and their properties. */
      public static final String WRAPPER_SEPARATOR = ";";
  
      /** Separators for specially wrapped classes and their properties. */
      public static final String PROPERTY_SEPARATOR = "=";
  
      /**
       * Create a wrapper of a specific flavor.
       * <p>When using a 'mapped' flavor, will also set the requested
       * System property before creating the wrapper class itself.</p>
       * @param flavor name of a wrapper class; simple names or FQCN's
       * @return a ProcessorWrapper of the desired type; null if error
       */
      public static final ProcessorWrapper getWrapper(String flavor)
      {
  
          // Attempt to lookup the flavor: if found, use the 
          //  value we got, otherwise default to the same value
          String className = null;
  
          className = wrapperMapper.getProperty(flavor, flavor);
  
          // Strip off any optional properties and set them
          int pos = className.indexOf(WRAPPER_SEPARATOR);
  
          if (pos > 0)
          {
  
              // Rip off the property part, and save it
              String prop = className.substring(pos + 1);
  
              className = className.substring(0, pos);
  
              // Separate into property=value
              int pos2 = prop.indexOf(PROPERTY_SEPARATOR);
              String val = "";  // default to blank string
  
              if (pos2 > 0)
              {
                  val = prop.substring(pos2 + 1);
                  prop = prop.substring(0, pos2);
              }
  
              // Set the property into the system
              // This will presumably be used by the specific 
              //  wrapper class later for some purpose
              System.getProperties().put(prop, val);  // Applet may throw SecurityException
          }
  
          // Constuct one of the asked-for Wrappers
          Class wClass = null;
          ProcessorWrapper wrapper = null;
  
          try
          {
              wClass = Class.forName(className);
              wrapper = (ProcessorWrapper) wClass.newInstance();
          }
          catch (Exception e)
          {
              System.err.println("Exception creating flavor(" + className
                                 + ") threw: " + e.toString());
              e.printStackTrace();
          }
  
          return wrapper;
      }
  }  // end of class ProcessorWrapper
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/ProcessorWrapper.properties
  
  Index: ProcessorWrapper.properties
  ===================================================================
  # wrapperMapper lookups for ProcessorWrapper implementations
  xalan=org.apache.qetest.xslwrapper.XalanWrapper
  xalan1=org.apache.qetest.xslwrapper.XalanWrapper
  # for now, simply use TRAX interface for xalan2
  xalan2=org.apache.qetest.xslwrapper.TraxWrapper
  trax=org.apache.qetest.xslwrapper.TraxWrapper
  
  # anything after the semicolon; is set as a System property by ProcessorWrapper
  trax.s2s=org.apache.qetest.xslwrapper.TraxWrapper;trax.wrapper.type=sax-to-sax
  trax.s2t=org.apache.qetest.xslwrapper.TraxWrapper;trax.wrapper.type=sax-to-stream
  trax.d2d=org.apache.qetest.xslwrapper.TraxWrapper;trax.wrapper.type=dom-to-dom
  trax.filter=org.apache.qetest.xslwrapper.TraxWrapper;trax.wrapper.type=as-xml-filter
  
  # Wrappers implemented for comparison purposes
  lotusxsl=org.apache.qetest.xslwrapper.LotusXSLWrapper
  xt=org.apache.qetest.xslwrapper.XTWrapper
  saxon=org.apache.qetest.xslwrapper.SaxonWrapper
  saxon.s2s=org.apache.qetest.xslwrapper.SaxonWrapper;trax.wrapper.type=sax-to-sax
  saxon.d2d=org.apache.qetest.xslwrapper.SaxonWrapper;trax.wrapper.type=dom-to-dom
  saxon.filter=org.apache.qetest.xslwrapper.SaxonWrapper;trax.wrapper.type=as-xml-filter
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/SaxonWrapper.class
  
  	<<Binary file>>
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/SaxonWrapper.java
  
  Index: SaxonWrapper.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * SaxonWrapper.java
   *
   */
  package org.apache.qetest.xslwrapper;
  
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Properties;
  
  import java.io.PrintWriter;  // currently only used in unimplemented setDiagnosticsOutput
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.OutputStream;
  import java.io.IOException;
  
  // For utility method source
  import java.net.URL;
  import java.net.MalformedURLException;
  
  // Needed SAX classes
  import org.xml.sax.*;
  
  // A Saxon-specific TRaX-like wrapper
  // NOTE: package name subject to change!
  import com.icl.saxon.*;
  import com.icl.saxon.trax.*;
  import com.icl.saxon.trax.serialize.*;  // Note: Saxon does not provide
  
  // real implementations of serializers
  //  so this needs work!
  import org.w3c.dom.Node;
  
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  
  // For the DOM examples we use Xerces
  import org.apache.xerces.parsers.DOMParser;
  import org.apache.xerces.dom.DocumentImpl;
  
  /**
   * Implementation of a ProcessorWrapper for the TRax interface of SAXON 5.5.1.
   * @todo share constants between TraxWrapper, SaxonWrapper
   * @todo document how we perform various types of transforms
   * @author Shane Curcuru
   * @version $Id: SaxonWrapper.java,v 1.1 2000/11/01 23:27:00 curcuru Exp $
   */
  public class SaxonWrapper extends ProcessorWrapper
  {
  
      /** No-op Ctor for the SAXON TRAX interface wrapper. */
      public SaxonWrapper(){}
  
      /** Reference to current processor - SAXON flavor - convenience method. */
      protected com.icl.saxon.trax.Processor processor = null;
  
      /**
       * NEEDSDOC Method getTraxProcessor 
       *
       *
       * NEEDSDOC (getTraxProcessor) @return
       */
      public com.icl.saxon.trax.Processor getTraxProcessor()
      {
          return (processor);
      }
  
      /** A preprocessed stylesheet that we're saving. */
      private Templates savedStylesheet = null;
  
      /** Whatever parameters the user has set. */
      private Hashtable params = null;
  
      /** Which type of transform we should perform. */
      protected int transformType = DEFAULT_TYPE;
  
      /** Constants for different types of transforms. */
      public static final String FILE_TO_FILE = "file-to-file";
  
      /** NEEDSDOC Field FILE_TO_FILE_TYPE          */
      public static final int FILE_TO_FILE_TYPE = 1;
  
      /** NEEDSDOC Field DOM_TO_DOM          */
      public static final String DOM_TO_DOM = "dom-to-dom";
  
      /** NEEDSDOC Field DOM_TO_DOM_TYPE          */
      public static final int DOM_TO_DOM_TYPE = 2;
  
      /** NEEDSDOC Field SAX_TO_SAX          */
      public static final String SAX_TO_SAX = "sax-to-sax";
  
      /** NEEDSDOC Field SAX_TO_SAX_TYPE          */
      public static final int SAX_TO_SAX_TYPE = 3;
  
      /** NEEDSDOC Field SAX_TO_STREAM          */
      public static final String SAX_TO_STREAM = "sax-to-stream";
  
      /** NEEDSDOC Field SAX_TO_STREAM_TYPE          */
      public static final int SAX_TO_STREAM_TYPE = 4;
  
      /** NEEDSDOC Field DOM_TO_STREAM          */
      public static final String DOM_TO_STREAM = "dom-to-stream";
  
      /** NEEDSDOC Field DOM_TO_STREAM_TYPE          */
      public static final int DOM_TO_STREAM_TYPE = 5;
  
      /** NEEDSDOC Field STREAM_TO_DOM          */
      public static final String STREAM_TO_DOM = "stream-to-dom";
  
      /** NEEDSDOC Field STREAM_TO_DOM_TYPE          */
      public static final int STREAM_TO_DOM_TYPE = 6;
  
      /** NEEDSDOC Field AS_XML_FILTER          */
      public static final String AS_XML_FILTER = "as-xml-filter";
  
      /** NEEDSDOC Field AS_XML_FILTER_TYPE          */
      public static final int AS_XML_FILTER_TYPE = 7;
  
      /** NEEDSDOC Field DEFAULT_TRANSFORM          */
      public static final String DEFAULT_TRANSFORM = FILE_TO_FILE;
  
      /** NEEDSDOC Field DEFAULT_TYPE          */
      public static final int DEFAULT_TYPE = FILE_TO_FILE_TYPE;
  
      /**
       * Mapping of transform types to integer constants.
       */
      protected static Hashtable typeMap = null;
  
      // Static class initializer for our typeMap
      static
      {
          typeMap = new Hashtable();
  
          typeMap.put(FILE_TO_FILE, new Integer(FILE_TO_FILE_TYPE));
          typeMap.put(DOM_TO_DOM, new Integer(DOM_TO_DOM_TYPE));
          typeMap.put(SAX_TO_SAX, new Integer(SAX_TO_SAX_TYPE));
          typeMap.put(SAX_TO_STREAM, new Integer(SAX_TO_STREAM_TYPE));
          typeMap.put(DOM_TO_STREAM, new Integer(DOM_TO_STREAM_TYPE));
          typeMap.put(STREAM_TO_DOM, new Integer(STREAM_TO_DOM_TYPE));
          typeMap.put(AS_XML_FILTER, new Integer(AS_XML_FILTER_TYPE));
      }
      ;
  
      /** Constants for system properties, etc.. */
      public static final String TRAX_PROCESSOR = "trax.processor";
  
      /** NEEDSDOC Field XSLT          */
      public static final String XSLT = "xslt";
  
      /** NEEDSDOC Field TRAX_PROCESSOR_XSLT          */
      public static final String TRAX_PROCESSOR_XSLT = TRAX_PROCESSOR + "."
                                                           + XSLT;
  
      /** NEEDSDOC Field ORG_XML_SAX_DRIVER          */
      public static final String ORG_XML_SAX_DRIVER = "org.xml.sax.driver";
  
      /** NEEDSDOC Field DEFAULT_PROCESSOR          */
      public static final String DEFAULT_PROCESSOR = "com.icl.saxon.StyleSheet";  // SAXON 5.5.1
  
      /** NEEDSDOC Field DEFAULT_PARSER          */
      public static final String DEFAULT_PARSER =
          "org.apache.xerces.parsers.SAXParser";  // TODO which parser to use?
  
      /** NEEDSDOC Field TRAX_WRAPPER_TYPE          */
      public static final String TRAX_WRAPPER_TYPE = "trax.wrapper.type";
  
      /**
       * Construct a processor of the appropriate flavor, optionally specifying a liaison.
       * <p>May throw exceptions related to the creating of a new processor.</p>
       * <p>Note liaison support is not really implemented - a TODO item.</p>
       * <p>This method defaults the "trax.processor.xslt" system
       * property to the Xalan 2.x implementation.  It also reads
       * the "trax.wrapper.type" property to determine how we should
       * perform transformations: eg, SAX2SAX, DOM2DOM, FILE2FILE, etc..</p>
       * @param liaisonClassName [optional] if non-null & non-blank,
       * classname of an XML liaison
       * @return (Object)processor as a side effect; null if error
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public Object createNewProcessor(String liaisonClassName)
              throws java.lang.Exception  // Cover all exception cases
      {
  
          // Cleanup any prior objects 
          cleanup();
  
          // Default the System property trax.processor.xslt
          //      to the FQCN of the xalan implementation, if needed
          Properties props = System.getProperties();
  
          // Get, with default, then put back to install default
          String val = props.getProperty(TRAX_PROCESSOR_XSLT,
                                         DEFAULT_PROCESSOR);
  
          props.put(TRAX_PROCESSOR_XSLT, val);
  
          // Get, with default, then put back to install default
          val = props.getProperty(ORG_XML_SAX_DRIVER, DEFAULT_PARSER);
  
          props.put(ORG_XML_SAX_DRIVER, val);
  
          // Publish any changs.
          // Note that this call may throw SecurityException in 
          //  some cases, cf. if called from an applet
          System.setProperties(props);
  
          // Also get the 'type' of transformation we should perform
          //  with TRAX - using SAX, using files, DOMs, whatever
          try
          {
              Integer i =
                  (Integer) typeMap.get(System.getProperty(TRAX_WRAPPER_TYPE,
                      DEFAULT_TRANSFORM));
  
              transformType = i.intValue();
          }
          catch (Exception e)
          {
  
              // Just set the default; ignore the exception
              transformType = DEFAULT_TYPE;
          }
  
          // Get a processor of 'xslt' stuff (i.e. Xalan)
          processor = Processor.newInstance(XSLT);
          p = (Object) processor;
  
          // Return here; will be null if error or exception raised
          return (p);
      }
  
      /**
       * Get a description of the wrappered processor.
       * @return info-string describing the processor and possibly it's common options
       */
      public String getDescription()
      {
  
          if (processor == null)
          {
              return ("ERROR: must call createNewProcessor first from: "
                      + getDescription());
          }
          else
          {
              StringBuffer buf = new StringBuffer("TRaX");
  
              buf.append(";");
              buf.append("Java");
              buf.append(";");
              buf.append(
                  System.getProperties().getProperty(TRAX_PROCESSOR_XSLT));  // TODO - improve this
              buf.append(";");
              buf.append(System.getProperties().getProperty(TRAX_WRAPPER_TYPE));  // TODO - improve this
  
              return buf.toString();
          }
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * <p>May throw exceptions related to asking the processor to perform the process.</p>
       * <p>Attempts to ask each processor to accomplish the task in the simplest
       * and most obvious manner.  Often copied from various processor's samples.</p>
       * <p>This also respects the "trax.wrapper.type" System property to
       * support different types of transforms.</p>
       * @param xmlSource name of source XML file
       * @param xslStylesheet name of stylesheet XSL file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(
              String xmlSource, String xslStylesheet, String resultFile)
                  throws java.lang.Exception  // Cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long xmlTime = 0;
          long xslTime = 0;
  
          // Create trax-specific sources
          InputSource xsl = new InputSource(xslStylesheet);  // TODO change to be URI
          InputSource xml = new InputSource(xmlSource);  // TODO change to be URI
  
          // May throw IOException
          // Note: use OutputStream derivative, not Writer derivative, so that 
          //  the processor can properly control the output encoding!
          FileOutputStream resultStream = new FileOutputStream(resultFile);
  
          // Begin timing just the stylesheet creation
          startTime = System.currentTimeMillis();
  
          // Read and compile the stylesheet
          Templates templates = processor.process(xsl);
  
          xslTime = System.currentTimeMillis() - startTime;
  
          switch (transformType)
          {
  
          // Each case does timing just on the transformation
          case FILE_TO_FILE_TYPE :  // OK SAXON
              startTime = System.currentTimeMillis();
  
              Transformer transformer = templates.newTransformer();
  
              applyParams(transformer, params);
              transformer.transform(xml, new Result(resultStream));
  
              xmlTime = System.currentTimeMillis() - startTime;
              break;
          case DOM_TO_DOM_TYPE :
              xmlTime = transformDOM2DOM(templates, xml, resultStream);
              break;
          case SAX_TO_SAX_TYPE :
              xmlTime = transformSAX2SAX(templates, xml, resultStream);
              break;
          case SAX_TO_STREAM_TYPE :
              throw new java.lang.IllegalStateException("bad transformType("
                                                        + transformType
                                                        + ") for: "
                                                        + TRAX_WRAPPER_TYPE);
  
          // break;
          case DOM_TO_STREAM_TYPE :
              throw new java.lang.IllegalStateException("bad transformType("
                                                        + transformType
                                                        + ") for: "
                                                        + TRAX_WRAPPER_TYPE);
  
          // break;
          case STREAM_TO_DOM_TYPE :
              throw new java.lang.IllegalStateException("bad transformType("
                                                        + transformType
                                                        + ") for: "
                                                        + TRAX_WRAPPER_TYPE);
  
          // break;
          case AS_XML_FILTER_TYPE :
              xmlTime = transformAsXMLFilter(templates, xml, resultStream);
              break;
          default :
              throw new java.lang.IllegalStateException("bad transformType("
                                                        + transformType
                                                        + ") for: "
                                                        + TRAX_WRAPPER_TYPE);
          }
  
          // Force output stream closed, just in case
          resultStream.close();
  
          // Return the sum of stylesheet create + transform time
          return (xslTime + xmlTime);
      }
  
      /**
       * Transform an xml document using SAX.
       *
       * NEEDSDOC @param templates
       * NEEDSDOC @param xml
       * NEEDSDOC @param out
       * @return milliseconds process time took
       *
       * @throws IOException
       * @throws SAXException
       * @throws TransformException
       */
      private long transformSAX2SAX(
              Templates templates, InputSource xml, OutputStream out)
                  throws TransformException, SAXException, IOException
      {
  
          long startTime = 0;
          Transformer transformer = templates.newTransformer();
  
          applyParams(transformer, params);
  
          OutputFormat format = templates.getOutputFormat();
          Serializer serializer = SerializerFactory.getSerializer(format);
  
          serializer.setOutputStream(out);
          transformer.setContentHandler(serializer.asContentHandler());  // Use a TRaX serializer provided with Xalan
  
          //x transformer.setProperty("http://xml.apache.org/xslt/sourcebase", xml.getSystemId());
          //x XMLReader reader = XMLReaderFactory.createXMLReader();
          XMLReader reader = new com.icl.saxon.aelfred.SAXDriver();  // Use SAXON's favorite parser
  
          reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
                            true);
  
          //x reader.setFeature("http://apache.org/xml/features/validation/dynamic", true);
          ContentHandler chandler = transformer.getInputContentHandler();
  
          reader.setContentHandler(chandler);
  
          if (chandler instanceof org.xml.sax.ext.LexicalHandler)
              reader.setProperty(
                  "http://xml.org/sax/properties/lexical-handler", chandler);
          else
              reader.setProperty(
                  "http://xml.org/sax/properties/lexical-handler", null);
  
          // Only time the actual parsing (transforming)
          startTime = System.currentTimeMillis();
  
          reader.parse(xml);  // Or should we call transformer.parse(), as the SAXON example has?
  
          return (System.currentTimeMillis() - startTime);
      }
  
      /**
       * Transform an xml document using .
       *
       * NEEDSDOC @param templates
       * NEEDSDOC @param xml
       * NEEDSDOC @param out
       * @return milliseconds process time took
       *
       * @throws IOException
       * @throws SAXException
       * @throws TransformException
       */
      private long transformAsXMLFilter(
              Templates templates, InputSource xml, OutputStream out)
                  throws TransformException, SAXException, IOException
      {
  
          long startTime = 0;
          Transformer transformer = templates.newTransformer();
  
          applyParams(transformer, params);
  
          // Set the result handling to be a serialization to out
          OutputFormat format = templates.getOutputFormat();
          Serializer serializer = SerializerFactory.getSerializer(format);
  
          serializer.setOutputStream(out);
          transformer.setContentHandler(serializer.asContentHandler());
  
          // The transformer will use a SAX parser as it's reader.        
          // XMLReader reader = XMLReaderFactory.createXMLReader();
          // transformer.setParent(reader);
          // Instead of the standard (xerces) parser, use SAXON's favorite parser
          transformer.setParent(new com.icl.saxon.aelfred.SAXDriver());
  
          // Now, when you call transformer.parse, it will set itself as 
          // the content handler for the parser object (it's "parent"), and 
          // will then call the parse method on the parser.
          // Only time the actual parsing (transforming)
          startTime = System.currentTimeMillis();
  
          transformer.parse(xml);
  
          return (System.currentTimeMillis() - startTime);
      }
  
      /**
       * Transform an xml document using DOMs.
       *
       * NEEDSDOC @param templates
       * NEEDSDOC @param xml
       * NEEDSDOC @param out
       * @return milliseconds process time took
       *
       * @throws Exception
       */
      private long transformDOM2DOM(
              Templates templates, InputSource xml, OutputStream out)
                  throws Exception  // Just cover all cases, since we don't care which kind gets thrown
      {
  
          long startTime = 0;
  
          if (!processor.getFeature("http://xml.org/trax/features/dom/input"))
          {
              throw new org.xml.sax.SAXNotSupportedException(
                  "DOM node processing not supported!");
          }
  
          DocumentBuilderFactory dfactory =
              DocumentBuilderFactory.newInstance();
          DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
  
          /**
           *   NOTE: this part should really be done as a DOM, but it doesn't
           *   fit with our model - a big TODO for later on
           * // Parse in the stylesheet
           * Node xslDoc = docBuilder.parse(new InputSource(xslID));
           *
           * // Create the template from the DOM of the stylesheet
           * Templates templates = processor.processFromNode(xslDoc);
           */
  
          // Here, time the parsing of the XML doc, the transformation, 
          //  and the serialization: this seems equivalent to what we 
          //  time in the other methods
          startTime = System.currentTimeMillis();
  
          // Parse the XML data document the same way
          Node xmlDoc = docBuilder.parse(xml);
  
          // Saxon example uses a slightly different way:
          // Use Xerces DOM
          //sx DOMParser parser = new DOMParser();
          //sx parser.parse(source(indir+"books.xml"));
          //sx Document xmlDoc = parser.getDocument();
          //sx Node outNode = new DocumentImpl();
          // Run the transformation from the DOM nodes
          org.w3c.dom.Document outNode = docBuilder.newDocument();
          Transformer transformer = templates.newTransformer();
  
          applyParams(transformer, params);
          transformer.transformNode(xmlDoc, new Result(outNode));
  
          // Use the serializers to output the result to disk
          OutputFormat format = templates.getOutputFormat();
          Serializer serializer = SerializerFactory.getSerializer(format);
  
          serializer.setOutputStream(out);
          serializer.asDOMSerializer().serialize(outNode);
  
          return (System.currentTimeMillis() - startTime);
      }
  
      /**
       * Preprocess a stylesheet and set it into the processor, based on string inputs.
       * @todo Does NOT respect the "trax.wrapper.type" System property yet.
       * @param xslStylesheet name of stylesheet XSL file
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long preProcessStylesheet(String xslStylesheet)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create trax-specific sources
          InputSource xsl = new InputSource(xslStylesheet);
  
          // Begin timing the whole process
          startTime = System.currentTimeMillis();
  
          // Read and compile the stylesheet
          savedStylesheet = processor.process(xsl);
          endTime = System.currentTimeMillis();
          stylesheetReady = true;
  
          return (endTime - startTime);
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * @todo Does NOT respect the "trax.wrapper.type" System property yet.
       * @param xmlSource name of source XML file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(String xmlSource, String resultFile)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create trax-specific sources
          InputSource xml = new InputSource(xmlSource);
  
          // May throw IOException
          FileOutputStream resultStream = new FileOutputStream(resultFile);
  
          // Use the precompiled stylesheet
          Transformer transformer = savedStylesheet.newTransformer();
  
          applyParams(transformer, params);
  
          // Begin timing the whole process
          startTime = System.currentTimeMillis();
  
          // HACK: this should work off of transformType as well!
          transformer.transform(xml, new Result(resultStream));
  
          endTime = System.currentTimeMillis();
  
          // Force output stream closed, just in case
          resultStream.close();
  
          return (endTime - startTime);
      }
  
      /**
       * Reset the state.
       */
      public void reset()
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Does not appear to be applicable for TRaX?
          stylesheetReady = false;
          savedStylesheet = null;
          params = null;
      }
  
      /**
       * Set diagnostics output PrintWriter.  
       *
       * NEEDSDOC @param pw
       */
      public void setDiagnosticsOutput(java.io.PrintWriter pw)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Unimplemented; silently fail
          // processor.setDiagnosticsOutput(pw);
      }
  
      /**
       * Set the indent level of the processor.  
       *
       * NEEDSDOC @param i
       */
      public void setIndent(int i)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Unimplemented; silently fail
          // TODO: implement for Xalan/LotusXSL specific setFeature()
          // processor.getXMLProcessorLiaison().setIndent(i);
      }
  
      /**
       * Set a String name=value param in the processor, if applicable.  
       *
       * NEEDSDOC @param key
       * NEEDSDOC @param expression
       */
      public void setStylesheetParam(String key, String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          if (params == null)
              params = new Hashtable();
  
          // Just put the param in our hash; individual methods will 
          //  make use of these later when needed
          params.put(key, expression);
      }
  
      /**
       * Set a String namespace:name=value param in the processor, if applicable.
       * @todo Needs Implementation: namespace is currently <b>ignored!</b>
       * @param namespace of the param
       * @param key name of the param
       * @param expression value of the param
       */
      public void setStylesheetParam(String namespace, String key,
                                     String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          if (params == null)
              params = new Hashtable();
  
          // Just put the param in our hash; individual methods will 
          //  make use of these later when needed
          params.put(key, expression);
      }
  
      /**
       * Apply our set of parameters to a transformer.  
       *
       * NEEDSDOC @param t
       * NEEDSDOC @param h
       */
      protected void applyParams(Transformer t, Hashtable h)
      {
  
          if (params == null)
              return;
  
          for (Enumeration enum = h.keys();
                  enum.hasMoreElements(); /* no increment portion */ )
          {
              Object key = enum.nextElement();
  
              t.setParameter(key.toString(), null /* namespace TBD */,
                             h.get(key));
          }
      }
  
      /** Worker method to cleanup any internal state. */
      private void cleanup()
      {
  
          processor = null;
          p = null;
          stylesheetReady = false;
          savedStylesheet = null;
          params = null;
      }
  
      /**
       * Create an InputSource that refers to a given File.
       * @author Michael.Kay@icl.com
       *
       * NEEDSDOC @param filename
       *
       * NEEDSDOC ($objectName$) @return
       */
      private static InputSource source(String filename)
      {
  
          File file = new File(filename);
          String path = file.getAbsolutePath();
          URL url = null;
  
          try
          {
              url = new URL(path);
          }
          catch (MalformedURLException ex)
          {
              try
              {
  
                  // This is a bunch of weird code that is required to
                  // make a valid URL on the Windows platform, due
                  // to inconsistencies in what getAbsolutePath returns.
                  String fs = System.getProperty("file.separator");
  
                  if (fs.length() == 1)
                  {
                      char sep = fs.charAt(0);
  
                      if (sep != '/')
                          path = path.replace(sep, '/');
  
                      if (path.charAt(0) != '/')
                          path = '/' + path;
                  }
  
                  path = "file://" + path;
                  url = new URL(path);
              }
              catch (MalformedURLException e)
              {
                  return null;
              }
          }
  
          return (new InputSource(url.toString()));
      }
  }  // end of class SaxonWrapper
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/TraxWrapper.java
  
  Index: TraxWrapper.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * TraxWrapper.java
   *
   */
  package org.apache.qetest.xslwrapper;
  
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Properties;
  
  import java.io.PrintWriter;  // currently only used in unimplemented setDiagnosticsOutput
  import java.io.ByteArrayOutputStream;
  import java.io.FileOutputStream;
  import java.io.OutputStream;
  import java.io.IOException;
  
  // Needed SAX classes
  import org.xml.sax.InputSource;
  import org.xml.sax.helpers.XMLReaderFactory;
  import org.xml.sax.XMLReader;
  import org.xml.sax.ContentHandler;
  import org.xml.sax.SAXException;
  
  // A generic TRaX-compliant wrapper
  // NOTE: package name subject to change!
  import org.apache.trax.Processor;
  import org.apache.trax.Result;
  import org.apache.trax.Templates;
  import org.apache.trax.Transformer;
  import org.apache.trax.TransformException;
  import org.apache.serialize.SerializerFactory;
  import org.apache.serialize.Serializer;
  import org.apache.serialize.OutputFormat;
  
  import org.w3c.dom.Node;
  
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  
  /**
   * Implementation of a ProcessorWrapper for a TrAX-compilant XSLT processor.
   * <p>Currently defaults to the Xalan 2.x implementation, although
   * it should respect the trax.processor.xslt system property.</p>
   * @todo add better support for liaisons, or different processing
   * models (DOM, SAX, from files, whatever), etc.
   * @todo share constants between TraxWrapper, SaxonWrapper
   * @todo document how we perform various types of transforms
   * @author Shane Curcuru
   * @version $Id: TraxWrapper.java,v 1.1 2000/11/01 23:27:00 curcuru Exp $
   */
  public class TraxWrapper extends ProcessorWrapper
  {
  
      /** No-op Ctor for the generic TRAX interface wrapper. */
      public TraxWrapper(){}
  
      /** Reference to current processor - TRaX flavor - convenience method. */
      protected org.apache.trax.Processor processor = null;
  
      /**
       * NEEDSDOC Method getTraxProcessor 
       *
       *
       * NEEDSDOC (getTraxProcessor) @return
       */
      public org.apache.trax.Processor getTraxProcessor()
      {
          return (processor);
      }
  
      /** A preprocessed stylesheet that we're saving. */
      private Templates savedStylesheet = null;
  
      /** Whatever parameters the user has set. */
      private Hashtable params = null;
  
      /** Which type of transform we should perform. */
      protected int transformType = DEFAULT_TYPE;
  
      /** Constants for different types of transforms. */
      public static final String FILE_TO_FILE = "file-to-file";
  
      /** NEEDSDOC Field FILE_TO_FILE_TYPE          */
      public static final int FILE_TO_FILE_TYPE = 1;
  
      /** NEEDSDOC Field DOM_TO_DOM          */
      public static final String DOM_TO_DOM = "dom-to-dom";
  
      /** NEEDSDOC Field DOM_TO_DOM_TYPE          */
      public static final int DOM_TO_DOM_TYPE = 2;
  
      /** NEEDSDOC Field SAX_TO_SAX          */
      public static final String SAX_TO_SAX = "sax-to-sax";
  
      /** NEEDSDOC Field SAX_TO_SAX_TYPE          */
      public static final int SAX_TO_SAX_TYPE = 3;
  
      /** NEEDSDOC Field SAX_TO_STREAM          */
      public static final String SAX_TO_STREAM = "sax-to-stream";
  
      /** NEEDSDOC Field SAX_TO_STREAM_TYPE          */
      public static final int SAX_TO_STREAM_TYPE = 4;
  
      /** NEEDSDOC Field DOM_TO_STREAM          */
      public static final String DOM_TO_STREAM = "dom-to-stream";
  
      /** NEEDSDOC Field DOM_TO_STREAM_TYPE          */
      public static final int DOM_TO_STREAM_TYPE = 5;
  
      /** NEEDSDOC Field STREAM_TO_DOM          */
      public static final String STREAM_TO_DOM = "stream-to-dom";
  
      /** NEEDSDOC Field STREAM_TO_DOM_TYPE          */
      public static final int STREAM_TO_DOM_TYPE = 6;
  
      /** NEEDSDOC Field AS_XML_FILTER          */
      public static final String AS_XML_FILTER = "as-xml-filter";
  
      /** NEEDSDOC Field AS_XML_FILTER_TYPE          */
      public static final int AS_XML_FILTER_TYPE = 7;
  
      /** NEEDSDOC Field DEFAULT_TRANSFORM          */
      public static final String DEFAULT_TRANSFORM = FILE_TO_FILE;
  
      /** NEEDSDOC Field DEFAULT_TYPE          */
      public static final int DEFAULT_TYPE = FILE_TO_FILE_TYPE;
  
      /**
       * Mapping of transform types to integer constants.
       */
      protected static Hashtable typeMap = null;
  
      // Static class initializer for our typeMap
      static
      {
          typeMap = new Hashtable();
  
          typeMap.put(FILE_TO_FILE, new Integer(FILE_TO_FILE_TYPE));
          typeMap.put(DOM_TO_DOM, new Integer(DOM_TO_DOM_TYPE));
          typeMap.put(SAX_TO_SAX, new Integer(SAX_TO_SAX_TYPE));
          typeMap.put(SAX_TO_STREAM, new Integer(SAX_TO_STREAM_TYPE));
          typeMap.put(DOM_TO_STREAM, new Integer(DOM_TO_STREAM_TYPE));
          typeMap.put(STREAM_TO_DOM, new Integer(STREAM_TO_DOM_TYPE));
          typeMap.put(AS_XML_FILTER, new Integer(AS_XML_FILTER_TYPE));
      }
      ;
  
      /** Constants for system properties, etc.. */
      public static final String TRAX_PROCESSOR = "trax.processor";
  
      /** NEEDSDOC Field XSLT          */
      public static final String XSLT = "xslt";
  
      /** NEEDSDOC Field TRAX_PROCESSOR_XSLT          */
      public static final String TRAX_PROCESSOR_XSLT = TRAX_PROCESSOR + "."
                                                           + XSLT;
  
      /** NEEDSDOC Field ORG_XML_SAX_DRIVER          */
      public static final String ORG_XML_SAX_DRIVER = "org.xml.sax.driver";
  
      /** NEEDSDOC Field DEFAULT_PROCESSOR          */
      public static final String DEFAULT_PROCESSOR =
          "org.apache.xalan.processor.StylesheetProcessor";
  
      /** NEEDSDOC Field DEFAULT_PARSER          */
      public static final String DEFAULT_PARSER =
          "org.apache.xerces.parsers.SAXParser";
  
      /** NEEDSDOC Field TRAX_WRAPPER_TYPE          */
      public static final String TRAX_WRAPPER_TYPE = "trax.wrapper.type";
  
      /**
       * Construct a processor of the appropriate flavor, optionally specifying a liaison.
       * <p>May throw exceptions related to the creating of a new processor.</p>
       * <p>Note liaison support is not really implemented - a TODO item.</p>
       * <p>This method defaults the "trax.processor.xslt" system
       * property to the Xalan 2.x implementation.  It also reads
       * the "trax.wrapper.type" property to determine how we should
       * perform transformations: eg, SAX2SAX, DOM2DOM, FILE2FILE, etc..</p>
       * @param liaisonClassName [optional] if non-null & non-blank,
       * classname of an XML liaison
       * @return (Object)processor as a side effect; null if error
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public Object createNewProcessor(String liaisonClassName)
              throws java.lang.Exception  // Cover all exception cases
      {
  
          // Cleanup any prior objects 
          cleanup();
  
          // Default the System property trax.processor.xslt
          //      to the FQCN of the xalan implementation, if needed
          Properties props = System.getProperties();
  
          // Get, with default, then put back to install default
          String val = props.getProperty(TRAX_PROCESSOR_XSLT,
                                         DEFAULT_PROCESSOR);
  
          props.put(TRAX_PROCESSOR_XSLT, val);
  
          // Get, with default, then put back to install default
          val = props.getProperty(ORG_XML_SAX_DRIVER, DEFAULT_PARSER);
  
          props.put(ORG_XML_SAX_DRIVER, val);
  
          // Publish any changs.
          // Note that this call may throw SecurityException in 
          //  some cases, cf. if called from an applet
          System.setProperties(props);
  
          // Also get the 'type' of transformation we should perform
          //  with TRAX - using SAX, using files, DOMs, whatever
          try
          {
              Integer i =
                  (Integer) typeMap.get(System.getProperty(TRAX_WRAPPER_TYPE,
                      DEFAULT_TRANSFORM));
  
              transformType = i.intValue();
          }
          catch (Exception e)
          {
  
              // Just set the default; ignore the exception
              transformType = DEFAULT_TYPE;
          }
  
          // Get a processor of 'xslt' stuff (i.e. Xalan)
          processor = Processor.newInstance(XSLT);
          p = (Object) processor;
  
          // Return here; will be null if error or exception raised
          return (p);
      }
  
      /**
       * Get a description of the wrappered processor.
       * @return info-string describing the processor and possibly it's common options
       */
      public String getDescription()
      {
  
          if (processor == null)
          {
              return ("ERROR: must call createNewProcessor first from: "
                      + getDescription());
          }
          else
          {
              StringBuffer buf = new StringBuffer("TRaX");
  
              buf.append(";");
              buf.append("Java");
              buf.append(";");
              buf.append(
                  System.getProperties().getProperty(TRAX_PROCESSOR_XSLT));  // TODO - improve this
              buf.append(";");
              buf.append(System.getProperties().getProperty(TRAX_WRAPPER_TYPE));  // TODO - improve this
  
              return buf.toString();
          }
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * <p>May throw exceptions related to asking the processor to perform the process.</p>
       * <p>Attempts to ask each processor to accomplish the task in the simplest
       * and most obvious manner.  Often copied from various processor's samples.</p>
       * <p>This also respects the "trax.wrapper.type" System property to
       * support different types of transforms.</p>
       * @param xmlSource name of source XML file
       * @param xslStylesheet name of stylesheet XSL file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(
              String xmlSource, String xslStylesheet, String resultFile)
                  throws java.lang.Exception  // Cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long xmlTime = 0;
          long xslTime = 0;
  
          // Create trax-specific sources
          InputSource xsl = new InputSource(xslStylesheet);
          InputSource xml = new InputSource(xmlSource);
  
          // May throw IOException
          // Note: use OutputStream derivative, not Writer derivative, so that 
          //  the processor can properly control the output encoding!
          FileOutputStream resultStream = new FileOutputStream(resultFile);
  
          // Begin timing just the stylesheet creation
          startTime = System.currentTimeMillis();
  
          // Read and compile the stylesheet
          Templates templates = processor.process(xsl);
  
          xslTime = System.currentTimeMillis() - startTime;
  
          switch (transformType)
          {
  
          // Each case does timing just on the transformation
          case FILE_TO_FILE_TYPE :
              startTime = System.currentTimeMillis();
  
              Transformer transformer = templates.newTransformer();
  
              applyParams(transformer, params);
              transformer.transform(xml, new Result(resultStream));
  
              xmlTime = System.currentTimeMillis() - startTime;
              break;
          case DOM_TO_DOM_TYPE :
              xmlTime = transformDOM2DOM(templates, xml, resultStream);
              break;
          case SAX_TO_SAX_TYPE :
              xmlTime = transformSAX2SAX(templates, xml, resultStream);
              break;
          case SAX_TO_STREAM_TYPE :
              xmlTime = transformSAX2Stream(templates, xml, resultStream);
              break;
          case DOM_TO_STREAM_TYPE :
              throw new java.lang.IllegalStateException("bad transformType("
                                                        + transformType
                                                        + ") for: "
                                                        + TRAX_WRAPPER_TYPE);
  
          // break;
          case STREAM_TO_DOM_TYPE :
              throw new java.lang.IllegalStateException("bad transformType("
                                                        + transformType
                                                        + ") for: "
                                                        + TRAX_WRAPPER_TYPE);
  
          // break;
          case AS_XML_FILTER_TYPE :
              xmlTime = transformAsXMLFilter(templates, xml, resultStream);
              break;
          default :
              throw new java.lang.IllegalStateException("bad transformType("
                                                        + transformType
                                                        + ") for: "
                                                        + TRAX_WRAPPER_TYPE);
          }
  
          // Force output stream closed, just in case
          resultStream.close();
  
          // Return the sum of stylesheet create + transform time
          return (xslTime + xmlTime);
      }
  
      /**
       * Transform an xml document using SAX.
       *
       * NEEDSDOC @param templates
       * NEEDSDOC @param xml
       * NEEDSDOC @param out
       * @return milliseconds process time took
       *
       * @throws IOException
       * @throws SAXException
       * @throws TransformException
       */
      private long transformSAX2SAX(
              Templates templates, InputSource xml, OutputStream out)
                  throws TransformException, SAXException, IOException
      {
  
          long startTime = 0;
          Transformer transformer = templates.newTransformer();
  
          applyParams(transformer, params);
  
          OutputFormat format = templates.getOutputFormat();
          Serializer serializer = SerializerFactory.getSerializer(format);
  
          serializer.setOutputStream(out);
          transformer.setContentHandler(serializer.asContentHandler());
          transformer.setProperty("http://xml.apache.org/xslt/sourcebase",
                                  xml.getSystemId());
  
          XMLReader reader = XMLReaderFactory.createXMLReader();
  
          reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
                            true);
          reader.setFeature("http://apache.org/xml/features/validation/dynamic",
                            true);
  
          ContentHandler chandler = transformer.getInputContentHandler();
  
          reader.setContentHandler(chandler);
  
          if (chandler instanceof org.xml.sax.ext.LexicalHandler)
              reader.setProperty(
                  "http://xml.org/sax/properties/lexical-handler", chandler);
          else
              reader.setProperty(
                  "http://xml.org/sax/properties/lexical-handler", null);
  
          // Only time the actual parsing (transforming)
          startTime = System.currentTimeMillis();
  
          reader.parse(xml);
  
          return (System.currentTimeMillis() - startTime);
      }
  
      /**
       * Transform an xml document using SAX to a stream (then write separately.
       *
       * NEEDSDOC @param templates
       * NEEDSDOC @param xml
       * NEEDSDOC @param out
       * @return milliseconds process time took
       *
       * @throws IOException
       * @throws SAXException
       * @throws TransformException
       */
      private long transformSAX2Stream(
              Templates templates, InputSource xml, OutputStream out)
                  throws TransformException, SAXException, IOException
      {
  
          long startTime = 0;
          Transformer transformer = templates.newTransformer();
  
          applyParams(transformer, params);
  
          OutputFormat format = templates.getOutputFormat();
          Serializer serializer = SerializerFactory.getSerializer(format);
          ByteArrayOutputStream baos = new ByteArrayOutputStream();  // ???? How best to do this?
  
          serializer.setOutputStream(baos);
          transformer.setContentHandler(serializer.asContentHandler());
          transformer.setProperty("http://xml.apache.org/xslt/sourcebase",
                                  xml.getSystemId());
  
          XMLReader reader = XMLReaderFactory.createXMLReader();
  
          reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
                            true);
          reader.setFeature("http://apache.org/xml/features/validation/dynamic",
                            true);
  
          ContentHandler chandler = transformer.getInputContentHandler();
  
          reader.setContentHandler(chandler);
  
          if (chandler instanceof org.xml.sax.ext.LexicalHandler)
              reader.setProperty(
                  "http://xml.org/sax/properties/lexical-handler", chandler);
          else
              reader.setProperty(
                  "http://xml.org/sax/properties/lexical-handler", null);
  
          // Only time the actual parsing (transforming)
          startTime = System.currentTimeMillis();
  
          reader.parse(xml);
  
          long endTime = System.currentTimeMillis();
  
          // Now actually write the output to disk
          out.write(baos.toByteArray());
          out.flush();  // Should we close() as well?
  
          return (endTime - startTime);
      }
  
      /**
       * Transform an xml document using .
       *
       * NEEDSDOC @param templates
       * NEEDSDOC @param xml
       * NEEDSDOC @param out
       * @return milliseconds process time took
       *
       * @throws IOException
       * @throws SAXException
       * @throws TransformException
       */
      private long transformAsXMLFilter(
              Templates templates, InputSource xml, OutputStream out)
                  throws TransformException, SAXException, IOException
      {
  
          long startTime = 0;
          Transformer transformer = templates.newTransformer();
  
          applyParams(transformer, params);
  
          // Set the result handling to be a serialization to out
          OutputFormat format = templates.getOutputFormat();
          Serializer serializer = SerializerFactory.getSerializer(format);
  
          serializer.setOutputStream(out);
          transformer.setContentHandler(serializer.asContentHandler());
  
          // The transformer will use a SAX parser as it's reader.        
          XMLReader reader = XMLReaderFactory.createXMLReader();
  
          transformer.setParent(reader);
  
          // Now, when you call transformer.parse, it will set itself as 
          // the content handler for the parser object (it's "parent"), and 
          // will then call the parse method on the parser.
          // Only time the actual parsing (transforming)
          startTime = System.currentTimeMillis();
  
          transformer.parse(xml);
  
          return (System.currentTimeMillis() - startTime);
      }
  
      /**
       * Transform an xml document using DOMs.
       *
       * NEEDSDOC @param templates
       * NEEDSDOC @param xml
       * NEEDSDOC @param out
       * @return milliseconds process time took
       *
       * @throws Exception
       */
      private long transformDOM2DOM(
              Templates templates, InputSource xml, OutputStream out)
                  throws Exception  // Just cover all cases, since we don't care which kind gets thrown
      {
  
          long startTime = 0;
  
          if (!processor.getFeature("http://xml.org/trax/features/dom/input"))
          {
              throw new org.xml.sax.SAXNotSupportedException(
                  "DOM node processing not supported!");
          }
  
          DocumentBuilderFactory dfactory =
              DocumentBuilderFactory.newInstance();
          DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
  
          /**
           *   NOTE: this part should really be done as a DOM, but it doesn't
           *   fit with our model - a big TODO for later on
           * // Parse in the stylesheet
           * Node xslDoc = docBuilder.parse(new InputSource(xslID));
           *
           * // Create the template from the DOM of the stylesheet
           * Templates templates = processor.processFromNode(xslDoc);
           */
  
          // Here, time the parsing of the XML doc, the transformation, 
          //  and the serialization: this seems equivalent to what we 
          //  time in the other methods
          startTime = System.currentTimeMillis();
  
          // Parse the XML data document the same way
          Node xmlDoc = docBuilder.parse(xml);
  
          // Run the transformation from the DOM nodes
          org.w3c.dom.Document outNode = docBuilder.newDocument();
          Transformer transformer = templates.newTransformer();
  
          applyParams(transformer, params);
          transformer.transformNode(xmlDoc, new Result(outNode));
  
          // Use the serializers to output the result to disk
          OutputFormat format = templates.getOutputFormat();
          Serializer serializer = SerializerFactory.getSerializer(format);
  
          serializer.setOutputStream(out);
          serializer.asDOMSerializer().serialize(outNode);
  
          return (System.currentTimeMillis() - startTime);
      }
  
      /**
       * Preprocess a stylesheet and set it into the processor, based on string inputs.
       * @todo Does NOT respect the "trax.wrapper.type" System property yet.
       * @param xslStylesheet name of stylesheet XSL file
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long preProcessStylesheet(String xslStylesheet)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create trax-specific sources
          InputSource xsl = new InputSource(xslStylesheet);
  
          // Begin timing the whole process
          startTime = System.currentTimeMillis();
  
          // Read and compile the stylesheet
          savedStylesheet = processor.process(xsl);
          endTime = System.currentTimeMillis();
          stylesheetReady = true;
  
          return (endTime - startTime);
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * @todo Does NOT respect the "trax.wrapper.type" System property yet.
       * @param xmlSource name of source XML file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(String xmlSource, String resultFile)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create trax-specific sources
          InputSource xml = new InputSource(xmlSource);
  
          // May throw IOException
          FileOutputStream resultStream = new FileOutputStream(resultFile);
  
          // Use the precompiled stylesheet
          Transformer transformer = savedStylesheet.newTransformer();
  
          applyParams(transformer, params);
  
          // Begin timing the whole process
          startTime = System.currentTimeMillis();
  
          // HACK: this should work off of transformType as well!
          transformer.transform(xml, new Result(resultStream));
  
          endTime = System.currentTimeMillis();
  
          // Force output stream closed, just in case
          resultStream.close();
  
          return (endTime - startTime);
      }
  
      /**
       * Reset the state.
       */
      public void reset()
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Does not appear to be applicable for TRaX?
          stylesheetReady = false;
          savedStylesheet = null;
          params = null;
      }
  
      /**
       * Set diagnostics output PrintWriter.  
       *
       * NEEDSDOC @param pw
       */
      public void setDiagnosticsOutput(java.io.PrintWriter pw)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Unimplemented; silently fail
          // processor.setDiagnosticsOutput(pw);
      }
  
      /**
       * Set the indent level of the processor.  
       *
       * NEEDSDOC @param i
       */
      public void setIndent(int i)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Unimplemented; silently fail
          // TODO: implement for Xalan/LotusXSL specific setFeature()
          // processor.getXMLProcessorLiaison().setIndent(i);
      }
  
      /**
       * Set a String name=value param in the processor, if applicable.  
       *
       * NEEDSDOC @param key
       * NEEDSDOC @param expression
       */
      public void setStylesheetParam(String key, String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          if (params == null)
              params = new Hashtable();
  
          // Just put the param in our hash; individual methods will 
          //  make use of these later when needed
          params.put(key, expression);
      }
  
      /**
       * Set a String namespace:name=value param in the processor, if applicable.
       * @todo Needs Implementation: namespace is currently <b>ignored!</b>
       * @param namespace of the param
       * @param key name of the param
       * @param expression value of the param
       */
      public void setStylesheetParam(String namespace, String key,
                                     String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          if (params == null)
              params = new Hashtable();
  
          // Just put the param in our hash; individual methods will 
          //  make use of these later when needed
          params.put(key, expression);
      }
  
      /**
       * Apply our set of parameters to a transformer.  
       *
       * NEEDSDOC @param t
       * NEEDSDOC @param h
       */
      protected void applyParams(Transformer t, Hashtable h)
      {
  
          if (params == null)
              return;
  
          for (Enumeration enum = h.keys();
                  enum.hasMoreElements(); /* no increment portion */ )
          {
              Object key = enum.nextElement();
  
              t.setParameter(key.toString(), null /* namespace TBD */,
                             h.get(key));
          }
      }
  
      /** Worker method to cleanup any internal state. */
      private void cleanup()
      {
  
          processor = null;
          p = null;
          stylesheetReady = false;
          savedStylesheet = null;
          params = null;
      }
  }  // end of class TraxWrapper
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/XTWrapper.class
  
  	<<Binary file>>
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/XTWrapper.java
  
  Index: XTWrapper.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * XTWrapper.java
   *
   */
  package org.apache.qetest.xslwrapper;
  
  import java.io.File;
  import java.io.PrintWriter;
  
  import java.net.URL;
  
  // The XT (James Clark) implementation
  import org.xml.sax.InputSource;
  import org.xml.sax.Parser;
  
  import com.jclark.xsl.sax.XMLProcessorEx;
  import com.jclark.xsl.sax.XSLProcessorImpl;
  import com.jclark.xsl.sax.OutputMethodHandlerImpl;
  import com.jclark.xsl.sax.FileDestination;
  
  /**
   * Implementation of a ProcessorWrapper for XT.
   * @author Shane Curcuru
   * @version $Id: XTWrapper.java,v 1.1 2000/11/01 23:27:00 curcuru Exp $
   */
  public class XTWrapper extends ProcessorWrapper
  {
  
      /** No-op Ctor for the Xalan-J 1.x wrapper. */
      public XTWrapper(){}
  
      /** Reference to current processor - XT flavor - convenience method. */
      protected com.jclark.xsl.sax.XSLProcessorImpl processor = null;
  
      /**
       * NEEDSDOC Method getXTProcessor 
       *
       *
       * NEEDSDOC (getXTProcessor) @return
       */
      public com.jclark.xsl.sax.XSLProcessorImpl getXTProcessor()
      {
          return (processor);
      }
  
      /**
       * Construct a processor of the appropriate flavor, optionally specifying a liaison.
       * <p>May throw exceptions related to the creating of a new processor.</p>
       * <ul>XT supports ??? liaisons:
       * <li>com.jclark.xml.sax.CommentDriver (default)</li>
       * </ul>
       * @param liaisonClassName [optional] if non-null & non-blank, classname of an XML liaison
       * @return (Object)processor as a side effect; null if error
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public Object createNewProcessor(String liaisonClassName)
              throws java.lang.Exception  // Cover all exception cases
      {
  
          // Cleanup any prior objects 
          cleanup();
  
          processor = new com.jclark.xsl.sax.XSLProcessorImpl();
  
          if (liaisonClassName == null)
              liaisonClassName = "com.jclark.xml.sax.CommentDriver";  // default
  
          try
          {
              Object parserObj = Class.forName(liaisonClassName).newInstance();
  
              if (parserObj instanceof XMLProcessorEx)
                  processor.setParser((XMLProcessorEx) parserObj);
              else
                  processor.setParser((org.xml.sax.Parser) parserObj);
          }
          catch (Exception e)
          {
              System.err.println("createNewProcesor(xt) threw: "
                                 + e.toString());
              e.printStackTrace();
  
              processor = null;
          }
  
          p = (Object) processor;
  
          // Return here; will be null if error or exception raised
          return (p);
      }
  
      /**
       * Get a description of the wrappered processor.
       * @return info-string describing the processor and possibly it's common options
       */
      public String getDescription()
      {
  
          if (processor == null)
          {
              return ("ERROR: must call createNewProcessor first from: "
                      + getDescription());
          }
          else
          {
              StringBuffer buf = new StringBuffer("XT");
  
              buf.append(";");
              buf.append("JAVA");
              buf.append(";");
              buf.append("unknown_version");
              buf.append(";");
              buf.append("unknown_liaison");
  
              return buf.toString();
          }
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * <p>May throw exceptions related to asking the processor to perform the process.</p>
       * <p>Attempts to ask each processor to accomplish the task in the simplest
       * and most obvious manner.  Often copied from various processor's samples.</p>
       * @param xmlSource name of source XML file
       * @param xslStylesheet name of stylesheet XSL file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(
              String xmlSource, String xslStylesheet, String resultFile)
                  throws java.lang.Exception  // Cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create XT-specific sources
          OutputMethodHandlerImpl outHandler =
              new OutputMethodHandlerImpl(processor);
  
          outHandler.setDestination(new FileDestination(new File(resultFile)));
  
          InputSource xmlIS = xtInputSourceFromString(xmlSource);
          InputSource xslIS = xtInputSourceFromString(xslStylesheet);
  
          // Begin timing the process: stylesheet, output, and process
          startTime = System.currentTimeMillis();
  
          processor.loadStylesheet(xslIS);
          processor.setOutputMethodHandler(outHandler);
          processor.parse(xmlIS);
  
          endTime = System.currentTimeMillis();
  
          return (endTime - startTime);
      }
  
      /**
       * Worker method for using XT to process.  
       *
       * NEEDSDOC @param name
       *
       * NEEDSDOC ($objectName$) @return
       */
      private InputSource xtInputSourceFromString(String name)
      {
  
          File file = new File(name);
          String path = file.getAbsolutePath();
  
          // Add absolute / to beginning if needed
          if (path.charAt(0) != '/')
              path = '/' + path;
  
          try
          {
              java.net.URL temp = new URL("file", "", path);
  
              return (new InputSource(temp.toString()));
          }
          catch (Exception e)
          {
              System.err.println("xtInputSourceFromString(xt) of: " + name
                                 + " threw: " + e.toString());
              e.printStackTrace();
  
              return (null);
          }
      }
  
      /**
       * Preprocess a stylesheet and set it into the processor, based on string inputs.
       * @param xslStylesheet name of stylesheet XSL file
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long preProcessStylesheet(String xslStylesheet)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create XT-specific source
          InputSource xslIS = xtInputSourceFromString(xslStylesheet);
  
          // Begin timing loading the stylesheet
          startTime = System.currentTimeMillis();
  
          processor.loadStylesheet(xslIS);  // side effect: also sets the stylesheet
  
          endTime = System.currentTimeMillis();
          stylesheetReady = true;
  
          return (endTime - startTime);
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * @param xmlSource name of source XML file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(String xmlSource, String resultFile)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Ensure we (apparently) have already processed a stylesheet
          if (!stylesheetReady)
              throw new java.lang.IllegalStateException(
                  "You must call preProcessStylesheet first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create XT-specific sources
          OutputMethodHandlerImpl outHandler =
              new OutputMethodHandlerImpl(processor);
  
          outHandler.setDestination(new FileDestination(new File(resultFile)));
  
          InputSource xmlIS = xtInputSourceFromString(xmlSource);
  
          // Begin timing the process: stylesheet, output, and process
          startTime = System.currentTimeMillis();
  
          processor.setOutputMethodHandler(outHandler);
          processor.parse(xmlIS);
  
          endTime = System.currentTimeMillis();
  
          return (endTime - startTime);
      }
  
      /**
       * Reset the state.
       */
      public void reset()
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // processor.reset();  // FIXME: no applicable API?
          stylesheetReady = false;
      }
  
      /**
       * Set diagnostics output PrintWriter:unimplemented.  
       *
       * NEEDSDOC @param pw
       */
      public void setDiagnosticsOutput(java.io.PrintWriter pw)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          //processor.setDiagnosticsOutput(pw);  // FIXME: no applicable API?
      }
  
      /**
       * Set the indent level of the processor:unimplemented.  
       *
       * NEEDSDOC @param i
       */
      public void setIndent(int i)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // processor.getXMLProcessorLiaison().setIndent(i); // FIXME: needs to be implemented
      }
  
      /**
       * Set a String name=value param in the processor, if applicable.  
       *
       * NEEDSDOC @param key
       * NEEDSDOC @param expression
       */
      public void setStylesheetParam(String key, String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // NEEDSWORK: ensure XT is expecting the same kind of expression as other processors
          processor.setParameter(key, (Object) expression);
      }
  
      /**
       * Set a String namespace:name=value param in the processor, if applicable.
       * @todo Needs Implementation: namespace is currently <b>ignored!</b>
       * @param namespace of the param
       * @param key name of the param
       * @param expression value of the param
       */
      public void setStylesheetParam(String namespace, String key,
                                     String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // NEEDSWORK: ensure XT is expecting the same kind of expression as other processors
          processor.setParameter(key, (Object) expression);
      }
  
      /** Worker method to cleanup any internal state. */
      private void cleanup()
      {
  
          processor = null;
          p = null;
          stylesheetReady = false;
      }
  }  // end of class XTWrapper
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/XalanWrapper.java
  
  Index: XalanWrapper.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xalan" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 2000, Lotus
   * Development Corporation., http://www.lotus.com.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   *
   * XalanWrapper.java
   *
   */
  package org.apache.qetest.xslwrapper;
  
  import java.util.Vector;
  
  import java.io.PrintWriter;
  
  // The Xalan (apache) implementation
  import org.apache.xalan.xslt.XSLTProcessor;
  import org.apache.xalan.xslt.XSLTProcessorFactory;
  import org.apache.xalan.xslt.XSLTInputSource;
  import org.apache.xalan.xslt.XSLTResultTarget;
  import org.apache.xalan.xslt.XSLProcessorVersion;
  
  /**
   * Implementation of a ProcessorWrapper for Xalan 1.x builds.
   * <p>See TraxWrapper for a Xalan 2.x wrapper solution.</p>
   * @author Shane Curcuru
   * @version $Id: XalanWrapper.java,v 1.1 2000/11/01 23:27:00 curcuru Exp $
   */
  public class XalanWrapper extends ProcessorWrapper
  {
  
      /** No-op Ctor for the Xalan-J 1.x wrapper. */
      public XalanWrapper(){}
  
      /** Reference to current processor - Xalan flavor - convenience method. */
      protected org.apache.xalan.xslt.XSLTProcessor processor = null;
  
      /**
       * NEEDSDOC Method getXalanProcessor 
       *
       *
       * NEEDSDOC (getXalanProcessor) @return
       */
      public org.apache.xalan.xslt.XSLTProcessor getXalanProcessor()
      {
          return (processor);
      }
  
      /**
       * Construct a processor of the appropriate flavor, optionally specifying a liaison.
       * <p>May throw exceptions related to the creating of a new processor.</p>
       * <ul>Xalan supports two liaisons:
       * <li>org.apache.xalan.xpath.dtm.DTMLiaison</li>
       * <li>org.apache.xalan.xpath.xdom.XercesLiaison</li>
       * </ul>
       * @param liaisonClassName [optional] if non-null & non-blank, classname of an XML liaison
       * @return (Object)processor as a side effect; null if error
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public Object createNewProcessor(String liaisonClassName)
              throws java.lang.Exception  // Cover all exception cases
      {
  
          // Cleanup any prior objects 
          cleanup();
  
          if (liaisonClassName != null)
              processor =
                  org.apache.xalan.xslt.XSLTProcessorFactory.getProcessorUsingLiaisonName(
                      liaisonClassName);
          else
              processor =
                  org.apache.xalan.xslt.XSLTProcessorFactory.getProcessor();
  
          p = (Object) processor;
  
          // Return here; will be null if error or exception raised
          return (p);
      }
  
      /**
       * Get a description of the wrappered processor.
       * @return info-string describing the processor and possibly it's common options
       */
      public String getDescription()
      {
  
          if (processor == null)
          {
              return ("ERROR: must call createNewProcessor first from: "
                      + getDescription());
          }
          else
          {
              StringBuffer buf = new StringBuffer(XSLProcessorVersion.PRODUCT);
  
              buf.append(";");
              buf.append(XSLProcessorVersion.LANGUAGE);
              buf.append(";");
              buf.append(XSLProcessorVersion.S_VERSION);
              buf.append(";");
              buf.append(processor.getXMLProcessorLiaison());
  
              return buf.toString();
          }
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * <p>May throw exceptions related to asking the processor to perform the process.</p>
       * <p>Attempts to ask each processor to accomplish the task in the simplest
       * and most obvious manner.  Often copied from various processor's samples.</p>
       * @param xmlSource name of source XML file
       * @param xslStylesheet name of stylesheet XSL file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(
              String xmlSource, String xslStylesheet, String resultFile)
                  throws java.lang.Exception  // Cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create xalan-specific sources
          org.apache.xalan.xslt.XSLTInputSource xml =
              new org.apache.xalan.xslt.XSLTInputSource(xmlSource);
          org.apache.xalan.xslt.XSLTInputSource xsl =
              new org.apache.xalan.xslt.XSLTInputSource(xslStylesheet);
          org.apache.xalan.xslt.XSLTResultTarget result =
              new org.apache.xalan.xslt.XSLTResultTarget(resultFile);
  
          // Begin timing the whole process
          startTime = System.currentTimeMillis();
  
          processor.process(xml, xsl, result);
  
          endTime = System.currentTimeMillis();
  
          return (endTime - startTime);
      }
  
      /**
       * Preprocess a stylesheet and set it into the processor, based on string inputs.
       * @param xslStylesheet name of stylesheet XSL file
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long preProcessStylesheet(String xslStylesheet)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create xalan-specific sources
          org.apache.xalan.xslt.StylesheetRoot sRoot;
          org.apache.xalan.xslt.XSLTInputSource xsl =
              new org.apache.xalan.xslt.XSLTInputSource(xslStylesheet);
  
          // Begin timing loading the stylesheet
          startTime = System.currentTimeMillis();
          sRoot = processor.processStylesheet(xsl);  // side effect: also sets the stylesheet
          endTime = System.currentTimeMillis();
          stylesheetReady = true;
  
          return (endTime - startTime);
      }
  
      /**
       * Process the xmlSource using the xslStylesheet to produce the resultFile.
       * @param xmlSource name of source XML file
       * @param resultFile name of output file, presumably XML
       * @return milliseconds process time took or ProcessorWrapper.ERROR
       * @exception Exception may be thrown by underlying operation
       *
       * @throws java.lang.Exception
       */
      public long processToFile(String xmlSource, String resultFile)
              throws java.lang.Exception  // should cover all exception cases
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          // Ensure we (apparently) have already processed a stylesheet
          if (!stylesheetReady)
              throw new java.lang.IllegalStateException(
                  "You must call preProcessStylesheet first!");
  
          // Declare variables ahead of time to minimize latency
          long startTime = 0;
          long endTime = 0;
  
          // Create xalan-specific sources
          org.apache.xalan.xslt.XSLTInputSource xml =
              new org.apache.xalan.xslt.XSLTInputSource(xmlSource);
          org.apache.xalan.xslt.XSLTResultTarget result =
              new org.apache.xalan.xslt.XSLTResultTarget(resultFile);
  
          // Begin timing the whole process
          startTime = System.currentTimeMillis();
  
          processor.process(xml, null, result);
  
          endTime = System.currentTimeMillis();
  
          return (endTime - startTime);
      }
  
      /**
       * Reset the state.
       * <p>This needs to be called after a process() call is invoked,
       * if the processor is to be used again.</p>
       */
      public void reset()
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.reset();
  
          stylesheetReady = false;
      }
  
      /**
       * Set diagnostics output PrintWriter.  
       *
       * NEEDSDOC @param pw
       */
      public void setDiagnosticsOutput(java.io.PrintWriter pw)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.setDiagnosticsOutput(pw);
      }
  
      /**
       * Set the indent level of the processor.  
       *
       * NEEDSDOC @param i
       */
      public void setIndent(int i)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.getXMLProcessorLiaison().setIndent(i);
      }
  
      /**
       * Set a String name=value param in the processor, if applicable.  
       *
       * NEEDSDOC @param key
       * NEEDSDOC @param expression
       */
      public void setStylesheetParam(String key, String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.setStylesheetParam(key, expression);
      }
  
      /**
       * Set a String namespace:name=value param in the processor, if applicable.
       * @todo Needs Implementation: namespace is currently <b>ignored!</b>
       * @param namespace of the param
       * @param key name of the param
       * @param expression value of the param
       */
      public void setStylesheetParam(String namespace, String key,
                                     String expression)
      {
  
          // Ensure we (apparently) have some processor
          if (processor == null)
              throw new java.lang.IllegalStateException(
                  "You must call createNewProcessor first!");
  
          processor.setStylesheetParam(key, expression);
      }
  
      /** Worker method to cleanup any internal state. */
      private void cleanup()
      {
  
          processor = null;
          p = null;
          stylesheetReady = false;
      }
  }  // end of class XalanWrapper
  
  
  
  
  1.1                  xml-xalan/test/java/src/org/apache/qetest/xslwrapper/package.html
  
  Index: package.html
  ===================================================================
  <html>
    <title>XSL-TEST xslwrapper package.</title>
    <body>
      <p>This package provides an abstraction layer for XSLT processors through {@link ProcessorWrapper}.</p>
      <dl>
        <dt><b>Author: </b></dt><dd><a href="mailto:shane_curcuru@lotus.com">Shane_Curcuru@lotus.com</a></dd>
        <dt><b>Program(s) Under Test: </b></dt>
        <dd><a href="http://xml.apache.org/xalan-j" target="_top">Xalan-J 2.x XSLT Processor</a></dd>
        <dd><a href="http://xml.apache.org/xalan" target="_top">Xalan-J 1.x XSLT Processor</a></dd>
      </dl>
      <p>This package also provides a number of implementations 
      for varying XSLT processors, including Xalan-J 1.x, 
      Xalan-J 2.x using the TRAX interface, and others.<p>
      <p>This allows many test classes to eliminate dependencies 
      on a specific processor and be able to plug-and-play 
      different processors to compare test results.<p>
      <p>See ProcessorWrapper.properties for info on how a -flavor 
      can get mapped into a specific implementation and 'type' 
      of a ProcessorWrapper.</p>
    </body>
  </html>