KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icl > saxon > TransformerFactoryImpl


1 package com.icl.saxon;
2
3 import com.icl.saxon.trace.TraceListener;
4 import com.icl.saxon.output.*;
5 import com.icl.saxon.om.NodeInfo;
6 import com.icl.saxon.om.Namespace;
7 import com.icl.saxon.om.NamePool;
8 import com.icl.saxon.om.Builder;
9 import com.icl.saxon.om.DocumentInfo;
10
11 import java.io.*;
12
13 import java.util.Properties JavaDoc;
14 import java.util.Enumeration JavaDoc;
15
16 import org.xml.sax.*;
17 import org.xml.sax.helpers.ParserAdapter JavaDoc;
18
19 import org.w3c.dom.Document JavaDoc;
20 import org.w3c.dom.Node JavaDoc;
21
22 import javax.xml.parsers.SAXParserFactory JavaDoc;
23 import javax.xml.transform.*;
24 import javax.xml.transform.stream.*;
25 import javax.xml.transform.sax.*;
26 import javax.xml.transform.dom.*;
27
28
29 /**
30  * A TransformerFactoryImpl instance can be used to create Transformer and Template
31  * objects.
32  *
33  * <p>The system property that determines which Factory implementation
34  * to create is named "javax.xml.transform.TransformerFactory". This
35  * property names a concrete subclass of the TransformerFactory abstract
36  * class. If the property is not defined, a platform default is be used.</p>
37  *
38  * <p>This implementation class implements the abstract methods on both the
39  * javax.xml.transform.TransformerFactory and javax.xml.transform.sax.SAXTransformerFactory
40  * classes.
41  */

