Testing utility that logs out TraceListener events

diff --git a/java/src/org/apache/qetest/xalanj2/LoggingTraceListener.java b/java/src/org/apache/qetest/xalanj2/LoggingTraceListener.java
new file mode 100644
index 0000000..02cc712
--- /dev/null
+++ b/java/src/org/apache/qetest/xalanj2/LoggingTraceListener.java
@@ -0,0 +1,389 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ *
+ * Copyright (c) 2001 The Apache Software Foundation.  All rights 
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:  
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Xalan" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written 
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 2000, Lotus
+ * Development Corporation., http://www.lotus.com.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+/*
+ *
+ * LoggingTraceListener.java
+ *
+ */
+package org.apache.qetest.xalanj2;
+import org.apache.qetest.*;
+
+import java.io.IOException;
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeIterator;
+
+import org.apache.xalan.trace.TraceListener;
+import org.apache.xalan.trace.GenerateEvent;
+import org.apache.xalan.trace.SelectionEvent;
+import org.apache.xalan.trace.TracerEvent;
+import org.apache.xalan.templates.ElemTemplate;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.ElemTextLiteral;
+import org.apache.xalan.templates.Constants;
+import org.apache.xpath.axes.ContextNodeList;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.XPath;
+
+/**
+ * Logging TraceListener interface.
+ * Implementation of the TraceListener interface that
+ * prints each event to our logger as it occurs.
+ * Future improvements: allow you to specify a set of 
+ * expected events to validate.
+ * @author shane_curcuru@lotus.com
+ * @version $Id$
+ */
+public class LoggingTraceListener extends LoggingHandler 
+       implements TraceListener
+{
+
+    /**
+     * Accesor method for a brief description of this service.  
+     * @return String "LoggingTraceListener: logs and counts trace events"
+     */
+    public String getDescription()
+    {
+        return "LoggingTraceListener: logs and counts trace events";
+    }
+
+
+    /** No-op sets logger to default.  */
+    public LoggingTraceListener()
+    {
+        setLogger(getDefaultLogger());
+    }
+
+    /**
+     * Ctor that calls setLogger automatically.  
+     *
+     * @param r Logger we should log to
+     */
+    public LoggingTraceListener(Logger l)
+    {
+        setLogger(l);
+    }
+
+
+    /**
+     * Set a default handler for us to wrapper - no-op.
+     * Since you can add multiple TraceListeners, there's no sense 
+     * in us wrappering another one.
+     * @param default Object unused
+     */
+    public void setDefaultHandler(Object noop)
+    {
+        /* no-op */
+    }
+
+
+    /**
+     * Accessor method for our default handler - no-op.
+     * @return null
+     */
+    public Object getDefaultHandler()
+    {
+        return null;
+    }
+
+
+    /** Prefixed to all logger msg output for TraceListener.  */
+    public final String prefix = "LTL:";
+
+
+    /** Cheap-o string representation of last event we got.  */
+    protected String lastItem = NOTHING_HANDLED;
+
+
+    /**
+     * Accessor for string representation of last trace event.  
+     * @param s string to set
+     */
+    protected void setLastItem(String s)
+    {
+        lastItem = s;
+    }
+
+
+    /**
+     * Accessor for string representation of last trace event.  
+     * @return last event string we had
+     */
+    public String getLast()
+    {
+        return lastItem;
+    }
+
+
+    /** Constant for getCounters()[]: trace events.  */
+    public static final int TYPE_TRACE = 0;
+
+    /** Constant for getCounters()[]: generated events.  */
+    public static final int TYPE_GENERATED = 1;
+
+    /** Constant for getCounters()[]: selected events.  */
+    public static final int TYPE_SELECTED = 2;
+
+    /** 
+     * Counters for how many events we've handled.  
+     * Index into array are the TYPE_* constants.
+     */
+    protected int[] counters = 
+    {
+        0, /* trace */
+        0, /* generated */
+        0  /* selected */
+    };
+
+
+    /**
+     * Get a list of counters of all items we've logged.
+     * Returned as trace, generated, selected
+     * Index into array are the TYPE_* constants.
+     *
+     * @return array of int counters for each item we log
+     */
+    public int[] getCounters()
+    {
+        return counters;
+    }
+
+    /**
+     * Reset all items or counters we've handled.  
+     */
+    public void reset()
+    {
+        setLastItem(NOTHING_HANDLED);
+        for (int i = 0; i < counters.length; i++)
+        {
+            counters[i] = 0;
+        }
+    }
+
+    /** setExpected, etc. not yet implemented.  */
+
+    ////////////////// Implement TraceListener ////////////////// 
+    /**
+     * Logging implementation of TraceListener method.
+     * Method that is called when a trace event occurs.
+     * The method is blocking.  It must return before processing continues.
+     *
+     * @param tracerEvent the trace event.
+     */
+    public void trace(TracerEvent tracerEvent)
+    {
+        counters[TYPE_TRACE]++;
+
+        StringBuffer buf = new StringBuffer("trace:");
+        int dumpLevel = XalanDumper.DUMP_DEFAULT;
+        if (null != tracerEvent.m_mode) // not terribly elegant way to do it
+            dumpLevel = XalanDumper.DUMP_NOCLOSE;
+        switch (tracerEvent.m_styleNode.getXSLToken())
+        {
+            // Specific handling for most common 'interesting' items
+            case Constants.ELEMNAME_TEXTLITERALRESULT :
+                buf.append(XalanDumper.dump((ElemTextLiteral) tracerEvent.m_styleNode, dumpLevel));
+                break;
+
+            case Constants.ELEMNAME_TEMPLATE :
+                buf.append(XalanDumper.dump((ElemTemplate) tracerEvent.m_styleNode, dumpLevel));
+                break;
+
+            default :
+                buf.append(XalanDumper.dump((ElemTemplateElement) tracerEvent.m_styleNode, dumpLevel));
+        }
+        if (null != tracerEvent.m_mode)
+            buf.append(XalanDumper.SEP + "m_mode=" + tracerEvent.m_mode + XalanDumper.RBRACKET);
+
+        setLastItem(buf.toString());
+        logger.logMsg(level, prefix + getLast());
+    }
+
+    /**
+     * Logging implementation of TraceListener method.
+     * Method that is called just after the formatter listener is called.
+     *
+     * @param selectionEvent the selected event.
+     * @throws javax.xml.transform.TransformerException never thrown
+     */
+    public void selected(SelectionEvent selectionEvent) 
+            throws javax.xml.transform.TransformerException
+    {
+        counters[TYPE_SELECTED]++;
+
+        StringBuffer buf = new StringBuffer("selected:");
+        ElemTemplateElement styleNodeElem = (ElemTemplateElement) selectionEvent.m_styleNode;
+        ElemTemplateElement parent = (ElemTemplateElement) styleNodeElem.getParentNode();
+        if (parent == styleNodeElem.getStylesheetRoot().getDefaultRootRule())
+        {
+            buf.append("[default-root-rule]");
+        }
+        else if (parent == styleNodeElem.getStylesheetRoot().getDefaultTextRule())
+        {
+            buf.append("[default-text-rule]");
+        }
+        else if (parent == styleNodeElem.getStylesheetRoot().getDefaultRule())
+        {
+            buf.append("[default-rule]");
+        }
+        else
+            buf.append(XalanDumper.dump(styleNodeElem, XalanDumper.DUMP_NOCLOSE));
+
+        buf.append(selectionEvent.m_attributeName + "="
+                   + selectionEvent.m_xpath.getPatternString() + ";");
+
+        if (selectionEvent.m_selection.getType() == selectionEvent.m_selection.CLASS_NODESET)
+        {
+            NodeIterator nl = selectionEvent.m_selection.nodeset();
+            if (nl instanceof ContextNodeList)
+            {
+                try
+                {
+                    nl = ((ContextNodeList)nl).cloneWithReset();
+                }
+                catch(CloneNotSupportedException cnse)
+                {
+                    buf.append("[Can't trace nodelist, threw: CloneNotSupportedException]");
+                }
+                Node pos = nl.nextNode();
+
+                if (null == pos)
+                {
+                    buf.append("[empty node list]");
+                }
+                else // (null == pos)
+                {
+                    while (null != pos)
+                    {
+                        buf.append(" " + pos);
+                        pos = nl.nextNode();
+                    }
+                }
+            }
+            else // (nl instanceof ContextNodeList)
+            {
+                buf.append("[Can't trace nodelist: it isn't a ContextNodeList]");
+            }
+        }
+        else // (selectionEvent.m_selection.getType() == selectionEvent.m_selection.CLASS_NODESET)
+        {
+            buf.append("[" + selectionEvent.m_selection.str() +"]");
+        }
+        buf.append(XalanDumper.RBRACKET);   // Since we said DUMP_NOCLOSE above
+        setLastItem(buf.toString());
+        logger.logMsg(level, prefix + getLast());
+    }
+
+    /**
+     * Logging implementation of TraceListener method.
+     * Method that is called just after the formatter listener is called.
+     *
+     * @param generateEvent the generate event.
+     */
+    public void generated(GenerateEvent generateEvent)
+    {
+        counters[TYPE_GENERATED]++;
+
+        StringBuffer buf = new StringBuffer("generated:");
+        switch (generateEvent.m_eventtype)
+        {
+            case GenerateEvent.EVENTTYPE_STARTDOCUMENT :
+                buf.append("STARTDOCUMENT");
+            break;
+
+            case GenerateEvent.EVENTTYPE_ENDDOCUMENT :
+                buf.append("ENDDOCUMENT");
+            break;
+
+            case GenerateEvent.EVENTTYPE_STARTELEMENT :
+                buf.append("STARTELEMENT[" + generateEvent.m_name + "]"); // just hardcode [ LBRACKET ] RBRACKET here
+            break;
+
+            case GenerateEvent.EVENTTYPE_ENDELEMENT :
+                buf.append("ENDELEMENT[" + generateEvent.m_name + "]");
+            break;
+
+            case GenerateEvent.EVENTTYPE_CHARACTERS :
+                String chars1 = new String(generateEvent.m_characters, generateEvent.m_start, generateEvent.m_length);
+                buf.append("CHARACTERS[" + chars1 + "]");
+            break;
+
+            case GenerateEvent.EVENTTYPE_CDATA :
+                String chars2 = new String(generateEvent.m_characters, generateEvent.m_start, generateEvent.m_length);
+                buf.append("CDATA[" + chars2 + "]");
+            break;
+
+            case GenerateEvent.EVENTTYPE_COMMENT :
+                buf.append("COMMENT[" + generateEvent.m_data + "]");
+            break;
+
+            case GenerateEvent.EVENTTYPE_PI :
+                buf.append("PI[" + generateEvent.m_name + ", " + generateEvent.m_data + "]");
+            break;
+
+            case GenerateEvent.EVENTTYPE_ENTITYREF :
+                buf.append("ENTITYREF[" + generateEvent.m_name + "]");
+            break;
+
+            case GenerateEvent.EVENTTYPE_IGNORABLEWHITESPACE :
+                buf.append("IGNORABLEWHITESPACE");
+            break;
+        }
+        setLastItem(buf.toString());
+        logger.logMsg(level, prefix + getLast());
+    }
+}