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