blob: 1d5cbc60bed668b18f3adf147ac07df54695bf4f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id$
*/
/*
*
* XPathASTTestlet.java
*
*/
package org.apache.qetest.rwapi;
import org.apache.qetest.*;
import org.apache.qetest.xsl.StylesheetDatalet;
import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.NodeIterator;
import org.apache.xpath.rwapi.XPathFactory;
import org.apache.xpath.rwapi.eval.Evaluator;
import org.apache.xpath.rwapi.expression.*;
import org.apache.xpath.rwapi.impl.parser.SimpleNode;
import org.apache.xpath.rwapi.impl.parser.XPath;
import org.apache.xpath.rwapi.impl.parser.XPathTreeConstants;
import java.io.StringReader;
/**
* Testlet for testing XPath AST construction and manipulation.
*
* <p>NEEDSWORK: Crash test: little validation performed. Meant to
* let us quickly see what kinds of AST's get built from various
* match= or select= patterns in our existing test suites.</p>
*
* <p><b>NOTE:</b> Feb-03 runs only with rwapi code from xslt20 branch!</p>
*
* @author Shane_Curcuru@us.ibm.com
* @version $Id$
*/
public class XPathASTTestlet extends FileTestlet
{
// Initialize our classname for TestletImpl's main() method
static { thisClassName = "org.apache.qetest.xsl.XPathASTTestlet"; }
// Initialize our defaultDatalet
{ defaultDatalet = (Datalet)new StylesheetDatalet(); }
/**
* Accesor method for a brief description of this test.
*
* @return String describing what this XPathASTTestlet does.
*/
public String getDescription()
{
return "XPathASTTestlet";
}
/**
* Run this XPathASTTestlet: execute it's test and return.
*
* @param Datalet to use as data point for the test.
*/
public void execute(Datalet d)
{
// Ensure we have the correct kind of datalet
StylesheetDatalet datalet = null;
try
{
datalet = (StylesheetDatalet)d;
}
catch (ClassCastException e)
{
logger.checkErr("Datalet provided is not a StylesheetDatalet; cannot continue with " + d);
return;
}
// Perform any other general setup needed;
// side effect of getting needed XPath data
if (testletInit(datalet))
{
try
{
// Transform our supplied input file...
testDatalet(datalet);
// ...and compare with gold data
checkDatalet(datalet);
}
// Handle any exceptions from the testing
catch (Throwable t)
{
handleException(datalet, t);
return;
}
}
}
// Since we're looking for attrs, they're not namespaced
public static final String ATTR_URI = null;
public static final String MATCH_PATTERNS = "match";
public static final String SELECT_PATTERNS = "select";
// Store collected patterns for our use
private Vector matchpats = new Vector();
private Vector selectpats = new Vector();
/**
* Worker method to perform any pre-processing needed.
* <p>Overridden to gather XPaths from input stylesheets to test.</p>
*
* @param datalet to test with
* @return false if we should abort; true otherwise
*/
protected boolean testletInit(StylesheetDatalet datalet)
{
if (null == datalet.inputName)
{
logger.logMsg(logger.WARNINGMSG, "Datalet had null inputName for "
+ datalet.xmlName);
return false;
}
// Find and parse the datalet.inputName file to a DOM
// Search whole DOM for match attrs and save; select attrs too
Document doc;
try
{
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
dfactory.setNamespaceAware(true);
DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
doc = docBuilder.parse(new InputSource(datalet.inputName));
NodeIterator iterator = ((DocumentTraversal)doc).
createNodeIterator(
doc,
NodeFilter.SHOW_ELEMENT,
new NamedAttrFilter(ATTR_URI, MATCH_PATTERNS),
true);
Node node = iterator.nextNode();
while (null != node)
{
matchpats.add(((Element)node).getAttributeNS(ATTR_URI, MATCH_PATTERNS));
node = iterator.nextNode();
}
iterator.detach();
iterator = ((DocumentTraversal)doc).
createNodeIterator(
doc,
NodeFilter.SHOW_ELEMENT,
new NamedAttrFilter(ATTR_URI, SELECT_PATTERNS),
true);
node = iterator.nextNode();
while (null != node)
{
selectpats.add(((Element)node).getAttributeNS(ATTR_URI, SELECT_PATTERNS));
node = iterator.nextNode();
}
iterator.detach();
}
catch (Exception e)
{
logger.logThrowable(Logger.ERRORMSG, e, getCheckDescription(datalet));
return false;
}
return true;
}
/**
* Worker method to actually perform the test.
*
* <p>For each match/select, create an AST and dump. Fail if we
* get a null or throw an exception, pass otherwise.</p>
*
* @param datalet to test with
* @throws allows any underlying exception to be thrown
*/
protected void testDatalet(StylesheetDatalet datalet)
throws Exception
{
//@todo Should we log a custom logElement here instead?
logger.logMsg(Logger.TRACEMSG, "executing with: inputName=" + datalet.inputName);
// Cheap-o validation: ensure non-null, dump contents
if (null != matchpats)
{
for (Enumeration elements = matchpats.elements();
elements.hasMoreElements(); /* no increment portion */ )
{
logXPath((String)elements.nextElement(), datalet.getDescription());
}
}
if (null != selectpats)
{
for (Enumeration elements = selectpats.elements();
elements.hasMoreElements(); /* no increment portion */ )
{
logXPath((String)elements.nextElement(), datalet.getDescription());
}
}
}
/**
* Cheap-o Worker method to dump out XPath info.
*
* <p>NEEDSWORK: Overrideen to put out a default pass record.</p>
*
* @param String to construct XPath from
* @param String used as comment for where this test came from
*/
protected void logXPath(String xpStr, String desc)
{
String comment = desc + " XPATH{" + xpStr + "}";
try
{
// Construct AST, ensure non-null, and dump
XPath parser = new XPath(new StringReader(xpStr));
SimpleNode tree = parser.XPath2();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
tree.dump("|", new PrintStream(baos)); // NOTE encoding issues are not necessarily covered here!
logger.logMsg(logger.STATUSMSG, comment + " creates " + baos.toString());
Expr expr = (Expr) tree.jjtGetChild(0);
// Note: asking to abbreviate or not does not let you
// know what the original text format was. Since
// the majority of our pre-existing tests use the
// abbreviated forms, do that
String ab = expr.getString(true);
if (xpStr.equals(ab))
logger.checkPass(comment + " getString OK");
else
logger.checkFail(comment + " getString NOTOK: " + ab);
}
catch (Throwable t)
{
logger.logThrowable(Logger.ERRORMSG, t, comment);
logger.checkErr(comment
+ " threw: " + t.toString());
}
}
/**
* Worker method to validate output file with gold.
*
* <p>NEEDSWORK: Overrideen to put out a default pass record.</p>
*
* @param datalet to test with
* @throws allows any underlying exception to be thrown
*/
protected void checkDatalet(StylesheetDatalet datalet)
throws Exception
{
// NEEDSWORK: once we have 'real' validation of the
// contents of the AST, we want to update this!
logger.checkPass(getCheckDescription(datalet));
}
/**
* 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, getCheckDescription(datalet));
logger.checkErr(getCheckDescription(datalet)
+ " threw: " + t.toString());
}
/**
* Worker method to construct a description.
*
* Simply concatenates useful info to override getDescription().
*
* @param datalet to test with
* @param e Throwable that was thrown
*/
protected String getCheckDescription(StylesheetDatalet datalet)
{
return getDescription() + " "
+ datalet.getDescription();
}
/**
* Inner class for walking DOM to find select= and match= attrs.
*/
class NamedAttrFilter implements NodeFilter
{
private String namespaceURI;
private String localName;
public NamedAttrFilter(String ns, String lname)
{
namespaceURI = ns;
localName = lname;
}
/** Implement NodeFilter method. */
public short acceptNode(Node n)
{
if ("" != ((Element)n).getAttributeNS(namespaceURI, localName))
return FILTER_ACCEPT;
else
return FILTER_SKIP;
}
}
} // end of class XPathASTTestlet