• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id$
20  */
21 
22 /*
23  *
24  * TransformStateTestlet.java
25  *
26  */
27 package org.apache.qetest.xalanj2;
28 
29 import java.util.Hashtable;
30 
31 import javax.xml.transform.Transformer;
32 import javax.xml.transform.TransformerFactory;
33 import javax.xml.transform.sax.SAXResult;
34 import javax.xml.transform.stream.StreamSource;
35 
36 import org.apache.qetest.Datalet;
37 import org.apache.qetest.Logger;
38 import org.apache.qetest.LoggingHandler;
39 import org.apache.qetest.QetestUtils;
40 import org.apache.qetest.TestletImpl;
41 import org.apache.qetest.XMLFileLogger;
42 import org.apache.xalan.templates.ElemTemplate;
43 import org.apache.xalan.templates.ElemTemplateElement;
44 import org.apache.xalan.transformer.TransformState;
45 import org.apache.xalan.transformer.TransformerClient;
46 import org.w3c.dom.Node;
47 import org.w3c.dom.traversal.NodeIterator;
48 import org.xml.sax.Attributes;
49 import org.xml.sax.ContentHandler;
50 import org.xml.sax.Locator;
51 import org.xml.sax.SAXException;
52 
53 /**
54  * Testlet for testing TransformState of a stylesheet.
55  *
56  * In progress - data-driven tests for tooling API's.
57  * Currently uses cheap-o validation method
58  *
59  * @author Shane_Curcuru@lotus.com
60  * @version $Id$
61  */
62 public class TransformStateTestlet extends TestletImpl
63         implements ContentHandler, TransformerClient
64 
65 {
66     // Initialize our classname for TestletImpl's main() method
67     static { thisClassName = "org.apache.qetest.xsl.TransformStateTestlet"; }
68 
69     // Initialize our defaultDatalet
70     { defaultDatalet = (Datalet)new TransformStateDatalet(); }
71 
72     /**
73      * Class-wide copy of TransformStateDatalet.
74      * This is used in execute() and in various worker methods
75      * underneath the ContentHandler interface.
76      */
77     protected TransformStateDatalet tsDatalet = null;
78 
79     /**
80      * Accesor method for a brief description of this test.
81      *
82      * @return String describing what this TransformStateTestlet does.
83      */
getDescription()84     public String getDescription()
85     {
86         return "TransformStateTestlet";
87     }
88 
89 
90     /**
91      * Run this TransformStateTestlet: execute it's test and return.
92      *
93      * @param Datalet to use as data point for the test.
94      */
execute(Datalet d)95     public void execute(Datalet d)
96 	{
97         try
98         {
99             tsDatalet = (TransformStateDatalet)d;
100         }
101         catch (ClassCastException e)
102         {
103             logger.checkErr("Datalet provided is not a TransformStateDatalet; cannot continue with " + d);
104             return;
105         }
106 
107         logger.logMsg(Logger.STATUSMSG, "About to test: "
108                       + (null == tsDatalet.inputName
109                          ? tsDatalet.xmlName
110                          : tsDatalet.inputName));
111         try
112         {
113             // Perform the transform
114             TransformerFactory factory = TransformerFactory.newInstance();
115             logger.logMsg(Logger.TRACEMSG, "---- About to newTransformer " + QetestUtils.filenameToURL(tsDatalet.inputName));
116             Transformer transformer = factory.newTransformer(new StreamSource(QetestUtils.filenameToURL(tsDatalet.inputName)));
117             logger.logMsg(Logger.TRACEMSG, "---- About to transform " + QetestUtils.filenameToURL(tsDatalet.xmlName) + " into: SAXResult(this-no disk output)");
118 
119             // Note most validation happens here: we get ContentHandler
120             //  callbacks from being in the SAXResult, and that's where
121             //  we do our validation
122             transformer.transform(new StreamSource(QetestUtils.filenameToURL(tsDatalet.xmlName)),
123                                   new SAXResult(this)); // use us to handle result
124 
125             logger.logMsg(Logger.INFOMSG, "---- Afterwards, this.transformState=" + transformState);
126         }
127         catch (Throwable t)
128         {
129             // Put the logThrowable first, so it appears before
130             //  the Fail record, and gets color-coded
131             logger.logThrowable(Logger.ERRORMSG, t, getDescription() + " " + tsDatalet.getDescription());
132             logger.checkFail(getDescription() + " " + tsDatalet.getDescription()
133                              + " threw: " + t.toString());
134             return;
135         }
136 	}
137     ////////////////// partially Implement LoggingHandler //////////////////
138     /** Cheap-o string representation of last event we got.  */
139     protected String lastItem = LoggingHandler.NOTHING_HANDLED;
140 
141 
142     /**
143      * Accessor for string representation of last event we got.
144      * @param s string to set
145      */
setLastItem(String s)146     protected void setLastItem(String s)
147     {
148         lastItem = s;
149     }
150 
151 
152     /**
153      * Accessor for string representation of last event we got.
154      * @return last event string we had
155      */
getLast()156     public String getLast()
157     {
158         return lastItem;
159     }
160 
161     /**
162      * Worker routine to validate a TransformState based on an event/value.
163      * Note: this may not be threadsafe!
164      * //@todo actually add validation code - just logs out now
165      * @param ts TransformState to validate, if null, just logs it
166      * @param event our String constant of START_ELEMENT, etc.
167      * @param value any String value of the current event
168      */
validateTransformState(TransformState ts, String event, String value)169     protected void validateTransformState(TransformState ts, String event, String value)
170     {
171         if(null == transformState)
172         {
173             // We should never have a null TransformState since the
174             //  transformer should have always filled it in
175             logger.checkErr("validateTransformState(ts-NULL!, " + event + ")=" + value);
176             return;
177         }
178         logTransformStateDump(logger, Logger.INFOMSG, ts, event, value);
179 
180         // Cheap-o validation: only validate items on column 99
181         if (99 == ts.getCurrentElement().getColumnNumber())
182         {
183             int line = ts.getCurrentElement().getLineNumber();
184             // Get cheap-o validation from the datalet for this line..
185             String exp = (String)tsDatalet.validate99.get(line + ".current.name");
186             // .. If there's an expected value for this line's property..
187             if (null != exp)
188                 // .. Then check if it's equal and report pass/fail
189                 checkString(ts.getCurrentTemplate().getName().toString(), exp,
190                             "Validate L" + line + "C99 .current.name");
191 
192             exp = (String)tsDatalet.validate99.get(line + ".current.match");
193             if (null != exp)
194                 checkString(ts.getCurrentTemplate().getMatch().getPatternString(), exp,
195                             "Validate L" + line + "C99 .current.match");
196 
197             exp = (String)tsDatalet.validate99.get(line + ".current.mode");
198             if (null != exp)
199                 checkString(ts.getCurrentTemplate().getMode().toString(), exp,
200                             "Validate L" + line + "C99 .current.mode");
201 
202 
203             exp = (String)tsDatalet.validate99.get(line + ".matched.name");
204             if (null != exp)
205                 checkString(ts.getMatchedTemplate().getName().toString(), exp,
206                             "Validate L" + line + "C99 .matched.name");
207 
208             exp = (String)tsDatalet.validate99.get(line + ".matched.match");
209             if (null != exp)
210                 checkString(ts.getMatchedTemplate().getMatch().getPatternString(), exp,
211                             "Validate L" + line + "C99 .matched.match");
212 
213             exp = (String)tsDatalet.validate99.get(line + ".matched.mode");
214             if (null != exp)
215                 checkString(ts.getMatchedTemplate().getMode().toString(), exp,
216                             "Validate L" + line + "C99 .matched.mode");
217         }
218 
219 /***********************************************
220 // Comment out validation using ExpectedObjects since they hang 28-Jun-01 -sc
221         // See if we have a matching expected state for this event
222         //@todo use event string as part of hashkey!!!
223         String marker = ExpectedTransformState.getHashKey(ts);
224         // Add on the event as well; see ExpectedTransformState.getHashKey()
225         //  for why we have to do this separately
226         marker += ExpectedTransformState.SEP + event;
227         ExpectedTransformState ets = (ExpectedTransformState)tsDatalet.expectedTransformStates.get(marker);
228         logger.logMsg(Logger.TRACEMSG, "ETS-HACK:" + marker + "=" + ets);
229         if (null != ets)
230         {
231             // Ask it to validate itself as needed
232             synchronized(ets) // voodoo: attempt to solve hang problems
233             {
234                 ExpectedObjectCheckService.check(logger, ts, ets, "Compare ExpectedTransformState of " + marker);
235             }
236         }
237 // Comment out validation using ExpectedObjects since they hang 28-Jun-01 -sc
238 ***********************************************/
239     }
240 
checkString(String act, String exp, String comment)241     private void checkString(String act, String exp, String comment)
242     {
243         if (exp.equals(act))
244             logger.checkPass(comment);
245         else
246             logger.checkFail(comment + "; act(" + act + ") exp(" + exp + ")");
247     }
248 
249     ////////////////// Utility methods for TransformState //////////////////
250     /**
251      * Utility method to dump data from TransformState.
252      * @return String describing various bits of the state
253      */
logTransformStateDump(Logger logger, int traceLoggingLevel, TransformState ts, String event, String value)254     protected void logTransformStateDump(Logger logger, int traceLoggingLevel,
255             TransformState ts, String event, String value)
256     {
257         String elemName = "transformStateDump";
258         Hashtable attrs = new Hashtable();
259         attrs.put("event", event);
260         if (null != value)
261             attrs.put("value", value);
262         attrs.put("location", "L" + ts.getCurrentElement().getLineNumber()
263                   + "C" + ts.getCurrentElement().getColumnNumber());
264 
265         StringBuffer buf = new StringBuffer();
266         ElemTemplateElement elem = ts.getCurrentElement(); // may be actual or default template
267         buf.append("  <currentElement>"
268                 + XMLFileLogger.escapeString(XalanDumper.dump(elem, XalanDumper.DUMP_DEFAULT)) + "</currentElement>");
269 
270         ElemTemplate currentTempl = ts.getCurrentTemplate(); // Actual current template
271         buf.append("\n  <currentTemplate>"
272                 + XMLFileLogger.escapeString(XalanDumper.dump(currentTempl, XalanDumper.DUMP_DEFAULT)) + "</currentTemplate>");
273 
274         ElemTemplate matchTempl = ts.getMatchedTemplate(); // Actual matched template
275         if (matchTempl != currentTempl)
276             buf.append("\n  <matchedTemplate>"
277                 + XMLFileLogger.escapeString(XalanDumper.dump(matchTempl, XalanDumper.DUMP_DEFAULT)) + "</matchedTemplate>");
278 
279         // Optimization: skip most logging when on endElement
280         if (!END_ELEMENT.equals(event))
281         {
282             Node n = ts.getCurrentNode();   // current context node in source tree
283             buf.append("\n  <currentNode>"
284                     + XMLFileLogger.escapeString(XalanDumper.dump(n, XalanDumper.DUMP_DEFAULT)) + "</currentNode>");
285 
286             Node matchedNode = ts.getMatchedNode(); // node in source matched via getMatchedTemplate
287             // Optimization: only output if different
288             if (n != matchedNode)
289                 buf.append("\n  <matchedNode>"
290                         + XMLFileLogger.escapeString(XalanDumper.dump(matchedNode, XalanDumper.DUMP_DEFAULT)) + "</matchedNode>");
291 
292             NodeIterator contextNodeList = ts.getContextNodeList(); // current context node list
293             Node rootNode = contextNodeList.getRoot();
294             // Optimization: only output if different
295             if (n != rootNode)
296                 buf.append("\n  <contextNodeListGetRoot>"
297                         + XMLFileLogger.escapeString(XalanDumper.dump(rootNode, XalanDumper.DUMP_DEFAULT)) + "</contextNodeListGetRoot>");
298 
299             Transformer transformer = ts.getTransformer(); // current transformer working
300             // Optimization: only dump transformer at startElement to save space
301             if (START_ELEMENT.equals(event))
302             {
303                 buf.append("\n  <transformer>"
304                         + XMLFileLogger.escapeString(XalanDumper.dump(transformer, XalanDumper.DUMP_DEFAULT)) + "</transformer>");
305             }
306             else
307             {
308                 // Just log error case if transformer is ever null
309                 if (null == transformer) {
310                     buf.append("\n  <transformer>"
311                             + "ERROR! Transformer was null!" + "</transformer>");
312                 }
313             }
314         }
315 
316         logger.logElement(traceLoggingLevel, elemName, attrs, buf.toString());
317     }
318 
319     //-----------------------------------------------------------
320     //---- Implement the TransformerClient interface
321     //-----------------------------------------------------------
322     /**
323      * A TransformState object that we use to log state data.
324      * This is the equivalent of the defaultHandler, even though
325      * that's not really the right metaphor.  This class could be
326      * upgraded to have both a default ContentHandler and a
327      * defaultTransformerClient in the future.
328      */
329     protected TransformState transformState = null;
330 
331 
332     /**
333      * Implement TransformerClient.setTransformState interface.
334      * Pass in a reference to a TransformState object, which
335      * can be used during SAX ContentHandler events to obtain
336      * information about he state of the transformation. This
337      * method will be called before each startDocument event.
338      *
339      * @param ts A reference to a TransformState object
340      */
setTransformState(TransformState ts)341     public void setTransformState(TransformState ts)
342     {
343         transformState = ts;
344     }
345 
346     //-----------------------------------------------------------
347     //---- Implement the ContentHandler interface
348     //-----------------------------------------------------------
349     protected final String START_ELEMENT = "startElement:";
350     protected final String END_ELEMENT = "endElement:";
351     protected final String CHARACTERS = "characters:";
352 
353     // String Locator.getPublicId() null if none available
354     // String Locator.getPublicId() null if none available
355     // int Locator.getLineNumber() -1 if none available
356     // int Locator.getColumnNumber() -1 if none available
357     protected Locator ourLocator = null;
358 
359     /**
360      * Implement ContentHandler.setDocumentLocator.
361      * If available, this should always be called prior to a
362      * startDocument event.
363      */
setDocumentLocator(Locator locator)364     public void setDocumentLocator (Locator locator)
365     {
366         // Note: this implies this class is !not! threadsafe
367         ourLocator = locator; // future use
368         if (null != locator)
369             setLastItem("setDocumentLocator.getSystemId():" + locator.getSystemId());
370         else
371             setLastItem("setDocumentLocator:NULL");
372         logger.logMsg(Logger.INFOMSG, getLast());
373     }
374 
375 
376     /** Cached TransformState object during lifetime startDocument -> endDocument.  */
377     // Note: is this correct? Will it always be the same object?
378     protected TransformState docCachedTransformState = null;
379     /** Implement ContentHandler.startDocument.  */
startDocument()380     public void startDocument ()
381         throws SAXException
382     {
383         setLastItem("startDocument");
384         logger.logMsg(Logger.INFOMSG, getLast());
385         // Comment out check call since the spec'd functionality
386         //  is very likely to change to *not* be in startDocument 19-Jun-01 -sc
387         // logger.check((null != transformState), true, "transformState non-null in startDocument");
388         logger.logMsg(Logger.STATUSMSG, "transformState in startDocument is: " + transformState);
389         docCachedTransformState = transformState; // see endDocument
390     }
391 
392 
393     /** Implement ContentHandler.endDocument.  */
endDocument()394     public void endDocument()
395         throws SAXException
396     {
397         setLastItem("endDocument");
398         logger.logMsg(Logger.INFOMSG, getLast());
399         // Comment out check call since the spec'd functionality
400         //  is very likely to change to *not* be in startDocument 19-Jun-01 -sc
401         // logger.checkObject(docCachedTransformState, transformState,
402         //               "transformState same in endDocument as startDocument"); // see startDocument
403         logger.logMsg(Logger.STATUSMSG, "transformState in endDocument is: " + transformState);
404         docCachedTransformState = null;
405     }
406 
407 
408     /** Implement ContentHandler.startPrefixMapping.  */
startPrefixMapping(String prefix, String uri)409     public void startPrefixMapping (String prefix, String uri)
410         throws SAXException
411     {
412         setLastItem("startPrefixMapping: " + prefix + ", " + uri);
413         logger.logMsg(Logger.INFOMSG, getLast());
414     }
415 
416 
417     /** Implement ContentHandler.endPrefixMapping.  */
endPrefixMapping(String prefix)418     public void endPrefixMapping (String prefix)
419         throws SAXException
420     {
421         setLastItem("endPrefixMapping: " + prefix);
422         logger.logMsg(Logger.INFOMSG, getLast());
423     }
424 
425 
426     /** Implement ContentHandler.startElement.  */
startElement(String namespaceURI, String localName, String qName, Attributes atts)427     public void startElement (String namespaceURI, String localName,
428                               String qName, Attributes atts)
429         throws SAXException
430     {
431         StringBuffer buf = new StringBuffer();
432         buf.append(namespaceURI + ", "
433                    + localName + ", " + qName + ";");
434 
435         int n = atts.getLength();
436         for(int i = 0; i < n; i++)
437         {
438             buf.append(", " + atts.getQName(i));
439         }
440         setLastItem(START_ELEMENT + buf.toString());
441 
442         validateTransformState(transformState, START_ELEMENT, buf.toString());
443     }
444 
445 
446     /** Implement ContentHandler.endElement.  */
endElement(String namespaceURI, String localName, String qName)447     public void endElement (String namespaceURI, String localName, String qName)
448         throws SAXException
449     {
450         setLastItem(END_ELEMENT + namespaceURI + ", " + localName + ", " + qName);
451 
452         validateTransformState(transformState, END_ELEMENT, null);
453     }
454 
455 
456     /** Implement ContentHandler.characters.  */
characters(char ch[], int start, int length)457     public void characters (char ch[], int start, int length)
458         throws SAXException
459     {
460         String s = new String(ch, start, length);
461         setLastItem(CHARACTERS + "\"" + s + "\"");
462 
463         validateTransformState(transformState, CHARACTERS, s);
464     }
465 
466 
467     /** Implement ContentHandler.ignorableWhitespace.  */
ignorableWhitespace(char ch[], int start, int length)468     public void ignorableWhitespace (char ch[], int start, int length)
469         throws SAXException
470     {
471         setLastItem("ignorableWhitespace: len " + length);
472         logger.logMsg(Logger.INFOMSG, getLast());
473     }
474 
475 
476     /** Implement ContentHandler.processingInstruction.  */
processingInstruction(String target, String data)477     public void processingInstruction (String target, String data)
478         throws SAXException
479     {
480         setLastItem("processingInstruction: " + target + ", " + data);
481         logger.logMsg(Logger.INFOMSG, getLast());
482     }
483 
484 
485     /** Implement ContentHandler.skippedEntity.  */
skippedEntity(String name)486     public void skippedEntity (String name)
487         throws SAXException
488     {
489         setLastItem("skippedEntity: " + name);
490         logger.logMsg(Logger.INFOMSG, getLast());
491     }
492 
493 }  // end of class TransformStateTestlet
494 
495