blob: 6c4aa73be9e4c8b1b164e62e3817355e759a69ae [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$
*/
/*
*
* TransformStateTestlet.java
*
*/
package org.apache.qetest.xalanj2;
import java.util.Hashtable;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.qetest.Datalet;
import org.apache.qetest.Logger;
import org.apache.qetest.LoggingHandler;
import org.apache.qetest.QetestUtils;
import org.apache.qetest.TestletImpl;
import org.apache.qetest.XMLFileLogger;
import org.apache.xalan.templates.ElemTemplate;
import org.apache.xalan.templates.ElemTemplateElement;
import org.apache.xalan.transformer.TransformState;
import org.apache.xalan.transformer.TransformerClient;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
/**
* Testlet for testing TransformState of a stylesheet.
*
* In progress - data-driven tests for tooling API's.
* Currently uses cheap-o validation method
*
* @author Shane_Curcuru@lotus.com
* @version $Id$
*/
public class TransformStateTestlet extends TestletImpl
implements ContentHandler, TransformerClient
{
// Initialize our classname for TestletImpl's main() method
static { thisClassName = "org.apache.qetest.xsl.TransformStateTestlet"; }
// Initialize our defaultDatalet
{ defaultDatalet = (Datalet)new TransformStateDatalet(); }
/**
* Class-wide copy of TransformStateDatalet.
* This is used in execute() and in various worker methods
* underneath the ContentHandler interface.
*/
protected TransformStateDatalet tsDatalet = null;
/**
* Accesor method for a brief description of this test.
*
* @return String describing what this TransformStateTestlet does.
*/
public String getDescription()
{
return "TransformStateTestlet";
}
/**
* Run this TransformStateTestlet: execute it's test and return.
*
* @param Datalet to use as data point for the test.
*/
public void execute(Datalet d)
{
try
{
tsDatalet = (TransformStateDatalet)d;
}
catch (ClassCastException e)
{
logger.checkErr("Datalet provided is not a TransformStateDatalet; cannot continue with " + d);
return;
}
logger.logMsg(Logger.STATUSMSG, "About to test: "
+ (null == tsDatalet.inputName
? tsDatalet.xmlName
: tsDatalet.inputName));
try
{
// Perform the transform
TransformerFactory factory = TransformerFactory.newInstance();
logger.logMsg(Logger.TRACEMSG, "---- About to newTransformer " + QetestUtils.filenameToURL(tsDatalet.inputName));
Transformer transformer = factory.newTransformer(new StreamSource(QetestUtils.filenameToURL(tsDatalet.inputName)));
logger.logMsg(Logger.TRACEMSG, "---- About to transform " + QetestUtils.filenameToURL(tsDatalet.xmlName) + " into: SAXResult(this-no disk output)");
// Note most validation happens here: we get ContentHandler
// callbacks from being in the SAXResult, and that's where
// we do our validation
transformer.transform(new StreamSource(QetestUtils.filenameToURL(tsDatalet.xmlName)),
new SAXResult(this)); // use us to handle result
logger.logMsg(Logger.INFOMSG, "---- Afterwards, this.transformState=" + transformState);
}
catch (Throwable t)
{
// Put the logThrowable first, so it appears before
// the Fail record, and gets color-coded
logger.logThrowable(Logger.ERRORMSG, t, getDescription() + " " + tsDatalet.getDescription());
logger.checkFail(getDescription() + " " + tsDatalet.getDescription()
+ " threw: " + t.toString());
return;
}
}
////////////////// partially Implement LoggingHandler //////////////////
/** Cheap-o string representation of last event we got. */
protected String lastItem = LoggingHandler.NOTHING_HANDLED;
/**
* Accessor for string representation of last event we got.
* @param s string to set
*/
protected void setLastItem(String s)
{
lastItem = s;
}
/**
* Accessor for string representation of last event we got.
* @return last event string we had
*/
public String getLast()
{
return lastItem;
}
/**
* Worker routine to validate a TransformState based on an event/value.
* Note: this may not be threadsafe!
* //@todo actually add validation code - just logs out now
* @param ts TransformState to validate, if null, just logs it
* @param event our String constant of START_ELEMENT, etc.
* @param value any String value of the current event
*/
protected void validateTransformState(TransformState ts, String event, String value)
{
if(null == transformState)
{
// We should never have a null TransformState since the
// transformer should have always filled it in
logger.checkErr("validateTransformState(ts-NULL!, " + event + ")=" + value);
return;
}
logTransformStateDump(logger, Logger.INFOMSG, ts, event, value);
// Cheap-o validation: only validate items on column 99
if (99 == ts.getCurrentElement().getColumnNumber())
{
int line = ts.getCurrentElement().getLineNumber();
// Get cheap-o validation from the datalet for this line..
String exp = (String)tsDatalet.validate99.get(line + ".current.name");
// .. If there's an expected value for this line's property..
if (null != exp)
// .. Then check if it's equal and report pass/fail
checkString(ts.getCurrentTemplate().getName().toString(), exp,
"Validate L" + line + "C99 .current.name");
exp = (String)tsDatalet.validate99.get(line + ".current.match");
if (null != exp)
checkString(ts.getCurrentTemplate().getMatch().getPatternString(), exp,
"Validate L" + line + "C99 .current.match");
exp = (String)tsDatalet.validate99.get(line + ".current.mode");
if (null != exp)
checkString(ts.getCurrentTemplate().getMode().toString(), exp,
"Validate L" + line + "C99 .current.mode");
exp = (String)tsDatalet.validate99.get(line + ".matched.name");
if (null != exp)
checkString(ts.getMatchedTemplate().getName().toString(), exp,
"Validate L" + line + "C99 .matched.name");
exp = (String)tsDatalet.validate99.get(line + ".matched.match");
if (null != exp)
checkString(ts.getMatchedTemplate().getMatch().getPatternString(), exp,
"Validate L" + line + "C99 .matched.match");
exp = (String)tsDatalet.validate99.get(line + ".matched.mode");
if (null != exp)
checkString(ts.getMatchedTemplate().getMode().toString(), exp,
"Validate L" + line + "C99 .matched.mode");
}
/***********************************************
// Comment out validation using ExpectedObjects since they hang 28-Jun-01 -sc
// See if we have a matching expected state for this event
//@todo use event string as part of hashkey!!!
String marker = ExpectedTransformState.getHashKey(ts);
// Add on the event as well; see ExpectedTransformState.getHashKey()
// for why we have to do this separately
marker += ExpectedTransformState.SEP + event;
ExpectedTransformState ets = (ExpectedTransformState)tsDatalet.expectedTransformStates.get(marker);
logger.logMsg(Logger.TRACEMSG, "ETS-HACK:" + marker + "=" + ets);
if (null != ets)
{
// Ask it to validate itself as needed
synchronized(ets) // voodoo: attempt to solve hang problems
{
ExpectedObjectCheckService.check(logger, ts, ets, "Compare ExpectedTransformState of " + marker);
}
}
// Comment out validation using ExpectedObjects since they hang 28-Jun-01 -sc
***********************************************/
}
private void checkString(String act, String exp, String comment)
{
if (exp.equals(act))
logger.checkPass(comment);
else
logger.checkFail(comment + "; act(" + act + ") exp(" + exp + ")");
}
////////////////// Utility methods for TransformState //////////////////
/**
* Utility method to dump data from TransformState.
* @return String describing various bits of the state
*/
protected void logTransformStateDump(Logger logger, int traceLoggingLevel,
TransformState ts, String event, String value)
{
String elemName = "transformStateDump";
Hashtable attrs = new Hashtable();
attrs.put("event", event);
if (null != value)
attrs.put("value", value);
attrs.put("location", "L" + ts.getCurrentElement().getLineNumber()
+ "C" + ts.getCurrentElement().getColumnNumber());
StringBuffer buf = new StringBuffer();
ElemTemplateElement elem = ts.getCurrentElement(); // may be actual or default template
buf.append(" <currentElement>"
+ XMLFileLogger.escapeString(XalanDumper.dump(elem, XalanDumper.DUMP_DEFAULT)) + "</currentElement>");
ElemTemplate currentTempl = ts.getCurrentTemplate(); // Actual current template
buf.append("\n <currentTemplate>"
+ XMLFileLogger.escapeString(XalanDumper.dump(currentTempl, XalanDumper.DUMP_DEFAULT)) + "</currentTemplate>");
ElemTemplate matchTempl = ts.getMatchedTemplate(); // Actual matched template
if (matchTempl != currentTempl)
buf.append("\n <matchedTemplate>"
+ XMLFileLogger.escapeString(XalanDumper.dump(matchTempl, XalanDumper.DUMP_DEFAULT)) + "</matchedTemplate>");
// Optimization: skip most logging when on endElement
if (!END_ELEMENT.equals(event))
{
Node n = ts.getCurrentNode(); // current context node in source tree
buf.append("\n <currentNode>"
+ XMLFileLogger.escapeString(XalanDumper.dump(n, XalanDumper.DUMP_DEFAULT)) + "</currentNode>");
Node matchedNode = ts.getMatchedNode(); // node in source matched via getMatchedTemplate
// Optimization: only output if different
if (n != matchedNode)
buf.append("\n <matchedNode>"
+ XMLFileLogger.escapeString(XalanDumper.dump(matchedNode, XalanDumper.DUMP_DEFAULT)) + "</matchedNode>");
NodeIterator contextNodeList = ts.getContextNodeList(); // current context node list
Node rootNode = contextNodeList.getRoot();
// Optimization: only output if different
if (n != rootNode)
buf.append("\n <contextNodeListGetRoot>"
+ XMLFileLogger.escapeString(XalanDumper.dump(rootNode, XalanDumper.DUMP_DEFAULT)) + "</contextNodeListGetRoot>");
Transformer transformer = ts.getTransformer(); // current transformer working
// Optimization: only dump transformer at startElement to save space
if (START_ELEMENT.equals(event))
{
buf.append("\n <transformer>"
+ XMLFileLogger.escapeString(XalanDumper.dump(transformer, XalanDumper.DUMP_DEFAULT)) + "</transformer>");
}
else
{
// Just log error case if transformer is ever null
if (null == transformer) {
buf.append("\n <transformer>"
+ "ERROR! Transformer was null!" + "</transformer>");
}
}
}
logger.logElement(traceLoggingLevel, elemName, attrs, buf.toString());
}
//-----------------------------------------------------------
//---- Implement the TransformerClient interface
//-----------------------------------------------------------
/**
* A TransformState object that we use to log state data.
* This is the equivalent of the defaultHandler, even though
* that's not really the right metaphor. This class could be
* upgraded to have both a default ContentHandler and a
* defaultTransformerClient in the future.
*/
protected TransformState transformState = null;
/**
* Implement TransformerClient.setTransformState interface.
* Pass in a reference to a TransformState object, which
* can be used during SAX ContentHandler events to obtain
* information about he state of the transformation. This
* method will be called before each startDocument event.
*
* @param ts A reference to a TransformState object
*/
public void setTransformState(TransformState ts)
{
transformState = ts;
}
//-----------------------------------------------------------
//---- Implement the ContentHandler interface
//-----------------------------------------------------------
protected final String START_ELEMENT = "startElement:";
protected final String END_ELEMENT = "endElement:";
protected final String CHARACTERS = "characters:";
// String Locator.getPublicId() null if none available
// String Locator.getPublicId() null if none available
// int Locator.getLineNumber() -1 if none available
// int Locator.getColumnNumber() -1 if none available
protected Locator ourLocator = null;
/**
* Implement ContentHandler.setDocumentLocator.
* If available, this should always be called prior to a
* startDocument event.
*/
public void setDocumentLocator (Locator locator)
{
// Note: this implies this class is !not! threadsafe
ourLocator = locator; // future use
if (null != locator)
setLastItem("setDocumentLocator.getSystemId():" + locator.getSystemId());
else
setLastItem("setDocumentLocator:NULL");
logger.logMsg(Logger.INFOMSG, getLast());
}
/** Cached TransformState object during lifetime startDocument -> endDocument. */
// Note: is this correct? Will it always be the same object?
protected TransformState docCachedTransformState = null;
/** Implement ContentHandler.startDocument. */
public void startDocument ()
throws SAXException
{
setLastItem("startDocument");
logger.logMsg(Logger.INFOMSG, getLast());
// Comment out check call since the spec'd functionality
// is very likely to change to *not* be in startDocument 19-Jun-01 -sc
// logger.check((null != transformState), true, "transformState non-null in startDocument");
logger.logMsg(Logger.STATUSMSG, "transformState in startDocument is: " + transformState);
docCachedTransformState = transformState; // see endDocument
}
/** Implement ContentHandler.endDocument. */
public void endDocument()
throws SAXException
{
setLastItem("endDocument");
logger.logMsg(Logger.INFOMSG, getLast());
// Comment out check call since the spec'd functionality
// is very likely to change to *not* be in startDocument 19-Jun-01 -sc
// logger.checkObject(docCachedTransformState, transformState,
// "transformState same in endDocument as startDocument"); // see startDocument
logger.logMsg(Logger.STATUSMSG, "transformState in endDocument is: " + transformState);
docCachedTransformState = null;
}
/** Implement ContentHandler.startPrefixMapping. */
public void startPrefixMapping (String prefix, String uri)
throws SAXException
{
setLastItem("startPrefixMapping: " + prefix + ", " + uri);
logger.logMsg(Logger.INFOMSG, getLast());
}
/** Implement ContentHandler.endPrefixMapping. */
public void endPrefixMapping (String prefix)
throws SAXException
{
setLastItem("endPrefixMapping: " + prefix);
logger.logMsg(Logger.INFOMSG, getLast());
}
/** Implement ContentHandler.startElement. */
public void startElement (String namespaceURI, String localName,
String qName, Attributes atts)
throws SAXException
{
StringBuffer buf = new StringBuffer();
buf.append(namespaceURI + ", "
+ localName + ", " + qName + ";");
int n = atts.getLength();
for(int i = 0; i < n; i++)
{
buf.append(", " + atts.getQName(i));
}
setLastItem(START_ELEMENT + buf.toString());
validateTransformState(transformState, START_ELEMENT, buf.toString());
}
/** Implement ContentHandler.endElement. */
public void endElement (String namespaceURI, String localName, String qName)
throws SAXException
{
setLastItem(END_ELEMENT + namespaceURI + ", " + localName + ", " + qName);
validateTransformState(transformState, END_ELEMENT, null);
}
/** Implement ContentHandler.characters. */
public void characters (char ch[], int start, int length)
throws SAXException
{
String s = new String(ch, start, length);
setLastItem(CHARACTERS + "\"" + s + "\"");
validateTransformState(transformState, CHARACTERS, s);
}
/** Implement ContentHandler.ignorableWhitespace. */
public void ignorableWhitespace (char ch[], int start, int length)
throws SAXException
{
setLastItem("ignorableWhitespace: len " + length);
logger.logMsg(Logger.INFOMSG, getLast());
}
/** Implement ContentHandler.processingInstruction. */
public void processingInstruction (String target, String data)
throws SAXException
{
setLastItem("processingInstruction: " + target + ", " + data);
logger.logMsg(Logger.INFOMSG, getLast());
}
/** Implement ContentHandler.skippedEntity. */
public void skippedEntity (String name)
throws SAXException
{
setLastItem("skippedEntity: " + name);
logger.logMsg(Logger.INFOMSG, getLast());
}
} // end of class TransformStateTestlet