KickJava   Java API By Example, From Geeks To Geeks.

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


1 package com.icl.saxon;
2 import com.icl.saxon.om.*;
3 import com.icl.saxon.tree.TreeBuilder;
4 import com.icl.saxon.tinytree.TinyBuilder;
5 import com.icl.saxon.expr.*;
6 import com.icl.saxon.om.Axis;
7 import com.icl.saxon.pattern.AnyNodeTest;
8 import com.icl.saxon.output.*;
9 import com.icl.saxon.trace.*; // e.g.
10
import com.icl.saxon.style.XSLStyleSheet;
11 import com.icl.saxon.style.TerminationException;
12 import com.icl.saxon.sort.NodeOrderComparer;
13
14 import javax.xml.transform.*;
15 import javax.xml.transform.stream.*;
16 import javax.xml.transform.sax.*;
17 import javax.xml.transform.dom.*;
18 import java.util.Properties;
19
20
21 import org.xml.sax.*;
22 import org.xml.sax.helpers.*;
23 import org.xml.sax.ext.*;
24
25 import org.w3c.dom.Node;
26 import org.w3c.dom.Document;
27 import org.w3c.dom.Element;
28
29 import java.net.URL;
30 import java.util.*;
31 import java.io.*;
32
33
34 /**
35   * <B>Controller</B> processes an XML file, calling registered node handlers
36   * when appropriate to process its elements, character content, and attributes. <P>
37   * @version 10 December 1999: methods for building the tree extracted to class Builder,
38   * methods for maintaining rulesets extracted to RuleManager.<p>
39   * The Controller class now incorporates the previous <b>StylesheetInstance</b> class.
40   * A StyleSheetInstance represents a single execution of a prepared stylesheet.
41   * A PreparedStyleSheet can be used any number of times, in series or in parallel,
42   * but each use of it to render a source document requires a separate Controller
43   * object, which is not reusable or shareable.<p>
44   * The Controller is capable of comparing whether nodes are in document order;
45   * therefore it acts as a NodeOrderComparer.
46   * @author Michael H. Kay (mhkay@iclway.co.uk)
47   */

