package client.net.sf.saxon.ce.trace;
//import java.io.PrintStream;
import java.util.Iterator;
import java.util.logging.Logger;
import client.net.sf.saxon.ce.Controller;
import client.net.sf.saxon.ce.Version;
import client.net.sf.saxon.ce.expr.XPathContext;
import client.net.sf.saxon.ce.expr.parser.CodeInjector;
import client.net.sf.saxon.ce.lib.GenericLogHandler;
import client.net.sf.saxon.ce.lib.StandardErrorListener;
import client.net.sf.saxon.ce.lib.TraceListener;
import client.net.sf.saxon.ce.om.Item;
import client.net.sf.saxon.ce.om.NodeInfo;
import client.net.sf.saxon.ce.om.StandardNames;
import client.net.sf.saxon.ce.om.StructuredQName;
import client.net.sf.saxon.ce.tree.util.FastStringBuffer;
import client.net.sf.saxon.ce.tree.util.Navigator;
import client.net.sf.saxon.ce.value.Whitespace;
/**
* This is the standard trace listener equivalent to that used when the -T option is specified on the command line.
* There are two variants, represented by subclasses: one for XSLT, and one for XQuery. The two variants
* differ in that they present the trace output in terms of constructs used in the relevant host language.
*/
public abstract class AbstractTraceListener implements TraceListener {
private int indent = 0;
//private PrintStream out = System.err;
private Logger logger = Logger.getLogger("Trace");
/*@NotNull*/ private static StringBuffer spaceBuffer = new StringBuffer(" ");
/**
* Get the associated CodeInjector to be used at compile time to generate the tracing calls
*/
public CodeInjector getCodeInjector() {
return new TraceCodeInjector();
}
private long t_total;
/**
* Called at start
*/
public void open() {
prevModule = "";
t_total = System.currentTimeMillis();
logger.finest("<trace " +
"saxon-version=\"" + Version.getProductVersion()+ "\" " +
getOpeningAttributes() + '>');
indent++;
}
protected abstract String getOpeningAttributes();
/**
* Called at end
*/
public void close() {
t_total = t_total - System.currentTimeMillis();
indent--;
logger.finest("</trace>");
GenericLogHandler.dumpTrace();
}
public void terminate() {
indent = 0;
// xml trace will have have been dumped already
}
/**
* Called when an instruction in the stylesheet gets processed
*/
public void enterChooseItem(String test) {
if (test == "") {
logger.finest(AbstractTraceListener.spaces(indent) + "<xsl:otherwise>");
} else {
logger.finest(AbstractTraceListener.spaces(indent) + "<xsl:when test=\"" + escape(test) + "\">");
}
indent++;
}
public void leaveChooseItem(String test) {
if (test == "") {
logger.finest(AbstractTraceListener.spaces(indent) + "</xsl:otherwise>");
} else {
logger.finest(AbstractTraceListener.spaces(indent) + "</xsl:when>");
}
indent--;
}
private static String prevModule = "";
public void enter(/*@NotNull*/ InstructionInfo info, XPathContext context) {
int infotype = info.getConstructType();
StructuredQName qName = info.getObjectName();
String tag = tag(infotype);
if (tag==null) {
// this TraceListener ignores some events to reduce the volume of output
return;
}
String file = StandardErrorListener.abbreviatePath(info.getSystemId());
String msg = AbstractTraceListener.spaces(indent) + '<' + tag;
String name = (String)info.getProperty("name");
if (name!=null) {
msg += " name=\"" + escape(name) + '"';
} else if (qName != null) {
msg += " name=\"" + escape(qName.getDisplayName()) + '"';
}
Iterator props = info.getProperties();
while (props.hasNext()) {
String prop = (String)props.next();
Object val = info.getProperty(prop);
if (prop.startsWith("{")) {
// It's a QName in Clark notation: we'll strip off the namespace
int rcurly = prop.indexOf('}');
if (rcurly > 0) {
prop = prop.substring(rcurly+1);
}
}
if (val != null && !prop.equals("name") && !prop.equals("expression")) {
msg += ' ' + prop + "=\"" + escape(val.toString()) + '"';
}
}
String newModule = escape(file);
if (!newModule.equals(prevModule)) {
prevModule = newModule;
msg += " module=\"" + newModule + "\">";
} else {
msg += ">";
}
logger.finest(msg);
indent++;
}
/**
* Escape a string for XML output (in an attribute delimited by double quotes).
* This method also collapses whitespace (since the value may be an XPath expression that
* was originally written over several lines).
*/
public String escape(/*@Nullable*/ String in) {
if (in==null) {
return "";
}
CharSequence collapsed = Whitespace.collapseWhitespace(in);
FastStringBuffer sb = new FastStringBuffer(collapsed.length() + 10);
for (int i=0; i<collapsed.length(); i++) {
char c = collapsed.charAt(i);
if (c=='<') {
sb.append("<");
} else if (c=='>') {
sb.append(">");
} else if (c=='&') {
sb.append("&");
} else if (c=='\"') {
sb.append(""");
} else if (c=='\n') {
sb.append("
");
} else if (c=='\r') {
sb.append("
");
} else if (c=='\t') {
sb.append(" ");
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Called after an instruction of the stylesheet got processed
*/
public void leave(/*@NotNull*/ InstructionInfo info) {
int infotype = info.getConstructType();
String tag = tag(infotype);
if (tag==null) {
// this TraceListener ignores some events to reduce the volume of output
return;
}
indent--;
logger.finest(AbstractTraceListener.spaces(indent) + "</" + tag + '>');
}
protected abstract String tag(int construct);
/**
* Called when an item becomes the context item
*/
public void startCurrentItem(Item item) {
if (item instanceof NodeInfo) {
NodeInfo curr = (NodeInfo) item;
logger.finest(AbstractTraceListener.spaces(indent) + "<source node=\"" + Navigator.getPath(curr)
// + "\" line=\"" + curr.getLineNumber()
+ "\" file=\"" + escape(StandardErrorListener.abbreviatePath(curr.getSystemId()))
+ "\">");
}
indent++;
}
/**
* Called after a node of the source tree got processed
*/
public void endCurrentItem(Item item) {
indent--;
if (item instanceof NodeInfo) {
NodeInfo curr = (NodeInfo) item;
logger.finest(AbstractTraceListener.spaces(indent) + "</source><!-- " +
Navigator.getPath(curr) + " -->");
}
}
/**
* Get n spaces
*/
private static String spaces(int n) {
while (spaceBuffer.length() < n) {
spaceBuffer.append(AbstractTraceListener.spaceBuffer);
}
return spaceBuffer.substring(0, n);
}
/**
* Set the output destination (default is System.err)
* @param stream the output destination for tracing output
*/
// public void setOutputDestination(PrintStream stream) {
// out = stream;
// }
/**
* Get the output destination
*/
// public PrintStream getOutputDestination() {
// return out;
// }
}
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.