001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: PrintTraceListener.java 468644 2006-10-28 06:56:42Z minchau $
020     */
021    package org.apache.xalan.trace;
022    
023    import java.lang.reflect.Constructor;
024    import java.lang.reflect.Method;
025    
026    import javax.xml.transform.SourceLocator;
027    
028    import org.apache.xalan.templates.Constants;
029    import org.apache.xalan.templates.ElemTemplate;
030    import org.apache.xalan.templates.ElemTemplateElement;
031    import org.apache.xalan.templates.ElemTextLiteral;
032    import org.apache.xml.dtm.DTM;
033    import org.apache.xml.dtm.ref.DTMNodeProxy;
034    import org.apache.xml.serializer.SerializerTrace;
035    
036    import org.w3c.dom.Node;
037    
038    /**
039     * Implementation of the TraceListener interface that
040     * prints each event to standard out as it occurs.
041     *
042     * @see org.apache.xalan.trace.TracerEvent
043     * @xsl.usage advanced
044     */
045    public class PrintTraceListener implements TraceListenerEx3
046    {
047    
048      /**
049       * Construct a trace listener.
050       *
051       * @param pw PrintWriter to use for tracing events
052       */
053      public PrintTraceListener(java.io.PrintWriter pw)
054      {
055        m_pw = pw;
056      }
057    
058      /**
059       * The print writer where the events should be written.
060       */
061      java.io.PrintWriter m_pw;
062    
063      /**
064       * This needs to be set to true if the listener is to print an event whenever a template is invoked.
065       */
066      public boolean m_traceTemplates = false;
067    
068      /**
069       * Set to true if the listener is to print events that occur as each node is 'executed' in the stylesheet.
070       */
071      public boolean m_traceElements = false;
072    
073      /**
074       * Set to true if the listener is to print information after each result-tree generation event.
075       */
076      public boolean m_traceGeneration = false;
077    
078      /**
079       * Set to true if the listener is to print information after each selection event.
080       */
081      public boolean m_traceSelection = false;
082    
083      /**
084       * Set to true if the listener is to print information after each extension event.
085       */
086      public boolean m_traceExtension = false;
087    
088      /**
089       * Print information about a TracerEvent.
090       *
091       * @param ev the trace event.
092       */
093      public void _trace(TracerEvent ev)
094      {
095    
096        switch (ev.m_styleNode.getXSLToken())
097        {
098        case Constants.ELEMNAME_TEXTLITERALRESULT :
099          if (m_traceElements)
100          {
101            m_pw.print(ev.m_styleNode.getSystemId()+ " Line #" + ev.m_styleNode.getLineNumber() + ", "
102                       + "Column #" + ev.m_styleNode.getColumnNumber() + " -- "
103                       + ev.m_styleNode.getNodeName() + ": ");
104    
105            ElemTextLiteral etl = (ElemTextLiteral) ev.m_styleNode;
106            String chars = new String(etl.getChars(), 0, etl.getChars().length);
107    
108            m_pw.println("    " + chars.trim());
109          }
110          break;
111        case Constants.ELEMNAME_TEMPLATE :
112          if (m_traceTemplates || m_traceElements)
113          {
114            ElemTemplate et = (ElemTemplate) ev.m_styleNode;
115    
116            m_pw.print(et.getSystemId()+ " Line #" + et.getLineNumber() + ", " + "Column #"
117                       + et.getColumnNumber() + ": " + et.getNodeName() + " ");
118    
119            if (null != et.getMatch())
120            {
121              m_pw.print("match='" + et.getMatch().getPatternString() + "' ");
122            }
123    
124            if (null != et.getName())
125            {
126              m_pw.print("name='" + et.getName() + "' ");
127            }
128    
129            m_pw.println();
130          }
131          break;
132        default :
133          if (m_traceElements)
134          {
135            m_pw.println(ev.m_styleNode.getSystemId()+ " Line #" + ev.m_styleNode.getLineNumber() + ", "
136                         + "Column #" + ev.m_styleNode.getColumnNumber() + ": "
137                         + ev.m_styleNode.getNodeName());
138          }
139        }
140      }
141      
142      int m_indent = 0;
143      
144      /**
145       * Print information about a TracerEvent.
146       *
147       * @param ev the trace event.
148       */
149      public void trace(TracerEvent ev)
150      {
151    //      m_traceElements = true;
152    //      m_traceTemplates = true;
153    //      
154    //      for(int i = 0; i < m_indent; i++)
155    //              m_pw.print(" ");
156    //    m_indent = m_indent+2;
157    //      m_pw.print("trace: ");
158            _trace(ev);
159      }
160      
161      /**
162       * Method that is called when the end of a trace event occurs.
163       * The method is blocking.  It must return before processing continues.
164       *
165       * @param ev the trace event.
166       */
167      public void traceEnd(TracerEvent ev)
168      {
169    //      m_traceElements = true;
170    //      m_traceTemplates = true;
171    //      
172    //      m_indent = m_indent-2;
173    //      for(int i = 0; i < m_indent; i++)
174    //              m_pw.print(" ");
175    //      m_pw.print("etrac: ");
176    //      _trace(ev);
177      }
178    
179    
180      /**
181       * Method that is called just after a select attribute has been evaluated.
182       *
183       * @param ev the generate event.
184       *
185       * @throws javax.xml.transform.TransformerException
186       */
187    public void selected(SelectionEvent ev)
188        throws javax.xml.transform.TransformerException {
189    
190        if (m_traceSelection) {
191            ElemTemplateElement ete = (ElemTemplateElement) ev.m_styleNode;
192            Node sourceNode = ev.m_sourceNode;
193    
194            SourceLocator locator = null;
195            if (sourceNode instanceof DTMNodeProxy) {
196                int nodeHandler = ((DTMNodeProxy) sourceNode).getDTMNodeNumber();
197                locator =
198                    ((DTMNodeProxy) sourceNode).getDTM().getSourceLocatorFor(
199                        nodeHandler);
200            }
201    
202            if (locator != null)
203                m_pw.println(
204                    "Selected source node '"
205                        + sourceNode.getNodeName()
206                        + "', at "
207                        + locator);
208            else
209                m_pw.println(
210                    "Selected source node '" + sourceNode.getNodeName() + "'");
211    
212            if (ev.m_styleNode.getLineNumber() == 0) {
213    
214                // You may not have line numbers if the selection is occuring from a
215                // default template.
216                ElemTemplateElement parent =
217                    (ElemTemplateElement) ete.getParentElem();
218    
219                if (parent == ete.getStylesheetRoot().getDefaultRootRule()) {
220                    m_pw.print("(default root rule) ");
221                } else if (
222                    parent == ete.getStylesheetRoot().getDefaultTextRule()) {
223                    m_pw.print("(default text rule) ");
224                } else if (parent == ete.getStylesheetRoot().getDefaultRule()) {
225                    m_pw.print("(default rule) ");
226                }
227    
228                m_pw.print(
229                    ete.getNodeName()
230                        + ", "
231                        + ev.m_attributeName
232                        + "='"
233                        + ev.m_xpath.getPatternString()
234                        + "': ");
235            } else {
236                m_pw.print(
237                    ev.m_styleNode.getSystemId()
238                        + " Line #"
239                        + ev.m_styleNode.getLineNumber()
240                        + ", "
241                        + "Column #"
242                        + ev.m_styleNode.getColumnNumber()
243                        + ": "
244                        + ete.getNodeName()
245                        + ", "
246                        + ev.m_attributeName
247                        + "='"
248                        + ev.m_xpath.getPatternString()
249                        + "': ");
250            }
251    
252            if (ev.m_selection.getType() == ev.m_selection.CLASS_NODESET) {
253                m_pw.println();
254    
255                org.apache.xml.dtm.DTMIterator nl = ev.m_selection.iter();
256                
257                // The following lines are added to fix bug#16222.
258                // The main cause is that the following loop change the state of iterator, which is shared
259                // with the transformer. The fix is that we record the initial state before looping, then 
260                // restore the state when we finish it, which is done in the following lines added.
261                int currentPos = DTM.NULL;
262                currentPos = nl.getCurrentPos();
263                nl.setShouldCacheNodes(true); // This MUST be done before we clone the iterator!
264                org.apache.xml.dtm.DTMIterator clone = null;
265                // End of block
266                
267                try {
268                    clone = nl.cloneWithReset();
269                } catch (CloneNotSupportedException cnse) {
270                    m_pw.println(
271                        "     [Can't trace nodelist because it it threw a CloneNotSupportedException]");
272                    return;
273                }
274                int pos = clone.nextNode();
275    
276                if (DTM.NULL == pos) {
277                    m_pw.println("     [empty node list]");
278                } else {
279                    while (DTM.NULL != pos) {
280                        // m_pw.println("     " + ev.m_processor.getXPathContext().getDTM(pos).getNode(pos));
281                        DTM dtm = ev.m_processor.getXPathContext().getDTM(pos);
282                        m_pw.print("     ");
283                        m_pw.print(Integer.toHexString(pos));
284                        m_pw.print(": ");
285                        m_pw.println(dtm.getNodeName(pos));
286                        pos = clone.nextNode();
287                    }
288                }
289                            
290                // Restore the initial state of the iterator, part of fix for bug#16222.
291                nl.runTo(-1);
292                nl.setCurrentPos(currentPos);
293                    // End of fix for bug#16222
294                            
295            } else {
296                m_pw.println(ev.m_selection.str());
297            }
298        }
299    }
300      /**
301       * Method that is called after an xsl:apply-templates or xsl:for-each 
302       * selection occurs.
303       *
304       * @param ev the generate event.
305       *
306       * @throws javax.xml.transform.TransformerException
307       */
308      public void selectEnd(EndSelectionEvent ev) 
309         throws javax.xml.transform.TransformerException
310      {
311            // Nothing for right now.
312      }
313    
314    
315      /**
316       * Print information about a Generate event.
317       *
318       * @param ev the trace event.
319       */
320      public void generated(GenerateEvent ev)
321      {
322    
323        if (m_traceGeneration)
324        {
325          switch (ev.m_eventtype)
326          {
327          case SerializerTrace.EVENTTYPE_STARTDOCUMENT :
328            m_pw.println("STARTDOCUMENT");
329            break;
330          case SerializerTrace.EVENTTYPE_ENDDOCUMENT :
331            m_pw.println("ENDDOCUMENT");
332            break;
333          case SerializerTrace.EVENTTYPE_STARTELEMENT :
334            m_pw.println("STARTELEMENT: " + ev.m_name);
335            break;
336          case SerializerTrace.EVENTTYPE_ENDELEMENT :
337            m_pw.println("ENDELEMENT: " + ev.m_name);
338            break;
339          case SerializerTrace.EVENTTYPE_CHARACTERS :
340          {
341            String chars = new String(ev.m_characters, ev.m_start, ev.m_length);
342    
343            m_pw.println("CHARACTERS: " + chars);
344          }
345          break;
346          case SerializerTrace.EVENTTYPE_CDATA :
347          {
348            String chars = new String(ev.m_characters, ev.m_start, ev.m_length);
349    
350            m_pw.println("CDATA: " + chars);
351          }
352          break;
353          case SerializerTrace.EVENTTYPE_COMMENT :
354            m_pw.println("COMMENT: " + ev.m_data);
355            break;
356          case SerializerTrace.EVENTTYPE_PI :
357            m_pw.println("PI: " + ev.m_name + ", " + ev.m_data);
358            break;
359          case SerializerTrace.EVENTTYPE_ENTITYREF :
360            m_pw.println("ENTITYREF: " + ev.m_name);
361            break;
362          case SerializerTrace.EVENTTYPE_IGNORABLEWHITESPACE :
363            m_pw.println("IGNORABLEWHITESPACE");
364            break;
365          }
366        }
367      }
368    
369      /**
370       * Print information about an extension event.
371       *
372       * @param ev the extension event to print information about
373       */
374      public void extension(ExtensionEvent ev) {
375        if (m_traceExtension) {
376          switch (ev.m_callType) {
377            case ExtensionEvent.DEFAULT_CONSTRUCTOR:
378              m_pw.println("EXTENSION: " + ((Class)ev.m_method).getName() + "#<init>");
379              break;
380            case ExtensionEvent.METHOD:
381              m_pw.println("EXTENSION: " + ((Method)ev.m_method).getDeclaringClass().getName() + "#" + ((Method)ev.m_method).getName());
382              break;
383            case ExtensionEvent.CONSTRUCTOR:
384              m_pw.println("EXTENSION: " + ((Constructor)ev.m_method).getDeclaringClass().getName() + "#<init>");
385              break;
386          }
387        }
388      }
389    
390    
391      /**
392       * Print information about an extension event.
393       *
394       * @param ev the extension event to print information about
395       */
396      public void extensionEnd(ExtensionEvent ev) {
397        // do nothing
398      }
399    
400    }