Source code: org/apache/xalan/transformer/TransformerImpl.java
1 /*
2 * Copyright 1999-2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 /*
17 * $Id: TransformerImpl.java,v 1.158 2004/02/23 21:33:14 igorh Exp $
18 */
19 package org.apache.xalan.transformer;
20
21 import java.io.IOException;
22 import java.io.StringWriter;
23 import java.util.Enumeration;
24 import java.util.Properties;
25 import java.util.Stack;
26 import java.util.StringTokenizer;
27 import java.util.Vector;
28
29 import javax.xml.parsers.DocumentBuilder;
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import javax.xml.parsers.ParserConfigurationException;
32 import javax.xml.transform.ErrorListener;
33 import javax.xml.transform.OutputKeys;
34 import javax.xml.transform.Result;
35 import javax.xml.transform.Source;
36 import javax.xml.transform.SourceLocator;
37 import javax.xml.transform.Transformer;
38 import javax.xml.transform.TransformerException;
39 import javax.xml.transform.URIResolver;
40 import javax.xml.transform.dom.DOMResult;
41 import javax.xml.transform.dom.DOMSource;
42 import javax.xml.transform.sax.SAXResult;
43 import javax.xml.transform.sax.SAXSource;
44 import javax.xml.transform.stream.StreamResult;
45 import javax.xml.transform.stream.StreamSource;
46
47 import org.apache.xalan.extensions.ExtensionsTable;
48 import org.apache.xalan.res.XSLMessages;
49 import org.apache.xalan.res.XSLTErrorResources;
50 import org.apache.xml.serializer.Method;
51 import org.apache.xml.serializer.Serializer;
52 import org.apache.xml.serializer.SerializerFactory;
53 import org.apache.xalan.templates.AVT;
54 import org.apache.xalan.templates.Constants;
55 import org.apache.xalan.templates.ElemAttributeSet;
56 import org.apache.xalan.templates.ElemForEach;
57 import org.apache.xalan.templates.ElemSort;
58 import org.apache.xalan.templates.ElemTemplate;
59 import org.apache.xalan.templates.ElemTemplateElement;
60 import org.apache.xalan.templates.ElemTextLiteral;
61 import org.apache.xalan.templates.ElemVariable;
62 import org.apache.xalan.templates.OutputProperties;
63 import org.apache.xalan.templates.Stylesheet;
64 import org.apache.xalan.templates.StylesheetComposed;
65 import org.apache.xalan.templates.StylesheetRoot;
66 import org.apache.xalan.templates.XUnresolvedVariable;
67 import org.apache.xalan.trace.GenerateEvent;
68 import org.apache.xalan.trace.TraceManager;
69 import org.apache.xml.dtm.DTM;
70 import org.apache.xml.dtm.DTMIterator;
71 import org.apache.xml.dtm.DTMManager;
72 import org.apache.xml.dtm.DTMWSFilter;
73 import org.apache.xml.serializer.ToHTMLSAXHandler;
74 import org.apache.xml.serializer.ToSAXHandler;
75 import org.apache.xml.serializer.ToTextSAXHandler;
76 import org.apache.xml.serializer.ToTextStream;
77 import org.apache.xml.serializer.ToXMLSAXHandler;
78 import org.apache.xml.serializer.SerializationHandler;
79 import org.apache.xml.utils.BoolStack;
80 import org.apache.xml.utils.DOMBuilder;
81 import org.apache.xml.utils.NodeVector;
82 import org.apache.xml.utils.ObjectPool;
83 import org.apache.xml.utils.ObjectStack;
84 import org.apache.xml.utils.QName;
85 import org.apache.xml.utils.SAXSourceLocator;
86 import org.apache.xml.utils.ThreadControllerWrapper;
87 import org.apache.xpath.Arg;
88 import org.apache.xpath.ExtensionsProvider;
89 import org.apache.xpath.VariableStack;
90 import org.apache.xpath.XPathContext;
91 import org.apache.xpath.functions.FuncExtFunction;
92 import org.apache.xpath.objects.XObject;
93 import org.xml.sax.Attributes;
94 import org.xml.sax.ContentHandler;
95 import org.xml.sax.SAXException;
96 import org.xml.sax.SAXNotRecognizedException;
97 import org.xml.sax.SAXNotSupportedException;
98 import org.xml.sax.ext.DeclHandler;
99 import org.xml.sax.ext.LexicalHandler;
100
101 /**
102 * This class implements the
103 * {@link javax.xml.transform.Transformer} interface, and is the core
104 * representation of the transformation execution.</p>
105 * @xsl.usage advanced
106 */
107 public class TransformerImpl extends Transformer
108 implements Runnable, DTMWSFilter, ExtensionsProvider, org.apache.xml.serializer.SerializerTrace
109 {
110
111 // Synch object to gaurd against setting values from the TrAX interface
112 // or reentry while the transform is going on.
113
114 /** NEEDSDOC Field m_reentryGuard */
115 private Boolean m_reentryGuard = new Boolean(true);
116
117 /**
118 * This is null unless we own the stream.
119 */
120 private java.io.FileOutputStream m_outputStream = null;
121
122 /**
123 * True if the parser events should be on the main thread,
124 * false if not. Experemental. Can not be set right now.
125 */
126 private boolean m_parserEventsOnMain = true;
127
128 /** The thread that the transformer is running on. */
129 private Thread m_transformThread;
130
131 /** The base URL of the source tree. */
132 private String m_urlOfSource = null;
133
134 /** The Result object at the start of the transform, if any. */
135 private Result m_outputTarget = null;
136
137 /**
138 * The output format object set by the user. May be null.
139 */
140 private OutputProperties m_outputFormat;
141
142
143 /**
144 * The content handler for the source input tree.
145 */
146 ContentHandler m_inputContentHandler;
147
148 /**
149 * The content handler for the result tree.
150 */
151 private ContentHandler m_outputContentHandler = null;
152
153 // /*
154 // * Use member variable to store param variables as they're
155 // * being created, use member variable so we don't
156 // * have to create a new vector every time.
157 // */
158 // private Vector m_newVars = new Vector();
159
160 /** The JAXP Document Builder, mainly to create Result Tree Fragments. */
161 DocumentBuilder m_docBuilder = null;
162
163 /**
164 * A pool of ResultTreeHandlers, for serialization of a subtree to text.
165 * Please note that each of these also holds onto a Text Serializer.
166 */
167 private ObjectPool m_textResultHandlerObjectPool =
168 new ObjectPool(ToTextStream.class);
169
170 /**
171 * Related to m_textResultHandlerObjectPool, this is a pool of
172 * StringWriters, which are passed to the Text Serializers.
173 * (I'm not sure if this is really needed any more. -sb)
174 */
175 private ObjectPool m_stringWriterObjectPool =
176 new ObjectPool(StringWriter.class);
177
178 /**
179 * A static text format object, which can be used over and
180 * over to create the text serializers.
181 */
182 private OutputProperties m_textformat = new OutputProperties(Method.TEXT);
183
184 // Commenteded out in response to problem reported by
185 // Nicola Brown <Nicola.Brown@jacobsrimell.com>
186 // /**
187 // * Flag to let us know if an exception should be reported inside the
188 // * postExceptionFromThread method. This is needed if the transform is
189 // * being generated from SAX events, and thus there is no central place
190 // * to report the exception from. (An exception is usually picked up in
191 // * the main thread from the transform thread in {@link #transform(Source source)}
192 // * from {@link #getExceptionThrown()}. )
193 // */
194 // private boolean m_reportInPostExceptionFromThread = false;
195
196 /**
197 * A node vector used as a stack to track the current
198 * ElemTemplateElement. Needed for the
199 * org.apache.xalan.transformer.TransformState interface,
200 * so a tool can discover the calling template. Note the use of an array
201 * for this limits the recursion depth to 4K.
202 */
203 ObjectStack m_currentTemplateElements
204 = new ObjectStack(XPathContext.RECURSIONLIMIT);
205
206 /** The top of the currentTemplateElements stack. */
207 //int m_currentTemplateElementsTop = 0;
208
209 /**
210 * A node vector used as a stack to track the current
211 * ElemTemplate that was matched.
212 * Needed for the
213 * org.apache.xalan.transformer.TransformState interface,
214 * so a tool can discover the matched template
215 */
216 Stack m_currentMatchTemplates = new Stack();
217
218 /**
219 * A node vector used as a stack to track the current
220 * node that was matched.
221 * Needed for the
222 * org.apache.xalan.transformer.TransformState interface,
223 * so a tool can discover the matched
224 * node.
225 */
226 NodeVector m_currentMatchedNodes = new NodeVector();
227
228 /**
229 * The root of a linked set of stylesheets.
230 */
231 private StylesheetRoot m_stylesheetRoot = null;
232
233 /**
234 * If this is set to true, do not warn about pattern
235 * match conflicts.
236 */
237 private boolean m_quietConflictWarnings = true;
238
239 /**
240 * The liason to the XML parser, so the XSL processor
241 * can handle included files, and the like, and do the
242 * initial parse of the XSL document.
243 */
244 private XPathContext m_xcontext;
245
246 /**
247 * Object to guard agains infinite recursion when
248 * doing queries.
249 */
250 private StackGuard m_stackGuard;
251
252 /**
253 * Output handler to bottleneck SAX events.
254 */
255 private SerializationHandler m_serializationHandler;
256
257 /** The key manager, which manages xsl:keys. */
258 private KeyManager m_keyManager = new KeyManager();
259
260 /**
261 * Stack for the purposes of flagging infinite recursion with
262 * attribute sets.
263 */
264 Stack m_attrSetStack = null;
265
266 /**
267 * The table of counters for xsl:number support.
268 * @see ElemNumber
269 */
270 CountersTable m_countersTable = null;
271
272 /**
273 * Is > 0 when we're processing a for-each.
274 */
275 BoolStack m_currentTemplateRuleIsNull = new BoolStack();
276
277 /**
278 * Keeps track of the result delivered by any EXSLT <code>func:result</code>
279 * instruction that has been executed for the currently active EXSLT
280 * <code>func:function</code>
281 */
282 ObjectStack m_currentFuncResult = new ObjectStack();
283
284 /**
285 * The message manager, which manages error messages, warning
286 * messages, and other types of message events.
287 */
288 private MsgMgr m_msgMgr;
289
290 /**
291 * This is a compile-time flag to turn off calling
292 * of trace listeners. Set this to false for optimization purposes.
293 */
294 public static boolean S_DEBUG = false;
295
296 /**
297 * The SAX error handler, where errors and warnings are sent.
298 */
299 private ErrorListener m_errorHandler =
300 new org.apache.xml.utils.DefaultErrorHandler();
301
302 /**
303 * The trace manager.
304 */
305 private TraceManager m_traceManager = new TraceManager(this);
306
307 /**
308 * If the transform thread throws an exception, the exception needs to
309 * be stashed away so that the main thread can pass it on to the
310 * client.
311 */
312 private Exception m_exceptionThrown = null;
313
314 /**
315 * The InputSource for the source tree, which is needed if the
316 * parse thread is not the main thread, in order for the parse
317 * thread's run method to get to the input source.
318 * (Delete this if reversing threads is outlawed. -sb)
319 */
320 private Source m_xmlSource;
321
322 /**
323 * This is needed for support of setSourceTreeDocForThread(Node doc),
324 * which must be called in order for the transform thread's run
325 * method to obtain the root of the source tree to be transformed.
326 */
327 private int m_doc;
328
329 /**
330 * If the the transform is on the secondary thread, we
331 * need to know when it is done, so we can return.
332 */
333 private boolean m_isTransformDone = false;
334
335 /** Flag to to tell if the tranformer needs to be reset. */
336 private boolean m_hasBeenReset = false;
337
338 /** NEEDSDOC Field m_shouldReset */
339 private boolean m_shouldReset = true;
340
341 /**
342 * NEEDSDOC Method setShouldReset
343 *
344 *
345 * NEEDSDOC @param shouldReset
346 */
347 public void setShouldReset(boolean shouldReset)
348 {
349 m_shouldReset = shouldReset;
350 }
351
352 /**
353 * A stack of current template modes.
354 */
355 private Stack m_modes = new Stack();
356
357 //==========================================================
358 // SECTION: Constructor
359 //==========================================================
360
361 /**
362 * Construct a TransformerImpl.
363 *
364 * @param stylesheet The root of the stylesheet tree.
365 */
366 public TransformerImpl(StylesheetRoot stylesheet)
367 // throws javax.xml.transform.TransformerException
368 {
369 setStylesheet(stylesheet);
370 setXPathContext(new XPathContext(this));
371 getXPathContext().setNamespaceContext(stylesheet);
372 m_stackGuard = new StackGuard(this);
373 }
374
375 // ================ ExtensionsTable ===================
376
377 /**
378 * The table of ExtensionHandlers.
379 */
380 private ExtensionsTable m_extensionsTable = null;
381
382 /**
383 * Get the extensions table object.
384 *
385 * @return The extensions table.
386 */
387 public ExtensionsTable getExtensionsTable()
388 {
389 return m_extensionsTable;
390 }
391
392 /**
393 * If the stylesheet contains extensions, set the extensions table object.
394 *
395 *
396 * @param sroot The stylesheet.
397 * @throws javax.xml.transform.TransformerException
398 */
399 void setExtensionsTable(StylesheetRoot sroot)
400 throws javax.xml.transform.TransformerException
401 {
402 try
403 {
404 if (sroot.getExtensions() != null)
405 m_extensionsTable = new ExtensionsTable(sroot);
406 }
407 catch (javax.xml.transform.TransformerException te)
408 {te.printStackTrace();}
409 }
410
411 //== Implementation of the XPath ExtensionsProvider interface.
412
413 public boolean functionAvailable(String ns, String funcName)
414 throws javax.xml.transform.TransformerException
415 {
416 return getExtensionsTable().functionAvailable(ns, funcName);
417 }
418
419 public boolean elementAvailable(String ns, String elemName)
420 throws javax.xml.transform.TransformerException
421 {
422 return getExtensionsTable().elementAvailable(ns, elemName);
423 }
424
425 public Object extFunction(String ns, String funcName,
426 Vector argVec, Object methodKey)
427 throws javax.xml.transform.TransformerException
428 {//System.out.println("TransImpl.extFunction() " + ns + " " + funcName +" " + getExtensionsTable());
429 return getExtensionsTable().extFunction(ns, funcName,
430 argVec, methodKey,
431 getXPathContext().getExpressionContext());
432 }
433
434 public Object extFunction(FuncExtFunction extFunction, Vector argVec)
435 throws javax.xml.transform.TransformerException
436 {
437 return getExtensionsTable().extFunction(extFunction, argVec,
438 getXPathContext().getExpressionContext());
439 }
440
441 //=========================
442
443 /**
444 * Reset the state. This needs to be called after a process() call
445 * is invoked, if the processor is to be used again.
446 */
447 public void reset()
448 {
449
450 if (!m_hasBeenReset && m_shouldReset)
451 {
452 m_hasBeenReset = true;
453
454 if (this.m_outputStream != null)
455 {
456 try
457 {
458 m_outputStream.close();
459 }
460 catch (java.io.IOException ioe){}
461 }
462
463 m_outputStream = null;
464
465 // I need to look more carefully at which of these really
466 // needs to be reset.
467 m_countersTable = null;
468
469 m_xcontext.reset();
470
471 m_xcontext.getVarStack().reset();
472 resetUserParameters();
473
474
475 m_currentTemplateElements.removeAllElements();
476 m_currentMatchTemplates.removeAllElements();
477 m_currentMatchedNodes.removeAllElements();
478
479 m_serializationHandler = null;
480 m_outputTarget = null;
481 m_keyManager = new KeyManager();
482 m_attrSetStack = null;
483 m_countersTable = null;
484 m_currentTemplateRuleIsNull = new BoolStack();
485 m_xmlSource = null;
486 m_doc = DTM.NULL;
487 m_isTransformDone = false;
488 m_transformThread = null;
489
490 // m_inputContentHandler = null;
491 // For now, reset the document cache each time.
492 m_xcontext.getSourceTreeManager().reset();
493 }
494
495 // m_reportInPostExceptionFromThread = false;
496 }
497
498 /**
499 * <code>getProperty</code> returns the current setting of the
500 * property described by the <code>property</code> argument.
501 *
502 * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory?
503 *
504 * @param property a <code>String</code> value
505 * @return a <code>boolean</code> value
506 */
507 public boolean getProperty(String property)
508 {
509 return false;
510 }
511
512 /**
513 * Set a runtime property for this <code>TransformerImpl</code>.
514 *
515 * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory?
516 *
517 * @param property a <code>String</code> value
518 * @param value an <code>Object</code> value
519 */
520 public void setProperty(String property, Object value)
521 {
522 }
523
524 // ========= Transformer Interface Implementation ==========
525
526 /**
527 * Get true if the parser events should be on the main thread,
528 * false if not. Experimental. Can not be set right now.
529 *
530 * @return true if the parser events should be on the main thread,
531 * false if not.
532 * @xsl.usage experimental
533 */
534 public boolean isParserEventsOnMain()
535 {
536 return m_parserEventsOnMain;
537 }
538
539 /**
540 * Get the thread that the transform process is on.
541 *
542 * @return The thread that the transform process is on, or null.
543 * @xsl.usage internal
544 */
545 public Thread getTransformThread()
546 {
547 return m_transformThread;
548 }
549
550 /**
551 * Get the thread that the transform process is on.
552 *
553 * @param t The transform thread, may be null.
554 * @xsl.usage internal
555 */
556 public void setTransformThread(Thread t)
557 {
558 m_transformThread = t;
559 }
560
561 /** NEEDSDOC Field m_hasTransformThreadErrorCatcher */
562 private boolean m_hasTransformThreadErrorCatcher = false;
563
564 /**
565 * Return true if the transform was initiated from the transform method,
566 * otherwise it was probably done from a pure parse events.
567 *
568 * NEEDSDOC ($objectName$) @return
569 */
570 public boolean hasTransformThreadErrorCatcher()
571 {
572 return m_hasTransformThreadErrorCatcher;
573 }
574
575 /**
576 * Process the source tree to SAX parse events.
577 * @param source The input for the source tree.
578 *
579 * @throws TransformerException
580 */
581 public void transform(Source source) throws TransformerException
582 {
583 transform(source, true);
584 }
585
586 /**
587 * Process the source tree to SAX parse events.
588 * @param source The input for the source tree.
589 * @param shouldRelease Flag indicating whether to release DTMManager.
590 *
591 * @throws TransformerException
592 */
593 public void transform(Source source, boolean shouldRelease) throws TransformerException
594 {
595
596 try
597 {
598
599 // Patch for bugzilla #13863. If we don't reset the namespaceContext
600 // then we will get a NullPointerException if transformer is reused
601 // (for stylesheets that use xsl:key). Not sure if this should go
602 // here or in reset(). -is
603 if(getXPathContext().getNamespaceContext() == null){
604 getXPathContext().setNamespaceContext(getStylesheet());
605 }
606 String base = source.getSystemId();
607
608 // If no systemID of the source, use the base of the stylesheet.
609 if(null == base)
610 {
611 base = m_stylesheetRoot.getBaseIdentifier();
612 }
613
614 // As a last resort, use the current user dir.
615 if(null == base)
616 {
617 String currentDir = "";
618 try {
619 currentDir = System.getProperty("user.dir");
620 }
621 catch (SecurityException se) {}// user.dir not accessible from applet
622
623 if (currentDir.startsWith(java.io.File.separator))
624 base = "file://" + currentDir;
625 else
626 base = "file:///" + currentDir;
627
628 base = base + java.io.File.separatorChar
629 + source.getClass().getName();
630 }
631 setBaseURLOfSource(base);
632 DTMManager mgr = m_xcontext.getDTMManager();
633 /*
634 * According to JAXP1.2, new SAXSource()/StreamSource()
635 * should create an empty input tree, with a default root node.
636 * new DOMSource()creates an empty document using DocumentBuilder.
637 * newDocument(); Use DocumentBuilder.newDocument() for all 3 situations,
638 * since there is no clear spec. how to create an empty tree when
639 * both SAXSource() and StreamSource() are used.
640 */
641 if ((source instanceof StreamSource && source.getSystemId()==null &&
642 ((StreamSource)source).getInputStream()==null &&
643 ((StreamSource)source).getReader()==null)||
644 (source instanceof SAXSource &&
645 ((SAXSource)source).getInputSource()==null &&
646 ((SAXSource)source).getXMLReader()==null )||
647 (source instanceof DOMSource && ((DOMSource)source).getNode()==null)){
648 try {
649 DocumentBuilderFactory builderF =
650 DocumentBuilderFactory.newInstance();
651 DocumentBuilder builder = builderF.newDocumentBuilder();
652 String systemID = source.getSystemId();
653 source = new DOMSource(builder.newDocument());
654
655 // Copy system ID from original, empty Source to new Source
656 if (systemID != null) {
657 source.setSystemId(systemID);
658 }
659 } catch (ParserConfigurationException e) {
660 fatalError(e);
661 }
662 }
663 DTM dtm = mgr.getDTM(source, false, this, true, true);
664 dtm.setDocumentBaseURI(base);
665
666 boolean hardDelete = true; // %REVIEW% I have to think about this. -sb
667
668 try
669 {
670 // NOTE: This will work because this is _NOT_ a shared DTM, and thus has
671 // only a single Document node. If it could ever be an RTF or other
672 // shared DTM, look at dtm.getDocumentRoot(nodeHandle).
673 this.transformNode(dtm.getDocument());
674 }
675 finally
676 {
677 if (shouldRelease)
678 mgr.release(dtm, hardDelete);
679 }
680
681 // Kick off the parse. When the ContentHandler gets
682 // the startDocument event, it will call transformNode( node ).
683 // reader.parse( xmlSource );
684 // This has to be done to catch exceptions thrown from
685 // the transform thread spawned by the STree handler.
686 Exception e = getExceptionThrown();
687
688 if (null != e)
689 {
690 if (e instanceof javax.xml.transform.TransformerException)
691 {
692 throw (javax.xml.transform.TransformerException) e;
693 }
694 else if (e instanceof org.apache.xml.utils.WrappedRuntimeException)
695 {
696 fatalError(
697 ((org.apache.xml.utils.WrappedRuntimeException) e).getException());
698 }
699 else
700 {
701 throw new javax.xml.transform.TransformerException(e);
702 }
703 }
704 else if (null != m_serializationHandler)
705 {
706 m_serializationHandler.endDocument();
707 }
708 }
709 catch (org.apache.xml.utils.WrappedRuntimeException wre)
710 {
711 Throwable throwable = wre.getException();
712
713 while (throwable
714 instanceof org.apache.xml.utils.WrappedRuntimeException)
715 {
716 throwable =
717 ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
718 }
719
720 fatalError(throwable);
721 }
722
723 // Patch attributed to David Eisenberg <david@catcode.com>
724 catch (org.xml.sax.SAXParseException spe)
725 {
726 fatalError(spe);
727 }
728 catch (org.xml.sax.SAXException se)
729 {
730 m_errorHandler.fatalError(new TransformerException(se));
731 }
732 finally
733 {
734 m_hasTransformThreadErrorCatcher = false;
735
736 // This looks to be redundent to the one done in TransformNode.
737 reset();
738 }
739 }
740
741 private void fatalError(Throwable throwable) throws TransformerException
742 {
743 if (throwable instanceof org.xml.sax.SAXParseException)
744 m_errorHandler.fatalError(new TransformerException(throwable.getMessage(),new SAXSourceLocator((org.xml.sax.SAXParseException)throwable)));
745 else
746 m_errorHandler.fatalError(new TransformerException(throwable));
747
748 }
749
750 /**
751 * Get the base URL of the source.
752 *
753 * @return The base URL of the source tree, or null.
754 */
755 public String getBaseURLOfSource()
756 {
757 return m_urlOfSource;
758 }
759
760 /**
761 * Get the base URL of the source.
762 *
763 *
764 * NEEDSDOC @param base
765 * @return The base URL of the source tree, or null.
766 */
767 public void setBaseURLOfSource(String base)
768 {
769 m_urlOfSource = base;
770 }
771
772 /**
773 * Get the original output target.
774 *
775 * @return The Result object used to kick of the transform or null.
776 */
777 public Result getOutputTarget()
778 {
779 return m_outputTarget;
780 }
781
782 /**
783 * Set the original output target. This is useful when using a SAX transform and
784 * supplying a ContentHandler or when the URI of the output target should
785 * not be the same as the systemID of the original output target.
786 *
787 *
788 * NEEDSDOC @param outputTarget
789 */
790 public void setOutputTarget(Result outputTarget)
791 {
792 m_outputTarget = outputTarget;
793 }
794
795 /**
796 * Get an output property that is in effect for the
797 * transformation. The property specified may be a property
798 * that was set with setOutputProperty, or it may be a
799 * property specified in the stylesheet.
800 *
801 * @param name A non-null String that specifies an output
802 * property name, which may be namespace qualified.
803 *
804 * NEEDSDOC @param qnameString
805 *
806 * @return The string value of the output property, or null
807 * if no property was found.
808 *
809 * @throws IllegalArgumentException If the property is not supported.
810 *
811 * @see javax.xml.transform.OutputKeys
812 */
813 public String getOutputProperty(String qnameString)
814 throws IllegalArgumentException
815 {
816
817 String value = null;
818 OutputProperties props = getOutputFormat();
819
820 value = props.getProperty(qnameString);
821
822 if (null == value)
823 {
824 if (!OutputProperties.isLegalPropertyKey(qnameString))
825 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: "
826 //+ qnameString);
827 }
828
829 return value;
830 }
831
832 /**
833 * Get the value of a property, without using the default properties. This
834 * can be used to test if a property has been explicitly set by the stylesheet
835 * or user.
836 *
837 * @param name The property name, which is a fully-qualified URI.
838 *
839 * NEEDSDOC @param qnameString
840 *
841 * @return The value of the property, or null if not found.
842 *
843 * @throws IllegalArgumentException If the property is not supported,
844 * and is not namespaced.
845 */
846 public String getOutputPropertyNoDefault(String qnameString)
847 throws IllegalArgumentException
848 {
849
850 String value = null;
851 OutputProperties props = getOutputFormat();
852
853 value = (String) props.getProperties().get(qnameString);
854
855 if (null == value)
856 {
857 if (!OutputProperties.isLegalPropertyKey(qnameString))
858 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: "
859 // + qnameString);
860 }
861
862 return value;
863 }
864
865 /**
866 * Set the value of a property. Recognized properties are:
867 *
868 * <p>"http://xml.apache.org/xslt/sourcebase" - the base URL for the
869 * source, which is needed when pure SAX ContentHandler transformation
870 * is to be done.</p>
871 *
872 * @param name The property name, which is a fully-qualified URI.
873 * @param value The requested value for the property.
874 * @throws IllegalArgumentException if the property name is not legal.
875 */
876 public void setOutputProperty(String name, String value)
877 throws IllegalArgumentException
878 {
879
880 synchronized (m_reentryGuard)
881 {
882
883 // Get the output format that was set by the user, otherwise get the
884 // output format from the stylesheet.
885 if (null == m_outputFormat)
886 {
887 m_outputFormat =
888 (OutputProperties) getStylesheet().getOutputComposed().clone();
889 }
890
891 if (!OutputProperties.isLegalPropertyKey(name))
892 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
893 //+ name);
894
895 m_outputFormat.setProperty(name, value);
896 }
897 }
898
899 /**
900 * Set the output properties for the transformation. These
901 * properties will override properties set in the templates
902 * with xsl:output.
903 *
904 * <p>If argument to this function is null, any properties
905 * previously set will be removed.</p>
906 *
907 * @param oformat A set of output properties that will be
908 * used to override any of the same properties in effect
909 * for the transformation.
910 *
911 * @see javax.xml.transform.OutputKeys
912 * @see java.util.Properties
913 *
914 * @throws IllegalArgumentException if any of the argument keys are not
915 * recognized and are not namespace qualified.
916 */
917 public void setOutputProperties(Properties oformat)
918 throws IllegalArgumentException
919 {
920
921 synchronized (m_reentryGuard)
922 {
923 if (null != oformat)
924 {
925
926 // See if an *explicit* method was set.
927 String method = (String) oformat.get(OutputKeys.METHOD);
928
929 if (null != method)
930 m_outputFormat = new OutputProperties(method);
931 else if(m_outputFormat==null)
932 m_outputFormat = new OutputProperties();
933
934 m_outputFormat.copyFrom(oformat);
935 // copyFrom does not set properties that have been already set, so
936 // this must be called after, which is a bit in the reverse from
937 // what one might think.
938 m_outputFormat.copyFrom(m_stylesheetRoot.getOutputProperties());
939 }
940 else {
941 // if oformat is null JAXP says that any props previously set are removed
942 // and we are to revert back to those in the templates object (i.e. Stylesheet).
943 m_outputFormat = null;
944 }
945 }
946 }
947
948 /**
949 * Get a copy of the output properties for the transformation. These
950 * properties will override properties set in the templates
951 * with xsl:output.
952 *
953 * <p>Note that mutation of the Properties object returned will not
954 * effect the properties that the transformation contains.</p>
955 *
956 * @return A copy of the set of output properties in effect
957 * for the next transformation.
958 *
959 * NEEDSDOC ($objectName$) @return
960 */
961 public Properties getOutputProperties()
962 {
963 return (Properties) getOutputFormat().getProperties().clone();
964 }
965
966 /**
967 * Create a result ContentHandler from a Result object, based
968 * on the current OutputProperties.
969 *
970 * @param outputTarget Where the transform result should go,
971 * should not be null.
972 *
973 * @return A valid ContentHandler that will create the
974 * result tree when it is fed SAX events.
975 *
976 * @throws TransformerException
977 */
978 public SerializationHandler createSerializationHandler(Result outputTarget)
979 throws TransformerException
980 {
981 SerializationHandler xoh =
982 createSerializationHandler(outputTarget, getOutputFormat());
983 return xoh;
984 }
985
986 /**
987 * Create a ContentHandler from a Result object and an OutputProperties.
988 *
989 * @param outputTarget Where the transform result should go,
990 * should not be null.
991 * @param format The OutputProperties object that will contain
992 * instructions on how to serialize the output.
993 *
994 * @return A valid ContentHandler that will create the
995 * result tree when it is fed SAX events.
996 *
997 * @throws TransformerException
998 */
999 public SerializationHandler createSerializationHandler(
1000 Result outputTarget, OutputProperties format)
1001 throws TransformerException
1002 {
1003
1004 SerializationHandler xoh;
1005
1006 // If the Result object contains a Node, then create
1007 // a ContentHandler that will add nodes to the input node.
1008 org.w3c.dom.Node outputNode = null;
1009
1010 if (outputTarget instanceof DOMResult)
1011 {
1012 outputNode = ((DOMResult) outputTarget).getNode();
1013
1014 org.w3c.dom.Document doc;
1015 short type;
1016
1017 if (null != outputNode)
1018 {
1019 type = outputNode.getNodeType();
1020 doc = (org.w3c.dom.Node.DOCUMENT_NODE == type)
1021 ? (org.w3c.dom.Document) outputNode
1022 : outputNode.getOwnerDocument();
1023 }
1024 else
1025 {
1026 doc = org.apache.xml.utils.DOMHelper.createDocument();
1027 outputNode = doc;
1028 type = outputNode.getNodeType();
1029
1030 ((DOMResult) outputTarget).setNode(outputNode);
1031 }
1032
1033 ContentHandler handler =
1034 (org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE == type)
1035 ? new DOMBuilder(doc, (org.w3c.dom.DocumentFragment) outputNode)
1036 : new DOMBuilder(doc, outputNode);
1037 String encoding = format.getProperty(OutputKeys.ENCODING);
1038 xoh = new ToXMLSAXHandler(handler, (LexicalHandler)handler, encoding);
1039 }
1040 else if (outputTarget instanceof SAXResult)
1041 {
1042 ContentHandler handler = ((SAXResult) outputTarget).getHandler();
1043
1044 if (null == handler)
1045 throw new IllegalArgumentException(
1046 "handler can not be null for a SAXResult");
1047
1048 LexicalHandler lexHandler;
1049 if (handler instanceof LexicalHandler)
1050 lexHandler = (LexicalHandler) handler;
1051 else
1052 lexHandler = null;
1053
1054 String encoding = format.getProperty(OutputKeys.ENCODING);
1055 String method = format.getProperty(OutputKeys.METHOD);
1056 if (org.apache.xml.serializer.Method.HTML.equals(method))
1057 {
1058 xoh = new ToHTMLSAXHandler(handler, lexHandler, encoding);
1059 }
1060 else if (org.apache.xml.serializer.Method.TEXT.equals(method))
1061 {
1062 xoh = new ToTextSAXHandler(handler, lexHandler, encoding);
1063 }
1064 else
1065 {
1066 ToXMLSAXHandler toXMLSAXHandler = new ToXMLSAXHandler(handler, lexHandler, encoding);
1067 toXMLSAXHandler.setShouldOutputNSAttr(false);
1068 xoh = toXMLSAXHandler;
1069
1070 }
1071
1072 String publicID = format.getProperty(OutputKeys.DOCTYPE_PUBLIC);
1073 String systemID = format.getProperty(OutputKeys.DOCTYPE_SYSTEM);
1074 if (systemID != null)
1075 xoh.setDoctypeSystem(systemID);
1076 if (publicID != null)
1077 xoh.setDoctypePublic(publicID);
1078
1079 if (handler instanceof TransformerClient) {
1080 XalanTransformState state = new XalanTransformState();
1081 ((TransformerClient)handler).setTransformState(state);
1082 ((ToSAXHandler)xoh).setTransformState(state);
1083 }
1084
1085
1086 }
1087
1088 // Otherwise, create a ContentHandler that will serialize the
1089 // result tree to either a stream or a writer.
1090 else if (outputTarget instanceof StreamResult)
1091 {
1092 StreamResult sresult = (StreamResult) outputTarget;
1093 String method = format.getProperty(OutputKeys.METHOD);
1094
1095 try
1096 {
1097 SerializationHandler serializer =
1098 (SerializationHandler) SerializerFactory.getSerializer(format.getProperties());
1099
1100 if (null != sresult.getWriter())
1101 serializer.setWriter(sresult.getWriter());
1102 else if (null != sresult.getOutputStream())
1103 serializer.setOutputStream(sresult.getOutputStream());
1104 else if (null != sresult.getSystemId())
1105 {
1106 String fileURL = sresult.getSystemId();
1107
1108 if (fileURL.startsWith("file:///"))
1109 {
1110 if (fileURL.substring(8).indexOf(":") >0)
1111 fileURL = fileURL.substring(8);
1112 else
1113 fileURL = fileURL.substring(7);
1114 }
1115
1116 m_outputStream = new java.io.FileOutputStream(fileURL);
1117
1118 serializer.setOutputStream(m_outputStream);
1119
1120 xoh = serializer;
1121 }
1122 else
1123 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!");
1124
1125 // handler = serializer.asContentHandler();
1126
1127 // this.setSerializer(serializer);
1128
1129 xoh = serializer;
1130 }
1131// catch (UnsupportedEncodingException uee)
1132// {
1133// throw new TransformerException(uee);
1134// }
1135 catch (IOException ioe)
1136 {
1137 throw new TransformerException(ioe);
1138 }
1139 }
1140 else
1141 {
1142 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE, new Object[]{outputTarget.getClass().getName()})); //"Can't transform to a Result of type "
1143 //+ outputTarget.getClass().getName()
1144 //+ "!");
1145 }
1146
1147 // before we forget, lets make the created handler hold a reference
1148 // to the current TransformImpl object
1149 xoh.setTransformer(this);
1150
1151 SourceLocator srcLocator = getStylesheet();
1152 xoh.setSourceLocator(srcLocator);
1153
1154
1155 return xoh;
1156
1157
1158 }
1159
1160 /**
1161 * Process the source tree to the output result.
1162 * @param xmlSource The input for the source tree.
1163 * @param outputTarget The output source target.
1164 *
1165 * @throws TransformerException
1166 */
1167 public void transform(Source xmlSource, Result outputTarget)
1168 throws TransformerException
1169 {
1170 transform(xmlSource, outputTarget, true);
1171 }
1172
1173 /**
1174 * Process the source tree to the output result.
1175 * @param xmlSource The input for the source tree.
1176 * @param outputTarget The output source target.
1177 * @param shouldRelease Flag indicating whether to release DTMManager.
1178 *
1179 * @throws TransformerException
1180 */
1181 public void transform(Source xmlSource, Result outputTarget, boolean shouldRelease)
1182 throws TransformerException
1183 {
1184
1185 synchronized (m_reentryGuard)
1186 {
1187 SerializationHandler xoh = createSerializationHandler(outputTarget);
1188 this.setSerializationHandler(xoh);
1189
1190 m_outputTarget = outputTarget;
1191
1192 transform(xmlSource, shouldRelease);
1193 }
1194 }
1195
1196 /**
1197 * Process the source node to the output result, if the
1198 * processor supports the "http://xml.org/trax/features/dom/input"
1199 * feature.
1200 * %REVIEW% Do we need a Node version of this?
1201 * @param node The input source node, which can be any valid DTM node.
1202 * @param outputTarget The output source target.
1203 *
1204 * @throws TransformerException
1205 */
1206 public void transformNode(int node, Result outputTarget)
1207 throws TransformerException
1208 {
1209
1210
1211 SerializationHandler xoh = createSerializationHandler(outputTarget);
1212 this.setSerializationHandler(xoh);
1213
1214 m_outputTarget = outputTarget;
1215
1216 transformNode(node);
1217 }
1218
1219 /**
1220 * Process the source node to the output result, if the
1221 * processor supports the "http://xml.org/trax/features/dom/input"
1222 * feature.
1223 * %REVIEW% Do we need a Node version of this?
1224 * @param node The input source node, which can be any valid DTM node.
1225 * @param outputTarget The output source target.
1226 *
1227 * @throws TransformerException
1228 */
1229 public void transformNode(int node) throws TransformerException
1230 {
1231 //dml
1232 setExtensionsTable(getStylesheet());
1233 // Make sure we're not writing to the same output content handler.
1234 synchronized (m_serializationHandler)
1235 {
1236 m_hasBeenReset = false;
1237
1238 XPathContext xctxt = getXPathContext();
1239 DTM dtm = xctxt.getDTM(node);
1240
1241 try
1242 {
1243 pushGlobalVars(node);
1244
1245 // ==========
1246 // Give the top-level templates a chance to pass information into
1247 // the context (this is mainly for setting up tables for extensions).
1248 StylesheetRoot stylesheet = this.getStylesheet();
1249 int n = stylesheet.getGlobalImportCount();
1250
1251 for (int i = 0; i < n; i++)
1252 {
1253 StylesheetComposed imported = stylesheet.getGlobalImport(i);
1254 int includedCount = imported.getIncludeCountComposed();
1255
1256 for (int j = -1; j < includedCount; j++)
1257 {
1258 Stylesheet included = imported.getIncludeComposed(j);
1259
1260 included.runtimeInit(this);
1261
1262 for (ElemTemplateElement child = included.getFirstChildElem();
1263 child != null; child = child.getNextSiblingElem())
1264 {
1265 child.runtimeInit(this);
1266 }
1267 }
1268 }
1269 // ===========
1270 // System.out.println("Calling applyTemplateToNode - "+Thread.currentThread().getName());
1271 DTMIterator dtmIter = new org.apache.xpath.axes.SelfIteratorNoPredicate();
1272 dtmIter.setRoot(node, xctxt);
1273 xctxt.pushContextNodeList(dtmIter);
1274 try
1275 {
1276 this.applyTemplateToNode(null, null, node);
1277 }
1278 finally
1279 {
1280 xctxt.popContextNodeList();
1281 }
1282 // m_stylesheetRoot.getStartRule().execute(this);
1283
1284 // System.out.println("Done with applyTemplateToNode - "+Thread.currentThread().getName());
1285 if (null != m_serializationHandler)
1286 {
1287 m_serializationHandler.endDocument();
1288 }
1289 }
1290 catch (Exception se)
1291 {
1292
1293 // System.out.println(Thread.currentThread().getName()+" threw an exception! "
1294 // +se.getMessage());
1295 // If an exception was thrown, we need to make sure that any waiting
1296 // handlers can terminate, which I guess is best done by sending
1297 // an endDocument.
1298
1299 // SAXSourceLocator
1300 while(se instanceof org.apache.xml.utils.WrappedRuntimeException)
1301 {
1302 Exception e = ((org.apache.xml.utils.WrappedRuntimeException)se).getException();
1303 if(null != e)
1304 se = e;
1305 }
1306
1307 if (null != m_serializationHandler)
1308 {
1309 try
1310 {
1311 if(se instanceof org.xml.sax.SAXParseException)
1312 m_serializationHandler.fatalError((org.xml.sax.SAXParseException)se);
1313 else if(se instanceof TransformerException)
1314 {
1315 TransformerException te = ((TransformerException)se);
1316 SAXSourceLocator sl = new SAXSourceLocator( te.getLocator() );
1317 m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(te.getMessage(), sl, te));
1318 }
1319 else
1320 {
1321 m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(se.getMessage(), new SAXSourceLocator(), se));
1322 }
1323 }
1324 catch (Exception e){}
1325 }
1326
1327 if(se instanceof TransformerException)
1328 {
1329 m_errorHandler.fatalError((TransformerException)se);
1330 }
1331 else if(se instanceof org.xml.sax.SAXParseException)
1332 {
1333 m_errorHandler.fatalError(new TransformerException(se.getMessage(),
1334 new SAXSourceLocator((org.xml.sax.SAXParseException)se),
1335 se));
1336 }
1337 else
1338 {
1339 m_errorHandler.fatalError(new TransformerException(se));
1340 }
1341
1342 }
1343 finally
1344 {
1345 this.reset();
1346 }
1347 }
1348 }
1349
1350 /**
1351 * Get a SAX2 ContentHandler for the input.
1352 *
1353 * @return A valid ContentHandler, which should never be null, as
1354 * long as getFeature("http://xml.org/trax/features/sax/input")
1355 * returns true.
1356 */
1357 public ContentHandler getInputContentHandler()
1358 {
1359 return getInputContentHandler(false);
1360 }
1361
1362 /**
1363 * Get a SAX2 ContentHandler for the input.
1364 *
1365 * @param doDocFrag true if a DocumentFragment should be created as
1366 * the root, rather than a Document.
1367 *
1368 * @return A valid ContentHandler, which should never be null, as
1369 * long as getFeature("http://xml.org/trax/features/sax/input")
1370 * returns true.
1371 */
1372 public ContentHandler getInputContentHandler(boolean doDocFrag)
1373 {
1374
1375 if (null == m_inputContentHandler)
1376 {
1377
1378 // if(null == m_urlOfSource && null != m_stylesheetRoot)
1379 // m_urlOfSource = m_stylesheetRoot.getBaseIdentifier();
1380 m_inputContentHandler = new TransformerHandlerImpl(this, doDocFrag,
1381 m_urlOfSource);
1382 }
1383
1384 return m_inputContentHandler;
1385 }
1386
1387 /**
1388 * Get a SAX2 DeclHandler for the input.
1389 * @return A valid DeclHandler, which should never be null, as
1390 * long as getFeature("http://xml.org/trax/features/sax/input")
1391 * returns true.
1392 */
1393 public DeclHandler getInputDeclHandler()
1394 {
1395
1396 if (m_inputContentHandler instanceof DeclHandler)
1397 return (DeclHandler) m_inputContentHandler;
1398 else
1399 return null;
1400 }
1401
1402 /**
1403 * Get a SAX2 LexicalHandler for the input.
1404 * @return A valid LexicalHandler, which should never be null, as
1405 * long as getFeature("http://xml.org/trax/features/sax/input")
1406 * returns true.
1407 */
1408 public LexicalHandler getInputLexicalHandler()
1409 {
1410
1411 if (m_inputContentHandler instanceof LexicalHandler)
1412 return (LexicalHandler) m_inputContentHandler;
1413 else
1414 return null;
1415 }
1416
1417 /**
1418 * Set the output properties for the transformation. These
1419 * properties will override properties set in the templates
1420 * with xsl:output.
1421 *
1422 * @param oformat A valid OutputProperties object (which will
1423 * not be mutated), or null.
1424 */
1425 public void setOutputFormat(OutputProperties oformat)
1426 {
1427 m_outputFormat = oformat;
1428 }
1429
1430 /**
1431 * Get the output properties used for the transformation.
1432 *
1433 * @return the output format that was set by the user,
1434 * otherwise the output format from the stylesheet.
1435 */
1436 public OutputProperties getOutputFormat()
1437 {
1438
1439 // Get the output format that was set by the user, otherwise get the
1440 // output format from the stylesheet.
1441 OutputProperties format = (null == m_outputFormat)
1442 ? getStylesheet().getOutputComposed()
1443 : m_outputFormat;
1444
1445 return format;
1446 }
1447
1448 /**
1449 * Set a parameter for the templates.
1450 *
1451 * @param name The name of the parameter.
1452 * @param namespace The namespace of the parameter.
1453 * @param value The value object. This can be any valid Java object
1454 * -- it's up to the processor to provide the proper
1455 * coersion to the object, or simply pass it on for use
1456 * in extensions.
1457 */
1458 public void setParameter(String name, String namespace, Object value)
1459 {
1460
1461 VariableStack varstack = getXPathContext().getVarStack();
1462 QName qname = new QName(namespace, name);
1463 XObject xobject = XObject.create(value, getXPathContext());
1464
1465 StylesheetRoot sroot = m_stylesheetRoot;
1466 Vector vars = sroot.getVariablesAndParamsComposed();
1467 int i = vars.size();
1468 while (--i >= 0)
1469 {
1470 ElemVariable variable = (ElemVariable)vars.elementAt(i);
1471 if(variable.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE &&
1472 variable.getName().equals(qname))
1473 {
1474 varstack.setGlobalVariable(i, xobject);
1475 }
1476 }
1477 }
1478
1479 /** NEEDSDOC Field m_userParams */
1480 Vector m_userParams;
1481
1482 /**
1483 * Set a parameter for the transformation.
1484 *
1485 * @param name The name of the parameter,
1486 * which may have a namespace URI.
1487 * @param value The value object. This can be any valid Java object
1488 * -- it's up to the processor to provide the proper
1489 * coersion to the object, or simply pass it on for use
1490 * in extensions.
1491 */
1492 public void setParameter(String name, Object value)
1493 {
1494
1495 if (value == null) {
1496 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name}));
1497 }
1498
1499 StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
1500
1501 try
1502 {
1503
1504 // The first string might be the namespace, or it might be
1505 // the local name, if the namespace is null.
1506 String s1 = tokenizer.nextToken();
1507 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
1508
1509 if (null == m_userParams)
1510 m_userParams = new Vector();
1511
1512 if (null == s2)
1513 {
1514 replaceOrPushUserParam(new QName(s1), XObject.create(value, getXPathContext()));
1515 setParameter(s1, null, value);
1516 }
1517 else
1518 {
1519 replaceOrPushUserParam(new QName(s1, s2), XObject.create(value, getXPathContext()));
1520 setParameter(s2, s1, value);
1521 }
1522 }
1523 catch (java.util.NoSuchElementException nsee)
1524 {
1525
1526 // Should throw some sort of an error.
1527 }
1528 }
1529
1530 /**
1531 * NEEDSDOC Method replaceOrPushUserParam
1532 *
1533 *
1534 * NEEDSDOC @param qname
1535 * NEEDSDOC @param xval
1536 */
1537 private void replaceOrPushUserParam(QName qname, XObject xval)
1538 {
1539
1540 int n = m_userParams.size();
1541
1542 for (int i = n - 1; i >= 0; i--)
1543 {
1544 Arg arg = (Arg) m_userParams.elementAt(i);
1545
1546 if (arg.getQName().equals(qname))
1547 {
1548 m_userParams.setElementAt(new Arg(qname, xval, true), i);
1549
1550 return;
1551 }
1552 }
1553
1554 m_userParams.addElement(new Arg(qname, xval, true));
1555 }
1556
1557 /**
1558 * Get a parameter that was explicitly set with setParameter
1559 * or setParameters.
1560 *
1561 *
1562 * NEEDSDOC @param name
1563 * @return A parameter that has been set with setParameter
1564 * or setParameters,
1565 * *not* all the xsl:params on the stylesheet (which require
1566 * a transformation Source to be evaluated).
1567 */
1568 public Object getParameter(String name)
1569 {
1570
1571 try
1572 {
1573
1574 // VariableStack varstack = getXPathContext().getVarStack();
1575 // The first string might be the namespace, or it might be
1576 // the local name, if the namespace is null.
1577 QName qname = QName.getQNameFromString(name);
1578
1579 if (null == m_userParams)
1580 return null;
1581
1582 int n = m_userParams.size();
1583
1584 for (int i = n - 1; i >= 0; i--)
1585 {
1586 Arg arg = (Arg) m_userParams.elementAt(i);
1587
1588 if (arg.getQName().equals(qname))
1589 {
1590 return arg.getVal().object();
1591 }
1592 }
1593
1594 return null;
1595 }
1596 catch (java.util.NoSuchElementException nsee)
1597 {
1598
1599 // Should throw some sort of an error.
1600 return null;
1601 }
1602 }
1603
1604 /**
1605 * Reset parameters that the user specified for the transformation.
1606 * Called during transformer.reset() after we have cleared the
1607 * variable stack. We need to make sure that user params are
1608 * reset so that the transformer object can be reused.
1609 */
1610 private void resetUserParameters()
1611 {
1612
1613 try
1614 {
1615
1616 if (null == m_userParams)
1617 return;
1618
1619 int n = m_userParams.size();
1620 for (int i = n - 1; i >= 0; i--)
1621 {
1622 Arg arg = (Arg) m_userParams.elementAt(i);
1623 QName name = arg.getQName();
1624 // The first string might be the namespace, or it might be
1625 // the local name, if the namespace is null.
1626 String s1 = name.getNamespace();
1627 String s2 = name.getLocalPart();
1628
1629 setParameter(s2, s1, arg.getVal().object());
1630
1631 }
1632
1633 }
1634 catch (java.util.NoSuchElementException nsee)
1635 {
1636 // Should throw some sort of an error.
1637
1638 }
1639 }
1640
1641 /**
1642 * Set a bag of parameters for the transformation. Note that
1643 * these will not be additive, they will replace the existing
1644 * set of parameters.
1645 *
1646 * @param name The name of the parameter,
1647 * which may have a namespace URI.
1648 * @param value The value object. This can be any valid Java object
1649 * -- it's up to the processor to provide the proper
1650 * coersion to the object, or simply pass it on for use
1651 * in extensions.
1652 *
1653 * NEEDSDOC @param params
1654 */
1655 public void setParameters(Properties params)
1656 {
1657
1658 clearParameters();
1659
1660 Enumeration names = params.propertyNames();
1661
1662 while (names.hasMoreElements())
1663 {
1664 String name = params.getProperty((String) names.nextElement());
1665 StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
1666
1667 try
1668 {
1669
1670 // The first string might be the namespace, or it might be
1671 // the local name, if the namespace is null.
1672 String s1 = tokenizer.nextToken();
1673 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
1674
1675 if (null == s2)
1676 setParameter(s1, null, params.getProperty(name));
1677 else
1678 setParameter(s2, s1, params.getProperty(name));
1679 }
1680 catch (java.util.NoSuchElementException nsee)
1681 {
1682
1683 // Should throw some sort of an error.
1684 }
1685 }
1686 }
1687
1688 /**
1689 * Reset the parameters to a null list.
1690 */
1691 public void clearParameters()
1692 {
1693
1694 synchronized (m_reentryGuard)
1695 {
1696 VariableStack varstack = new VariableStack();
1697
1698 m_xcontext.setVarStack(varstack);
1699
1700 m_userParams = null;
1701 }
1702 }
1703
1704
1705 /**
1706 * Internal -- push the global variables from the Stylesheet onto
1707 * the context's runtime variable stack.
1708 * <p>If we encounter a variable
1709 * that is already defined in the variable stack, we ignore it. This
1710 * is because the second variable definition will be at a lower import
1711 * precedence. Presumably, global"variables at the same import precedence
1712 * with the same name will have been caught during the recompose process.
1713 * <p>However, if we encounter a parameter that is already defined in the
1714 * variable stack, we need to see if this is a parameter whose value was
1715 * supplied by a setParameter call. If so, we need to "receive" the one
1716 * already in the stack, ignoring this one. If it is just an earlier
1717 * xsl:param or xsl:variable definition, we ignore it using the same
1718 * reasoning as explained above for the variable.
1719 *
1720 * @param contextNode The root of the source tree, can't be null.
1721 *
1722 * @throws TransformerException
1723 */
1724 protected void pushGlobalVars(int contextNode) throws TransformerException
1725 {
1726
1727 XPathContext xctxt = m_xcontext;
1728 VariableStack vs = xctxt.getVarStack();
1729 StylesheetRoot sr = getStylesheet();
1730 Vector vars = sr.getVariablesAndParamsComposed();
1731
1732 int i = vars.size();
1733 vs.link(i);
1734
1735 while (--i >= 0)
1736 {
1737 ElemVariable v = (ElemVariable) vars.elementAt(i);
1738
1739 // XObject xobj = v.getValue(this, contextNode);
1740 XObject xobj = new XUnresolvedVariable(v, contextNode, this,
1741 vs.getStackFrame(), 0, true);
1742
1743 if(null == vs.elementAt(i))
1744 vs.setGlobalVariable(i, xobj);
1745 }
1746
1747 }
1748
1749 /**
1750 * Set an object that will be used to resolve URIs used in
1751 * document(), etc.
1752 * @param resolver An object that implements the URIResolver interface,
1753 * or null.
1754 */
1755 public void setURIResolver(URIResolver resolver)
1756 {
1757
1758 synchronized (m_reentryGuard)
1759 {
1760 m_xcontext.getSourceTreeManager().setURIResolver(resolver);
1761 }
1762 }
1763
1764 /**
1765 * Get an object that will be used to resolve URIs used in
1766 * document(), etc.
1767 *
1768 * @return An object that implements the URIResolver interface,
1769 * or null.
1770 */
1771 public URIResolver getURIResolver()
1772 {
1773 return m_xcontext.getSourceTreeManager().getURIResolver();
1774 }
1775
1776 // ======== End Transformer Implementation ========
1777
1778 /**
1779 * Set the content event handler.
1780 *
1781 * @param resolver The new content handler.
1782 *
1783 * NEEDSDOC @param handler
1784 * @throws java.lang.NullPointerException If the handler
1785 * is null.
1786 * @see org.xml.sax.XMLReader#setContentHandler
1787 */
1788 public void setContentHandler(ContentHandler handler)
1789 {
1790
1791 if (handler == null)
1792 {
1793 throw new NullPointerException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_CONTENT_HANDLER, null)); //"Null content handler");
1794 }
1795 else
1796 {
1797 m_outputContentHandler = handler;
1798
1799 if (null == m_serializationHandler)
1800 {
1801 ToXMLSAXHandler h = new ToXMLSAXHandler();
1802 h.setContentHandler(handler);
1803 h.setTransformer(this);
1804
1805 m_serializationHandler = h;
1806 }
1807 else
1808 m_serializationHandler.setContentHandler(handler);
1809 }
1810 }
1811
1812 /**
1813 * Get the content event handler.
1814 *
1815 * @return The current content handler, or null if none was set.
1816 * @see org.xml.sax.XMLReader#getContentHandler
1817 */
1818 public ContentHandler getContentHandler()
1819 {
1820 return m_outputContentHandler;
1821 }
1822
1823 /**
1824 * Given a stylesheet element, create a result tree fragment from it's
1825 * contents. The fragment will be built within the shared RTF DTM system
1826 * used as a variable stack.
1827 * @param templateParent The template element that holds the fragment.
1828 * @return the NodeHandle for the root node of the resulting RTF.
1829 *
1830 * @throws TransformerException
1831 * @xsl.usage advanced
1832 */
1833 public int transformToRTF(ElemTemplateElement templateParent)
1834 throws TransformerException
1835 {
1836 // Retrieve a DTM to contain the RTF. At this writing, this may be a
1837 // multi-document DTM (SAX2RTFDTM).
1838 DTM dtmFrag = m_xcontext.getRTFDTM();
1839 return transformToRTF(templateParent,dtmFrag);
1840 }
1841
1842 /**
1843 * Given a stylesheet element, create a result tree fragment from it's
1844 * contents. The fragment will also use the shared DTM system, but will
1845 * obtain its space from the global variable pool rather than the dynamic
1846 * variable stack. This allows late binding of XUnresolvedVariables without
1847 * the risk that their content will be discarded when the variable stack
1848 * is popped.
1849 *
1850 * @param templateParent The template element that holds the fragment.
1851 * @return the NodeHandle for the root node of the resulting RTF.
1852 *
1853 * @throws TransformerException
1854 * @xsl.usage advanced
1855 */
1856 public int transformToGlobalRTF(ElemTemplateElement templateParent)
1857 throws TransformerException
1858 {
1859 // Retrieve a DTM to contain the RTF. At this writing, this may be a
1860 // multi-document DTM (SAX2RTFDTM).
1861 DTM dtmFrag = m_xcontext.getGlobalRTFDTM();
1862 return transformToRTF(templateParent,dtmFrag);
1863 }
1864
1865 /**
1866 * Given a stylesheet element, create a result tree fragment from it's
1867 * contents.
1868 * @param templateParent The template element that holds the fragment.
1869 * @param dtmFrag The DTM to write the RTF into
1870 * @return the NodeHandle for the root node of the resulting RTF.
1871 *
1872 * @throws TransformerException
1873 * @xsl.usage advanced
1874 */
1875 private int transformToRTF(ElemTemplateElement templateParent,DTM dtmFrag)
1876 throws TransformerException
1877 {
1878
1879 XPathContext xctxt = m_xcontext;
1880
1881 ContentHandler rtfHandler = dtmFrag.getContentHandler();
1882
1883 // Obtain the ResultTreeFrag's root node.
1884 // NOTE: In SAX2RTFDTM, this value isn't available until after
1885 // the startDocument has been issued, so assignment has been moved
1886 // down a bit in the code.
1887 int resultFragment; // not yet reliably = dtmFrag.getDocument();
1888
1889 // Save the current result tree handler.
1890 SerializationHandler savedRTreeHandler = this.m_serializationHandler;
1891
1892
1893 // And make a new handler for the RTF.
1894 ToSAXHandler h = new ToXMLSAXHandler();
1895 h.setContentHandler(rtfHandler);
1896 h.setTransformer(this);
1897
1898 // Replace the old handler (which was already saved)
1899 m_serializationHandler = h;
1900
1901 // use local variable for the current handler
1902 SerializationHandler rth = m_serializationHandler;
1903
1904 try
1905 {
1906 rth.startDocument();
1907
1908 // startDocument is "bottlenecked" in RTH. We need it acted upon immediately,
1909 // to set the DTM's state as in-progress, so that if the xsl:variable's body causes
1910 // further RTF activity we can keep that from bashing this DTM.
1911 rth.flushPending();
1912
1913 try
1914 {
1915
1916 // Do the transformation of the child elements.
1917 executeChildTemplates(templateParent, true);
1918
1919 // Make sure everything is flushed!
1920 rth.flushPending();
1921
1922 // Get the document ID. May not exist until the RTH has not only
1923 // received, but flushed, the startDocument, and may be invalid
1924 // again after the document has been closed (still debating that)
1925 // ... so waiting until just before the end seems simplest/safest.
1926 resultFragment = dtmFrag.getDocument();
1927 }
1928 finally
1929 {
1930 rth.endDocument();
1931 }
1932 }
1933 catch (org.xml.sax.SAXException se)
1934 {
1935 throw new TransformerException(se);
1936 }
1937 finally
1938 {
1939
1940 // Restore the previous result tree handler.
1941 this.m_serializationHandler = savedRTreeHandler;
1942 }
1943
1944 return resultFragment;
1945 }
1946
1947 /**
1948 * Get the StringWriter pool, so that StringWriter
1949 * objects may be reused.
1950 *
1951 * @return The string writer pool, not null.
1952 * @xsl.usage internal
1953 */
1954 public ObjectPool getStringWriterPool()
1955 {
1956 return m_stringWriterObjectPool;
1957 }
1958
1959 /**
1960 * Take the contents of a template element, process it, and
1961 * convert it to a string.
1962 *
1963 * @param elem The parent element whose children will be output
1964 * as a string.
1965 * @param transformer The XSLT transformer instance.
1966 * @param sourceNode The current source node context.
1967 * @param mode The current xslt mode.
1968 *
1969 * @return The stringized result of executing the elements children.
1970 *
1971 * @throws TransformerException
1972 * @xsl.usage advanced
1973 */
1974 public String transformToString(ElemTemplateElement elem)
1975 throws TransformerException
1976 {
1977 ElemTemplateElement firstChild = elem.getFirstChildElem();
1978 if(null == firstChild)
1979 return "";
1980 if(elem.hasTextLitOnly() && org.apache.xalan.processor.TransformerFactoryImpl.m_optimize)
1981 {
1982 return ((ElemTextLiteral)firstChild).getNodeValue();
1983 }
1984
1985 // Save the current result tree handler.
1986 SerializationHandler savedRTreeHandler = this.m_serializationHandler;
1987
1988 // Create a Serializer object that will handle the SAX events
1989 // and build the ResultTreeFrag nodes.
1990 StringWriter sw = (StringWriter) m_stringWriterObjectPool.getInstance();
1991
1992 m_serializationHandler =
1993 (ToTextStream) m_textResultHandlerObjectPool.getInstance();
1994
1995 if (null == m_serializationHandler)
1996 {
1997 // if we didn't get one from the pool, go make a new one
1998
1999
2000 Serializer serializer = org.apache.xml.serializer.SerializerFactory.getSerializer(
2001 m_textformat.getProperties());
2002 m_serializationHandler = (SerializationHandler) serializer;
2003 }
2004
2005 m_serializationHandler.setTransformer(this);
2006 m_serializationHandler.setWriter(sw);
2007
2008
2009 String result;
2010
2011 try
2012 {
2013 /* Don't call startDocument, the SerializationHandler will
2014 * generate its own internal startDocument call anyways
2015 */
2016 // this.m_serializationHandler.startDocument();
2017
2018 // Do the transformation of the child elements.
2019 executeChildTemplates(elem, true);
2020 this.m_serializationHandler.endDocument();
2021
2022 result = sw.toString();
2023 }
2024 catch (org.xml.sax.SAXException se)
2025 {
2026 throw new TransformerException(se);
2027 }
2028 finally
2029 {
2030 sw.getBuffer().setLength(0);
2031
2032 try
2033 {
2034 sw.close();
2035 }
2036 catch (Exception ioe){}
2037
2038 m_stringWriterObjectPool.freeInstance(sw);
2039 m_serializationHandler.reset();
2040 m_textResultHandlerObjectPool.freeInstance(m_serializationHandler);
2041
2042 // Restore the previous result tree handler.
2043 m_serializationHandler = savedRTreeHandler;
2044 }
2045
2046 return result;
2047 }
2048
2049 /**
2050 * Given an element and mode, find the corresponding
2051 * template and process the contents.
2052 *
2053 * @param xslInstruction The calling element.
2054 * @param template The template to use if xsl:for-each, or null.
2055 * @param child The source context node.
2056 * @param mode The current mode, may be null.
2057 * @throws TransformerException
2058 * @return true if applied a template, false if not.
2059 * @xsl.usage advanced
2060 */
2061 public boolean applyTemplateToNode(ElemTemplateElement xslInstruction, // xsl:apply-templates or xsl:for-each
2062 ElemTemplate template, int child)
2063 throws TransformerException
2064 {
2065
2066 DTM dtm = m_xcontext.getDTM(child);
2067 short nodeType = dtm.getNodeType(child);
2068 boolean isDefaultTextRule = false;
2069 boolean isApplyImports = false;
2070
2071 if (null == template)
2072 {
2073 int maxImportLevel, endImportLevel=0;
2074 isApplyImports = ((xslInstruction == null)
2075 ? false
2076 : xslInstruction.getXSLToken()
2077 == Constants.ELEMNAME_APPLY_IMPORTS);
2078
2079 if (isApplyImports)
2080 {
2081 maxImportLevel =
2082 xslInstruction.getStylesheetComposed().getImportCountComposed() - 1;
2083 endImportLevel =
2084 xslInstruction.getStylesheetComposed().getEndImportCountComposed();
2085 }
2086 else
2087 {
2088 maxImportLevel = -1;
2089 }
2090
2091 // If we're trying an xsl:apply-imports at the top level (ie there are no
2092 // imported stylesheets), we need to indicate that there is no matching template.
2093 // The above logic will calculate a maxImportLevel of -1 which indicates
2094 // that we should find any template. This is because a value of -1 for
2095 // maxImportLevel has a special meaning. But we don't want that.
2096 // We want to match -no- templates. See bugzilla bug 1170.
2097 if (isApplyImports && (maxImportLevel == -1))
2098 {
2099 template = null;
2100 }
2101 else
2102 {
2103
2104 // Find the XSL template that is the best match for the
2105 // element.
2106 XPathContext xctxt = m_xcontext;
2107
2108 try
2109 {
2110 xctxt.pushNamespaceContext(xslInstruction);
2111
2112 QName mode = this.getMode();
2113
2114 if (isApplyImports)
2115 template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode,
2116 maxImportLevel, endImportLevel, m_quietConflictWarnings, dtm);
2117 else
2118 template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode,
2119 m_quietConflictWarnings, dtm);
2120
2121 }
2122 finally
2123 {
2124 xctxt.popNamespaceContext();
2125 }
2126 }
2127
2128 // If that didn't locate a node, fall back to a default template rule.
2129 // See http://www.w3.org/TR/xslt#built-in-rule.
2130 if (null == template)
2131 {
2132 switch (nodeType)
2133 {
2134 case DTM.DOCUMENT_FRAGMENT_NODE :
2135 case DTM.ELEMENT_NODE :
2136 template = m_stylesheetRoot.getDefaultRule();
2137 break;
2138 case DTM.CDATA_SECTION_NODE :
2139 case DTM.TEXT_NODE :
2140 case DTM.ATTRIBUTE_NODE :
2141 template = m_stylesheetRoot.getDefaultTextRule();
2142 isDefaultTextRule = true;
2143 break;
2144 case DTM.DOCUMENT_NODE :
2145 template = m_stylesheetRoot.getDefaultRootRule();
2146 break;
2147 default :
2148
2149 // No default rules for processing instructions and the like.
2150 return false;
2151 }
2152 }
2153 }
2154
2155 // If we are processing the default text rule, then just clone
2156 // the value directly to the result tree.
2157 try
2158 {
2159 pushElemTemplateElement(template);
2160 m_xcontext.pushCurrentNode(child);
2161 pushPairCurrentMatched(template, child);
2162
2163 // Fix copy copy29 test.
2164 if (!isApplyImports) {
2165 DTMIterator cnl = new org.apache.xpath.NodeSetDTM(child, m_xcontext.getDTMManager());
2166 m_xcontext.pushContextNodeList(cnl);
2167 }
2168
2169 if (isDefaultTextRule)
2170 {
2171 switch (nodeType)
2172 {
2173 case DTM.CDATA_SECTION_NODE :
2174 case DTM.TEXT_NODE :
2175 ClonerToResultTree.cloneToResultTree(child, nodeType,
2176 dtm, getResultTreeHandler(), false);
2177 break;
2178 case DTM.ATTRIBUTE_NODE :
2179 dtm.dispatchCharactersEvents(child, getResultTreeHandler(), false);
2180 break;
2181 }
2182 }
2183 else
2184 {
2185
2186 // Fire a trace event for the template.
2187
2188 if (TransformerImpl.S_DEBUG)
2189 getTraceManager().fireTraceEvent(template);
2190 // And execute the child templates.
2191 // 9/11/00: If template has been compiled, hand off to it
2192 // since much (most? all?) of the processing has been inlined.
2193 // (It would be nice if there was a single entry point that
2194 // worked for both... but the interpretive system works by
2195 // having the Tranformer execute the children, while the
2196 // compiled obviously has to run its own code. It's
2197 // also unclear that "execute" is really the right name for
2198 // that entry point.)
2199 m_xcontext.setSAXLocator(template);
2200 // m_xcontext.getVarStack().link();
2201 m_xcontext.getVarStack().link(template.m_frameSize);
2202 executeChildTemplates(template, true);
2203
2204 if (TransformerImpl.S_DEBUG)
2205 getTraceManager().fireTraceEndEvent(template);
2206 }
2207 }
2208 catch (org.xml.sax.SAXException se)
2209 {
2210 throw new TransformerException(se);
2211 }
2212 finally
2213 {
2214 if (!isDefaultTextRule)
2215 m_xcontext.getVarStack().unlink();
2216 m_xcontext.popCurrentNode();
2217 if (!isApplyImports) {
2218 m_xcontext.popContextNodeList();
2219 popCurrentMatched();
2220 }
2221 popElemTemplateElement();
2222 }
2223
2224 return true;
2225 }
2226
2227
2228 /**
2229 * Execute each of the children of a template element. This method
2230 * is only for extension use.
2231 *
2232 * @param elem The ElemTemplateElement that contains the children
2233 * that should execute.
2234 * @param sourceNode The current context node.
2235 * NEEDSDOC @param context
2236 * @param mode The current mode.
2237 * @param handler The ContentHandler to where the result events
2238 * should be fed.
2239 *
2240 * @throws TransformerException
2241 * @xsl.usage advanced
2242 */
2243 public void executeChildTemplates(
2244 ElemTemplateElement elem, org.w3c.dom.Node context, QName mode, ContentHandler handler)
2245 throws TransformerException
2246 {
2247
2248 XPathContext xctxt = m_xcontext;
2249
2250 try
2251 {
2252 if(null != mode)
2253 pushMode(mode);
2254 xctxt.pushCurrentNode(xctxt.getDTMHandleFromNode(context));
2255 executeChildTemplates(elem, handler);
2256 }
2257 finally
2258 {
2259 xctxt.popCurrentNode();
2260
2261 // I'm not sure where or why this was here. It is clearly in
2262 // error though, without a corresponding pushMode().
2263 if (null != mode)
2264 popMode();
2265 }
2266 }
2267
2268 /**
2269 * Execute each of the children of a template element.
2270 *
2271 * @param transformer The XSLT transformer instance.
2272 *
2273 * @param elem The ElemTemplateElement that contains the children
2274 * that should execute.
2275 * @param sourceNode The current context node.
2276 * @param mode The current mode.
2277 * @param shouldAddAttrs true if xsl:attributes should be executed.
2278 *
2279 * @throws TransformerException
2280 * @xsl.usage advanced
2281 */
2282 public void executeChildTemplates(
2283 ElemTemplateElement elem, boolean shouldAddAttrs)
2284 throws TransformerException
2285 {
2286
2287 // Does this element have any children?
2288 ElemTemplateElement t = elem.getFirstChildElem();
2289
2290 if (null == t)
2291 return;
2292
2293 if(elem.hasTextLitOnly() && org.apache.xalan.processor.TransformerFactoryImpl.m_optimize)
2294 {
2295 char[] chars = ((ElemTextLiteral)t).getChars();
2296 try
2297 {
2298 // Have to push stuff on for tooling...
2299 this.pushElemTemplateElement(t);
2300 m_serializationHandler.characters(chars, 0, chars.length);
2301 }
2302 catch(SAXException se)
2303 {
2304 throw new TransformerException(se);
2305 }
2306 finally
2307 {
2308 this.popElemTemplateElement();
2309 }
2310 return;
2311 }
2312
2313// // Check for infinite loops if we have to.
2314// boolean check = (m_stackGuard.m_recursionLimit > -1);
2315//
2316// if (check)
2317// getStackGuard().push(elem, xctxt.getCurrentNode());
2318
2319 XPathContext xctxt = m_xcontext;
2320 xctxt.pushSAXLocatorNull();
2321 int currentTemplateElementsTop = m_currentTemplateElements.size();
2322 m_currentTemplateElements.push(null);
2323
2324 try
2325 {
2326 // Loop through the children of the template, calling execute on
2327 // each of them.
2328 for (; t != null; t = t.getNextSiblingElem())
2329 {
2330 if (!shouldAddAttrs
2331 && t.getXSLToken() == Constants.ELEMNAME_ATTRIBUTE)
2332 continue;
2333
2334 xctxt.setSAXLocator(t);
2335 m_currentTemplateElements.setElementAt(t,currentTemplateElementsTop);
2336 t.execute(this);
2337 }
2338 }
2339 catch(RuntimeException re)
2340 {
2341 TransformerException te = new TransformerException(re);
2342 te.setLocator(t);
2343 throw te;
2344 }
2345 finally
2346 {
2347 m_currentTemplateElements.pop();
2348 xctxt.popSAXLocator();
2349 }
2350
2351 // Check for infinite loops if we have to
2352// if (check)
2353// getStackGuard().pop();
2354 }
2355 /**
2356 * Execute each of the children of a template element.
2357 *
2358 * @param elem The ElemTemplateElement that contains the children
2359 * that should execute.
2360 * @param handler The ContentHandler to where the result events
2361 * should be fed.
2362 *
2363 * @throws TransformerException
2364 * @xsl.usage advanced
2365 */
2366 public void executeChildTemplates(
2367 ElemTemplateElement elem, ContentHandler handler)
2368 throws TransformerException
2369 {
2370
2371 SerializationHandler xoh = this.getSerializationHandler();
2372
2373 // These may well not be the same! In this case when calling
2374 // the Redirect extension, it has already set the ContentHandler
2375 // in the Transformer.
2376 SerializationHandler savedHandler = xoh;
2377
2378 try
2379 {
2380 xoh.flushPending();
2381
2382 // %REVIEW% Make sure current node is being pushed.
2383 LexicalHandler lex = null;
2384 if (handler instanceof LexicalHandler) {
2385 lex = (LexicalHandler) handler;
2386 }
2387 m_serializationHandler = new ToXMLSAXHandler(handler, lex, savedHandler.getEncoding());
2388 m_serializationHandler.setTransformer(this);
2389 executeChildTemplates(elem, true);
2390 }
2391 catch (TransformerException e)
2392 {
2393 throw e;
2394 }
2395 catch (SAXException se) {
2396 throw new TransformerException(se);
2397 }
2398 finally
2399 {
2400 m_serializationHandler = savedHandler;
2401 }
2402 }
2403
2404 /**
2405 * Get the keys for the xsl:sort elements.
2406 * Note: Should this go into ElemForEach?
2407 *
2408 * @param foreach Valid ElemForEach element, not null.
2409 * @param sourceNodeContext The current node context in the source tree,
2410 * needed to evaluate the Attribute Value Templates.
2411 *
2412 * @return A Vector of NodeSortKeys, or null.
2413 *
2414 * @throws TransformerException
2415 * @xsl.usage advanced
2416 */
2417 public Vector processSortKeys(ElemForEach foreach, int sourceNodeContext)
2418 throws TransformerException
2419 {
2420
2421 Vector keys = null;
2422 XPathContext xctxt = m_xcontext;
2423 int nElems = foreach.getSortElemCount();
2424
2425 if (nElems > 0)
2426 keys = new Vector();
2427
2428 // March backwards, collecting the sort keys.
2429 for (int i = 0; i < nElems; i++)
2430 {
2431 ElemSort sort = foreach.getSortElem(i);
2432
2433 if (TransformerImpl.S_DEBUG)
2434 getTraceManager().fireTraceEvent(sort);
2435
2436 String langString =
2437 (null != sort.getLang())
2438 ? sort.getLang().evaluate(xctxt, sourceNodeContext, foreach) : null;
2439 String dataTypeString = sort.getDataType().evaluate(xctxt,
2440 sourceNodeContext, foreach);
2441
2442 if (dataTypeString.indexOf(":") >= 0)
2443 System.out.println(
2444 "TODO: Need to write the hooks for QNAME sort data type");
2445 else if (!(dataTypeString.equalsIgnoreCase(Constants.ATTRVAL_DATATYPE_TEXT))
2446 &&!(dataTypeString.equalsIgnoreCase(
2447 Constants.ATTRVAL_DATATYPE_NUMBER)))
2448 foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2449 new Object[]{ Constants.ATTRNAME_DATATYPE,
2450 dataTypeString });
2451
2452 boolean treatAsNumbers =
2453 ((null != dataTypeString) && dataTypeString.equals(
2454 Constants.ATTRVAL_DATATYPE_NUMBER)) ? true : false;
2455 String orderString = sort.getOrder().evaluate(xctxt, sourceNodeContext,
2456 foreach);
2457
2458 if (!(orderString.equalsIgnoreCase(Constants.ATTRVAL_ORDER_ASCENDING))
2459 &&!(orderString.equalsIgnoreCase(
2460 Constants.ATTRVAL_ORDER_DESCENDING)))
2461 foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2462 new Object[]{ Constants.ATTRNAME_ORDER,
2463 orderString });
2464
2465 boolean descending =
2466 ((null != orderString) && orderString.equals(
2467 Constants.ATTRVAL_ORDER_DESCENDING)) ? true : false;
2468 AVT caseOrder = sort.getCaseOrder();
2469 boolean caseOrderUpper;
2470
2471 if (null != caseOrder)
2472 {
2473 String caseOrderString = caseOrder.evaluate(xctxt, sourceNodeContext,
2474 foreach);
2475
2476 if (!(caseOrderString.equalsIgnoreCase(Constants.ATTRVAL_CASEORDER_UPPER))
2477 &&!(caseOrderString.equalsIgnoreCase(
2478 Constants.ATTRVAL_CASEORDER_LOWER)))
2479 foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2480 new Object[]{ Constants.ATTRNAME_CASEORDER,
2481 caseOrderString });
2482
2483 caseOrderUpper =
2484 ((null != caseOrderString) && caseOrderString.equals(
2485 Constants.ATTRVAL_CASEORDER_UPPER)) ? true : false;
2486 }
2487 else
2488 {
2489 caseOrderUpper = false;
2490 }
2491
2492 keys.addElement(new NodeSortKey(this, sort.getSelect(), treatAsNumbers,
2493 descending, langString, caseOrderUpper,
2494 foreach));
2495 if (TransformerImpl.S_DEBUG)
2496 getTraceManager().fireTraceEndEvent(sort);
2497 }
2498
2499 return keys;
2500 }
2501
2502 //==========================================================
2503 // SECTION: TransformState implementation
2504 //==========================================================
2505
2506 /**
2507 * Get the stack of ElemTemplateElements.
2508 *
2509 * @return A copy of stack that contains the xsl element instructions,
2510 * the earliest called in index zero, and the latest called in index size()-1.
2511 */
2512 public Vector getElementCallstack()
2513 {
2514 Vector elems = new Vector();
2515 int nStackSize = m_currentTemplateElements.size();
2516 for(int i = 0; i < nStackSize; i++)
2517 {
2518 ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements.elementAt(i);
2519 if(null != elem)
2520 {
2521 elems.addElement(elem);
2522 }
2523 }
2524 return elems;
2525 }
2526
2527 /**
2528 * Get the count of how many elements are
2529 * active.
2530 * @return The number of active elements on
2531 * the currentTemplateElements stack.
2532 */
2533 public int getCurrentTemplateElementsCount()
2534 {
2535 return m_currentTemplateElements.size();
2536 }
2537
2538
2539 /**
2540 * Get the count of how many elements are
2541 * active.
2542 * @return The number of active elements on
2543 * the currentTemplateElements stack.
2544 */
2545 public ObjectStack getCurrentTemplateElements()
2546 {
2547 return m_currentTemplateElements;
2548 }
2549
2550 /**
2551 * Push the current template element.
2552 *
2553 * @param elem The current ElemTemplateElement (may be null, and then
2554 * set via setCurrentElement).
2555 */
2556 public void pushElemTemplateElement(ElemTemplateElement elem)
2557 {
2558 m_currentTemplateElements.push(elem);
2559 }
2560
2561 /**
2562 * Pop the current template element.
2563 */
2564 public void popElemTemplateElement()
2565 {
2566 m_currentTemplateElements.pop();
2567 }
2568
2569 /**
2570 * Set the top of the current template elements
2571 * stack.
2572 *
2573 * @param e The current ElemTemplateElement about to
2574 * be executed.
2575 */
2576 public void setCurrentElement(ElemTemplateElement e)
2577 {
2578 m_currentTemplateElements.setTop(e);
2579 }
2580
2581 /**
2582 * Retrieves the current ElemTemplateElement that is
2583 * being executed.
2584 *
2585 * @return The current ElemTemplateElement that is executing,
2586 * should not normally be null.
2587 */
2588 public ElemTemplateElement getCurrentElement()
2589 {
2590 return (m_currentTemplateElements.size() > 0) ?
2591 (ElemTemplateElement) m_currentTemplateElements.peek() : null;
2592 }
2593
2594 /**
2595 * This method retrieves the current context node
2596 * in the source tree.
2597 *
2598 * @return The current context node (should never be null?).
2599 */
2600 public int getCurrentNode()
2601 {
2602 return m_xcontext.getCurrentNode();
2603 }
2604
2605 /**
2606 * Get the call stack of xsl:template elements.
2607 *
2608 * @return A copy of stack that contains the xsl:template
2609 * (ElemTemplate) instructions, the earliest called in index
2610 * zero, and the latest called in index size()-1.
2611 */
2612 public Vector getTemplateCallstack()
2613 {
2614 Vector elems = new Vector();
2615 int nStackSize = m_currentTemplateElements.size();
2616 for(int i = 0; i < nStackSize; i++)
2617 {
2618 ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements.elementAt(i);
2619 if(null != elem && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE))
2620 {
2621 elems.addElement(elem);
2622 }
2623 }
2624 return elems;
2625 }
2626
2627
2628 /**
2629 * This method retrieves the xsl:template
2630 * that is in effect, which may be a matched template
2631 * or a named template.
2632 *
2633 * <p>Please note that the ElemTemplate returned may
2634 * be a default template, and thus may not have a template
2635 * defined in the stylesheet.</p>
2636 *
2637 * @return The current xsl:template, should not be null.
2638 */
2639 public ElemTemplate getCurrentTemplate()
2640 {
2641
2642 ElemTemplateElement elem = getCurrentElement();
2643
2644 while ((null != elem)
2645 && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE))
2646 {
2647 elem = elem.getParentElem();
2648 }
2649
2650 return (ElemTemplate) elem;
2651 }
2652
2653 /**
2654 * Push both the current xsl:template or xsl:for-each onto the
2655 * stack, along with the child node that was matched.
2656 * (Note: should this only be used for xsl:templates?? -sb)
2657 *
2658 * @param template xsl:template or xsl:for-each.
2659 * @param child The child that was matched.
2660 */
2661 public void pushPairCurrentMatched(ElemTemplateElement template, int child)
2662 {
2663 m_currentMatchTemplates.push(template);
2664 m_currentMatchedNodes.push(child);
2665 }
2666
2667 /**
2668 * Pop the elements that were pushed via pushPairCurrentMatched.
2669 */
2670 public void popCurrentMatched()
2671 {
2672 m_currentMatchTemplates.pop();
2673 m_currentMatchedNodes.pop();
2674 }
2675
2676 /**
2677 * This method retrieves the xsl:template
2678 * that was matched. Note that this may not be
2679 * the same thing as the current template (which
2680 * may be from getCurrentElement()), since a named
2681 * template may be in effect.
2682 *
2683 * @return The pushed template that was pushed via pushPairCurrentMatched.
2684 */
2685 public ElemTemplate getMatchedTemplate()
2686 {
2687 return (ElemTemplate) m_currentMatchTemplates.peek();
2688 }
2689
2690 /**
2691 * Retrieves the node in the source tree that matched
2692 * the template obtained via getMatchedTemplate().
2693 *
2694 * @return The matched node that corresponds to the
2695 * match attribute of the current xsl:template.
2696 */
2697 public int getMatchedNode()
2698 {
2699 return m_currentMatchedNodes.peepTail();
2700 }
2701
2702 /**
2703 * Get the current context node list.
2704 *
2705 * @return A reset clone of the context node list.
2706 */
2707 public DTMIterator getContextNodeList()
2708 {
2709
2710 try
2711 {
2712 DTMIterator cnl = m_xcontext.getContextNodeList();
2713
2714 return (cnl == null) ? null : (DTMIterator) cnl.cloneWithReset();
2715 }
2716 catch (CloneNotSupportedException cnse)
2717 {
2718
2719 // should never happen.
2720 return null;
2721 }
2722 }
2723
2724 /**
2725 * Get the TrAX Transformer object in effect.
2726 *
2727 * @return This object.
2728 */
2729 public Transformer getTransformer()
2730 {
2731 return this;
2732 }
2733
2734 //==========================================================
2735 // SECTION: Accessor Functions
2736 //==========================================================
2737
2738 /**
2739 * Set the stylesheet for this processor. If this is set, then the
2740 * process calls that take only the input .xml will use
2741 * this instead of looking for a stylesheet PI. Also,
2742 * setting the stylesheet is needed if you are going
2743 * to use the processor as a SAX ContentHandler.
2744 *
2745 * @param stylesheetRoot A non-null StylesheetRoot object,
2746 * or null if you wish to clear the stylesheet reference.
2747 */
2748 public void setStylesheet(StylesheetRoot stylesheetRoot)
2749 {
2750 m_stylesheetRoot = stylesheetRoot;
2751 }
2752
2753 /**
2754 * Get the current stylesheet for this processor.
2755 *
2756 * @return The stylesheet that is associated with this
2757 * transformer.
2758 */
2759 public final StylesheetRoot getStylesheet()
2760 {
2761 return m_stylesheetRoot;
2762 }
2763
2764 /**
2765 * Get quietConflictWarnings property. If the quietConflictWarnings
2766 * property is set to true, warnings about pattern conflicts won't be
2767 * printed to the diagnostics stream.
2768 *
2769 * @return True if this transformer should not report
2770 * template match conflicts.
2771 */
2772 public boolean getQuietConflictWarnings()
2773 {
2774 return m_quietConflictWarnings;
2775 }
2776
2777 /**
2778 * If the quietConflictWarnings property is set to
2779 * true, warnings about pattern conflicts won't be
2780 * printed to the diagnostics stream.
2781 * False by default.
2782 * (Currently setting this property will have no effect.)
2783 *
2784 * @param b true if conflict warnings should be suppressed.
2785 */
2786 public void setQuietConflictWarnings(boolean b)
2787 {
2788 m_quietConflictWarnings = b;
2789 }
2790
2791 /**
2792 * Set the execution context for XPath.
2793 *
2794 * @param xcontext A non-null reference to the XPathContext
2795 * associated with this transformer.
2796 * @xsl.usage internal
2797 */
2798 public void setXPathContext(XPathContext xcontext)
2799 {
2800 m_xcontext = xcontext;
2801 }
2802
2803 /**
2804 * Get the XPath context associated with this transformer.
2805 *
2806 * @return The XPathContext reference, never null.
2807 */
2808 public final XPathContext getXPathContext()
2809 {
2810 return m_xcontext;
2811 }
2812
2813 /**
2814 * Get the object used to guard the stack from
2815 * recursion.
2816 *
2817 * @return The StackGuard object, which should never be null.
2818 * @xsl.usage internal
2819 */
2820 public StackGuard getStackGuard()
2821 {
2822 return m_stackGuard;
2823 }
2824
2825 /**
2826 * Get the recursion limit.
2827 * Used for infinite loop check. If the value is -1, do not
2828 * check for infinite loops. Anyone who wants to enable that
2829 * check should change the value of this variable to be the
2830 * level of recursion that they want to check. Be careful setting
2831 * this variable, if the number is too low, it may report an
2832 * infinite loop situation, when there is none.
2833 * Post version 1.0.0, we'll make this a runtime feature.
2834 *
2835 * @return The limit on recursion, or -1 if no check is to be made.
2836 */
2837 public int getRecursionLimit()
2838 {
2839 return m_stackGuard.getRecursionLimit();
2840 }
2841
2842 /**
2843 * Set the recursion limit.
2844 * Used for infinite loop check. If the value is -1, do not
2845 * check for infinite loops. Anyone who wants to enable that
2846 * check should change the value of this variable to be the
2847 * level of recursion that they want to check. Be careful setting
2848 * this variable, if the number is too low, it may report an
2849 * infinite loop situation, when there is none.
2850 * Post version 1.0.0, we'll make this a runtime feature.
2851 *
2852 * @param limit A number that represents the limit of recursion,
2853 * or -1 if no checking is to be done.
2854 */
2855 public void setRecursionLimit(int limit)
2856 {
2857 m_stackGuard.setRecursionLimit(limit);
2858 }
2859
2860 /**
2861 * Get the SerializationHandler object.
2862 *
2863 * @return The current SerializationHandler, which may not
2864 * be the main result tree manager.
2865 */
2866 public SerializationHandler getResultTreeHandler()
2867 {
2868 return m_serializationHandler;
2869 }
2870
2871 /**
2872 * Get the SerializationHandler object.
2873 *
2874 * @return The current SerializationHandler, which may not
2875 * be the main result tree manager.
2876 */
2877 public SerializationHandler getSerializationHandler()
2878 {
2879 return m_serializationHandler;
2880 }
2881
2882 /**
2883 * Get the KeyManager object.
2884 *
2885 * @return A reference to the KeyManager object, which should
2886 * never be null.
2887 */
2888 public KeyManager getKeyManager()
2889 {
2890 return m_keyManager;
2891 }
2892
2893 /**
2894 * Check to see if this is a recursive attribute definition.
2895 *
2896 * @param attrSet A non-null ElemAttributeSet reference.
2897 *
2898 * @return true if the attribute set is recursive.
2899 */
2900 public boolean isRecursiveAttrSet(ElemAttributeSet attrSet)
2901 {
2902
2903 if (null == m_attrSetStack)
2904 {
2905 m_attrSetStack = new Stack();
2906 }
2907
2908 if (!m_attrSetStack.empty())
2909 {
2910 int loc = m_attrSetStack.search(attrSet);
2911
2912 if (loc > -1)
2913 {
2914 return true;
2915 }
2916 }
2917
2918 return false;
2919 }
2920
2921 /**
2922 * Push an executing attribute set, so we can check for
2923 * recursive attribute definitions.
2924 *
2925 * @param attrSet A non-null ElemAttributeSet reference.
2926 */
2927 public void pushElemAttributeSet(ElemAttributeSet attrSet)
2928 {
2929 m_attrSetStack.push(attrSet);
2930 }
2931
2932 /**
2933 * Pop the current executing attribute set.
2934 */
2935 public void popElemAttributeSet()
2936 {
2937 m_attrSetStack.pop();
2938 }
2939
2940 /**
2941 * Get the table of counters, for optimized xsl:number support.
2942 *
2943 * @return The CountersTable, never null.
2944 */
2945 public CountersTable getCountersTable()
2946 {
2947
2948 if (null == m_countersTable)
2949 m_countersTable = new CountersTable();
2950
2951 return m_countersTable;
2952 }
2953
2954 /**
2955 * Tell if the current template rule is null, i.e. if we are
2956 * directly within an apply-templates. Used for xsl:apply-imports.
2957 *
2958 * @return True if the current template rule is null.
2959 */
2960 public boolean currentTemplateRuleIsNull()
2961 {
2962 return ((!m_currentTemplateRuleIsNull.isEmpty())
2963 && (m_currentTemplateRuleIsNull.peek() == true));
2964 }
2965
2966 /**
2967 * Push true if the current template rule is null, false
2968 * otherwise.
2969 *
2970 * @param b True if the we are executing an xsl:for-each
2971 * (or xsl:call-template?).
2972 */
2973 public void pushCurrentTemplateRuleIsNull(boolean b)
2974 {
2975 m_currentTemplateRuleIsNull.push(b);
2976 }
2977
2978 /**
2979 * Push true if the current template rule is null, false
2980 * otherwise.
2981 */
2982 public void popCurrentTemplateRuleIsNull()
2983 {
2984 m_currentTemplateRuleIsNull.pop();
2985 }
2986
2987 /**
2988 * Push a funcion result for the currently active EXSLT
2989 * <code>func:function</code>.
2990 *
2991 * @param val the result of executing an EXSLT
2992 * <code>func:result</code> instruction for the current
2993 * <code>func:function</code>.
2994 */
2995 public void pushCurrentFuncResult(Object val) {
2996 m_currentFuncResult.push(val);
2997 }
2998
2999 /**
3000 * Pops the result of the currently active EXSLT <code>func:function</code>.
3001 *
3002 * @return the value of the <code>func:function</code>
3003 */
3004 public Object popCurrentFuncResult() {
3005 return m_currentFuncResult.pop();
3006 }
3007
3008 /**
3009 * Determines whether an EXSLT <code>func:result</code> instruction has been
3010 * executed for the currently active EXSLT <code>func:function</code>.
3011 *
3012 * @return <code>true</code> if and only if a <code>func:result</code>
3013 * instruction has been executed
3014 */
3015 public boolean currentFuncResultSeen() {
3016 return !m_currentFuncResult.empty()
3017 && m_currentFuncResult.peek() != null;
3018 }
3019
3020 /**
3021 * Return the message manager.
3022 *
3023 * @return The message manager, never null.
3024 */
3025 public MsgMgr getMsgMgr()
3026 {
3027
3028 if (null == m_msgMgr)
3029 m_msgMgr = new MsgMgr(this);
3030
3031 return m_msgMgr;
3032 }
3033
3034 /**
3035 * Set the error event listener.
3036 *
3037 * @param listener The new error listener.
3038 * @throws IllegalArgumentException if
3039 */
3040 public void setErrorListener(ErrorListener listener)
3041 throws IllegalArgumentException
3042 {
3043
3044 synchronized (m_reentryGuard)
3045 {
3046 if (listener == null)
3047 throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler");
3048
3049 m_errorHandler = listener;
3050 }
3051 }
3052
3053 /**
3054 * Get the current error event handler.
3055 *
3056 * @return The current error handler, which should never be null.
3057 */
3058 public ErrorListener getErrorListener()
3059 {
3060 return m_errorHandler;
3061 }
3062
3063 /**
3064 * Get an instance of the trace manager for this transformation.
3065 * This object can be used to set trace listeners on various
3066 * events during the transformation.
3067 *
3068 * @return A reference to the TraceManager, never null.
3069 */
3070 public TraceManager getTraceManager()
3071 {
3072 return m_traceManager;
3073 }
3074
3075 /**
3076 * Look up the value of a feature.
3077 *
3078 * <p>The feature name is any fully-qualified URI. It is
3079 * possible for an TransformerFactory to recognize a feature name but
3080 * to be unable to return its value; this is especially true
3081 * in the case of an adapter for a SAX1 Parser, which has
3082 * no way of knowing whether the underlying parser is
3083 * validating, for example.</p>
3084 *
3085 * <h3>Open issues:</h3>
3086 * <dl>
3087 * <dt><h4>Should getFeature be changed to hasFeature?</h4></dt>
3088 * <dd>Keith Visco writes: Should getFeature be changed to hasFeature?
3089 * It returns a boolean which indicated whether the "state"
3090 * of feature is "true or false". I assume this means whether
3091 * or not a feature is supported? I know SAX is using "getFeature",
3092 * but to me "hasFeature" is cleaner.</dd>
3093 * </dl>
3094 *
3095 * @param name The feature name, which is a fully-qualified
3096 * URI.
3097 * @return The current state of the feature (true or false).
3098 * @throws org.xml.sax.SAXNotRecognizedException When the
3099 * TransformerFactory does not recognize the feature name.
3100 * @throws org.xml.sax.SAXNotSupportedException When the
3101 * TransformerFactory recognizes the feature name but
3102 * cannot determine its value at this time.
3103 *
3104 * @throws SAXNotRecognizedException
3105 * @throws SAXNotSupportedException
3106 */
3107 public boolean getFeature(String name)
3108 throws SAXNotRecognizedException, SAXNotSupportedException
3109 {
3110
3111 if ("http://xml.org/trax/features/sax/input".equals(name))
3112 return true;
3113 else if ("http://xml.org/trax/features/dom/input".equals(name))
3114 return true;
3115
3116 throw new SAXNotRecognizedException(name);
3117 }
3118
3119 // %TODO% Doc
3120
3121 /**
3122 * NEEDSDOC Method getMode
3123 *
3124 *
3125 * NEEDSDOC (getMode) @return
3126 */
3127 public QName getMode()
3128 {
3129 return m_modes.isEmpty() ? null : (QName) m_modes.peek();
3130 }
3131
3132 // %TODO% Doc
3133
3134 /**
3135 * NEEDSDOC Method pushMode
3136 *
3137 *
3138 * NEEDSDOC @param mode
3139 */
3140 public void pushMode(QName mode)
3141 {
3142 m_modes.push(mode);
3143 }
3144
3145 // %TODO% Doc
3146
3147 /**
3148 * NEEDSDOC Method popMode
3149 *
3150 */
3151 public void popMode()
3152 {
3153 m_modes.pop();
3154 }
3155
3156 /**
3157 * Called by SourceTreeHandler to start the transformation
3158 * in a separate thread
3159 *
3160 * NEEDSDOC @param priority
3161 */
3162 public void runTransformThread(int priority)
3163 {
3164
3165 // used in SourceTreeHandler
3166 Thread t = ThreadControllerWrapper.runThread(this, priority);
3167 this.setTransformThread(t);
3168 }
3169
3170 /**
3171 * Called by this.transform() if isParserEventsOnMain()==false.
3172 * Similar with runTransformThread(), but no priority is set
3173 * and setTransformThread is not set.
3174 */
3175 public void runTransformThread()
3176 {
3177 ThreadControllerWrapper.runThread(this, -1);
3178 }
3179
3180 /**
3181 * Called by CoRoutineSAXParser. Launches the CoroutineSAXParser
3182 * in a thread, and prepares it to invoke the parser from that thread
3183 * upon request.
3184 *
3185 */
3186 public static void runTransformThread(Runnable runnable)
3187 {
3188 ThreadControllerWrapper.runThread(runnable, -1);
3189 }
3190
3191 /**
3192 * Used by SourceTreeHandler to wait until the transform
3193 * completes
3194 *
3195 * @throws SAXException
3196 */
3197 public void waitTransformThread() throws SAXException
3198 {
3199
3200 // This is called to make sure the task is done.
3201 // It is possible that the thread has been reused -
3202 // but for a different transformation. ( what if we
3203 // recycle the transformer ? Not a problem since this is
3204 // still in use. )
3205 Thread transformThread = this.getTransformThread();
3206
3207 if (null != transformThread)
3208 {
3209 try
3210 {
3211 ThreadControllerWrapper.waitThread(transformThread, this);
3212
3213 if (!this.hasTransformThreadErrorCatcher())
3214 {
3215 Exception e = this.getExceptionThrown();
3216
3217 if (null != e)
3218 {
3219 e.printStackTrace();
3220 throw new org.xml.sax.SAXException(e);
3221 }
3222 }
3223
3224 this.setTransformThread(null);
3225 }
3226 catch (InterruptedException ie){}
3227 }
3228 }
3229
3230 /**
3231 * Get the exception thrown by the secondary thread (normally
3232 * the transform thread).
3233 *
3234 * @return The thrown exception, or null if no exception was
3235 * thrown.
3236 */
3237 public Exception getExceptionThrown()
3238 {
3239 return m_exceptionThrown;
3240 }
3241
3242 /**
3243 * Set the exception thrown by the secondary thread (normally
3244 * the transform thread).
3245 *
3246 * @param e The thrown exception, or null if no exception was
3247 * thrown.
3248 */
3249 public void setExceptionThrown(Exception e)
3250 {
3251 m_exceptionThrown = e;
3252 }
3253
3254 /**
3255 * This is just a way to set the document for run().
3256 *
3257 * @param doc A non-null reference to the root of the
3258 * tree to be transformed.
3259 */
3260 public void setSourceTreeDocForThread(int doc)
3261 {
3262 m_doc = doc;
3263 }
3264
3265 /**
3266 * Set the input source for the source tree, which is needed if the
3267 * parse thread is not the main thread, in order for the parse
3268 * thread's run method to get to the input source.
3269 *
3270 * @param source The input source for the source tree.
3271 */
3272 public void setXMLSource(Source source)
3273 {
3274 m_xmlSource = source;
3275 }
3276
3277 /**
3278 * Tell if the transform method is completed.
3279 *
3280 * @return True if transformNode has completed, or
3281 * an exception was thrown.
3282 */
3283 public boolean isTransformDone()
3284 {
3285
3286 synchronized (this)
3287 {
3288 return m_isTransformDone;
3289 }
3290 }
3291
3292 /**
3293 * Set if the transform method is completed.
3294 *
3295 * @param done True if transformNode has completed, or
3296 * an exception was thrown.
3297 */
3298 public void setIsTransformDone(boolean done)
3299 {
3300
3301 synchronized (this)
3302 {
3303 m_isTransformDone = done;
3304 }
3305 }
3306
3307 /**
3308 * From a secondary thread, post the exception, so that
3309 * it can be picked up from the main thread.
3310 *
3311 * @param e The exception that was thrown.
3312 */
3313 void postExceptionFromThread(Exception e)
3314 {
3315
3316 // Commented out in response to problem reported by Nicola Brown <Nicola.Brown@jacobsrimell.com>
3317 // if(m_reportInPostExceptionFromThread)
3318 // {
3319 // // Consider re-throwing the exception if this flag is set.
3320 // e.printStackTrace();
3321 // }
3322 // %REVIEW Need DTM equivelent?
3323 // if (m_inputContentHandler instanceof SourceTreeHandler)
3324 // {
3325 // SourceTreeHandler sth = (SourceTreeHandler) m_inputContentHandler;
3326 //
3327 // sth.setExceptionThrown(e);
3328 // }
3329 // ContentHandler ch = getContentHandler();
3330
3331 // if(ch instanceof SourceTreeHandler)
3332 // {
3333 // SourceTreeHandler sth = (SourceTreeHandler) ch;
3334 // ((TransformerImpl)(sth.getTransformer())).postExceptionFromThread(e);
3335 // }
3336 m_isTransformDone = true;
3337 m_exceptionThrown = e;
3338 ; // should have already been reported via the error handler?
3339
3340 synchronized (this)
3341 {
3342
3343 // See message from me on 3/27/2001 to Patrick Moore.
3344 // String msg = e.getMessage();
3345 // System.out.println(e.getMessage());
3346 // Is this really needed? -sb
3347 notifyAll();
3348
3349 // if (null == msg)
3350 // {
3351 //
3352 // // m_throwNewError = false;
3353 // e.printStackTrace();
3354 // }
3355 // throw new org.apache.xml.utils.WrappedRuntimeException(e);
3356 }
3357 }
3358
3359 /**
3360 * Run the transform thread.
3361 */
3362 public void run()
3363 {
3364
3365 m_hasBeenReset = false;
3366
3367 try
3368 {
3369
3370 // int n = ((SourceTreeHandler)getInputContentHandler()).getDTMRoot();
3371 // transformNode(n);
3372 try
3373 {
3374 m_isTransformDone = false;
3375
3376 // Should no longer be needed...
3377// if(m_inputContentHandler instanceof TransformerHandlerImpl)
3378// {
3379// TransformerHandlerImpl thi = (TransformerHandlerImpl)m_inputContentHandler;
3380// thi.waitForInitialEvents();
3381// }
3382
3383 transformNode(m_doc);
3384
3385 }
3386 catch (Exception e)
3387 {
3388 // e.printStackTrace();
3389
3390 // Strange that the other catch won't catch this...
3391 if (null != m_transformThread)
3392 postExceptionFromThread(e); // Assume we're on the main thread
3393 else
3394 throw new RuntimeException(e.getMessage());
3395 }
3396 finally
3397 {
3398 m_isTransformDone = true;
3399
3400 if (m_inputContentHandler instanceof TransformerHandlerImpl)
3401 {
3402 ((TransformerHandlerImpl) m_inputContentHandler).clearCoRoutine();
3403 }
3404
3405 // synchronized (this)
3406 // {
3407 // notifyAll();
3408 // }
3409 }
3410 }
3411 catch (Exception e)
3412 {
3413
3414 // e.printStackTrace();
3415 if (null != m_transformThread)
3416 postExceptionFromThread(e);
3417 else
3418 throw new RuntimeException(e.getMessage()); // Assume we're on the main thread.
3419 }
3420 }
3421
3422 // Fragment re-execution interfaces for a tool.
3423
3424 /**
3425 * This will get a snapshot of the current executing context
3426 *
3427 *
3428 * @return TransformSnapshot object, snapshot of executing context
3429 * @deprecated This is an internal tooling API that nobody seems to be using
3430 */
3431 public TransformSnapshot getSnapshot()
3432 {
3433 return new TransformSnapshotImpl(this);
3434 }
3435
3436 /**
3437 * This will execute the following XSLT instructions
3438 * from the snapshot point, after the stylesheet execution
3439 * context has been reset from the snapshot point.
3440 *
3441 * @param ts The snapshot of where to start execution
3442 *
3443 * @throws TransformerException
3444 * @deprecated This is an internal tooling API that nobody seems to be using
3445 */
3446 public void executeFromSnapshot(TransformSnapshot ts)
3447 throws TransformerException
3448 {
3449
3450 ElemTemplateElement template = getMatchedTemplate();
3451 int child = getMatchedNode();
3452
3453 pushElemTemplateElement(template); //needed??
3454 m_xcontext.pushCurrentNode(child); //needed??
3455 this.executeChildTemplates(template, true); // getResultTreeHandler());
3456 }
3457
3458 /**
3459 * This will reset the stylesheet execution context
3460 * from the snapshot point.
3461 *
3462 * @param ts The snapshot of where to start execution
3463 * @deprecated This is an internal tooling API that nobody seems to be using
3464 */
3465 public void resetToStylesheet(TransformSnapshot ts)
3466 {
3467 ((TransformSnapshotImpl) ts).apply(this);
3468 }
3469
3470 /**
3471 * NEEDSDOC Method stopTransformation
3472 *
3473 */
3474 public void stopTransformation(){}
3475
3476 /**
3477 * Test whether whitespace-only text nodes are visible in the logical
3478 * view of <code>DTM</code>. Normally, this function
3479 * will be called by the implementation of <code>DTM</code>;
3480 * it is not normally called directly from
3481 * user code.
3482 *
3483 * @param elementHandle int Handle of the element.
3484 * @return one of NOTSTRIP, STRIP, or INHERIT.
3485 */
3486 public short getShouldStripSpace(int elementHandle, DTM dtm)
3487 {
3488
3489 try
3490 {
3491 org.apache.xalan.templates.WhiteSpaceInfo info =
3492 m_stylesheetRoot.getWhiteSpaceInfo(m_xcontext, elementHandle, dtm);
3493
3494 if (null == info)
3495 {
3496 return DTMWSFilter.INHERIT;
3497 }
3498 else
3499 {
3500
3501 // System.out.println("getShouldStripSpace: "+info.getShouldStripSpace());
3502 return info.getShouldStripSpace()
3503 ? DTMWSFilter.STRIP : DTMWSFilter.NOTSTRIP;
3504 }
3505 }
3506 catch (TransformerException se)
3507 {
3508 return DTMWSFilter.INHERIT;
3509 }
3510 }
3511 /**
3512 * Initializer method.
3513 *
3514 * @param transformer non-null transformer instance
3515 * @param realHandler Content Handler instance
3516 */
3517 public void init(ToXMLSAXHandler h,Transformer transformer, ContentHandler realHandler)
3518 {
3519 h.setTransformer(transformer);
3520 h.setContentHandler(realHandler);
3521 }
3522
3523 public void setSerializationHandler(SerializationHandler xoh)
3524 {
3525 m_serializationHandler = xoh;
3526 }
3527
3528
3529
3530 /**
3531 * Fire off characters, cdate events.
3532 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, char[], int, int)
3533 */
3534 public void fireGenerateEvent(
3535 int eventType,
3536 char[] ch,
3537 int start,
3538 int length) {
3539
3540 GenerateEvent ge = new GenerateEvent(this, eventType, ch, start, length);
3541 m_traceManager.fireGenerateEvent(ge);
3542 }
3543
3544 /**
3545 * Fire off startElement, endElement events.
3546 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, Attributes)
3547 */
3548 public void fireGenerateEvent(
3549 int eventType,
3550 String name,
3551 Attributes atts) {
3552
3553 GenerateEvent ge = new GenerateEvent(this, eventType, name, atts);
3554 m_traceManager.fireGenerateEvent(ge);
3555 }
3556
3557 /**
3558 * Fire off processingInstruction events.
3559 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, String)
3560 */
3561 public void fireGenerateEvent(int eventType, String name, String data) {
3562 GenerateEvent ge = new GenerateEvent(this, eventType, name,data);
3563 m_traceManager.fireGenerateEvent(ge);
3564 }
3565
3566 /**
3567 * Fire off comment and entity ref events.
3568 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String)
3569 */
3570 public void fireGenerateEvent(int eventType, String data) {
3571 GenerateEvent ge = new GenerateEvent(this, eventType, data);
3572 m_traceManager.fireGenerateEvent(ge);
3573 }
3574
3575 /**
3576 * Fire off startDocument, endDocument events.
3577 * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int)
3578 */
3579 public void fireGenerateEvent(int eventType) {
3580 GenerateEvent ge = new GenerateEvent(this, eventType);
3581 m_traceManager.fireGenerateEvent(ge);
3582 }
3583
3584 /**
3585 * @see org.apache.xml.serializer.SerializerTrace#hasTraceListeners()
3586 */
3587 public boolean hasTraceListeners() {
3588 return m_traceManager.hasTraceListeners();
3589 }
3590
3591} // end TransformerImpl class
3592