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...@apache.org on 2001/11/02 21:47:31 UTC

cvs commit: xml-xalan/test/java/src/org/apache/qetest/xsl StylesheetTestlet.java XHTComparator.java XHTFileCheckService.java

curcuru     01/11/02 12:47:31

  Modified:    test/java/src/org/apache/qetest Configurable.java
                        SimpleFileCheckService.java
               test/java/src/org/apache/qetest/xsl StylesheetTestlet.java
                        XHTComparator.java XHTFileCheckService.java
  Log:
  Implement CheckService(s).applyAttributes(Properties) interface and implementations;
  Update StylesheetTestlet to pass thru appropriate testProps to any underlying
  fileChecker that it's using;
  reorganized StylesheetTestlet to use worker methods so subclassing is simpler
  
  Revision  Changes    Path
  1.2       +20 -1     xml-xalan/test/java/src/org/apache/qetest/Configurable.java
  
  Index: Configurable.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/test/java/src/org/apache/qetest/Configurable.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Configurable.java	2001/11/02 15:03:37	1.1
  +++ Configurable.java	2001/11/02 20:47:30	1.2
  @@ -56,6 +56,7 @@
    */
   
   package org.apache.qetest;
  +import java.util.Properties;
   
   /**
    * Interface with basic configuration API's.  
  @@ -65,7 +66,7 @@
    * utility and having it pass it to any underlying objects.
    *
    * @author Shane_Curcuru@lotus.com
  - * @version $Id: Configurable.java,v 1.1 2001/11/02 15:03:37 curcuru Exp $
  + * @version $Id: Configurable.java,v 1.2 2001/11/02 20:47:30 curcuru Exp $
    */
   public interface Configurable
   {
  @@ -91,6 +92,24 @@
        */
       public void setAttribute(String name, Object value)
           throws IllegalArgumentException;
  +
  +
  +    /**
  +     * Allows the user to set specific attributes on the testing 
  +     * utility or it's underlying product object under test.
  +     * 
  +     * This method should attempt to set any applicable attributes 
  +     * found in the given attrs onto itself, and will ignore any and 
  +     * all attributes it does not recognize.  It should never 
  +     * throw exceptions.  This method may overwrite any previous 
  +     * attributes that were set.  Currently since this takes a 
  +     * Properties block you may only be able to set objects that 
  +     * are Strings, although individual implementations may 
  +     * attempt to use Hashtable.get() on only the local part.
  +     * 
  +     * @param attrs Props of various name, value attrs.
  +     */
  +    public void applyAttributes(Properties attrs);
   
   
       /**
  
  
  
  1.6       +15 -1     xml-xalan/test/java/src/org/apache/qetest/SimpleFileCheckService.java
  
  Index: SimpleFileCheckService.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/test/java/src/org/apache/qetest/SimpleFileCheckService.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- SimpleFileCheckService.java	2001/11/02 15:04:02	1.5
  +++ SimpleFileCheckService.java	2001/11/02 20:47:30	1.6
  @@ -60,11 +60,12 @@
   import java.io.BufferedReader;
   import java.io.FileReader;
   import java.io.File;
  +import java.util.Properties;
   
   /**
    * Simply does .readLine of each file into string buffers and then String.equals().
    * @author Shane_Curcuru@lotus.com
  - * @version $Id: SimpleFileCheckService.java,v 1.5 2001/11/02 15:04:02 curcuru Exp $
  + * @version $Id: SimpleFileCheckService.java,v 1.6 2001/11/02 20:47:30 curcuru Exp $
    */
   public class SimpleFileCheckService implements CheckService
   {
  @@ -212,6 +213,19 @@
        */
       public void setAttribute(String name, Object value)
           throws IllegalArgumentException
  +    {
  +        /* no-op */        
  +    }
  +
  +    /**
  +     * Allows the user to set specific attributes on the testing 
  +     * utility or it's underlying product object under test.
  +     * 
  +     * No-op; this class does not have any supported attributes.
  +     * 
  +     * @param attrs Props of various name, value attrs.
  +     */
  +    public void applyAttributes(Properties attrs)
       {
           /* no-op */        
       }
  
  
  
  1.6       +88 -80    xml-xalan/test/java/src/org/apache/qetest/xsl/StylesheetTestlet.java
  
  Index: StylesheetTestlet.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/test/java/src/org/apache/qetest/xsl/StylesheetTestlet.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- StylesheetTestlet.java	2001/08/20 13:58:38	1.5
  +++ StylesheetTestlet.java	2001/11/02 20:47:30	1.6
  @@ -89,7 +89,7 @@
    * as different processing models, like SAX, DOM or Streams).
    *
    * @author Shane_Curcuru@lotus.com
  - * @version $Id: StylesheetTestlet.java,v 1.5 2001/08/20 13:58:38 curcuru Exp $
  + * @version $Id: StylesheetTestlet.java,v 1.6 2001/11/02 20:47:30 curcuru Exp $
    */
   public class StylesheetTestlet extends TestletImpl
   {
  @@ -127,11 +127,6 @@
               logger.checkErr("Datalet provided is not a StylesheetDatalet; cannot continue with " + d);
               return;
           }
  -
  -        logger.logMsg(Logger.STATUSMSG, "About to test: " 
  -                      + (null == datalet.inputName
  -                         ? datalet.xmlName
  -                         : datalet.inputName));
           //@todo validate our Datalet - ensure it has valid 
           //  and/or existing files available.
   
  @@ -172,98 +167,111 @@
           }
           catch (Throwable t)
           {
  -            logThrowable(t, getDescription() + " newWrapper/newProcessor threw");
  +            logger.logThrowable(Logger.ERRORMSG, t, getDescription() + " newWrapper/newProcessor threw");
               logger.checkErr(getDescription() + " newWrapper/newProcessor threw: " + t.toString());
               return;
           }
   
  -        // Test our supplied input file, and compare with gold
  +        // Transform our supplied input file, and compare with gold
           try
           {
  -            // Store local copies of XSL, XML references to avoid 
  -            //  potential for changing datalet            
  -            String inputName = datalet.inputName;
  -            String xmlName = datalet.xmlName;
  -
  -            //@todo Should we log a custom logElement here instead?
  -            logger.logMsg(Logger.TRACEMSG, "executing with: inputName=" + inputName
  -                          + " xmlName=" + xmlName + " outputName=" + datalet.outputName
  -                          + " goldName=" + datalet.goldName + " flavor="  + datalet.flavor);
  -
  -            // Simply have the wrapper do all the transforming
  -            //  or processing for us - we handle either normal .xsl 
  -            //  stylesheet tests or just .xml embedded tests
  -            long retVal = 0L;
  -            if (null == datalet.inputName)
  -            {
  -                // presume it's an embedded test
  -                long [] times = transformWrapper.transformEmbedded(xmlName, datalet.outputName);
  -                retVal = times[TransformWrapper.IDX_OVERALL];
  -            }
  -            else
  -            {
  -                // presume it's a normal stylesheet test
  -                long[] times = transformWrapper.transform(xmlName, inputName, datalet.outputName);
  -                retVal = times[TransformWrapper.IDX_OVERALL];
  -            }
  -
  -            // If we get here, attempt to validate the contents of 
  -            //  the last outputFile created
  -            CheckService fileChecker = (CheckService)datalet.options.get("fileCheckerImpl");
  -            // Supply default value
  -            if (null == fileChecker)
  -                fileChecker = new XHTFileCheckService();
  -            if (Logger.PASS_RESULT
  -                != fileChecker.check(logger,
  -                                     new File(datalet.outputName), 
  -                                     new File(datalet.goldName), 
  -                                     getDescription() + " " + datalet.getDescription())
  -               )
  -            {
  -                // Log a custom element with all the file refs first
  -                // Closely related to viewResults.xsl select='fileref"
  -                //@todo check that these links are valid when base 
  -                //  paths are either relative or absolute!
  -                Hashtable attrs = new Hashtable();
  -                attrs.put("idref", (new File(datalet.inputName)).getName());
  -                attrs.put("inputName", datalet.inputName);
  -                attrs.put("xmlName", datalet.xmlName);
  -                attrs.put("outputName", datalet.outputName);
  -                attrs.put("goldName", datalet.goldName);
  -                logger.logElement(Logger.STATUSMSG, "fileref", attrs, "Conformance test file references");
  -                // No longer need to log failure reason, this kind 
  -                //  of functionality should be kept in checkServices
  -            }
  +            testDatalet(datalet, transformWrapper);
           }
  -        // Note that this class can only validate positive test 
  -        //  cases - we don't handle ExpectedExceptions
  +        // Handle any exceptions from the testing
           catch (Throwable t)
           {
  -            // Put the logThrowable first, so it appears before 
  -            //  the Fail record, and gets color-coded
  -            logThrowable(t, getDescription() + " " + datalet.getDescription());
  -            logger.checkFail(getDescription() + " " + datalet.getDescription() 
  -                             + " threw: " + t.toString());
  +            handleException(datalet, t);
               return;
           }
   	}
   
   
  -    /**
  -     * Logs out throwable.toString() and stack trace to our Logger.
  -     * //@todo Copied from Reporter; should probably be moved into Logger.
  -     * @param throwable thrown throwable/exception to log out.
  -     * @param msg description of the throwable.
  +    /** 
  +     * Worker method to actually perform the transform.  
  +     *
  +     * Logs out applicable info; attempts to perform transformation; 
  +     * and if sucessful, validates output file.
  +     *
  +     * @param datalet to test with
  +     * @param transformWrapper to have perform the transform
  +     * @throws allows any underlying exception to be thrown
        */
  -    protected void logThrowable(Throwable throwable, String msg)
  +    protected void testDatalet(StylesheetDatalet datalet, TransformWrapper transformWrapper)
  +            throws Exception
       {
  -        StringWriter sWriter = new StringWriter();
  -        sWriter.write(msg + "\n");
  +        //@todo Should we log a custom logElement here instead?
  +        logger.logMsg(Logger.TRACEMSG, "executing with: inputName=" + datalet.inputName
  +                      + " xmlName=" + datalet.xmlName + " outputName=" + datalet.outputName
  +                      + " goldName=" + datalet.goldName + " flavor="  + datalet.flavor);
  +
  +        // Simply have the wrapper do all the transforming
  +        //  or processing for us - we handle either normal .xsl 
  +        //  stylesheet tests or just .xml embedded tests
  +        long retVal = 0L;
  +        if (null == datalet.inputName)
  +        {
  +            // presume it's an embedded test
  +            long [] times = transformWrapper.transformEmbedded(datalet.xmlName, datalet.outputName);
  +            retVal = times[TransformWrapper.IDX_OVERALL];
  +        }
  +        else
  +        {
  +            // presume it's a normal stylesheet test
  +            long[] times = transformWrapper.transform(datalet.xmlName, datalet.inputName, datalet.outputName);
  +            retVal = times[TransformWrapper.IDX_OVERALL];
  +        }
  +
  +        // If we get here, attempt to validate the contents of 
  +        //  the last outputFile created
  +        CheckService fileChecker = (CheckService)datalet.options.get("fileCheckerImpl");
  +        // Supply default value
  +        if (null == fileChecker)
  +            fileChecker = new XHTFileCheckService();
  +        // Apply any testing options to the fileChecker
  +        // Note: for the overall conformance test case, this is 
  +        //  a bit inefficient, but we don't necessarily know if 
  +        //  the checkService has already been setup or not    
  +        fileChecker.applyAttributes(datalet.options);    
  +        if (Logger.PASS_RESULT
  +            != fileChecker.check(logger,
  +                                 new File(datalet.outputName), 
  +                                 new File(datalet.goldName), 
  +                                 getDescription() + " " + datalet.getDescription())
  +           )
  +        {
  +            // Log a custom element with all the file refs first
  +            // Closely related to viewResults.xsl select='fileref"
  +            //@todo check that these links are valid when base 
  +            //  paths are either relative or absolute!
  +            Hashtable attrs = new Hashtable();
  +            attrs.put("idref", (new File(datalet.inputName)).getName());
  +            attrs.put("inputName", datalet.inputName);
  +            attrs.put("xmlName", datalet.xmlName);
  +            attrs.put("outputName", datalet.outputName);
  +            attrs.put("goldName", datalet.goldName);
  +            logger.logElement(Logger.STATUSMSG, "fileref", attrs, "Conformance test file references");
  +            // No longer need to log failure reason, this kind 
  +            //  of functionality should be kept in checkServices
  +        }
  +    }
   
  -        PrintWriter pWriter = new PrintWriter(sWriter);
  -        throwable.printStackTrace(pWriter);
   
  -        logger.logArbitrary(Logger.STATUSMSG, sWriter.toString());
  +    /** 
  +     * Worker method to validate or log exceptions thrown by testDatalet.  
  +     *
  +     * Provided so subclassing is simpler; our implementation merely 
  +     * calls checkErr and logs the exception.
  +     *
  +     * @param datalet to test with
  +     * @param e Throwable that was thrown
  +     */
  +    protected void handleException(StylesheetDatalet datalet, Throwable t)
  +    {
  +        // Put the logThrowable first, so it appears before 
  +        //  the Fail record, and gets color-coded
  +        logger.logThrowable(Logger.ERRORMSG, t, getDescription() + " " + datalet.getDescription());
  +        logger.checkFail(getDescription() + " " + datalet.getDescription() 
  +                         + " threw: " + t.toString());
       }
   }  // end of class StylesheetTestlet
   
  
  
  
  1.6       +48 -16    xml-xalan/test/java/src/org/apache/qetest/xsl/XHTComparator.java
  
  Index: XHTComparator.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/test/java/src/org/apache/qetest/xsl/XHTComparator.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- XHTComparator.java	2001/11/02 15:04:03	1.5
  +++ XHTComparator.java	2001/11/02 20:47:30	1.6
  @@ -73,7 +73,7 @@
   import java.net.URL;
   import java.net.MalformedURLException;
   
  -import java.util.Hashtable;
  +import java.util.Properties;
   import java.util.StringTokenizer;
   
   // DOM imports
  @@ -111,7 +111,7 @@
    * 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.5 2001/11/02 15:04:03 curcuru Exp $
  + * @version $Id: XHTComparator.java,v 1.6 2001/11/02 20:47:30 curcuru Exp $
    */
   public class XHTComparator
   {
  @@ -208,7 +208,7 @@
        */
       public boolean compare(String goldFileName, String testFileName,
                              PrintWriter reporter, boolean[] warning,
  -                           Hashtable attributes)
  +                           Properties attributes)
       {
   
           // parse the gold doc
  @@ -652,7 +652,7 @@
        *
        * NEEDSDOC (parse) @return
        */
  -    Document parse(String filename, PrintWriter reporter, String which, Hashtable attributes)
  +    Document parse(String filename, PrintWriter reporter, String which, Properties attributes)
       {
           // Force filerefs to be URI's if needed: note this is independent of any other files
           String docURI = filename;
  @@ -667,18 +667,8 @@
           // Always set namespaces on
           dfactory.setNamespaceAware(true);
           // Set other attributes here as needed
  -        if (null != attributes)
  -        {
  -            Object tmp = attributes.get("setValidating");
  -            if (null != tmp)
  -            {
  -                if (tmp instanceof Boolean)
  -                    dfactory.setValidating(((Boolean)tmp).booleanValue());
  -                else if (tmp instanceof String)
  -                    dfactory.setValidating(new Boolean((String)tmp).booleanValue());
  -            }
  -        }
  -
  +        applyAttributes(dfactory, attributes);
  +        
           String parseType = which + PARSE_TYPE + "[xml];";
           Document doc = null;
           try
  @@ -751,5 +741,47 @@
   
           return doc;
       }  // end of parse()
  +    
  +    /**
  +     * Pass applicable attributes onto our DocumentBuilderFactory.  
  +     *
  +     * Only passes thru attributes we explicitly know about and 
  +     * are constants from XHTFileCheckService.
  +     * 
  +     * @param dbf factory to attempt to set* onto
  +     * @param attrs various attributes we should try to set
  +     */
  +    protected void applyAttributes(DocumentBuilderFactory dfactory, Properties attributes)
  +    {
  +        if ((null == attributes) || (null == dfactory))
  +            return;
  +
  +        String tmp = attributes.getProperty(XHTFileCheckService.SETVALIDATING);
  +        if (null != tmp)
  +        {
  +            dfactory.setValidating(new Boolean(tmp).booleanValue());
  +        }
  +        tmp = attributes.getProperty(XHTFileCheckService.SETIGNORINGELEMENTCONTENTWHITESPACE);
  +        if (null != tmp)
  +        {
  +            dfactory.setIgnoringElementContentWhitespace(new Boolean(tmp).booleanValue());
  +        }
  +        tmp = attributes.getProperty(XHTFileCheckService.SETEXPANDENTITYREFERENCES);
  +        if (null != tmp)
  +        {
  +            dfactory.setExpandEntityReferences(new Boolean(tmp).booleanValue());
  +        }
  +        tmp = attributes.getProperty(XHTFileCheckService.SETIGNORINGCOMMENTS);
  +        if (null != tmp)
  +        {
  +            dfactory.setIgnoringComments(new Boolean(tmp).booleanValue());
  +        }
  +        tmp = attributes.getProperty(XHTFileCheckService.SETCOALESCING);
  +        if (null != tmp)
  +        {
  +            dfactory.setCoalescing(new Boolean(tmp).booleanValue());
  +        }
  +        /* Unknown attributes are ignored! */
  +    }
   
   }
  
  
  
  1.10      +76 -10    xml-xalan/test/java/src/org/apache/qetest/xsl/XHTFileCheckService.java
  
  Index: XHTFileCheckService.java
  ===================================================================
  RCS file: /home/cvs/xml-xalan/test/java/src/org/apache/qetest/xsl/XHTFileCheckService.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- XHTFileCheckService.java	2001/11/02 15:04:03	1.9
  +++ XHTFileCheckService.java	2001/11/02 20:47:30	1.10
  @@ -69,13 +69,15 @@
   import java.io.File;
   import java.io.PrintWriter;
   import java.io.StringWriter;
  +import java.util.Enumeration;
   import java.util.Hashtable;
  +import java.util.Properties;
   
   /**
    * Uses an XML/HTML/Text diff comparator to check or diff two files.
    * @see #check(Logger logger, Object actual, Object reference, String msg, String id)
    * @author Shane_Curcuru@lotus.com
  - * @version $Id: XHTFileCheckService.java,v 1.9 2001/11/02 15:04:03 curcuru Exp $
  + * @version $Id: XHTFileCheckService.java,v 1.10 2001/11/02 20:47:30 curcuru Exp $
    */
   public class XHTFileCheckService implements CheckService
   {
  @@ -288,11 +290,34 @@
           return check(logger, actual, reference, msg, null);
       }
   
  -    /**
  -     * Whether whitespace differences will cause a fail or not.  
  +    /** 
  +     * Prefix to all attrs we understand.  
  +     * Note: design-wise, it would be better to have these constants 
  +     * in the XHTComparator class, since we know we're tightly bound 
  +     * to them anyways, and they shouldn't really be bound to us.
  +     * But for my current purposes, it's simpler to put them here 
  +     * for documentation purposes.
        */
  -    public static final String ALLOW_WHITESPACE_DIFF = "urn:XHTFileCheckService:allowWhitespaceDiff";
  +    public static final String URN_XHTFILECHECKSERVICE = "urn:XHTFileCheckService:";
  +
  +    /** Whether whitespace differences will cause a fail or not.  */
  +    public static final String ALLOW_WHITESPACE_DIFF = URN_XHTFILECHECKSERVICE + "allowWhitespaceDiff";
  +
  +    /** If we should call parser.setValidating().  */
  +    public static final String SETVALIDATING = URN_XHTFILECHECKSERVICE + "setValidating";
  +
  +    /** If we should call parser.setIgnoringElementContentWhitespace().  */
  +    public static final String SETIGNORINGELEMENTCONTENTWHITESPACE = URN_XHTFILECHECKSERVICE + "setIgnoringElementContentWhitespace";
  +
  +    /** If we should call parser.setExpandEntityReferences().  */
  +    public static final String SETEXPANDENTITYREFERENCES = URN_XHTFILECHECKSERVICE + "setExpandEntityReferences";
  +
  +    /** If we should call parser.setIgnoringComments().  */
  +    public static final String SETIGNORINGCOMMENTS = URN_XHTFILECHECKSERVICE + "setIgnoringComments";
   
  +    /** If we should call parser.setCoalescing().  */
  +    public static final String SETCOALESCING = URN_XHTFILECHECKSERVICE + "setCoalescing";
  +
       /**
        * Whether whitespace differences will cause a fail or not.  
        * setAttribute("allow-whitespace-diff", true|false)
  @@ -302,16 +327,16 @@
       protected boolean allowWhitespaceDiff = false;  // default; backwards compatible
   
       /**
  -     * Hash of parser-like attributes that have been set.  
  +     * Properties/Hash of parser-like attributes that have been set.  
        */
  -    protected Hashtable attributes = null;
  +    protected Properties attributes = null;
   
       /**
        * Allows the user to set specific attributes on the testing 
        * utility or it's underlying product object under test.
        * 
        * Supports basic JAXP DocumentBuilder attributes, plus our own 
  -     * "allow-whitespace-diff" attribute.
  +     * ALLOW_WHITESPACE_DIFF attribute.
        * 
        * @param name The name of the attribute.
        * @param value The value of the attribute.
  @@ -325,22 +350,64 @@
           // Check for our own attributes first
           if (ALLOW_WHITESPACE_DIFF.equals(name))
           {
  -            //@todo set allowWhitespaceDiff based on value here
  +            try
  +            {
  +                allowWhitespaceDiff = (new Boolean((String)value)).booleanValue();
  +            } 
  +            catch (Throwable t)
  +            {
  +                // If it's an illegal value or type, ignore it
  +            }
           }
           else
           {
               if (null == attributes)
               {
  -                attributes = new Hashtable();
  +                attributes = new Properties();
               }
               attributes.put(name, value);
           }
       }
   
       /**
  +     * Allows the user to set specific attributes on the testing 
  +     * utility or it's underlying product object under test.
  +     * 
  +     * This method should attempt to set any applicable attributes 
  +     * found in the given attrs onto itself, and will ignore any and 
  +     * all attributes it does not recognize.  It should never 
  +     * throw exceptions.  This method will overwrite any previous 
  +     * attributes that were set.
  +     * This method will only set values that are Strings findable 
  +     * by the Properties.getProperty() method.
  +     * 
  +     * Future Work: this could be optimized by simply setting our 
  +     * Properties block to default from the passed-in one, but for 
  +     * now instead it only copies over the explicit values that 
  +     * we think are applicable.
  +     * 
  +     * @param attrs Props of various name, value attrs.
  +     */
  +    public void applyAttributes(Properties attrs)
  +    {
  +        attributes = null;
  +        for (Enumeration enum = attrs.propertyNames();
  +                enum.hasMoreElements(); /* no increment portion */ )
  +        {
  +            String key = (String)enum.nextElement();
  +            if (key.startsWith(URN_XHTFILECHECKSERVICE))
  +            {
  +                setAttribute(key, attrs.getProperty(key));
  +            }
  +        }
  +    }
  +
  +    /**
        * Allows the user to retrieve specific attributes on the testing 
        * utility or it's underlying product object under test.
        *
  +     * See applyAttributes for some limitations.
  +     *
        * @param name The name of the attribute.
        * @return value of supported attributes or null if not recognized.
        * @throws IllegalArgumentException thrown if the underlying
  @@ -354,7 +421,6 @@
           if (ALLOW_WHITESPACE_DIFF.equals(name))
           {
               return new Boolean(allowWhitespaceDiff);
  -            
           }
           else if (null != attributes)
           {
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: xalan-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: xalan-cvs-help@xml.apache.org