blob: 8747fff9b669f456269ff7bc23af4dfc5275baa5 [file]
/*
* 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$
*/
package org.apache.qetest.xalanj2;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Properties;
import javax.xml.transform.Transformer;
import org.apache.xalan.templates.ElemLiteralResult;
import org.apache.xalan.templates.ElemTemplate;
import org.apache.xalan.templates.ElemTemplateElement;
import org.apache.xalan.templates.ElemTextLiteral;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xml.dtm.ref.DTMNodeProxy;
import org.apache.xpath.XPath;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Static utility for dumping info about common Xalan objects.
* Cheap-o string representations of some common properties
* of various objects; supports some formatting and encapsulation
* but could use improvements.
* Note: currently purposefully outputs plain strings, not
* any XML-like elements, so it's easier for other XML-like
* logging utilities to output our data without escaping, etc.
*
* @author shane_curcuru@lotus.com
* @version $Id$
*/
public abstract class XalanDumper
{
// abstract class cannot be instantiated
/** Simple text constants: for items that are null. */
public static final String NULL = "NULL";
/** Simple text constants: separator between items. */
public static final String SEP = ";";
/** Simple text constants: beginning a block of items. */
public static final String LBRACKET = "[";
/** Simple text constants: ending a block of items. */
public static final String RBRACKET = "]";
/** Simple text constants: line number. */
public static final String LNUM = "L";
/** Simple text constants: column number. */
public static final String CNUM = "C";
/** Simple output formats: default behavior. */
public static final int DUMP_DEFAULT = 0;
/** Simple output formats: verbose: extra output. */
public static final int DUMP_VERBOSE = 1;
/** Simple output formats: a contained object. */
public static final int DUMP_CONTAINED = 2;
/** Simple output formats: don't close block. */
public static final int DUMP_NOCLOSE = 4;
/** Simple output formats: don't include id's or other items likely to change. */
public static final int DUMP_NOIDS = 8;
/** Cheap-o recursion marker: already recursing in Nodes/NodeLists. */
public static final int DUMP_NODE_RECURSION = 16;
/**
* Return String describing an ElemTemplateElement.
*
* @param elem the ElemTemplateElement to print info of
* @param dumpLevel what format/how much to dump
*/
public static String dump(ElemTemplateElement elem, int dumpLevel)
{
StringBuffer buf = new StringBuffer("ElemTemplateElement" + LBRACKET);
if (null == elem)
return buf.toString() + NULL + RBRACKET;
// Note for user if it's an LRE or an xsl element
if(elem instanceof ElemLiteralResult)
buf.append("LRE:");
else
buf.append("xsl:");
buf.append(elem.getNodeName());
buf.append(SEP + LNUM + elem.getLineNumber());
buf.append(SEP + CNUM + elem.getColumnNumber());
buf.append(SEP + "getLength=" + elem.getLength());
if (DUMP_VERBOSE == (dumpLevel & DUMP_VERBOSE))
{
// Only include systemIds (which are long) if verbose
buf.append(SEP + "getSystemId=" + elem.getSystemId());
buf.append(SEP + "getStylesheet=" + elem.getStylesheet().getSystemId());
}
try
{
Class cl = ((Object)elem).getClass();
Method getSelect = cl.getMethod("getSelect", null);
if(null != getSelect)
{
buf.append(SEP + "select=");
XPath xpath = (XPath)getSelect.invoke(elem, null);
buf.append(xpath.getPatternString());
}
}
catch(Exception e)
{
// no-op: just don't put in the select info for these items
}
if (DUMP_NOCLOSE == (dumpLevel & DUMP_NOCLOSE))
return buf.toString();
else
return buf.toString() + RBRACKET;
}
/**
* Return String describing an ElemTextLiteral.
*
* @param elem the ElemTextLiteral to print info of
* @param dumpLevel what format/how much to dump
*/
public static String dump(ElemTextLiteral elem, int dumpLevel)
{
StringBuffer buf = new StringBuffer("ElemTextLiteral" + LBRACKET);
if (null == elem)
return buf.toString() + NULL + RBRACKET;
buf.append(elem.getNodeName()); // I don't think this ever changes from #Text?
buf.append(SEP + LNUM + elem.getLineNumber());
buf.append(SEP + CNUM + elem.getColumnNumber());
String chars = new String(elem.getChars(), 0, elem.getChars().length);
buf.append(SEP + "chars=" + chars.trim());
if (DUMP_NOCLOSE == (dumpLevel & DUMP_NOCLOSE))
return buf.toString();
else
return buf.toString() + RBRACKET;
}
/**
* Return String describing an ElemTemplate.
*
* @param elem the ElemTemplate to print info of
* @param dumpLevel what format/how much to dump
*/
public static String dump(ElemTemplate elem, int dumpLevel)
{
StringBuffer buf = new StringBuffer("ElemTemplate" + LBRACKET);
if (null == elem)
return buf.toString() + NULL + RBRACKET;
buf.append("xsl:" + elem.getNodeName());
buf.append(SEP + LNUM + elem.getLineNumber());
buf.append(SEP + CNUM + elem.getColumnNumber());
if (DUMP_VERBOSE == (dumpLevel & DUMP_VERBOSE))
{
// Only include systemIds (which are long) if verbose
buf.append(SEP + "getSystemId=" + elem.getSystemId());
buf.append(SEP + "getStylesheet=" + elem.getStylesheet().getSystemId());
}
try
{
Class cl = ((Object)elem).getClass();
Method getSelect = cl.getMethod("getSelect", null);
if(null != getSelect)
{
buf.append(SEP + "select=");
XPath xpath = (XPath)getSelect.invoke(elem, null);
buf.append(xpath.getPatternString());
}
}
catch(Exception e)
{
// no-op: just don't put in the select info for these items
}
if (null != elem.getMatch())
buf.append(SEP + "match=" + elem.getMatch().getPatternString());
if (null != elem.getName())
buf.append(SEP + "name=" + elem.getName());
if (null != elem.getMode())
buf.append(SEP + "mode=" + elem.getMode());
buf.append(SEP + "priority=" + elem.getPriority());
if (DUMP_NOCLOSE == (dumpLevel & DUMP_NOCLOSE))
return buf.toString();
else
return buf.toString() + RBRACKET;
}
/**
* Return String describing a Transformer.
* Currently just returns info about a get selected public
* getter methods from a Transformer.
* Only really useful when it can do instanceof TransformerImpl
* to return custom info about Xalan
*
* @param t the Transformer to print info of
* @param dumpLevel what format/how much to dump
*/
public static String dump(Transformer trans, int dumpLevel)
{
if (null == trans)
return "Transformer" + LBRACKET + NULL + RBRACKET;
StringBuffer buf = new StringBuffer();
StringWriter sw = new StringWriter();
Properties p = trans.getOutputProperties();
if (null != p)
{
p.list(new PrintWriter(sw));
buf.append("getOutputProperties{" + sw.toString() + "}");
}
if (trans instanceof TransformerImpl)
{
final TransformerImpl timpl = (TransformerImpl)trans;
// We have a Xalan-J 2.x basic transformer
buf.append("getBaseURLOfSource=" + timpl.getBaseURLOfSource() + SEP);
// Result getOutputTarget()
// ContentHandler getInputContentHandler(boolean doDocFrag)
// DeclHandler getInputDeclHandler()
// LexicalHandler getInputLexicalHandler()
// OutputProperties getOutputFormat()
// Serializer getSerializer()
// ElemTemplateElement getCurrentElement()
// int getCurrentNode()
// ElemTemplate getCurrentTemplate()
// ElemTemplate getMatchedTemplate()
// int getMatchedNode()
// DTMIterator getContextNodeList()
// StylesheetRoot getStylesheet()
// int getRecursionLimit()
buf.append("getMode=" + timpl.getMode() + SEP);
}
return "Transformer" + LBRACKET
+ buf.toString() + RBRACKET;
}
/**
* Return String describing a Node.
* Currently just returns TracerEvent.printNode(n)
*
* @param n the Node to print info of
* @param dumpLevel what format/how much to dump
*/
public static String dump(Node n, int dumpLevel)
{
if (null == n)
return "Node" + LBRACKET + NULL + RBRACKET;
// Copied but modified from TracerEvent; ditch hashCode
StringBuffer buf = new StringBuffer();
if (n instanceof Element)
{
buf.append(n.getNodeName());
Node c = n.getFirstChild();
while (null != c)
{
if (c instanceof Attr)
{
buf.append(dump(c, dumpLevel | DUMP_NODE_RECURSION) + " ");
}
c = c.getNextSibling();
}
}
else
{
if (n instanceof Attr)
{
buf.append(n.getNodeName() + "=" + n.getNodeValue());
}
else
{
buf.append(n.getNodeName());
}
}
// If we're already recursing, don't bother printing out 'Node' again
if (DUMP_NODE_RECURSION == (dumpLevel & DUMP_NODE_RECURSION))
return LBRACKET + buf.toString() + RBRACKET;
else
return "Node" + LBRACKET + buf.toString() + RBRACKET;
}
/**
* Return String describing a DTMNodeProxy.
* This is the Xalan-J 2.x internal wrapper for Nodes.
*
* @param n the DTMNodeProxy to print info of
* @param dumpLevel what format/how much to dump
*/
public static String dump(DTMNodeProxy n, int dumpLevel)
{
if (null == n)
return "DTMNodeProxy" + LBRACKET + NULL + RBRACKET;
// Copied but modified from TracerEvent; ditch hashCode
StringBuffer buf = new StringBuffer();
if (DUMP_NOIDS != (dumpLevel & DUMP_NOIDS))
{
// Only include the DTM node number if asked
buf.append(n.getDTMNodeNumber());
}
if (n instanceof Element)
{
buf.append(n.getNodeName());
// Also output first x chars of value
buf.append(substr(n.getNodeValue()));
DTMNodeProxy c = (DTMNodeProxy)n.getFirstChild();
while (null != c)
{
buf.append(dump(c, dumpLevel | DUMP_NODE_RECURSION) + " ");
c = (DTMNodeProxy)c.getNextSibling();
}
}
else
{
if (n instanceof Attr)
{
buf.append(n.getNodeName() + "=" + n.getNodeValue());
}
else
{
buf.append(n.getNodeName());
// Also output first x chars of value
buf.append(substr(n.getNodeValue()));
}
}
// If we're already recursing, don't bother printing out 'Node' again
if (DUMP_NODE_RECURSION == (dumpLevel & DUMP_NODE_RECURSION))
return LBRACKET + buf.toString() + RBRACKET;
else
return "DTMNodeProxy" + LBRACKET + buf.toString() + RBRACKET;
}
/** Cheap-o worker method to substring a string. */
public static int MAX_SUBSTR = 8;
/** Cheap-o worker method to substring a string. */
public static String SUBSTR_PREFIX = ":";
/** Cheap-o worker method to substring a string. */
protected static String substr(String s)
{
if (null == s)
return "";
return SUBSTR_PREFIX + s.substring(0, Math.min(s.length(), MAX_SUBSTR));
}
/**
* Return String describing a NodeList.
* Currently just returns TracerEvent.printNode(n)
*
* @param nl the NodeList to print info of
* @param dumpLevel what format/how much to dump
*/
public static String dump(NodeList nl, int dumpLevel)
{
if (null == nl)
return "NodeList" + LBRACKET + NULL + RBRACKET;
StringBuffer buf = new StringBuffer();
int len = nl.getLength() - 1;
int i = 0;
while (i < len)
{
Node n = nl.item(i);
if (null != n)
{
buf.append(dump(n, dumpLevel) + ", ");
}
++i;
}
if (i == len)
{
Node n = nl.item(len);
if (null != n)
{
buf.append(dump(n, dumpLevel));
}
}
return "NodeList" + LBRACKET
+ buf.toString() + RBRACKET;
}
/**
* Print String type of node.
* @param n Node to report type of
* @return String type name
*/
public static String dumpNodeType(Node n)
{
if (null == n)
return NULL;
switch (n.getNodeType())
{
case Node.DOCUMENT_NODE :
return "DOCUMENT_NODE";
case Node.ELEMENT_NODE :
return "ELEMENT_NODE";
case Node.CDATA_SECTION_NODE :
return "CDATA_SECTION_NODE";
case Node.ENTITY_REFERENCE_NODE :
return "ENTITY_REFERENCE_NODE";
case Node.ATTRIBUTE_NODE :
return "ATTRIBUTE_NODE";
case Node.COMMENT_NODE :
return "COMMENT_NODE";
case Node.ENTITY_NODE :
return "ENTITY_NODE";
case Node.NOTATION_NODE :
return "NOTATION_NODE";
case Node.PROCESSING_INSTRUCTION_NODE :
return "PROCESSING_INSTRUCTION_NODE";
case Node.TEXT_NODE :
return "TEXT_NODE";
default :
return "UNKNOWN_NODE";
}
} // end of dumpNodeType()
}