48   
49 public class Controller extends Transformer implements NodeOrderComparer {
50
51     // Policies for handling recoverable errors
52

53     public final static int RECOVER_SILENTLY = 0;
54     public final static int RECOVER_WITH_WARNINGS = 1;
55     public final static int DO_NOT_RECOVER = 2;
56
57     private TransformerFactoryImpl factory;
58     private Bindery bindery; // holds values of global and local variables
59
private ContentHandler inputContentHandler = null;
60     private ContentHandler outputContentHandler = null;
61     private NamePool namePool;
62     private DecimalFormatManager decimalFormatManager;
63     private Emitter messageEmitter;
64     //private LexicalHandler lexicalHandler = null;
65
private RuleManager ruleManager;
66     private Properties outputProperties;
67     private Outputter currentOutputter;
68     private ParameterSet parameters;
69     private PreparedStyleSheet preparedStyleSheet;
70     private TraceListener traceListener; // e.g.
71
private boolean tracingIsSuspended = false;
72     private URIResolver standardURIResolver;
73     private URIResolver userURIResolver;
74     private ErrorListener errorListener;
75     private XSLStyleSheet styleSheetElement;
76     private int recoveryPolicy = RECOVER_WITH_WARNINGS;
77     private boolean outputterIsInitialized = false;
78     private int treeModel = Builder.TINY_TREE;
79     private boolean disableStripping = false;
80     
81     private DocumentPool sourceDocumentPool;
82     private Hashtable userDataTable;
83     private boolean lineNumbering;
84     private boolean preview;
85     private TransformerException transformFailure = null;
86     private String diagnosticName = null;
87
88     /**
89     * Default constructor is provided for Java-only programs, i.e. applications
90     * that use the RuleManager to set up Java handlers for nodes, without using
91     * a stylesheet
92     */

93     
94     public Controller() {
95         this(new TransformerFactoryImpl());
96         bindery = new Bindery();
97     }
98     
99     /**
100     * Create a Controller and initialise variables. Constructor is protected,
101     * the Controller should be created using newTransformer() in the PreparedStyleSheet
102     * class.
103     */

104
105     protected Controller(TransformerFactoryImpl factory) {
106         this.factory = factory;
107         namePool = NamePool.getDefaultNamePool();
108         //bindery = new Bindery();
109
standardURIResolver = new StandardURIResolver(factory);
110         userURIResolver = factory.getURIResolver();
111         
112         errorListener = factory.getErrorListener();
113         if (errorListener instanceof StandardErrorListener) {
114             ((StandardErrorListener)errorListener).setRecoveryPolicy(
115                             ((Integer)factory.getAttribute(FeatureKeys.RECOVERY_POLICY))
116                               .intValue());
117         }
118         sourceDocumentPool = new DocumentPool();
119         userDataTable = new Hashtable();
120         
121         TraceListener tracer = (TraceListener)factory.getAttribute(FeatureKeys.TRACE_LISTENER);
122         if (tracer!=null) {
123             addTraceListener(tracer);
124         }
125
126         Boolean num = (Boolean)factory.getAttribute(FeatureKeys.LINE_NUMBERING);
127         if (num!=null && num.booleanValue()) {
128             setLineNumbering(true);
129         }
130
131         Integer model = (Integer)factory.getAttribute(FeatureKeys.TREE_MODEL);
132         if (model!=null) {
133             setTreeModel(model.intValue());
134         }
135
136     }
137     
138     public TransformerFactoryImpl getTransformerFactory() {
139         return factory;
140     }
141
142     /**
143     * Set a diagnostic name for this transformation (accessible through toString())
144     */

145
146     public void setDiagnosticName(String name) {
147         diagnosticName = name;
148     }
149
150     public String toString() {
151         if (diagnosticName==null) {
152             return super.toString();
153         } else {
154             return diagnosticName;
155         }
156     }
157
158     //////////////////////////////////////////////////////////////////////////
159
// Methods to process the tree
160
//////////////////////////////////////////////////////////////////////////
161

162
163     /**
164     * Process a Document.<p>
165     * This method is intended for use when performing a pure Java transformation,
166     * without a stylesheet. Where there is an XSLT stylesheet, use transformDocument()
167     * or transform() instead: those methods set up information from the stylesheet before calling
168     * run(). <p>
169     * The process starts by calling the registered node
170     * handler to process the supplied node. Note that the same document can be processed
171     * any number of times, typically with different node handlers for each pass. The NodeInfo
172     * will typically be the root of a tree built using com.icl.saxon.om.Builder.<p>
173     */

174     
175     public void run(NodeInfo node) throws TransformerException
176     {
177         Context initialContext = makeContext(node);
178         applyTemplates(
179             initialContext,
180             new SingletonNodeSet(node),
181             getRuleManager().getMode(-1),
182             null);
183     }
184
185     /**
186     * ApplyTemplates to process selected nodes using the handlers registered for a particular
187     * mode.<br>
188     * @param select A node-set expression (or more accurately a node-list)
189     * that determines which nodes are selected.
190     * Note: if the nodes are to be sorted, the select Expression will take care of this.
191     * @param mode Identifies the processing mode. It should match the mode defined when the
192     * element handler was registered using setHandler with a mode parameter. Set this parameter to
193     * null to invoke the default mode.
194     * @param parameters A ParameterSet containing the parameters to the handler/template being invoked.
195     * Specify null if there are no parameters.
196     */

197
198     public void applyTemplates(Context c, Expression select, Mode mode, ParameterSet parameters)
199             throws TransformerException
200     {
201         // Get an enumerator to iterate through the selected nodes
202
NodeEnumeration enum;
203         if (select==null) {
204             enum = c.getCurrentNodeInfo().getEnumeration(Axis.CHILD, AnyNodeTest.getInstance());
205         } else {
206             enum = select.enumerate(c, false);
207         }
208             
209         // if the enumerator can't calculate last() position, we wrap it in one that can.
210

211         if (!(enum instanceof LastPositionFinder)) {
212             enum = new LookaheadEnumerator(enum);
213         }
214
215         int position = 1;
216         Context context = c.newContext();
217         context.setLastPositionFinder((LastPositionFinder)enum);
218         context.setMode(mode);
219         while(enum.hasMoreElements()) {
220             NodeInfo node = enum.nextElement();
221             //if (node==null) {
222
// System.err.println("Got null node from " + enum.getClass());
223
// node.getDisplayName(); // break it now
224
//}
225

226             context.setCurrentNode(node);
227             context.setContextNode(node);
228             context.setPosition(position++);
229             
230             // find the node handler for this node
231

232             NodeHandler eh = ruleManager.getHandler(node, mode, context);
233                 
234             if (eh==null) { // Use the default action for the node
235
// No need to open a new stack frame!
236
defaultAction(node, context);
237                 
238             } else {
239                 if (eh.needsStackFrame()) {
240                     bindery.openStackFrame(parameters);
241                     if (isTracing()) { // e.g.
242
traceListener.enterSource(eh, context);
243                         eh.start(node, context);
244                         traceListener.leaveSource(eh, context);
245                     } else {
246                         eh.start(node, context);
247                     }
248                     bindery.closeStackFrame();
249                 } else {
250                     if (isTracing()) { // e.g.
251
traceListener.enterSource(eh, context);
252                         eh.start(node, context);
253                         traceListener.leaveSource(eh, context);
254                     } else {
255                         eh.start(node, context);
256                     }
257                 }
258             }
259             
260         }
261     };
262
263     /**
264     * Perform the built-in template action for a given node
265     */

266     
267     private void defaultAction(NodeInfo node, Context context) throws TransformerException {
268         switch(node.getNodeType()) {
269             case NodeInfo.ROOT:
270             case NodeInfo.ELEMENT:
271                 applyTemplates(context, null, context.getMode(), null);
272                 return;
273             case NodeInfo.TEXT:
274             case NodeInfo.ATTRIBUTE:
275                 node.copyStringValue(getOutputter());
276                 return;
277             case NodeInfo.COMMENT:
278             case NodeInfo.PI:
279             case NodeInfo.NAMESPACE:
280                 // no action
281
return;
282         }
283     }
284                                 
285     /**
286     * Apply a template imported from the stylesheet containing the current template
287     */

288
289     public void applyImports(Context c, Mode mode, int min, int max, ParameterSet params)
290     throws TransformerException {
291         NodeInfo node = c.getCurrentNodeInfo();
292         NodeHandler nh = ruleManager.getHandler(node, mode, min, max, c);
293         
294         if (nh==null) { // use the default action for the node
295
defaultAction(node, c);
296         } else {
297             bindery.openStackFrame(params);
298             nh.start(node, c);
299             bindery.closeStackFrame();
300         }
301     }
302
303     /**
304     * Compare the position of two nodes in document order
305     * @param n1 The first node
306     * @param n2 The second node
307     * @return <0 if the first node is first in document order; >0 if
308     * the second node comes first in document order; 0 if the two parameters
309     * identify the same node
310     */

311     
312     public int compare(NodeInfo n1, NodeInfo n2) {
313         if (sourceDocumentPool.getNumberOfDocuments() == 1) {
314             return n1.compareOrder(n2);
315         }
316         int doc1 = sourceDocumentPool.getDocumentNumber(n1.getDocumentRoot());
317         int doc2 = sourceDocumentPool.getDocumentNumber(n2.getDocumentRoot());
318         if (doc1==doc2) {
319             return n1.compareOrder(n2);
320         } else {
321             return (doc1 - doc2);
322         }
323     }
324
325
326     ////////////////////////////////////////////////////////////////////////////////
327
// Methods for managing output destinations and formatting
328
////////////////////////////////////////////////////////////////////////////////
329

330     /**
331     * Set the output properties for the transformation. These
332     * properties will override properties set in the templates
333     * with xsl:output.
334     */

335     
336     public void setOutputProperties(Properties properties) {
337         Enumeration keys = properties.keys();
338         while(keys.hasMoreElements()) {
339             String key = (String)keys.nextElement();
340             setOutputProperty(key, (String)properties.get(key));
341         }
342     }
343
344     /**
345     * Get the output properties for the transformation.
346     */

347     
348     public Properties getOutputProperties() {
349         if (outputProperties == null) {
350             if (preparedStyleSheet==null) {
351                 return new Properties();
352             } else {
353                 outputProperties = preparedStyleSheet.getOutputProperties();
354             }
355         }
356         
357         // Make a copy, so that modifications to the returned properties have no effect
358

359         Properties newProps = new Properties();
360         Enumeration keys = outputProperties.keys();
361         while(keys.hasMoreElements()) {
362             String key = (String)keys.nextElement();
363             newProps.put(key, (String)outputProperties.get(key));
364         }
365         return newProps;
366     }
367
368     /**
369     * Set an output property for the transformation.
370     */

371
372     public void setOutputProperty(String name, String value) {
373         if (outputProperties == null) {
374             outputProperties = getOutputProperties();
375         }
376         if (!SaxonOutputKeys.isValidOutputKey(name)) {
377             throw new IllegalArgumentException(name);
378         }
379         outputProperties.put(name, value);
380     }
381
382     /**
383     * Get the value of an output property
384     */

385
386     public String getOutputProperty(String name) {
387         if (outputProperties == null) {
388             outputProperties = getOutputProperties();
389         }
390         return outputProperties.getProperty(name);
391         // TODO: validate that the name is a recognized property
392
}
393
394     /**
395     * Set a new output destination, supplying the output format details. <BR>
396     * This affects all further output until resetOutputDestination() is called. Note that
397     * it is the caller's responsibility to close the Writer after use.
398     * @param outputProperties Details of the new output format
399     * @param result Details of the new output destination
400     */

401
402     public void changeOutputDestination(Properties props, Result result)
403     throws TransformerException {
404         GeneralOutputter out = new GeneralOutputter(namePool);
405         out.setOutputDestination(props, result);
406         currentOutputter = out;
407     }
408
409     /**
410     * Set a simple StringBuffer output destination. Used during calls to
411     * xsl:attribute, xsl:comment, xsl:processing-instruction
412     */

413     
414     public void changeToTextOutputDestination(StringBuffer buffer) {
415         StringOutputter out = new StringOutputter(buffer);
416         out.setErrorListener(errorListener);
417         currentOutputter = out;
418     }
419
420     /**
421     * Get the current outputter
422     */

423
424     public Outputter getOutputter() {
425         return currentOutputter;
426     }
427
428     /**
429     * Close the current outputter, and revert to the previous outputter.
430     * @param outputter The outputter to revert to
431     */

432
433     public void resetOutputDestination(Outputter outputter) throws TransformerException {
434         //System.err.println("resetOutputDestination");
435
if (currentOutputter==null) {
436             throw new TransformerException("No outputter has been allocated");
437         }
438         currentOutputter.close();
439         currentOutputter = outputter;
440     }
441
442
443     ///////////////////////////////////////////////////////////////////////////////
444

445     /**
446     * Make an Emitter to be used for xsl:message output
447     */

448
449     public Emitter makeMessageEmitter() throws TransformerException {
450         String emitterClass = (String)factory.getAttribute(FeatureKeys.MESSAGE_EMITTER_CLASS);
451         
452         Object emitter = Loader.getInstance(emitterClass);
453         if (!(emitter instanceof Emitter)) {
454             throw new TransformerException(emitterClass + " is not an Emitter");
455         }
456         messageEmitter = (Emitter)emitter;
457         return messageEmitter;
458     }
459
460     /**
461     * Set the Emitter to be used for xsl:message output
462     */

463
464     public void setMessageEmitter(Emitter emitter) {
465         messageEmitter = emitter;
466     }
467
468     /**
469     * Get the Emitter used for xsl:message output
470     */

471
472     public Emitter getMessageEmitter() {
473        return messageEmitter;
474     }
475
476     /**
477     * Set the policy for handling recoverable errors
478     */

479
480     public void setRecoveryPolicy(int policy) {
481         recoveryPolicy = policy;
482         if (errorListener instanceof StandardErrorListener) {
483             ((StandardErrorListener)errorListener).setRecoveryPolicy(policy);
484         }
485     }
486
487     /**
488     * Get the policy for handling recoverable errors
489     */

490
491     public int getRecoveryPolicy() {
492         return recoveryPolicy;
493     }
494
495     /**
496     * Set the error listener
497     */

498     
499     public void setErrorListener(ErrorListener listener) {
500         errorListener = listener;
501     }
502     
503     /**
504     * Get the error listener
505     */

506     
507     public ErrorListener getErrorListener() {
508         return errorListener;
509     }
510
511     /**
512     * Report a recoverable error
513     * @throws TransformerException if the error listener decides not to recover
514     * from the error
515     */

516
517     public void reportRecoverableError(String message, SourceLocator location) throws TransformerException {
518         if (location==null) {
519             errorListener.warning(new TransformerException(message));
520         } else {
521             TransformerException err = new TransformerException(message, location);
522             errorListener.warning(err);
523         }
524     }
525
526     /**
527     * Report a recoverable error
528     * @throws TransformerException if the error listener decides not to recover
529     * from the error
530     */

531
532     public void reportRecoverableError(TransformerException err) throws TransformerException {
533         errorListener.warning(err);
534     }
535
536     /////////////////////////////////////////////////////////////////////////////////////////
537
// Methods for managing the Context and Bindery objects
538
/////////////////////////////////////////////////////////////////////////////////////////
539

540
541     /**
542     * Get the document pool. This is used only for source documents, not for stylesheet modules
543     */

544
545     public DocumentPool getDocumentPool() {
546         return sourceDocumentPool;
547     }
548     
549     /**
550     * Clear the document pool. This is sometimes useful when using the same Transformer
551     * for a sequence of transformations, but it isn't done automatically, because when
552     * the transformations use common look-up documents, the caching is beneficial.
553     */

554     
555     public void clearDocumentPool() {
556         sourceDocumentPool = new DocumentPool();
557     }
558
559     /**
560     * Set line numbering (of the source document) on or off
561     */

562
563     public void setLineNumbering(boolean onOrOff) {
564         lineNumbering = onOrOff;
565     }
566
567     /**
568     * Determine whether line numbering is enabled
569     */

570
571     public boolean isLineNumbering() {
572         return lineNumbering;
573     }
574
575     /**
576     * Create a new context with a given node as the current node and the only node in the current
577     * node list.
578     */

579
580     public Context makeContext(NodeInfo node) {
581         Context c = new Context(this);
582         c.setCurrentNode(node);
583         c.setContextNode(node);
584         c.setPosition(1);
585         c.setLast(1);
586         return c;
587     }
588
589     /**
590     * Get the current bindery
591     */

592
593     public Bindery getBindery() {
594         return bindery;
595     }
596
597     /**
598     * Get the primary URI resolver.
599     * @return the user-supplied URI resolver if there is one, or the system-defined one
600     * otherwise (Note, this isn't quite as JAXP specifies it).
601     */

602
603     public URIResolver getURIResolver() {
604         return (userURIResolver==null ? standardURIResolver : userURIResolver);
605     }
606
607     /**
608     * Get the fallback URI resolver.
609     * @return the the system-defined URIResolver
610     */

611
612     public URIResolver getStandardURIResolver() {
613         return standardURIResolver;
614     }
615
616
617     /**
618     * Get the KeyManager
619     */

620
621     public KeyManager getKeyManager() {
622         return styleSheetElement.getKeyManager();
623     }
624
625     /**
626     * Set the name pool to be used
627     */

628     
629     public void setNamePool(NamePool pool) {
630         namePool = pool;
631     }
632     
633     /**
634     * Get the name pool in use
635     */

636     
637     public NamePool getNamePool() {
638         return namePool;
639     }
640
641     /**
642     * Set the tree data model to use
643     */

644
645     public void setTreeModel(int model) {
646         treeModel = model;
647     }
648
649     /**
650     * Get the tree model in use
651     */

652
653     public int getTreeModel() {
654         return treeModel;
655     }
656
657     /**
658     * Disable whitespace stripping
659     */

660     
661     public void disableWhitespaceStripping(boolean disable) {
662         disableStripping = disable;
663     }
664     
665     /**
666     * Determine if whitespace stripping is disabled
667     */

668     
669     public boolean isWhitespaceStrippingDisabled() {
670         return disableStripping;
671     }
672
673     /**
674     * Make a builder for the selected tree model
675     */

676
677     public Builder makeBuilder() {
678         Builder b;
679         if (treeModel==Builder.TINY_TREE) {
680             b = new TinyBuilder();
681         } else {
682             b = new TreeBuilder();
683         }
684         Boolean timing = (Boolean)factory.getAttribute(FeatureKeys.TIMING);
685         b.setTiming((timing==null ? false : timing.booleanValue()));
686         b.setNamePool(namePool);
687         b.setLineNumbering(lineNumbering);
688         b.setErrorListener(errorListener);
689
690         Stripper stripper;
691         if (styleSheetElement==null) {
692             stripper = new Stripper(new Mode());
693         } else {
694             stripper = styleSheetElement.newStripper();
695         }
696         stripper.setController(this);
697         b.setStripper(stripper);
698         
699         return b;
700     }
701
702     //////////////////////////////////////////////////////////////////////
703
// Methods for handling decimal-formats
704
//////////////////////////////////////////////////////////////////////
705

706
707     public void setDecimalFormatManager(DecimalFormatManager manager) {
708         decimalFormatManager = manager;
709     }
710
711     public DecimalFormatManager getDecimalFormatManager() {
712         return decimalFormatManager;
713     }
714
715     ////////////////////////////////////////////////////////////////////////////////
716
// Methods for registering and retrieving handlers for template rules
717
////////////////////////////////////////////////////////////////////////////////
718

719     public void setRuleManager(RuleManager r) {
720         ruleManager = r;
721     }
722
723     public RuleManager getRuleManager() {
724         return ruleManager;
725     }
726
727     /////////////////////////////////////////////////////////////////////////
728
// Methods for tracing
729
/////////////////////////////////////////////////////////////////////////
730

731     public void setTraceListener(TraceListener trace) { // e.g.
732
traceListener = trace;
733     }
734
735     public TraceListener getTraceListener() { // e.g.
736
return traceListener;
737     }
738
739     public final boolean isTracing() { // e.g.
740
return traceListener != null && !tracingIsSuspended;
741     }
742     
743     public void pauseTracing(boolean pause) {
744         tracingIsSuspended = pause;
745     }
746
747     /**
748     * Associate this Controller with a compiled stylesheet
749     */

750
751     public void setPreparedStyleSheet(PreparedStyleSheet sheet) {
752         preparedStyleSheet = sheet;
753         styleSheetElement = (XSLStyleSheet)sheet.getStyleSheetDocument().getDocumentElement();
754         preview = (styleSheetElement.getPreviewManager() != null);
755         //setOutputProperties(sheet.getOutputProperties());
756
// above line deleted for bug 490964 - may have side-effects
757
}
758     
759     /**
760     * Does this transformation use preview mode?
761     */

762     
763     protected boolean usesPreviewMode() {
764         return preview;
765     }
766
767     /**
768     * Get the top element of the stylesheet document
769     */

770
771     private XSLStyleSheet getXSLStyleSheet() {
772          return styleSheetElement;
773     }
774     
775     /**
776     * Internal method to create and initialize a controller
777     */

778
779     private void initializeController() {
780         setRuleManager(styleSheetElement.getRuleManager());
781         setDecimalFormatManager(styleSheetElement.getDecimalFormatManager());
782
783         if (traceListener!=null) {
784             traceListener.open();
785         }
786
787         // get a new bindery, to clear out any variables from previous runs
788

789         bindery = new Bindery();
790         styleSheetElement.initialiseBindery(bindery);
791
792         // if parameters were supplied, set them up
793

794         bindery.defineGlobalParameters(parameters);
795     }
796
797     /**
798     * Adds the specified trace listener to receive trace events from
799     * this instance.
800     * Must be called before the invocation of the render method.
801     * @param trace the trace listener.
802     */

803     
804     public void addTraceListener(TraceListener trace) { // e.g.
805
traceListener = SaxonEventMulticaster.add(traceListener, trace);
806     }
807
808     /**
809     * Removes the specified trace listener so that the next invocation
810     * of the render method will not send trace events to the listener.
811     * @param trace the trace listener.
812     */

813     
814     public void removeTraceListener(TraceListener trace) { // e.g.
815
traceListener = SaxonEventMulticaster.remove(traceListener, trace);
816     }
817
818     /////////////////////////////////////////////////////////////////////////
819
// Allow user data to be associated with nodes on a tree
820
/////////////////////////////////////////////////////////////////////////
821

822     /**
823     * Get the named user data property for the node
824     * @param the name of the user data property to return
825     * @return The value of the named user data property.
826     * Returns null if no property of that name has been set using setUserData()
827     * for this NodeInfo object.
828     */

829
830     public Object getUserData(NodeInfo node, String name) {
831         String key = name + " " + node.generateId();
832         return userDataTable.get(key);
833     }
834     
835     /**
836     * Set a user data property for a node.
837     * @param name The name of the user data property to be set. Any existing user data property
838     * of the same name will be overwritten.
839     * @param userData an object to be saved with this element, which can be
840     * retrieved later using getUserData().
841     */

842     
843     public void setUserData(NodeInfo node, String name, Object data) {
844         String key = name + " " + node.generateId();
845         if (data==null) {
846             userDataTable.remove(key);
847         } else {
848             userDataTable.put(key, data);
849         }
850     }
851
852
853     /////////////////////////////////////////////////////////////////////////
854
// implement the javax.xml.transform.Transformer methods
855
/////////////////////////////////////////////////////////////////////////
856

857     /**
858     * Process the source tree to SAX parse events.
859     * @param source The input for the source tree.
860     * @param result The destination for the result tree.
861     * @throws TransformerException if the transformation fails. As a special case,
862     * the method throws a TerminationException (a subclass of TransformerException)
863     * if the transformation was terminated using xsl:message terminate="yes".
864     */

865     
866     public void transform(Source source, Result result) throws TransformerException {
867         if (preparedStyleSheet==null) {
868             throw new TransformerException("Stylesheet has not been prepared");
869         }
870
871         PreviewManager pm = styleSheetElement.getPreviewManager();
872         preview = (pm!=null);
873         
874         String path = "/";
875
876         try {
877             if (source instanceof NodeInfo) {
878                 // Any Saxon NodeInfo can be used directly as a Source
879
if (preview) {
880                     throw new TransformerException("Preview mode requires serial input");
881                 }
882                 transformDocument((NodeInfo)source, result);
883                 return;
884             }
885             if (source instanceof DOMSource) {
886                 DOMSource ds = (DOMSource)source;
887                 
888                 if (preview) {
889                     throw new TransformerException("Preview mode requires serial input");
890                 }
891                 if (disableStripping || !styleSheetElement.stripsWhitespace()) {
892                     
893                     if ( ds.getNode() instanceof NodeInfo ) {
894
895                         // bypass the tree building stage, and work on the tree as supplied
896

897                         transformDocument((NodeInfo)ds.getNode(), result);
898                         return;
899                     }
900                 }
901                 path = getPathToNode(ds.getNode());
902                 // System.err.println("path = " + path);
903
}
904                  
905             SAXSource in = factory.getSAXSource(source, false);
906             
907             // System.err.println("transform " + diagnosticName);
908

909             if (preview) {
910                 // run the build in preview mode
911
initializeController();
912                 //pm.setController(this);
913

914                 // in preview mode we don't try to use xsl:output properties
915
// for the principal output file, because in XSLT 1.1 they can
916
// be attribute value templates, and we can't evaluate these yet.
917

918                 if (outputProperties==null) {
919                     outputProperties = new Properties();
920                 }
921                 changeOutputDestination(outputProperties, result);
922             
923                 Builder sourceBuilder = makeBuilder();
924                 sourceBuilder.setController(this);
925                 sourceBuilder.setPreviewManager(pm);
926                 sourceBuilder.setNamePool(namePool);
927                 DocumentInfo doc = sourceBuilder.build(in);
928                 sourceDocumentPool.add(doc, null);
929                 sourceBuilder = null; // give the garbage collector a chance
930

931                 transformDocument(doc, result);
932                 resetOutputDestination(null);
933
934             } else {
935                 Builder sourceBuilder = makeBuilder();
936                 DocumentInfo doc = sourceBuilder.build(in);
937                 // ((com.icl.saxon.tinytree.TinyDocumentImpl)doc).diagnosticDump();
938
sourceDocumentPool.add(doc, null);
939                 sourceBuilder = null; // give the garbage collector a chance
940

941                 NodeInfo startNode = doc;
942                 if (!path.equals("/")) {
943                     Expression exp = Expression.make(path, new StandaloneContext(namePool));
944                     Context c = makeContext(doc);
945                     NodeEnumeration enum = exp.enumerate(c, false);
946                     if (enum.hasMoreElements()) {
947                         startNode = enum.nextElement();
948                     } else {
949                         throw new TransformerException("Problem finding the start node after converting DOM to Saxon tree");
950                     }
951                 }
952
953                 transformDocument(startNode, result);
954             }
955         } catch (TerminationException err) {
956             //System.err.println("Processing terminated using xsl:message");
957
throw err;
958         } catch (TransformerException err) {
959             Throwable cause = err.getException();
960             if (cause != null && cause instanceof SAXParseException) {
961                 // already reported
962
} else {
963                 errorListener.fatalError(err);
964             }
965             throw err;
966         }
967     }
968
969     /**
970     * Get an XPath expression referencing a node in a DOM
971     */

972     
973     private String getPathToNode(Node startNode) throws TransformerException {
974         short nodeType = startNode.getNodeType();
975         String path;
976         if ( nodeType == Node.DOCUMENT_NODE ) {
977             path = "/";
978         } else if ( nodeType == Node.ELEMENT_NODE ) {
979             path = "";
980             Node curr = startNode;
981             while ( nodeType == Node.ELEMENT_NODE ) {
982                 int count = 1;
983                 Node prior = curr.getPreviousSibling();
984                 while (prior != null) {
985                     short ptype = prior.getNodeType();
986                     if (ptype == Node.ELEMENT_NODE) {
987                         count++;
988                     } else if (ptype == Node.CDATA_SECTION_NODE ||
989                                  ptype == Node.ENTITY_REFERENCE_NODE) {
990                         throw new TransformerException(
991                             "Document contains CDATA or Entity nodes: can only transform starting at root");
992                     }
993                     prior = prior.getPreviousSibling();
994                 }
995                 if (!path.equals("")) {
996                     path = "/" + path;
997                 }
998                 path = "*[" + count + "]" + path;
999                 
1000                curr = curr.getParentNode();
1001                if (curr==null) {
1002                    throw new TransformerException("Supplied element is not within a Document");
1003                }
1004                nodeType = curr.getNodeType();
1005                if (nodeType == Node.DOCUMENT_NODE) {
1006                    path = "/" + path;
1007                } else if (nodeType == Node.CDATA_SECTION_NODE ||
1008                            nodeType == Node.ENTITY_REFERENCE_NODE) {
1009                    throw new TransformerException(
1010                        "Document contains CDATA or Entity nodes: can only transform starting at root");
1011                }
1012            }
1013        } else {
1014            throw new TransformerException("Start node must be either the root or an element");
1015        }
1016        return path;
1017    }
1018
1019    /**
1020    * Render a source XML document supplied as a tree. <br>
1021    * A new output destination should be created for each source document,
1022    * by using setOutputDetails(). <br>
1023    * @param startNode A Node that identifies the source document to be transformed and the
1024    * node where the transformation should start
1025    * @param result The output destination
1026    */

1027
1028    public void transformDocument(NodeInfo startNode, Result result)
1029    throws TransformerException {
1030        
1031        DocumentInfo sourceDoc;
1032        if (startNode instanceof DocumentInfo) {
1033            sourceDoc = (DocumentInfo)startNode;
1034        } else {
1035            sourceDoc = startNode.getDocumentRoot();
1036        }
1037
1038        transformFailure = null;
1039
1040        if (styleSheetElement==null) {
1041            throw new TransformerException("Stylesheet has not been prepared");
1042        }
1043
1044        if (sourceDoc.getNamePool()==null) {
1045            // must be a non-standard document implementation
1046
sourceDoc.setNamePool(preparedStyleSheet.getNamePool());
1047        }
1048
1049        if (sourceDoc.getNamePool() != preparedStyleSheet.getNamePool()) {
1050            throw new TransformerException("Source document and stylesheet must use the same name pool");
1051        }
1052
1053        Context context = makeContext(sourceDoc);
1054
1055        if (!preview) {
1056            initializeController();
1057            Properties xslOutputProps = new Properties();
1058            styleSheetElement.updateOutputProperties(xslOutputProps, context);
1059            // overlay the output properties defined via the API
1060
if (outputProperties!=null) {
1061                Enumeration enum = outputProperties.propertyNames();
1062                while (enum.hasMoreElements()) {
1063                    String p = (String)enum.nextElement();
1064                    String v = outputProperties.getProperty(p);
1065                    xslOutputProps.put(p, v);
1066                }
1067            }
1068            
1069            // deal with stylesheet chaining
1070
String nextInChain = xslOutputProps.getProperty(SaxonOutputKeys.NEXT_IN_CHAIN);
1071            if (nextInChain != null) {
1072                String baseURI = xslOutputProps.getProperty(SaxonOutputKeys.NEXT_IN_CHAIN_BASE_URI);
1073                result = prepareNextStylesheet(nextInChain, baseURI, result);
1074            }
1075                
1076            changeOutputDestination(xslOutputProps, result);
1077        }
1078
1079        // process the stylesheet document
1080
// (The main function of this phase is to evaluate global variables)
1081

1082        styleSheetElement.process(context);
1083
1084        // Process the source document using the handlers that have been set up
1085

1086        run(startNode);
1087
1088        if (traceListener!=null) {
1089            traceListener.close();
1090        }
1091
1092        if (!preview) {
1093            resetOutputDestination(null);
1094        }
1095
1096    }
1097
1098    /**
1099    * Prepare another stylesheet to handle the output of this one
1100    */

1101
1102    private Result prepareNextStylesheet(String href, String baseURI, Result result)
1103    throws TransformerException {
1104
1105        //TODO: should cache the results, we are recompiling the referenced
1106
//stylesheet each time it's used
1107

1108        //TODO: combine with similar method in XSLGeneralOutput
1109

1110        Source source = getURIResolver().resolve(href, baseURI);
1111        SAXSource saxSource = factory.getSAXSource(source, true);
1112
1113        Templates next = factory.newTemplates(source);
1114        TransformerHandler nextTransformer = factory.newTransformerHandler(next);
1115
1116        ContentHandlerProxy emitter = new ContentHandlerProxy();
1117        emitter.setUnderlyingContentHandler(nextTransformer);
1118        emitter.setSystemId(saxSource.getSystemId()); // pragmatic choice of system ID
1119
emitter.setRequireWellFormed(false);
1120        nextTransformer.setResult(result);
1121        
1122        return emitter;
1123    }
1124
1125    //////////////////////////////////////////////////////////////////////////
1126
// Handle parameters to the transformation
1127
//////////////////////////////////////////////////////////////////////////
1128

1129    /**
1130    * Set a parameter for the transformation.
1131    * @param name The name of the parameter in {uri}local format
1132    * @value The value object. This can be any valid Java object
1133    * it follows the same conversion rules as a value returned from a Saxon extension function.
1134    */

1135    
1136    public void setParameter(String expandedName, Object value) {
1137
1138        if (parameters == null) {
1139            parameters = new ParameterSet();
1140        }
1141
1142        Value result;
1143        try {
1144            result = FunctionProxy.convertJavaObjectToXPath(value, this);
1145        } catch (TransformerException err) {
1146            result = new StringValue(value.toString());
1147        }
1148        int fingerprint = getFingerprintForExpandedName(expandedName);
1149        parameters.put(fingerprint, result);
1150       
1151    }
1152
1153    /**
1154    * Set parameters supplied externally (typically, on the command line).
1155    * (non-TRAX method retained for backwards compatibility)
1156    * @param params A ParameterSet containing the (name, value) pairs.
1157    */

1158
1159    public void setParams(ParameterSet params) {
1160        this.parameters = params;
1161    }
1162
1163    /**
1164    * Get fingerprint for expanded name in {uri}local format
1165    */

1166
1167    private int getFingerprintForExpandedName(String expandedName) {
1168        String localName;
1169        String namespace;
1170
1171        if (expandedName.charAt(0)=='{') {
1172            int closeBrace = expandedName.indexOf('}');
1173            if (closeBrace < 0) {
1174                throw new IllegalArgumentException("No closing '}' in parameter name");
1175            }
1176            namespace = expandedName.substring(1, closeBrace);
1177            if (closeBrace == expandedName.length()) {
1178                throw new IllegalArgumentException("Missing local part in parameter name");
1179            }
1180            localName = expandedName.substring(closeBrace+1);
1181        } else {
1182            namespace = "";
1183            localName = expandedName;
1184        }
1185            
1186        return namePool.allocate("", namespace, localName);
1187    }
1188
1189    /**
1190    * Reset the parameters to a null list.
1191    */

1192    
1193    public void clearParameters() {
1194        parameters = null;
1195    }
1196
1197    /**
1198    * Get a parameter to the transformation
1199    */

1200
1201    public Object getParameter(String expandedName) {
1202        if (parameters==null) return null;
1203        int f = getFingerprintForExpandedName(expandedName);
1204        return parameters.get(f);
1205    }
1206  
1207    /**
1208    * Set an object that will be used to resolve URIs used in
1209    * document(), etc.
1210    * @param resolver An object that implements the URIResolver interface,
1211    * or null.
1212    */

1213    
1214    public void setURIResolver(URIResolver resolver) {
1215        userURIResolver = resolver;
1216    }
1217  
1218} // end of outer class Controller
1219

1220//
1221
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1222
// you may not use this file except in compliance with the License. You may obtain a copy of the
1223
// License at http://www.mozilla.org/MPL/
1224
//
1225
// Software distributed under the License is distributed on an "AS IS" basis,
1226
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1227
// See the License for the specific language governing rights and limitations under the License.
1228
//
1229
// The Original Code is: all this file.
1230
//
1231
// The Initial Developer of the Original Code is
1232
// Michael Kay of International Computers Limited (mhkay@iclway.co.uk).
1233
//
1234
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1235
//
1236
// Contributor(s):
1237
// Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
1238
//
1239
Popular Tags