42  
43 public class TransformerFactoryImpl extends SAXTransformerFactory {
44
45     private URIResolver resolver = new StandardURIResolver(this);
46     private ErrorListener listener = new StandardErrorListener();
47     private int treeModel = Builder.TINY_TREE;
48     private boolean lineNumbering = false;
49     private TraceListener traceListener = null;
50     private int recoveryPolicy = Controller.RECOVER_WITH_WARNINGS;
51     private String JavaDoc messageEmitterClass = "com.icl.saxon.output.MessageEmitter";
52     private String JavaDoc sourceParserClass;
53     private String JavaDoc styleParserClass;
54     private boolean timing = false;
55     private boolean allowExternalFunctions = true;
56
57     /**
58      * Default constructor.
59      */

60     public TransformerFactoryImpl() {}
61     
62     /**
63      * Process the Source into a Transformer object. Care must
64      * be given not to use this object in multiple threads running concurrently.
65      * Different TransformerFactories can be used concurrently by different
66      * threads.
67      *
68      * @param source An object that holds a URI, input stream, etc.
69      *
70      * @return A Transformer object that may be used to perform a transformation
71      * in a single thread, never null.
72      *
73      * @exception TransformerConfigurationException May throw this during the parse
74      * when it is constructing the Templates object and fails.
75      */

76      
77     public Transformer newTransformer(Source source)
78             throws TransformerConfigurationException {
79         Templates templates = newTemplates(source);
80         Transformer trans = templates.newTransformer();
81         //trans.setURIResolver(resolver);
82
//trans.setErrorListener(listener);
83
return trans;
84     }
85
86     /**
87      * Create a new Transformer object that performs a copy
88      * of the source to the result.
89      *
90      * @return A Transformer object that may be used to perform a transformation
91      * in a single thread, never null.
92      *
93      * @exception TransformerConfigurationException May throw this during
94      * the parse when it is constructing the
95      * Templates object and fails.
96      */

97      
98     public Transformer newTransformer()
99                     throws TransformerConfigurationException {
100                         
101         return new IdentityTransformer(this);
102     }
103
104
105     /**
106      * Process the Source into a Templates object, which is a
107      * a compiled representation of the source. This Templates object
108      * may then be used concurrently across multiple threads. Creating
109      * a Templates object allows the TransformerFactory to do detailed
110      * performance optimization of transformation instructions, without
111      * penalizing runtime transformation.
112      *
113      * @param source An object that holds a URL, input stream, etc.
114      *
115      * @return A Templates object capable of being used for transformation purposes,
116      * never null.
117      *
118      * @exception TransformerConfigurationException May throw this during the parse when it
119      * is constructing the Templates object and fails.
120      */

121
122     public Templates newTemplates(Source source)
123         throws TransformerConfigurationException {
124
125         PreparedStyleSheet pss = new PreparedStyleSheet(this);
126         SAXSource saxSource = getSAXSource(source, true);
127         pss.prepare(saxSource);
128         return pss;
129     }
130
131
132     /**
133     * Convert a supplied Source to a SAXSource
134     * @param source The supplied input source
135     * @param isStyleSheet true if the source is a stylesheet
136     * @return a SAXSource
137     */

138
139     public SAXSource getSAXSource(Source source, boolean isStyleSheet) {
140         if (source instanceof SAXSource) {
141             if (((SAXSource)source).getXMLReader()==null) {
142                 SAXSource ss = new SAXSource();
143                 ss.setInputSource(((SAXSource)source).getInputSource());
144                 ss.setSystemId(source.getSystemId());
145                 ss.setXMLReader((isStyleSheet ? getStyleParser() : getSourceParser()));
146                 return ss;
147             } else {
148                 return (SAXSource)source;
149             }
150        // } else if (source instanceof NodeInfo) {
151
// DocumentInfo docInfo = ((NodeInfo)source).getDocumentRoot();
152
// TreeDriver driver = new TreeDriver();
153
// driver.setDocumentInfo(docInfo);
154
// InputSource is = new InputSource("dummy");
155
// is.setSystemId(source.getSystemId());
156
// driver.setSystemId(source.getSystemId());
157
// return new SAXSource(driver, is);
158

159         } else if (source instanceof DOMSource) {
160             InputSource is = new InputSource("dummy");
161             Node JavaDoc startNode = ((DOMSource)source).getNode();
162             Document JavaDoc doc;
163             if (startNode instanceof Document JavaDoc) {
164                 doc = (Document JavaDoc)startNode;
165             } else {
166                 doc = startNode.getOwnerDocument();
167             }
168             DOMDriver driver;
169             if (startNode instanceof NodeInfo) {
170                 driver = new TreeDriver();
171             } else {
172                 driver = new DOMDriver();
173             }
174             driver.setStartNode(doc);
175             is.setSystemId(source.getSystemId());
176             driver.setSystemId(source.getSystemId());
177             return new SAXSource(driver, is);
178         } else if (source instanceof StreamSource) {
179             StreamSource ss = (StreamSource)source;
180             
181             // The current (17 April 2001) version of JAXP 1.1 StreamSource
182
// has a bug: if constructed with a File object representing
183
// the file "/usr/my.xml", it produces the invalid system id
184
// "file:////usr/my.xml", instead of "file:///usr/my.xml". The
185
// following code gets round this:
186
//[code now deleted, JAXP updated - 14 Nov 2001]
187

188             String JavaDoc url = source.getSystemId();
189             //if (url!=null && url.startsWith("file:////")) {
190
// url = "file:///" + url.substring(9);
191
//}
192

193             InputSource is = new InputSource(url);
194             is.setCharacterStream(ss.getReader());
195             is.setByteStream(ss.getInputStream());
196             return new SAXSource(
197                              (isStyleSheet ? getStyleParser() : getSourceParser()),
198                              is);
199         } else {
200             throw new IllegalArgumentException JavaDoc("Unknown type of source");
201         }
202     }
203
204
205     /**
206      * Get the stylesheet specification(s) associated
207      * via the xml-stylesheet processing instruction (see
208      * http://www.w3.org/TR/xml-stylesheet/) with the document
209      * document specified in the source parameter, and that match
210      * the given criteria. Note that it is possible to return several
211      * stylesheets, in which case they are applied as if they were
212      * a list of imports or cascades.
213      *
214      * @param source The XML source document.
215      * @param media The media attribute to be matched. May be null, in which
216      * case the prefered templates will be used (i.e. alternate = no).
217      * @param title The value of the title attribute to match. May be null.
218      * @param charset The value of the charset attribute to match. May be null.
219      *
220      * @return A Source object suitable for passing to the TransformerFactory.
221      *
222      * @throws TransformerConfigurationException.
223      */

224      
225     public Source getAssociatedStylesheet(
226         Source source, String JavaDoc media, String JavaDoc title, String JavaDoc charset)
227             throws TransformerConfigurationException {
228
229
230         PIGrabber grabber = new PIGrabber();
231         grabber.setCriteria(media, title, charset);
232         grabber.setBaseURI(source.getSystemId());
233         grabber.setURIResolver(resolver);
234
235         SAXSource saxSource = getSAXSource(source, false);
236         XMLReader parser = saxSource.getXMLReader();
237
238         parser.setContentHandler(grabber);
239         try {
240             parser.parse(saxSource.getInputSource()); // this parse will be aborted when the first start tag is found
241
} catch (SAXException err) {
242             if (err.getMessage().equals("#start#")) {
243                 // do nothing
244
} else {
245                 // TODO: the error handling here is not very nice...
246
System.err.println("Failed while looking for xml-stylesheet PI");
247                 System.err.println(err.getMessage());
248                 if (err.getException()!=null) {
249                     err.getException().printStackTrace();
250                 }
251                 if (err instanceof SAXParseException) {
252                     SAXParseException pe = (SAXParseException)err;
253                     System.err.println("At line " + pe.getLineNumber() + " in " + pe.getSystemId());
254                 }
255                 throw new TransformerConfigurationException(err);
256             }
257         } catch (java.io.IOException JavaDoc ierr) {
258             System.err.println(ierr.getMessage());
259             throw new TransformerConfigurationException(
260                 "XML parsing failure while looking for <?xml-stylesheet?>");
261         }
262         try {
263             SAXSource[] sources = grabber.getAssociatedStylesheets();
264             if (sources==null) {
265                 throw new TransformerConfigurationException(
266                     "No matching <?xml-stylesheet?> processing instruction found");
267             }
268             return compositeStylesheet(sources);
269         } catch (TransformerException err) {
270             if (err instanceof TransformerConfigurationException) {
271                 throw (TransformerConfigurationException)err;
272             } else {
273                 throw new TransformerConfigurationException(err);
274             }
275         }
276     }
277
278     /**
279     * Process a series of stylesheet inputs, treating them in import or cascade
280     * order. This is mainly for support of the getAssociatedStylesheets
281     * method, but may be useful for other purposes.
282     *
283     * @param sources An array of SAX InputSource objects.
284     * @returns A Source object representing a composite stylesheet.
285     */

286     
287     public Source compositeStylesheet(SAXSource[] sources)
288                         throws TransformerConfigurationException {
289
290         // TODO: this will fail if any of the SAXSources uses a non-standard parser.
291

292         if (sources.length == 1) {
293             return sources[0];
294         } else if (sources.length == 0) {
295             throw new TransformerConfigurationException(
296                             "No stylesheets were supplied");
297         }
298         
299         // create a new top-level stylesheet that imports all the others
300

301         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
302         sb.append("<xsl:stylesheet version='1.0' ");
303         sb.append(" xmlns:xsl='" + Namespace.XSLT + "'>");
304         for (int i=0; i<sources.length; i++) {
305             sb.append("<xsl:import HREF='" + sources[i].getInputSource().getSystemId() + "'/>");
306         }
307         sb.append("</xsl:stylesheet>");
308         InputSource composite = new InputSource();
309         composite.setCharacterStream(new StringReader(sb.toString()));
310         return new SAXSource(getSourceParser(), composite);
311     }
312
313     /**
314      * Set an object that is used by default during the transformation
315      * to resolve URIs used in xsl:import, or xsl:include.
316      *
317      * @param resolver An object that implements the URIResolver interface,
318      * or null.
319      */

320      
321     public void setURIResolver(URIResolver resolver) {
322         this.resolver = resolver;
323     }
324
325     /**
326      * Get the object that is used by default during the transformation
327      * to resolve URIs used in document(), xsl:import, or xsl:include.
328      *
329      * @return The URIResolver that was set with setURIResolver.
330      */

331      
332     public URIResolver getURIResolver() {
333         return resolver;
334     }
335
336     //======= CONFIGURATION METHODS =======
337

338     /**
339      * Look up the value of a feature.
340      *
341      * <p>The feature name is any absolute URI.</p>
342      * @param name The feature name, which is an absolute URI.
343      * @return The current state of the feature (true or false).
344      */

345      
346     public boolean getFeature(String JavaDoc name) {
347         if (name.equals(SAXSource.FEATURE)) return true;
348         if (name.equals(SAXResult.FEATURE)) return true;
349         if (name.equals(DOMSource.FEATURE)) return true;
350         if (name.equals(DOMResult.FEATURE)) return true;
351         if (name.equals(StreamSource.FEATURE)) return true;
352         if (name.equals(StreamResult.FEATURE)) return true;
353         if (name.equals(SAXTransformerFactory.FEATURE)) return true;
354         if (name.equals(SAXTransformerFactory.FEATURE_XMLFILTER)) return true;
355         throw new IllegalArgumentException JavaDoc("Unknown feature " + name);
356     }
357
358     /**
359      * Allows the user to set specific attributes on the underlying
360      * implementation. An attribute in this context is defined to
361      * be an option that the implementation provides.
362      *
363      * @param name The name of the attribute. This must be one of the constants
364      * defined in class FeatureKeys.
365      * @param value The value of the attribute.
366      * @throws IllegalArgumentException thrown if Saxon
367      * doesn't recognize the attribute.
368      */

369      
370     public void setAttribute(String JavaDoc name, Object JavaDoc value)
371                                     throws IllegalArgumentException JavaDoc {
372         if (name.equals(FeatureKeys.TREE_MODEL)) {
373             if (!(value instanceof Integer JavaDoc)) {
374                 throw new IllegalArgumentException JavaDoc("Tree model must be an Integer");
375             }
376             treeModel = ((Integer JavaDoc)value).intValue();
377
378         } else if (name.equals(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
379             if (!(value instanceof Boolean JavaDoc)) {
380                 throw new IllegalArgumentException JavaDoc("allow-external-functions must be a boolean");
381             }
382             allowExternalFunctions = ((Boolean JavaDoc)value).booleanValue();
383
384         } else if (name.equals(FeatureKeys.TIMING)) {
385             if (!(value instanceof Boolean JavaDoc)) {
386                 throw new IllegalArgumentException JavaDoc("Timing must be a boolean");
387             }
388             timing = ((Boolean JavaDoc)value).booleanValue();
389
390         } else if (name.equals(FeatureKeys.TRACE_LISTENER)) {
391             if (!(value instanceof TraceListener)) {
392                 throw new IllegalArgumentException JavaDoc("Trace listener is of wrong class");
393             }
394             traceListener = (TraceListener)value;
395
396         } else if (name.equals(FeatureKeys.LINE_NUMBERING)) {
397             if (!(value instanceof Boolean JavaDoc)) {
398                 throw new IllegalArgumentException JavaDoc("Line Numbering value must be Boolean");
399             }
400             lineNumbering = ((Boolean JavaDoc)value).booleanValue();
401         
402         } else if (name.equals(FeatureKeys.RECOVERY_POLICY)) {
403             if (!(value instanceof Integer JavaDoc)) {
404                 throw new IllegalArgumentException JavaDoc("Recovery Policy value must be Integer");
405             }
406             recoveryPolicy = ((Integer JavaDoc)value).intValue();
407
408         } else if (name.equals(FeatureKeys.MESSAGE_EMITTER_CLASS)) {
409             if (!(value instanceof String JavaDoc)) {
410                 throw new IllegalArgumentException JavaDoc("Message Emitter class must be a String");
411             }
412             messageEmitterClass = (String JavaDoc)value;
413
414         } else if (name.equals(FeatureKeys.SOURCE_PARSER_CLASS)) {
415             if (!(value instanceof String JavaDoc)) {
416                 throw new IllegalArgumentException JavaDoc("Source Parser class must be a String");
417             }
418             sourceParserClass = (String JavaDoc)value;
419
420         } else if (name.equals(FeatureKeys.STYLE_PARSER_CLASS)) {
421             if (!(value instanceof String JavaDoc)) {
422                 throw new IllegalArgumentException JavaDoc("Style Parser class must be a String");
423             }
424             styleParserClass = (String JavaDoc)value;
425
426         } else {
427             throw new IllegalArgumentException JavaDoc("Unknown attribute " + name);
428         }
429     }
430
431     /**
432      * Allows the user to retrieve specific attributes on the underlying
433      * implementation.
434      * @param name The name of the attribute.
435      * @return value The value of the attribute.
436      * @throws IllegalArgumentException thrown if the underlying
437      * implementation doesn't recognize the attribute.
438      */

439     public Object JavaDoc getAttribute(String JavaDoc name)
440         throws IllegalArgumentException JavaDoc{
441         if (name.equals(FeatureKeys.TREE_MODEL)) {
442             return new Integer JavaDoc(treeModel);
443
444         } else if (name.equals(FeatureKeys.TIMING)) {
445             return new Boolean JavaDoc(timing);
446
447         } else if (name.equals(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
448             return new Boolean JavaDoc(allowExternalFunctions);
449
450         } else if (name.equals(FeatureKeys.TRACE_LISTENER)) {
451             return traceListener;
452
453         } else if (name.equals(FeatureKeys.LINE_NUMBERING)) {
454             return new Boolean JavaDoc(lineNumbering);
455         
456         } else if (name.equals(FeatureKeys.RECOVERY_POLICY)) {
457             return new Integer JavaDoc(recoveryPolicy);
458
459         } else if (name.equals(FeatureKeys.MESSAGE_EMITTER_CLASS)) {
460             return messageEmitterClass;
461
462         } else if (name.equals(FeatureKeys.SOURCE_PARSER_CLASS)) {
463             return sourceParserClass;
464
465         } else if (name.equals(FeatureKeys.STYLE_PARSER_CLASS)) {
466             return styleParserClass;
467
468         } else {
469             throw new IllegalArgumentException JavaDoc("Unknown attribute " + name);
470         }
471     }
472
473     /**
474      * Set the error event listener for the TransformerFactory, which
475      * is used for the processing of transformation instructions,
476      * and not for the transformation itself.
477      *
478      * @param listener The new error listener.
479      * @throws IllegalArgumentException if listener is null.
480      */

481      
482     public void setErrorListener(ErrorListener listener)
483             throws IllegalArgumentException JavaDoc {
484         this.listener = listener;
485     }
486
487     /**
488      * Get the error event handler for the TransformerFactory.
489      *
490      * @return The current error handler, which should never be null.
491      */

492     public ErrorListener getErrorListener() {
493         return listener;
494     }
495
496     /**
497     * Get the parser for source documents
498     */

499
500     public XMLReader getSourceParser() throws TransformerFactoryConfigurationError {
501         if (sourceParserClass!=null) {
502             return makeParser(sourceParserClass);
503         } else {
504             try {
505                 return SAXParserFactory.newInstance().newSAXParser().getXMLReader();
506             } catch (Exception JavaDoc err) {
507                 throw new TransformerFactoryConfigurationError(err);
508             }
509         }
510     }
511
512     /**
513     * Get the parser for stylesheet documents
514     */

515
516     public XMLReader getStyleParser() throws TransformerFactoryConfigurationError {
517         if (styleParserClass!=null) {
518             return makeParser(styleParserClass);
519         } else {
520             try {
521                 return SAXParserFactory.newInstance().newSAXParser().getXMLReader();
522             } catch (Exception JavaDoc err) {
523                 throw new TransformerFactoryConfigurationError(err);
524             }
525         }
526     }
527
528   /**
529     * Create a new SAX XMLReader object using the class name provided.<br>
530     *
531     * The named class must exist and must implement the
532     * org.xml.sax.XMLReader or Parser interface.<br>
533     *
534     * This method returns an instance of the parser named.
535     *
536     * @param className A string containing the name of the
537     * SAX parser class, for example "com.microstar.sax.LarkDriver"
538     * @return an instance of the Parser class named, or null if it is not
539     * loadable or is not a Parser.
540     *
541     */

542     public static XMLReader makeParser (String JavaDoc className)
543     throws TransformerFactoryConfigurationError
544     {
545         Object JavaDoc obj;
546         try {
547             obj = Loader.getInstance(className);
548         } catch (TransformerException err) {
549             throw new TransformerFactoryConfigurationError(err);
550         }
551         if (obj instanceof XMLReader) {
552             return (XMLReader)obj;
553         }
554         if (obj instanceof Parser) {
555             return new ParserAdapter JavaDoc((Parser)obj);
556         }
557         throw new TransformerFactoryConfigurationError("Class " + className +
558                                  " is neither a SAX1 Parser nor a SAX2 XMLReader");
559     }
560
561     ///////////////////////////////////////////////////////////////////////////////
562
// Methods defined in class javax.xml.transform.sax.SAXTransformerFactory
563
///////////////////////////////////////////////////////////////////////////////
564

565      /**
566      * Get a TransformerHandler object that can process SAX
567      * ContentHandler events into a Result, based on the transformation
568      * instructions specified by the argument.
569      *
570      * @param src The Source of the transformation instructions.
571      *
572      * @return TransformerHandler ready to transform SAX events.
573      *
574      * @throws TransformerConfigurationException If for some reason the
575      * TransformerHandler can not be created.
576      */

577      
578     public TransformerHandler newTransformerHandler(Source src)
579     throws TransformerConfigurationException {
580         Templates tmpl = newTemplates(src);
581         return newTransformerHandler(tmpl);
582     }
583
584     /**
585      * Get a TransformerHandler object that can process SAX
586      * ContentHandler events into a Result, based on the Templates argument.
587      *
588      * @param templates The compiled transformation instructions.
589      *
590      * @return TransformerHandler ready to transform SAX events.
591      *
592      * @throws TransformerConfigurationException If for some reason the
593      * TransformerHandler can not be created.
594      */

595      
596     public TransformerHandler newTransformerHandler(Templates templates)
597     throws TransformerConfigurationException {
598         if (!(templates instanceof PreparedStyleSheet)) {
599             throw new TransformerConfigurationException("Templates object was not created by Saxon");
600         }
601         Controller controller = (Controller)templates.newTransformer();
602         if (controller.usesPreviewMode()) {
603             throw new TransformerConfigurationException("Preview mode is not available with a TransformerHandler");
604         }
605         TransformerHandlerImpl handler = new TransformerHandlerImpl(controller);
606         return handler;
607     }
608
609     /**
610      * Get a TransformerHandler object that can process SAX
611      * ContentHandler events into a Result. The transformation
612      * is defined as an identity (or copy) transformation, for example
613      * to copy a series of SAX parse events into a DOM tree.
614      *
615      * @return A non-null reference to a TransformerHandler, that may
616      * be used as a ContentHandler for SAX parse events.
617      *
618      * @throws TransformerConfigurationException If for some reason the
619      * TransformerHandler cannot be created.
620      */

621      
622     public TransformerHandler newTransformerHandler()
623     throws TransformerConfigurationException {
624         Controller controller = new IdentityTransformer(this);
625         return new IdentityTransformerHandler(controller);
626     }
627
628     /**
629      * Get a TemplatesHandler object that can process SAX
630      * ContentHandler events into a Templates object.
631      *
632      * @return A non-null reference to a TransformerHandler, that may
633      * be used as a ContentHandler for SAX parse events.
634      *
635      * @throws TransformerConfigurationException If for some reason the
636      * TemplatesHandler cannot be created.
637      */

638      
639     public TemplatesHandler newTemplatesHandler()
640     throws TransformerConfigurationException {
641         return new TemplatesHandlerImpl(this);
642     }
643
644     /**
645      * Create an XMLFilter that uses the given Source as the
646      * transformation instructions.
647      *
648      * @param src The Source of the transformation instructions.
649      *
650      * @return An XMLFilter object, or null if this feature is not supported.
651      *
652      * @throws TransformerConfigurationException If for some reason the
653      * XMLFilter cannot be created.
654      */

655      
656     public XMLFilter newXMLFilter(Source src)
657     throws TransformerConfigurationException {
658         Templates tmpl = newTemplates(src);
659         return newXMLFilter(tmpl);
660     }
661
662     /**
663      * Create an XMLFilter, based on the Templates argument..
664      *
665      * @param templates The compiled transformation instructions.
666      *
667      * @return An XMLFilter object, or null if this feature is not supported.
668      *
669      * @throws TransformerConfigurationException If for some reason the
670      * XMLFilter cannot be created.
671      */

672      
673     public XMLFilter newXMLFilter(Templates templates)
674     throws TransformerConfigurationException {
675         if (!(templates instanceof PreparedStyleSheet)) {
676             throw new TransformerConfigurationException("Supplied Templates object was not created using Saxon");
677         }
678         Controller controller = (Controller)templates.newTransformer();
679         return new Filter(controller);
680     }
681    
682
683 }
684
Popular Tags