001: package net.sf.saxon.trace;
002:
003: import net.sf.saxon.Version;
004: import net.sf.saxon.value.Value;
005: import net.sf.saxon.expr.XPathContext;
006: import net.sf.saxon.om.Item;
007: import net.sf.saxon.om.NamePool;
008: import net.sf.saxon.om.Navigator;
009: import net.sf.saxon.om.NodeInfo;
010:
011: import java.io.PrintStream;
012: import java.util.Iterator;
013:
014: /**
015: * This is the standard trace listener used when the -T option is specified on the command line.
016: * There are two variants, represented by subclasses: one for XSLT, and one for XQuery. The two variants
017: * differ in that they present the trace output in terms of constructs used in the relevant host language.
018: */
019:
020: public abstract class AbstractTraceListener implements TraceListener {
021: private int indent = 0;
022: private NamePool pool;
023: private PrintStream out = System.err;
024: private static StringBuffer spaceBuffer = new StringBuffer(
025: " ");
026:
027: /**
028: * Called at start
029: */
030:
031: public void open() {
032: out.println("<trace " + "saxon-version=\""
033: + Version.getProductVersion() + "\" "
034: + getOpeningAttributes() + '>');
035: indent++;
036: }
037:
038: protected abstract String getOpeningAttributes();
039:
040: /**
041: * Called at end
042: */
043:
044: public void close() {
045: indent--;
046: out.println("</trace>");
047: }
048:
049: /**
050: * Called when an instruction in the stylesheet gets processed
051: */
052:
053: public void enter(InstructionInfo info, XPathContext context) {
054: int infotype = info.getConstructType();
055: int objectNameCode = info.getObjectNameCode();
056: String tag = tag(infotype);
057: if (tag == null) {
058: // this TraceListener ignores some events to reduce the volume of output
059: return;
060: }
061: String file = AbstractTraceListener.truncateURI(info
062: .getSystemId());
063: pool = context.getNamePool();
064: String msg = AbstractTraceListener.spaces(indent) + '<' + tag;
065: String name = (String) info.getProperty("name");
066: if (name != null) {
067: msg += " name=\"" + escape(name) + '"';
068: } else if (objectNameCode != -1) {
069: msg += " name=\""
070: + escape(pool.getDisplayName(objectNameCode)) + '"';
071: }
072: Iterator props = info.getProperties();
073: while (props.hasNext()) {
074: String prop = (String) props.next();
075: Object val = info.getProperty(prop);
076: if (prop.startsWith("{")) {
077: // It's a QName in Clark notation: we'll strip off the namespace
078: int rcurly = prop.indexOf('}');
079: if (rcurly > 0) {
080: prop = prop.substring(rcurly + 1);
081: }
082: }
083: if (val != null && !prop.equals("name")
084: && !prop.equals("expression")) {
085: msg += ' ' + prop + "=\"" + escape(val.toString())
086: + '"';
087: }
088: }
089:
090: msg += " line=\"" + info.getLineNumber() + '"';
091:
092: int col = info.getColumnNumber();
093: if (col >= 0) {
094: msg += " column=\"" + info.getColumnNumber() + '"';
095: }
096:
097: msg += " module=\"" + escape(file) + "\">";
098: out.println(msg);
099: indent++;
100: }
101:
102: /**
103: * Escape a string for XML output (in an attribute delimited by double quotes).
104: * This method also collapses whitespace (since the value may be an XPath expression that
105: * was originally written over several lines).
106: */
107:
108: public String escape(String in) {
109: if (in == null) {
110: return "";
111: }
112: CharSequence collapsed = Value.collapseWhitespace(in);
113: StringBuffer sb = new StringBuffer(collapsed.length() + 10);
114: for (int i = 0; i < collapsed.length(); i++) {
115: char c = collapsed.charAt(i);
116: if (c == '<') {
117: sb.append("<");
118: } else if (c == '>') {
119: sb.append(">");
120: } else if (c == '&') {
121: sb.append("&");
122: } else if (c == '\"') {
123: sb.append(""");
124: } else if (c == '\n') {
125: sb.append("
");
126: } else if (c == '\r') {
127: sb.append("
");
128: } else if (c == '\t') {
129: sb.append("	");
130: } else {
131: sb.append(c);
132: }
133: }
134: return sb.toString();
135: }
136:
137: /**
138: * Called after an instruction of the stylesheet got processed
139: */
140:
141: public void leave(InstructionInfo info) {
142: int infotype = info.getConstructType();
143: String tag = tag(infotype);
144: if (tag == null) {
145: // this TraceListener ignores some events to reduce the volume of output
146: return;
147: }
148: indent--;
149: out.println(AbstractTraceListener.spaces(indent) + "</" + tag
150: + '>');
151: }
152:
153: protected abstract String tag(int construct);
154:
155: /**
156: * Called when an item becomes the context item
157: */
158:
159: public void startCurrentItem(Item item) {
160: if (item instanceof NodeInfo) {
161: NodeInfo curr = (NodeInfo) item;
162: out.println(AbstractTraceListener.spaces(indent)
163: + "<source node=\""
164: + Navigator.getPath(curr)
165: + "\" line=\""
166: + curr.getLineNumber()
167: + "\" file=\""
168: + AbstractTraceListener.truncateURI(curr
169: .getSystemId()) + "\">");
170: }
171: indent++;
172: }
173:
174: /**
175: * Called after a node of the source tree got processed
176: */
177:
178: public void endCurrentItem(Item item) {
179: indent--;
180: if (item instanceof NodeInfo) {
181: NodeInfo curr = (NodeInfo) item;
182: out.println(AbstractTraceListener.spaces(indent)
183: + "</source><!-- " + Navigator.getPath(curr)
184: + " -->");
185: }
186: }
187:
188: /**
189: * Truncate a URI to its last component
190: */
191:
192: private static String truncateURI(String uri) {
193: String file = uri;
194: if (file == null)
195: file = "";
196: while (true) {
197: int i = file.indexOf('/');
198: if (i >= 0 && i < file.length() - 6) {
199: file = file.substring(i + 1);
200: } else {
201: break;
202: }
203: }
204: return file;
205: }
206:
207: /**
208: * Get n spaces
209: */
210:
211: private static String spaces(int n) {
212: while (spaceBuffer.length() < n) {
213: spaceBuffer.append(AbstractTraceListener.spaceBuffer);
214: }
215: return AbstractTraceListener.spaceBuffer.substring(0, n);
216: }
217:
218: /**
219: * Set the output destination (default is System.err)
220: * @param stream the output destination for tracing output
221: */
222:
223: public void setOutputDestination(PrintStream stream) {
224: out = stream;
225: }
226:
227: /**
228: * Get the output destination
229: */
230:
231: public PrintStream getOutputDestination() {
232: return out;
233: }
234: }
235:
236: //
237: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
238: // you may not use this file except in compliance with the License. You may obtain a copy of the
239: // License at http://www.mozilla.org/MPL/
240: //
241: // Software distributed under the License is distributed on an "AS IS" basis,
242: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
243: // See the License for the specific language governing rights and limitations under the License.
244: //
245: // The Original Code is: all this file.
246: //
247: // The Initial Developer of the Original Code is Michael H. Kay
248: //
249: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
250: //
251: // Contributor(s): none
252: //
|