0001: package net.sf.saxon;
0002:
0003: import net.sf.saxon.event.*;
0004: import net.sf.saxon.expr.XPathContext;
0005: import net.sf.saxon.expr.XPathContextMajor;
0006: import net.sf.saxon.functions.Component;
0007: import net.sf.saxon.instruct.*;
0008: import net.sf.saxon.om.*;
0009: import net.sf.saxon.sort.IntHashMap;
0010: import net.sf.saxon.tinytree.TinyBuilder;
0011: import net.sf.saxon.trace.*;
0012: import net.sf.saxon.trans.*;
0013: import net.sf.saxon.tree.TreeBuilder;
0014: import net.sf.saxon.value.DateTimeValue;
0015: import org.xml.sax.SAXParseException;
0016:
0017: import javax.xml.transform.*;
0018: import javax.xml.transform.dom.DOMSource;
0019: import javax.xml.transform.stream.StreamResult;
0020: import java.io.OutputStream;
0021: import java.io.PrintStream;
0022: import java.util.*;
0023:
0024: /**
0025: * The Controller is Saxon's implementation of the JAXP Transformer class, and represents
0026: * an executing instance of a transformation or query. Multiple concurrent executions of
0027: * the same transformation or query will use different Controller instances. This class is
0028: * therefore not thread-safe.
0029: * <p>
0030: * The Controller is serially reusable, as required by JAXP: when one transformation or query
0031: * is finished, it can be used to run another. However, there is no advantage in doing this
0032: * rather than allocating a new Controller each time.
0033: * <p>
0034: * The Controller can also be used when running Java applications that use neither XSLT nor
0035: * XQuery. A dummy Controller is created when running free-standing XPath expressions.
0036: * <p>
0037: * The Controller holds those parts of the dynamic context that do not vary during the course
0038: * of a transformation or query, or that do not change once their value has been computed.
0039: * This also includes those parts of the static context that are required at run-time.
0040: * <p>
0041: * Wherever possible XSLT applications should use the JAXP Transformer class directly,
0042: * rather than relying on Saxon-specific methods in the Controller. However, some
0043: * features are currently available only through this class. This applies especially
0044: * to new features specific to XSLT 2.0, since the JAXP interface still supports
0045: * only XSLT 1.0. Such methods may be superseded in the future by JAXP methods.
0046: * <p>
0047: * Many methods on the Controller are designed for internal use and should not be
0048: * considered stable. From release 8.4 onwards, those methods that are considered sufficiently
0049: * stable to constitute path of the Saxon public API are labelled with the JavaDoc tag "since":
0050: * the value indicates the release at which the method was added to the public API.
0051: *
0052: * @author Michael H. Kay
0053: * @since 8.4
0054: */
0055:
0056: public class Controller extends Transformer implements
0057: InstructionInfoProvider {
0058:
0059: private Configuration config;
0060: private DocumentInfo principalSourceDocument;
0061: private Bindery bindery; // holds values of global and local variables
0062: private NamePool namePool;
0063: private Emitter messageEmitter;
0064: private RuleManager ruleManager;
0065: private Properties outputProperties;
0066: private GlobalParameterSet parameters;
0067: private PreparedStylesheet preparedStylesheet;
0068: private TraceListener traceListener;
0069: private boolean tracingPaused;
0070: private URIResolver standardURIResolver;
0071: private URIResolver userURIResolver;
0072: private Result principalResult;
0073: private String principalResultURI;
0074: private OutputURIResolver outputURIResolver;
0075: private ErrorListener errorListener;
0076: private Executable executable;
0077: private int treeModel = Builder.TINY_TREE;
0078: private Template initialTemplate = null;
0079: private HashSet allOutputDestinations;
0080: private DocumentPool sourceDocumentPool;
0081: private HashMap userDataTable;
0082: private DateTimeValue currentDateTime;
0083: private boolean dateTimePreset = false;
0084: private int initialMode = -1;
0085: private NodeInfo lastRememberedNode = null;
0086: private int lastRememberedNumber = -1;
0087: private ClassLoader classLoader;
0088:
0089: // private int nextLocalDocumentNumber = -1;
0090:
0091: /**
0092: * Create a Controller and initialise variables. Constructor is protected,
0093: * the Controller should be created using newTransformer() in the PreparedStylesheet
0094: * class.
0095: *
0096: * @param config The Configuration used by this Controller
0097: */
0098:
0099: public Controller(Configuration config) {
0100: this .config = config;
0101: // create a dummy executable
0102: executable = new Executable();
0103: executable.setConfiguration(config);
0104: executable.setHostLanguage(config.getHostLanguage());
0105: sourceDocumentPool = new DocumentPool();
0106: reset();
0107: }
0108:
0109: /**
0110: * Create a Controller and initialise variables. Constructor is protected,
0111: * the Controller should be created using newTransformer() in the PreparedStylesheet
0112: * class.
0113: *
0114: * @param config The Configuration used by this Controller
0115: * @param executable The executable used by this Controller
0116: */
0117:
0118: public Controller(Configuration config, Executable executable) {
0119: this .config = config;
0120: this .executable = executable;
0121: sourceDocumentPool = new DocumentPool();
0122: reset();
0123: }
0124:
0125: /**
0126: * <p>Reset this <code>Transformer</code> to its original configuration.</p>
0127: * <p/>
0128: * <p><code>Transformer</code> is reset to the same state as when it was created with
0129: * {@link javax.xml.transform.TransformerFactory#newTransformer()},
0130: * {@link javax.xml.transform.TransformerFactory#newTransformer(javax.xml.transform.Source source)} or
0131: * {@link javax.xml.transform.Templates#newTransformer()}.
0132: * <code>reset()</code> is designed to allow the reuse of existing <code>Transformer</code>s
0133: * thus saving resources associated with the creation of new <code>Transformer</code>s.</p>
0134: * <p>
0135: * <i>The above is from the JAXP specification. With Saxon, it's unlikely that reusing a Transformer will
0136: * give any performance benefits over creating a new one. The one case where it might be beneficial is
0137: * to reuse the document pool (the set of documents that have been loaded using the doc() or document()
0138: * functions). Therefore, this method does not clear the document pool. If you want to clear the document
0139: * pool, call the method {@link #clearDocumentPool} as well.</i>
0140: * <p/>
0141: * <p>The reset <code>Transformer</code> is not guaranteed to have the same {@link javax.xml.transform.URIResolver}
0142: * or {@link javax.xml.transform.ErrorListener} <code>Object</code>s, e.g. {@link Object#equals(Object obj)}.
0143: * It is guaranteed to have a functionally equal <code>URIResolver</code>
0144: * and <code>ErrorListener</code>.</p>
0145: *
0146: * @since 1.5
0147: */
0148:
0149: public void reset() {
0150: bindery = new Bindery();
0151: namePool = NamePool.getDefaultNamePool();
0152: standardURIResolver = config.getSystemURIResolver();
0153: userURIResolver = config.getURIResolver();
0154:
0155: outputURIResolver = config.getOutputURIResolver();
0156: errorListener = config.getErrorListener();
0157: if (errorListener instanceof StandardErrorListener) {
0158: // if using a standard error listener, make a fresh one
0159: // for each transformation, because it is stateful - and also because the
0160: // host language is now known (a Configuration can serve multiple host languages)
0161: PrintStream ps = ((StandardErrorListener) errorListener)
0162: .getErrorOutput();
0163: errorListener = ((StandardErrorListener) errorListener)
0164: .makeAnother(executable.getHostLanguage());
0165: ((StandardErrorListener) errorListener).setErrorOutput(ps);
0166: ((StandardErrorListener) errorListener)
0167: .setRecoveryPolicy(config.getRecoveryPolicy());
0168: }
0169:
0170: userDataTable = new HashMap(20);
0171:
0172: traceListener = null;
0173: tracingPaused = false;
0174: TraceListener tracer = config.getTraceListener();
0175: if (tracer != null) {
0176: addTraceListener(tracer);
0177: }
0178:
0179: setTreeModel(config.getTreeModel());
0180: principalSourceDocument = null;
0181: messageEmitter = null;
0182: outputProperties = null;
0183: parameters = null;
0184:
0185: principalResult = null;
0186: principalResultURI = null;
0187: initialTemplate = null;
0188: allOutputDestinations = null;
0189: currentDateTime = null;
0190: dateTimePreset = false;
0191: initialMode = -1;
0192: lastRememberedNode = null;
0193: lastRememberedNumber = -1;
0194: classLoader = null;
0195:
0196: }
0197:
0198: /**
0199: * Get the Configuration associated with this Controller. The Configuration holds
0200: * settings that potentially apply globally to many different queries and transformations.
0201: * @return the Configuration object
0202: * @since 8.4
0203: */
0204: public Configuration getConfiguration() {
0205: return config;
0206: }
0207:
0208: /**
0209: * Set the initial mode for the transformation.
0210: * <p>
0211: * XSLT 2.0 allows a transformation to be started in a mode other than the default mode.
0212: * The transformation then starts by looking for the template rule in this mode that best
0213: * matches the initial context node.
0214: * <p>
0215: * This method may eventually be superseded by a standard JAXP method.
0216: *
0217: * @param expandedModeName the name of the initial mode. The mode is
0218: * supplied as an expanded QName, that is "localname" if there is no
0219: * namespace, or "{uri}localname" otherwise
0220: * @since 8.4
0221: */
0222:
0223: public void setInitialMode(String expandedModeName) {
0224: if (expandedModeName == null)
0225: return;
0226: if (expandedModeName.equals(""))
0227: return;
0228: initialMode = namePool.allocateClarkName(expandedModeName);
0229: }
0230:
0231: ////////////////////////////////////////////////////////////////////////////////
0232: // Methods for managing output destinations and formatting
0233: ////////////////////////////////////////////////////////////////////////////////
0234:
0235: /**
0236: * Set the output properties for the transformation. These
0237: * properties will override properties set in the templates
0238: * with xsl:output.
0239: * <p>
0240: * As well as the properties defined in the JAXP OutputKeys class,
0241: * Saxon defines an additional set of properties in {@link SaxonOutputKeys}.
0242: * These fall into two categories: Constants representing serialization
0243: * properties defined in XSLT 2.0 (which are not yet supported by JAXP),
0244: * and constants supporting Saxon extensions to the set of serialization
0245: * properties.
0246: *
0247: * @param properties the output properties to be used for the
0248: * transformation. If the value is null, the properties are reset to
0249: * be the properties of the Templates object (that is, for XSLT 2.0,
0250: * the properties set in the unnamed xsl:output object).
0251: * @see SaxonOutputKeys
0252: * @since 8.4
0253: */
0254:
0255: public void setOutputProperties(Properties properties) {
0256: if (properties == null) {
0257: outputProperties = null;
0258: } else {
0259: Enumeration keys = properties.propertyNames();
0260: while (keys.hasMoreElements()) {
0261: String key = (String) keys.nextElement();
0262: setOutputProperty(key, properties.getProperty(key));
0263: }
0264: }
0265: }
0266:
0267: /**
0268: * Get the output properties for the transformation.
0269: * <p>
0270: * As well as the properties defined in the JAXP OutputKeys class,
0271: * Saxon defines an additional set of properties in {@link SaxonOutputKeys}.
0272: * These fall into two categories: Constants representing serialization
0273: * properties defined in XSLT 2.0 (which are not yet supported by JAXP),
0274: * and constants supporting Saxon extensions to the set of serialization
0275: * properties.
0276: *
0277: * @return the output properties being used for the transformation,
0278: * including properties defined in the stylesheet for the unnamed
0279: * output format
0280: * @see SaxonOutputKeys
0281: * @since 8.4
0282: */
0283:
0284: public Properties getOutputProperties() {
0285: if (outputProperties == null) {
0286: if (executable == null) {
0287: return new Properties();
0288: } else {
0289: outputProperties = executable
0290: .getDefaultOutputProperties();
0291: }
0292: }
0293:
0294: // Make a copy, so that modifications to the returned properties have no effect
0295:
0296: Properties newProps = new Properties();
0297: Enumeration keys = outputProperties.propertyNames();
0298: while (keys.hasMoreElements()) {
0299: String key = (String) keys.nextElement();
0300: newProps.put(key, outputProperties.getProperty(key));
0301: }
0302: return newProps;
0303: }
0304:
0305: /**
0306: * Set an output property for the transformation.
0307: * <p>
0308: * As well as the properties defined in the JAXP OutputKeys class,
0309: * Saxon defines an additional set of properties in {@link SaxonOutputKeys}.
0310: * These fall into two categories: Constants representing serialization
0311: * properties defined in XSLT 2.0 (which are not yet supported by JAXP),
0312: * and constants supporting Saxon extensions to the set of serialization
0313: * properties.
0314: *
0315: * @param name the name of the property
0316: * @param value the value of the property
0317: * @see SaxonOutputKeys
0318: * @since 8.4
0319: */
0320:
0321: public void setOutputProperty(String name, String value) {
0322: if (outputProperties == null) {
0323: outputProperties = getOutputProperties();
0324: }
0325: try {
0326: SaxonOutputKeys.checkOutputProperty(name, value,
0327: getConfiguration().getNameChecker());
0328: } catch (DynamicError err) {
0329: throw new IllegalArgumentException(err.getMessage());
0330: }
0331: outputProperties.put(name, value);
0332: }
0333:
0334: /**
0335: * Get the value of an output property.
0336: * <p>
0337: * As well as the properties defined in the JAXP OutputKeys class,
0338: * Saxon defines an additional set of properties in {@link SaxonOutputKeys}.
0339: * These fall into two categories: Constants representing serialization
0340: * properties defined in XSLT 2.0 (which are not yet supported by JAXP),
0341: * and constants supporting Saxon extensions to the set of serialization
0342: * properties.
0343: *
0344: * @param name the name of the requested property
0345: * @return the value of the requested property
0346: * @see SaxonOutputKeys
0347: * @since 8.4
0348: */
0349:
0350: public String getOutputProperty(String name) {
0351: try {
0352: SaxonOutputKeys.checkOutputProperty(name, null,
0353: getConfiguration().getNameChecker());
0354: } catch (DynamicError err) {
0355: throw new IllegalArgumentException(err.getMessage());
0356: }
0357: if (outputProperties == null) {
0358: if (executable == null) {
0359: return null;
0360: } else {
0361: outputProperties = executable
0362: .getDefaultOutputProperties();
0363: }
0364: }
0365: return outputProperties.getProperty(name);
0366: }
0367:
0368: /**
0369: * Set the base output URI.
0370: * This defaults to the system ID of the principal Result object, but
0371: * a different value can be set for use where there is no principal result.
0372: * The command line interface sets this to the current working directory.
0373: * <p>
0374: * The concept of the base output URI is new in XSLT 2.0: it defines the
0375: * base URI for resolving relative URIs in the <code>href</code> attribute
0376: * of the <code>xsl:result-document</code> instruction. This method may be
0377: * superseded by a standard JAXP method when JAXP is updated to support XSLT 2.0.
0378: *
0379: * @param uri the base output URI
0380: * @since 8.4
0381: */
0382:
0383: public void setBaseOutputURI(String uri) {
0384: principalResultURI = uri;
0385: }
0386:
0387: /**
0388: * Get the base output URI.
0389: * This defaults to the system ID of the principal Result object, but
0390: * a different value can be set for use where there is no principal result.
0391: * The command line interface sets this to the current working directory.
0392: * <p>
0393: * The concept of the base output URI is new in XSLT 2.0: it defines the
0394: * base URI for resolving relative URIs in the <code>href</code> attribute
0395: * of the <code>xsl:result-document</code> instruction. This method may be
0396: * superseded by a standard JAXP method when JAXP is updated to support XSLT 2.0.
0397: *
0398: * @return the base output URI
0399: * @since 8.4
0400: */
0401:
0402: public String getBaseOutputURI() {
0403: return principalResultURI;
0404: }
0405:
0406: /**
0407: * Get the principal result destination.
0408: * <p>
0409: * This method is intended for internal use only.
0410: */
0411:
0412: public Result getPrincipalResult() {
0413: return principalResult;
0414: }
0415:
0416: /**
0417: * Check that an output destination has not been used before.
0418: * <p>
0419: * This method is intended for internal use only.
0420: */
0421:
0422: public boolean checkUniqueOutputDestination(String uri) {
0423: if (allOutputDestinations == null) {
0424: allOutputDestinations = new HashSet(20);
0425: }
0426: if (allOutputDestinations.contains(uri)) {
0427: return false;
0428: }
0429: allOutputDestinations.add(uri);
0430: return true;
0431: }
0432:
0433: ///////////////////////////////////////////////////////////////////////////////
0434:
0435: /**
0436: * Set the initial named template to be used as the entry point.
0437: * <p>
0438: * XSLT 2.0 allows a transformation to start by executing a named template, rather than
0439: * by matching an initial context node in a source document. This method may eventually
0440: * be superseded by a standard JAXP method once JAXP supports XSLT 2.0.
0441: * <p>
0442: * Although the Saxon command line interface does not allow both a source document and
0443: * an initial template to be specified, this API has no such restriction.
0444: * <p>
0445: * Note that any parameters supplied using {@link #setParameter} are used as the values
0446: * of global stylesheet parameters. There is no way to supply values for local parameters
0447: * of the initial template.
0448: *
0449: * @param expandedName The expanded name of the template in {uri}local format
0450: * @throws XPathException if there is no named template with this name
0451: * @since 8.4
0452: */
0453:
0454: public void setInitialTemplate(String expandedName)
0455: throws XPathException {
0456: int fingerprint = namePool.allocateClarkName(expandedName);
0457: Template t = getExecutable().getNamedTemplate(fingerprint);
0458: if (t == null) {
0459: DynamicError err = new DynamicError(
0460: "There is no named template with expanded name "
0461: + expandedName);
0462: err.setErrorCode("XTDE0040");
0463: throw err;
0464: } else {
0465: initialTemplate = t;
0466: }
0467: }
0468:
0469: ///////////////////////////////////////////////////////////////////////////////
0470:
0471: /**
0472: * Make a PipelineConfiguration based on the properties of this Controller.
0473: * <p>
0474: * This interface is intended primarily for internal use, although it may be necessary
0475: * for applications to call it directly for use in conjunction with the experimental pull
0476: * API.
0477: */
0478:
0479: public PipelineConfiguration makePipelineConfiguration() {
0480: PipelineConfiguration pipe = new PipelineConfiguration();
0481: pipe.setConfiguration(getConfiguration());
0482: pipe.setErrorListener(getErrorListener());
0483: pipe
0484: .setURIResolver(userURIResolver == null ? standardURIResolver
0485: : userURIResolver);
0486: pipe.setController(this );
0487: if (getExecutable() != null) {
0488: // can be null for an IdentityTransformer
0489: pipe.setLocationProvider(getExecutable().getLocationMap());
0490: }
0491: return pipe;
0492: }
0493:
0494: /**
0495: * Make an Emitter to be used for xsl:message output.
0496: * <p>
0497: * This method is intended for internal use only.
0498: *
0499: * @exception XPathException if any dynamic error occurs; in
0500: * particular, if the registered MessageEmitter class is not an
0501: * Emitter
0502: * @return The newly constructed message Emitter
0503: */
0504:
0505: public Emitter makeMessageEmitter() throws XPathException {
0506: String emitterClass = config.getMessageEmitterClass();
0507:
0508: Object emitter = config.getInstance(emitterClass,
0509: getClassLoader());
0510: if (!(emitter instanceof Emitter)) {
0511: throw new DynamicError(emitterClass + " is not an Emitter");
0512: }
0513: setMessageEmitter((Emitter) emitter);
0514: return messageEmitter;
0515: }
0516:
0517: /**
0518: * Set the Emitter to be used for xsl:message output.
0519: * <p>
0520: * Recent versions of the JAXP interface specify that by default the
0521: * output of xsl:message is sent to the registered ErrorListener. Saxon
0522: * does not yet implement this convention. Instead, the output is sent
0523: * to a default message emitter, which is a slightly customised implementation
0524: * of the standard Saxon Emitter interface.
0525: * <p>
0526: * This interface can be used to change the way in which Saxon outputs
0527: * xsl:message output.
0528: * <p>
0529: * It is not necessary to use this interface in order to change the destination
0530: * to which messages are written: that can be achieved by obtaining the standard
0531: * message emitter and calling its {@link Emitter#setWriter} method.
0532: * <p>
0533: * This method is intended for use by advanced applications. The Emitter interface
0534: * itself is not part of the stable Saxon public API.
0535: *
0536: * @param emitter The emitter to receive xsl:message output.
0537: */
0538:
0539: public void setMessageEmitter(Emitter emitter) {
0540: messageEmitter = emitter;
0541: messageEmitter
0542: .setPipelineConfiguration(makePipelineConfiguration());
0543: }
0544:
0545: /**
0546: * Get the Emitter used for xsl:message output. This returns the emitter
0547: * previously supplied to the {@link #setMessageEmitter} method, or the
0548: * default message emitter otherwise.
0549: *
0550: * @return the Emitter being used for xsl:message output
0551: */
0552:
0553: public Emitter getMessageEmitter() {
0554: return messageEmitter;
0555: }
0556:
0557: /**
0558: * Make a CharacterMapExpander to handle the character map definitions in the serialization
0559: * properties.
0560: * <p>
0561: * This method is intended for internal use only.
0562: *
0563: * @param useMaps the expanded use-character-maps property: a space-separated list of names
0564: * of character maps to be used, each one expressed as an expanded-QName in Clark notation
0565: * (that is, {uri}local-name).
0566: * @return a CharacterMapExpander if one is required, or null if not (for example, if the
0567: * useMaps argument is an empty string).
0568: * @throws XPathException if a name in the useMaps property cannot be resolved to a declared
0569: * character map.
0570: */
0571:
0572: public CharacterMapExpander makeCharacterMapExpander(String useMaps)
0573: throws XPathException {
0574: CharacterMapExpander characterMapExpander = null;
0575: IntHashMap characterMapIndex = getExecutable()
0576: .getCharacterMapIndex();
0577: if (useMaps != null && characterMapIndex != null) {
0578: List characterMaps = new ArrayList(5);
0579: StringTokenizer st = new StringTokenizer(useMaps);
0580: while (st.hasMoreTokens()) {
0581: String expandedName = st.nextToken();
0582: int f = namePool
0583: .getFingerprintForExpandedName(expandedName);
0584: IntHashMap map = (IntHashMap) characterMapIndex.get(f);
0585: if (map == null) {
0586: throw new DynamicError("Character map '"
0587: + expandedName + "' has not been defined");
0588: }
0589: characterMaps.add(map);
0590: }
0591: if (characterMaps.size() > 0) {
0592: characterMapExpander = new CharacterMapExpander();
0593: characterMapExpander.setCharacterMaps(characterMaps);
0594: }
0595: }
0596: return characterMapExpander;
0597: }
0598:
0599: /**
0600: * Get the policy for handling recoverable errors.
0601: * <p>
0602: * This method is intended for internal use
0603: *
0604: * @return the current policy. This is obtained from the error listener; if the error listener is
0605: * not a StandardErrorListener, the value RECOVER_WITH_WARNINGS is returned.
0606: */
0607:
0608: public int getRecoveryPolicy() {
0609: if (errorListener instanceof StandardErrorListener) {
0610: return ((StandardErrorListener) errorListener)
0611: .getRecoveryPolicy();
0612: } else {
0613: return Configuration.RECOVER_WITH_WARNINGS;
0614: }
0615: }
0616:
0617: /**
0618: * Set the error listener.
0619: *
0620: * @param listener the ErrorListener to be used
0621: */
0622:
0623: public void setErrorListener(ErrorListener listener) {
0624: errorListener = listener;
0625: }
0626:
0627: /**
0628: * Get the error listener.
0629: *
0630: * @return the ErrorListener in use
0631: */
0632:
0633: public ErrorListener getErrorListener() {
0634: return errorListener;
0635: }
0636:
0637: /**
0638: * Report a recoverable error. This is an XSLT concept: by default, such an error results in a warning
0639: * message, and processing continues. In XQuery, however, there are no recoverable errors so a fatal
0640: * error is reported.
0641: * <p>
0642: * This method is intended for internal use only.
0643: *
0644: * @param err An exception holding information about the error
0645: * @exception DynamicError if the error listener decides not to
0646: * recover from the error
0647: */
0648:
0649: public void recoverableError(XPathException err)
0650: throws DynamicError {
0651: try {
0652: if (executable.getHostLanguage() == Configuration.XQUERY) {
0653: reportFatalError(err);
0654: throw err;
0655: } else {
0656: errorListener.error(err);
0657: }
0658: } catch (TransformerException e) {
0659: DynamicError de = DynamicError.makeDynamicError(e);
0660: de.setHasBeenReported();
0661: throw de;
0662: }
0663: }
0664:
0665: /**
0666: * Report a fatal error
0667: */
0668:
0669: public void reportFatalError(XPathException err) {
0670: if (!err.hasBeenReported()) {
0671: try {
0672: getErrorListener().fatalError(err);
0673: } catch (TransformerException e) {
0674: //
0675: }
0676: err.setHasBeenReported();
0677: }
0678: }
0679:
0680: /////////////////////////////////////////////////////////////////////////////////////////
0681: // Methods for managing the various runtime control objects
0682: /////////////////////////////////////////////////////////////////////////////////////////
0683:
0684: /**
0685: * Get the Executable object.
0686: * <p>
0687: * This method is intended for internal use only.
0688: *
0689: * @return the Executable (which represents the compiled stylesheet)
0690: */
0691:
0692: public Executable getExecutable() {
0693: return executable;
0694: }
0695:
0696: /**
0697: * Get the document pool. This is used only for source documents, not for stylesheet modules.
0698: * <p>
0699: * This method is intended for internal use only.
0700: *
0701: * @return the source document pool
0702: */
0703:
0704: public DocumentPool getDocumentPool() {
0705: return sourceDocumentPool;
0706: }
0707:
0708: /**
0709: * Clear the document pool.
0710: * This is sometimes useful when re-using the same Transformer
0711: * for a sequence of transformations, but it isn't done automatically, because when
0712: * the transformations use common look-up documents, the caching is beneficial.
0713: */
0714:
0715: public void clearDocumentPool() {
0716: sourceDocumentPool = new DocumentPool();
0717: }
0718:
0719: /**
0720: * Set the principal source document (used for evaluating global variables).
0721: * When a transformation is invoked using the {@link #transform} method, the
0722: * principal source document is set automatically. This method is useful in XQuery,
0723: * to define an initial context node for evaluating global variables, and also
0724: * in XSLT 2.0, when the transformation is started by invoking a named template.
0725: *
0726: * @param doc The principal source document
0727: * @since 8.4
0728: */
0729:
0730: public void setPrincipalSourceDocument(DocumentInfo doc) {
0731: principalSourceDocument = doc;
0732: }
0733:
0734: /**
0735: * Get the current bindery.
0736: * <p>
0737: * This method is intended for internal use only.
0738: *
0739: * @return the Bindery (in which values of all variables are held)
0740: */
0741:
0742: public Bindery getBindery() {
0743: return bindery;
0744: }
0745:
0746: /**
0747: * Get the principal source document. This returns the document
0748: * previously supplied to the {@link #setPrincipalSourceDocument} method, or the
0749: * principal source document set implicitly using methods such as {@link #transform}.
0750: * @return the principal source document
0751: * @since 8.4
0752: */
0753:
0754: public DocumentInfo getPrincipalSourceDocument() {
0755: return principalSourceDocument;
0756: }
0757:
0758: /**
0759: * Set an object that will be used to resolve URIs used in
0760: * document(), etc.
0761: *
0762: * @param resolver An object that implements the URIResolver interface, or
0763: * null.
0764: */
0765:
0766: public void setURIResolver(URIResolver resolver) {
0767: userURIResolver = resolver;
0768: }
0769:
0770: /**
0771: * Get the URI resolver.
0772: *
0773: * <p><i>This method changed in Saxon 8.5, to conform to the JAXP specification. If there
0774: * is no user-specified URIResolver, it now returns null; previously it returned the system
0775: * default URIResolver.</i></p>
0776: *
0777: * @return the user-supplied URI resolver if there is one, or null otherwise.
0778: */
0779:
0780: public URIResolver getURIResolver() {
0781: return userURIResolver;
0782: }
0783:
0784: /**
0785: * Get the fallback URI resolver. This is the URIResolver that Saxon uses when
0786: * the user-supplied URI resolver returns null.
0787: * <p>
0788: * This method is intended for internal use only.
0789: *
0790: * @return the the system-defined URIResolver
0791: */
0792:
0793: public URIResolver getStandardURIResolver() {
0794: return standardURIResolver;
0795: }
0796:
0797: /**
0798: * Set the URI resolver for secondary output documents.
0799: * <p>
0800: * XSLT 2.0 introduces the <code>xsl:result-document</code instruction,
0801: * allowing a transformation to have multiple result documents. JAXP does
0802: * not yet support this capability. This method allows an OutputURIResolver
0803: * to be specified that takes responsibility for deciding the destination
0804: * (and, if it wishes, the serialization properties) of secondary output files.
0805: * <p>
0806: * This method may eventually be superseded by a standard JAXP method.
0807: *
0808: * @param resolver An object that implements the OutputURIResolver
0809: * interface, or null.
0810: * @since 8.4
0811: */
0812:
0813: public void setOutputURIResolver(OutputURIResolver resolver) {
0814: if (resolver == null) {
0815: outputURIResolver = StandardOutputResolver.getInstance();
0816: } else {
0817: outputURIResolver = resolver;
0818: }
0819: }
0820:
0821: /**
0822: * Get the output URI resolver.
0823: *
0824: * @return the user-supplied URI resolver if there is one, or the
0825: * system-defined one otherwise.
0826: * @see #setOutputURIResolver
0827: * @since 8.4
0828: */
0829:
0830: public OutputURIResolver getOutputURIResolver() {
0831: return outputURIResolver;
0832: }
0833:
0834: /**
0835: * Get the KeyManager.
0836: * <p>
0837: * This method is intended for internal use only.
0838: *
0839: * @return the KeyManager, which holds details of all key declarations
0840: */
0841:
0842: public KeyManager getKeyManager() {
0843: return executable.getKeyManager();
0844: }
0845:
0846: /**
0847: * Get the name pool in use. The name pool is responsible for mapping QNames used in source
0848: * documents and compiled stylesheets and queries into numeric codes. All source documents
0849: * used by a given transformation or query must use the same name pool as the compiled stylesheet
0850: * or query.
0851: *
0852: * @return the name pool in use
0853: * @since 8.4
0854: */
0855:
0856: public NamePool getNamePool() {
0857: return namePool;
0858: }
0859:
0860: /**
0861: * Set the tree data model to use. This affects all source documents subsequently constructed using a
0862: * Builder obtained from this Controller. This includes a document built from a StreamSource or
0863: * SAXSource supplied as a parameter to the {@link #transform} method.
0864: *
0865: * @param model the required tree model: {@link Builder#STANDARD_TREE} or
0866: * {@link Builder#TINY_TREE}
0867: * @see net.sf.saxon.event.Builder
0868: * @since 8.4
0869: */
0870:
0871: public void setTreeModel(int model) {
0872: treeModel = model;
0873: }
0874:
0875: /**
0876: * Make a builder for the selected tree model.
0877: *
0878: * @return an instance of the Builder for the chosen tree model
0879: * @since 8.4
0880: */
0881:
0882: public Builder makeBuilder() {
0883: Builder b;
0884: if (treeModel == Builder.TINY_TREE) {
0885: b = new TinyBuilder();
0886: } else {
0887: b = new TreeBuilder();
0888: }
0889: b.setTiming(config.isTiming());
0890: b.setLineNumbering(config.isLineNumbering());
0891: b.setPipelineConfiguration(makePipelineConfiguration());
0892: return b;
0893: }
0894:
0895: /**
0896: * Make a Stripper configured to implement the whitespace stripping rules.
0897: * In the case of XSLT the whitespace stripping rules are normally defined
0898: * by <code>xsl:strip-space</code> and <code>xsl:preserve-space</code elements
0899: * in the stylesheet. Alternatively, stripping of all whitespace text nodes
0900: * may be defined at the level of the Configuration, using the method
0901: * {@link Configuration#setStripsAllWhiteSpace(boolean)}.
0902: *
0903: * @param b the Receiver to which the events filtered by this stripper are
0904: * to be sent (often a Builder). May be null if the stripper is not being used for filtering
0905: * into a Builder or other Receiver.
0906: * @return the required Stripper. A Stripper may be used in two ways. It acts as
0907: * a filter applied to an event stream, that can be used to remove the events
0908: * representing whitespace text nodes before they reach a Builder. Alternatively,
0909: * it can be used to define a view of an existing tree in which the whitespace
0910: * text nodes are dynamically skipped while navigating the XPath axes.
0911: * @since 8.4 - Generalized in 8.5 to accept any Receiver as an argument
0912: */
0913:
0914: public Stripper makeStripper(Receiver b) {
0915: if (config.isStripsAllWhiteSpace()) {
0916: if (b == null) {
0917: return AllElementStripper.getInstance();
0918: } else {
0919: Stripper s = new AllElementStripper();
0920: s.setUnderlyingReceiver(b);
0921: return s;
0922: }
0923: }
0924: Stripper stripper;
0925: if (executable == null) {
0926: stripper = new Stripper(new Mode(Mode.STRIPPER_MODE));
0927: } else {
0928: stripper = executable.newStripper();
0929: }
0930: stripper.setPipelineConfiguration(makePipelineConfiguration());
0931: //stripper.setController(this);
0932: if (b != null) {
0933: stripper.setUnderlyingReceiver(b);
0934: }
0935:
0936: return stripper;
0937: }
0938:
0939: /**
0940: * Add a document to the document pool.
0941: * <p>
0942: * This method is intended for internal use only.
0943: *
0944: * @param doc the root node of the document to be added
0945: * @param systemId thesystem ID of this document
0946: */
0947: public void registerDocument(DocumentInfo doc, String systemId) {
0948: sourceDocumentPool.add(doc, systemId);
0949: }
0950:
0951: ////////////////////////////////////////////////////////////////////////////////
0952: // Methods for registering and retrieving handlers for template rules
0953: ////////////////////////////////////////////////////////////////////////////////
0954:
0955: /**
0956: * Set the RuleManager, used to manage template rules for each mode.
0957: * <p>
0958: * This method is intended for internal use only.
0959: *
0960: * @param r the Rule Manager
0961: */
0962: public void setRuleManager(RuleManager r) {
0963: ruleManager = r;
0964: }
0965:
0966: /**
0967: * Get the Rule Manager.
0968: * <p>
0969: * This method is intended for internal use only.
0970: *
0971: * @return the Rule Manager, used to hold details of template rules for
0972: * all modes
0973: */
0974: public RuleManager getRuleManager() {
0975: return ruleManager;
0976: }
0977:
0978: /////////////////////////////////////////////////////////////////////////
0979: // Methods for tracing
0980: /////////////////////////////////////////////////////////////////////////
0981:
0982: /**
0983: * Get the TraceListener. By default, there is no TraceListener, and this
0984: * method returns null. A TraceListener may be added using the method
0985: * {@link #addTraceListener}. If more than one TraceListener has been added,
0986: * this method will return a composite TraceListener. Because this form
0987: * this takes is implementation-dependent, this method is not part of the
0988: * stable Saxon public API.
0989: *
0990: * @return the TraceListener used for XSLT or XQuery instruction tracing
0991: */
0992: public TraceListener getTraceListener() { // e.g.
0993: return traceListener;
0994: }
0995:
0996: /**
0997: * Test whether instruction execution is being traced. This will be true
0998: * if (a) at least one TraceListener has been registered using the
0999: * {@link #addTraceListener} method, and (b) tracing has not been temporarily
1000: * paused using the {@link #pauseTracing} method.
1001: *
1002: * @return true if tracing is active, false otherwise
1003: * @since 8.4
1004: */
1005:
1006: public final boolean isTracing() { // e.g.
1007: return traceListener != null && !tracingPaused;
1008: }
1009:
1010: /**
1011: * Pause or resume tracing. While tracing is paused, trace events are not sent to any
1012: * of the registered TraceListeners.
1013: *
1014: * @param pause true if tracing is to pause; false if it is to resume
1015: * @since 8.4
1016: */
1017: public final void pauseTracing(boolean pause) {
1018: tracingPaused = pause;
1019: }
1020:
1021: /**
1022: * Adds the specified trace listener to receive trace events from
1023: * this instance. Note that although TraceListeners can be added
1024: * or removed dynamically, this has no effect unless the stylesheet
1025: * or query has been compiled with tracing enabled. This is achieved
1026: * by calling {@link Configuration#setTraceListener} or by setting
1027: * the attribute {@link FeatureKeys#TRACE_LISTENER} on the
1028: * TransformerFactory. Conversely, if this property has been set in the
1029: * Configuration or TransformerFactory, the TraceListener will automatically
1030: * be added to every Controller that uses that Configuration.
1031: *
1032: * @param trace the trace listener.
1033: * @since 8.4
1034: */
1035:
1036: public void addTraceListener(TraceListener trace) { // e.g.
1037: traceListener = TraceEventMulticaster.add(traceListener, trace);
1038: }
1039:
1040: /**
1041: * Removes the specified trace listener so that the listener will no longer
1042: * receive trace events.
1043: *
1044: * @param trace the trace listener.
1045: * @since 8.4
1046: */
1047:
1048: public void removeTraceListener(TraceListener trace) { // e.g.
1049: traceListener = TraceEventMulticaster.remove(traceListener,
1050: trace);
1051: }
1052:
1053: /**
1054: * Associate this Controller with a compiled stylesheet.
1055: * <p>
1056: * This method is intended for internal use only.
1057: *
1058: * @param sheet the compiled stylesheet
1059: */
1060:
1061: public void setPreparedStylesheet(PreparedStylesheet sheet) {
1062: preparedStylesheet = sheet;
1063: executable = sheet.getExecutable();
1064: //setOutputProperties(sheet.getOutputProperties());
1065: // above line deleted for bug 490964 - may have side-effects
1066: }
1067:
1068: /**
1069: * Associate this Controller with an Executable. This method is used by the XQuery
1070: * processor. The Executable object is overkill in this case - the only thing it
1071: * currently holds are copies of the collation table.
1072: * <p>
1073: * This method is intended for internal use only
1074: * @param exec the Executable
1075: */
1076:
1077: public void setExecutable(Executable exec) {
1078: executable = exec;
1079: }
1080:
1081: /**
1082: * Initialize the controller ready for a new transformation. This method should not normally be called by
1083: * users (it is done automatically when transform() is invoked). However, it is available as a low-level API
1084: * especially for use with XQuery.
1085: */
1086:
1087: public void initializeController() throws XPathException {
1088: setRuleManager(executable.getRuleManager());
1089: //setDecimalFormatManager(executable.getDecimalFormatManager());
1090:
1091: if (traceListener != null) {
1092: traceListener.open();
1093: }
1094:
1095: // get a new bindery, to clear out any variables from previous runs
1096:
1097: bindery = new Bindery();
1098: executable.initialiseBindery(bindery);
1099:
1100: // create an initial stack frame, used for evaluating standalone expressions,
1101: // e.g. expressions within the filter of a match pattern. This stack frame
1102: // never gets closed, but no one will notice.
1103:
1104: //bindery.openStackFrame();
1105:
1106: // if parameters were supplied, set them up
1107:
1108: defineGlobalParameters(bindery);
1109: }
1110:
1111: /**
1112: * Define the global parameters of the transformation or query.
1113: * <p>
1114: * This method is intended for internal use only
1115: * @param bindery The Bindery, which holds values of global variables and parameters
1116: */
1117:
1118: public void defineGlobalParameters(Bindery bindery)
1119: throws XPathException {
1120: executable.checkAllRequiredParamsArePresent(parameters);
1121: bindery.defineGlobalParameters(parameters);
1122: }
1123:
1124: /////////////////////////////////////////////////////////////////////////
1125: // Allow user data to be associated with nodes on a tree
1126: /////////////////////////////////////////////////////////////////////////
1127:
1128: /**
1129: * Get user data associated with a key. To retrieve user data, two objects are required:
1130: * an arbitrary object that may be regarded as the container of the data (originally, and
1131: * typically still, a node in a tree), and a name. The name serves to distingush data objects
1132: * associated with the same node by different client applications.
1133: * <p>
1134: * This method is intended primarily for internal use, though it may also be
1135: * used by advanced applications.
1136: *
1137: * @param key an object acting as a key for this user data value. This must be equal
1138: * (in the sense of the equals() method) to the key supplied when the data value was
1139: * registered using {@link #setUserData}.
1140: * @param name the name of the required property
1141: * @return the value of the required property
1142: */
1143:
1144: public Object getUserData(Object key, String name) {
1145: String keyValue = key.hashCode() + " " + name;
1146: // System.err.println("getUserData " + name + " on object returning " + userDataTable.get(key));
1147: return userDataTable.get(keyValue);
1148: }
1149:
1150: /**
1151: * Set user data associated with a key. To store user data, two objects are required:
1152: * an arbitrary object that may be regarded as the container of the data (originally, and
1153: * typically still, a node in a tree), and a name. The name serves to distingush data objects
1154: * associated with the same node by different client applications.
1155: * <p>
1156: * This method is intended primarily for internal use, though it may also be
1157: * used by advanced applications.
1158: *
1159: * @param key an object acting as a key for this user data value. This must be equal
1160: * (in the sense of the equals() method) to the key supplied when the data value was
1161: * registered using {@link #setUserData}. If data for the given object and name already
1162: * exists, it is overwritten.
1163: * @param name the name of the required property
1164: * @param data the value of the required property
1165: */
1166:
1167: public void setUserData(Object key, String name, Object data) {
1168: // System.err.println("setUserData " + name + " on object to " + data);
1169: String keyVal = key.hashCode() + " " + name;
1170: if (data == null) {
1171: userDataTable.remove(keyVal);
1172: } else {
1173: userDataTable.put(keyVal, data);
1174: }
1175: }
1176:
1177: /////////////////////////////////////////////////////////////////////////
1178: // implement the javax.xml.transform.Transformer methods
1179: /////////////////////////////////////////////////////////////////////////
1180:
1181: /**
1182: * Perform a transformation from a Source document to a Result document.
1183: *
1184: * @exception XPathException if the transformation fails. As a
1185: * special case, the method throws a TerminationException (a subclass
1186: * of XPathException) if the transformation was terminated using
1187: * xsl:message terminate="yes".
1188: * @param source The input for the source tree. May be null if and only if an
1189: * initial template has been supplied.
1190: * @param result The destination for the result tree.
1191: */
1192:
1193: public void transform(Source source, Result result)
1194: throws TransformerException {
1195: if (preparedStylesheet == null) {
1196: throw new DynamicError("Stylesheet has not been prepared");
1197: }
1198:
1199: if (!dateTimePreset) {
1200: currentDateTime = null; // reset at start of each transformation
1201: }
1202:
1203: try {
1204: NodeInfo startNode = null;
1205: boolean wrap = true;
1206: int validationMode = config.getSchemaValidationMode();
1207: Source underSource = source;
1208: if (source instanceof AugmentedSource) {
1209: Boolean localWrap = ((AugmentedSource) source)
1210: .getWrapDocument();
1211: if (localWrap != null) {
1212: wrap = localWrap.booleanValue();
1213: }
1214: int localValidate = ((AugmentedSource) source)
1215: .getSchemaValidation();
1216: if (localValidate != Validation.DEFAULT) {
1217: validationMode = localValidate;
1218: }
1219: if (validationMode == Validation.STRICT
1220: || validationMode == Validation.LAX) {
1221: // If validation of a DOMSource or NodeInfo is requested, we must copy it, we can't wrap it
1222: wrap = false;
1223: }
1224: underSource = ((AugmentedSource) source)
1225: .getContainedSource();
1226: }
1227: Source s2 = config.getSourceResolver().resolveSource(
1228: underSource, config);
1229: if (s2 != null) {
1230: underSource = s2;
1231: }
1232: if (wrap
1233: && (underSource instanceof NodeInfo || underSource instanceof DOMSource)) {
1234: startNode = prepareInputTree(underSource);
1235: registerDocument(startNode.getDocumentRoot(),
1236: underSource.getSystemId());
1237:
1238: } else if (source == null) {
1239: if (initialTemplate == null) {
1240: throw new DynamicError(
1241: "Either a source document or an initial template must be specified");
1242: }
1243:
1244: } else {
1245: // The input is a SAXSource or StreamSource, or
1246: // a DOMSource with wrap=no: build the document tree
1247:
1248: Builder sourceBuilder = makeBuilder();
1249: Sender sender = new Sender(makePipelineConfiguration());
1250: Receiver r = sourceBuilder;
1251: if (config.isStripsAllWhiteSpace()
1252: || executable.stripsWhitespace()
1253: || validationMode == Validation.STRICT
1254: || validationMode == Validation.LAX) {
1255: r = makeStripper(sourceBuilder);
1256: }
1257: if (executable.stripsInputTypeAnnotations()) {
1258: r = config.getAnnotationStripper(r);
1259: }
1260: sender.send(source, r);
1261: DocumentInfo doc = (DocumentInfo) sourceBuilder
1262: .getCurrentRoot();
1263: registerDocument(doc, source.getSystemId());
1264: startNode = doc;
1265: }
1266:
1267: transformDocument(startNode, result);
1268:
1269: } catch (TerminationException err) {
1270: //System.err.println("Processing terminated using xsl:message");
1271: throw err;
1272: } catch (XPathException err) {
1273: Throwable cause = err.getException();
1274: if (cause != null && cause instanceof SAXParseException) {
1275: // This generally means the error was already reported.
1276: // But if a RuntimeException occurs in Saxon during a callback from
1277: // the Crimson parser, Crimson wraps this in a SAXParseException without
1278: // reporting it further.
1279: SAXParseException spe = (SAXParseException) cause;
1280: cause = spe.getException();
1281: if (cause instanceof RuntimeException) {
1282: reportFatalError(err);
1283: }
1284: } else {
1285: reportFatalError(err);
1286: }
1287: throw err;
1288: }
1289: }
1290:
1291: /**
1292: * Prepare an input tree for processing. This is used when either the initial
1293: * input, or a Source returned by the document() function, is a NodeInfo or a
1294: * DOMSource. The preparation consists of wrapping a DOM document inside a wrapper
1295: * that implements the NodeInfo interface, and/or adding a space-stripping wrapper
1296: * if the stylesheet strips whitespace nodes.
1297: * <p>
1298: * This method is intended for internal use.
1299: *
1300: * @param source the input tree. Must be either a DOMSource or a NodeInfo
1301: * @return the NodeInfo representing the input node, suitably wrapped.
1302: */
1303:
1304: public NodeInfo prepareInputTree(Source source) {
1305: NodeInfo start = unravel(source, getConfiguration());
1306: if (executable.stripsWhitespace()) {
1307: DocumentInfo docInfo = start.getDocumentRoot();
1308: StrippedDocument strippedDoc = new StrippedDocument(
1309: docInfo, makeStripper(null));
1310: start = strippedDoc.wrap(start);
1311: }
1312: return start;
1313: }
1314:
1315: /**
1316: * Get a NodeInfo corresponding to a DOM Node, either by wrapping or unwrapping the DOM Node.
1317: * <p>
1318: * This method is intended for internal use.
1319: */
1320:
1321: public static NodeInfo unravel(Source source, Configuration config) {
1322: List externalObjectModels = config.getExternalObjectModels();
1323: for (int m = 0; m < externalObjectModels.size(); m++) {
1324: ExternalObjectModel model = (ExternalObjectModel) externalObjectModels
1325: .get(m);
1326: NodeInfo node = model.unravel(source, config);
1327: if (node != null) {
1328: return node;
1329: }
1330: }
1331: if (source instanceof NodeInfo) {
1332: return (NodeInfo) source;
1333: }
1334: return null;
1335: }
1336:
1337: /**
1338: * Transform a source XML document supplied as a tree. <br>
1339: * A new output destination should be created for each source document,
1340: * by using setOutputDetails().
1341: * <p>
1342: * This method is intended for internal use. External applications should use
1343: * the {@link #transform} method, which is part of the JAXP interface. Note that
1344: * <code>NodeInfo</code> implements the JAXP <code>Source</code> interface, so
1345: * it may be supplied directly to the transform() method.
1346: *
1347: * @exception XPathException if any dynamic error occurs
1348: * @param startNode A Node that identifies the source document to be
1349: * transformed and the node where the transformation should start.
1350: * May be null if the transformation is to start using an initial template.
1351: * @param result The output destination
1352: */
1353:
1354: public void transformDocument(NodeInfo startNode, Result result)
1355: throws TransformerException {
1356:
1357: if (executable == null) {
1358: throw new DynamicError("Stylesheet has not been compiled");
1359: }
1360:
1361: // Determine whether we need to close the output stream at the end. We
1362: // do this if the Result object is a StreamResult and is supplied as a
1363: // system ID, not as a Writer or OutputStream
1364:
1365: boolean mustClose = (result instanceof StreamResult && ((StreamResult) result)
1366: .getOutputStream() == null);
1367:
1368: principalResult = result;
1369: if (principalResultURI == null) {
1370: principalResultURI = result.getSystemId();
1371: }
1372:
1373: XPathContextMajor initialContext = newXPathContext();
1374: initialContext.setOrigin(this );
1375:
1376: if (startNode != null) {
1377: DocumentInfo sourceDoc;
1378: if (startNode instanceof DocumentInfo) {
1379: sourceDoc = (DocumentInfo) startNode;
1380: } else {
1381: sourceDoc = startNode.getDocumentRoot();
1382: if (sourceDoc == null) {
1383: throw new DynamicError(
1384: "Source tree must have a document node as its root");
1385: }
1386: }
1387:
1388: principalSourceDocument = sourceDoc;
1389:
1390: if (sourceDoc.getConfiguration() == null) {
1391: // must be a non-standard document implementation
1392: throw new TransformerException(
1393: "The supplied source document must be associated with a Configuration");
1394: //sourceDoc.setConfiguration(getConfiguration());
1395: }
1396:
1397: if (sourceDoc.getNamePool() != preparedStylesheet
1398: .getTargetNamePool()) {
1399: throw new DynamicError(
1400: "Source document and stylesheet must use the same name pool");
1401: }
1402: SequenceIterator currentIter = SingletonIterator
1403: .makeIterator(sourceDoc);
1404: currentIter.next();
1405: initialContext.setCurrentIterator(currentIter);
1406: }
1407:
1408: initializeController();
1409:
1410: // In tracing/debugging mode, evaluate all the global variables first
1411: if (getConfiguration().getTraceListener() != null) {
1412: preEvaluateGlobals(initialContext);
1413: }
1414:
1415: Properties xslOutputProps = executable
1416: .getDefaultOutputProperties();
1417:
1418: // overlay the output properties defined via the API
1419: if (outputProperties != null) {
1420: Enumeration props = outputProperties.propertyNames();
1421: while (props.hasMoreElements()) {
1422: String p = (String) props.nextElement();
1423: String v = outputProperties.getProperty(p);
1424: xslOutputProps.put(p, v);
1425: }
1426: }
1427:
1428: // deal with stylesheet chaining
1429: String nextInChain = xslOutputProps
1430: .getProperty(SaxonOutputKeys.NEXT_IN_CHAIN);
1431: if (nextInChain != null) {
1432: String baseURI = xslOutputProps
1433: .getProperty(SaxonOutputKeys.NEXT_IN_CHAIN_BASE_URI);
1434: result = prepareNextStylesheet(nextInChain, baseURI, result);
1435: }
1436:
1437: initialContext.changeOutputDestination(xslOutputProps, result,
1438: true, Validation.PRESERVE, null);
1439: initialContext.getReceiver().startDocument(0);
1440:
1441: // Process the source document using the handlers that have been set up
1442:
1443: if (initialTemplate == null) {
1444: AxisIterator single = SingletonIterator
1445: .makeIterator(startNode);
1446: initialContext.setCurrentIterator(single);
1447: principalSourceDocument = (startNode == null ? null
1448: : startNode.getDocumentRoot());
1449: if (principalSourceDocument == null) {
1450: throw new DynamicError(
1451: "Source tree must be rooted at a document node");
1452: }
1453: TailCall tc = ApplyTemplates.applyTemplates(initialContext
1454: .getCurrentIterator(), getRuleManager().getMode(
1455: initialMode), null, null, initialContext, false, 0);
1456: while (tc != null) {
1457: tc = tc.processLeavingTail(initialContext);
1458: }
1459: } else {
1460: Template t = initialTemplate;
1461: XPathContextMajor c2 = initialContext.newContext();
1462: c2.setOrigin(this );
1463: c2.openStackFrame(t.getStackFrameMap());
1464: c2.setLocalParameters(new ParameterSet());
1465: c2.setTunnelParameters(new ParameterSet());
1466:
1467: TailCall tc = t.expand(c2);
1468: while (tc != null) {
1469: tc = tc.processLeavingTail(c2);
1470: }
1471: }
1472:
1473: if (traceListener != null) {
1474: traceListener.close();
1475: }
1476:
1477: initialContext.getReceiver().endDocument();
1478: initialContext.getReceiver().close();
1479:
1480: if (mustClose && result instanceof StreamResult) {
1481: OutputStream os = ((StreamResult) result).getOutputStream();
1482: if (os != null) {
1483: try {
1484: os.close();
1485: } catch (java.io.IOException err) {
1486: throw new DynamicError(err);
1487: }
1488: }
1489: }
1490:
1491: }
1492:
1493: /**
1494: * Pre-evaluate global variables (when debugging/tracing).
1495: * <p>
1496: * This method is intended for internal use.
1497: */
1498:
1499: public void preEvaluateGlobals(XPathContext context)
1500: throws XPathException {
1501: IntHashMap vars = getExecutable().getCompiledGlobalVariables();
1502: Iterator iter = vars.valueIterator();
1503: while (iter.hasNext()) {
1504: GlobalVariable var = (GlobalVariable) iter.next();
1505: var.evaluateVariable(context);
1506: }
1507: }
1508:
1509: /**
1510: * Prepare another stylesheet to handle the output of this one.
1511: * <p>
1512: * This method is intended for internal use, to support the
1513: * <code>saxon:next-in-chain</code> extension.
1514: *
1515: * @exception XPathException if any dynamic error occurs
1516: * @param href URI of the next stylesheet to be applied
1517: * @param baseURI base URI for resolving href if it's a relative
1518: * URI
1519: * @param result the output destination of the current stylesheet
1520: * @return a replacement destination for the current stylesheet
1521: */
1522:
1523: public Result prepareNextStylesheet(String href, String baseURI,
1524: Result result) throws TransformerException {
1525:
1526: // TODO: should cache the results, we are recompiling the referenced
1527: // stylesheet each time it's used
1528:
1529: Source source = null;
1530: if (userURIResolver != null) {
1531: source = userURIResolver.resolve(href, baseURI);
1532: }
1533: if (source == null) {
1534: source = standardURIResolver.resolve(href, baseURI);
1535: }
1536: TransformerFactoryImpl factory = new TransformerFactoryImpl();
1537: factory.setConfiguration(config);
1538: Templates next = factory.newTemplates(source);
1539: TransformerReceiver nextTransformer = new TransformerReceiver(
1540: (Controller) next.newTransformer());
1541:
1542: nextTransformer.setSystemId(principalResultURI);
1543: nextTransformer
1544: .setPipelineConfiguration(makePipelineConfiguration());
1545: nextTransformer.setResult(result);
1546:
1547: return nextTransformer;
1548: }
1549:
1550: //////////////////////////////////////////////////////////////////////////
1551: // Handle parameters to the transformation
1552: //////////////////////////////////////////////////////////////////////////
1553:
1554: /**
1555: * Set a parameter for the transformation.
1556: * <p>
1557: * The following table shows some of the classes that are supported
1558: * by this method. (Others may also be supported, but continued support is
1559: * not guaranteed.) Each entry in the table shows first the Java class of the
1560: * supplied object, and then the type of the resulting XPath value.
1561: * <p>
1562: * <table>
1563: * <thead>
1564: * <tr><th>Java Class</th><th>XPath 2.0 type</th></tr>
1565: * </thead>
1566: * <tbody>
1567: * <tr><td>String</td><td>xs:string</td></tr>
1568: * <tr><td>Boolean</td><td>xs:boolean</td></tr>
1569: * <tr><td>Integer</td><td>xs:integer</td></tr>
1570: * <tr><td>Long</td><td>xs:integer</td></tr>
1571: * <tr><td>Double</td><td>xs:double</td></tr>
1572: * <tr><td>Float</td><td>xs:float</td></tr>
1573: * <tr><td>BigDecimal</td><td>xs:decimal</td></tr>
1574: * <tr><td>BigInteger</td><td>xs:integer</td></tr>
1575: * <tr><td>Date</td><td>xs:dateTime</td></tr>
1576: * <tr><td>Array or List of any of the above</td><td>sequence of the above</td></tr>
1577: * <tr><td>null</td><td>empty sequence</td></tr>
1578: * </tbody></table>
1579: * <p>
1580: * A node may be supplied as a <code>NodeInfo</code> object, a sequence of nodes
1581: * as an array or List of <code>NodeInfo</code> objects.
1582: * <p>
1583: * In addition, any object that implements the Saxon {@link net.sf.saxon.value.Value} interface
1584: * may be supplied, and will be used without conversion.
1585: * <p>
1586: * A node belong to an external object model (such as DOM, JDOM, or XOM) may be supplied provided (a)
1587: * that the external object model is registered with the Configuration, and (b) that the node is part
1588: * of a document tree that has been registered in the document pool.
1589: *
1590: * @param expandedName The name of the parameter in {uri}local format
1591: * @param value The value object. This must follow the rules above.
1592: * Other formats in addition to those listed above may be accepted.
1593: * @since 8.4
1594: */
1595:
1596: public void setParameter(String expandedName, Object value) {
1597:
1598: if (parameters == null) {
1599: parameters = new GlobalParameterSet();
1600: }
1601:
1602: int fingerprint = namePool.allocateClarkName(expandedName);
1603: parameters.put(fingerprint, value);
1604:
1605: }
1606:
1607: /**
1608: * Reset the parameters to a null list.
1609: */
1610:
1611: public void clearParameters() {
1612: parameters = null;
1613: }
1614:
1615: /**
1616: * Get a parameter to the transformation. This returns the value of a parameter
1617: * that has been previously set using the {@link #setParameter} method. The value
1618: * is returned exactly as supplied, that is, before any conversion to an XPath value.
1619: *
1620: * @param expandedName the name of the required parameter, in
1621: * "{uri}local-name" format
1622: * @return the value of the parameter, if it exists, or null otherwise
1623: */
1624:
1625: public Object getParameter(String expandedName) {
1626: if (parameters == null) {
1627: return null;
1628: }
1629: int f = namePool.allocateClarkName(expandedName);
1630: return parameters.get(f);
1631: }
1632:
1633: /**
1634: * Set the current date and time for this query or transformation.
1635: * This method is provided primarily for testing purposes, to allow tests to be run with
1636: * a fixed date and time. The supplied date/time must include a timezone, which is used
1637: * as the implicit timezone. Calls are ignored if a current date/time has already been
1638: * established by calling getCurrentDateTime().
1639: *
1640: * <p>Note that comparisons of date/time values currently use the implicit timezone
1641: * taken from the system clock, not from the value supplied here.</p>
1642: */
1643:
1644: public void setCurrentDateTime(DateTimeValue dateTime)
1645: throws XPathException {
1646: if (currentDateTime == null) {
1647: if (dateTime.getComponent(Component.TIMEZONE) == null) {
1648: throw new DynamicError(
1649: "No timezone is present in supplied value of current date/time");
1650: }
1651: currentDateTime = dateTime;
1652: dateTimePreset = true;
1653: }
1654: }
1655:
1656: /**
1657: * Get the current date and time for this query or transformation.
1658: * All calls during one transformation return the same answer.
1659: * <p>
1660: * This method is intended for internal use.
1661: *
1662: * @return Get the current date and time. This will deliver the same value
1663: * for repeated calls within the same transformation
1664: */
1665:
1666: public DateTimeValue getCurrentDateTime() {
1667: if (currentDateTime == null) {
1668: currentDateTime = new DateTimeValue(
1669: new GregorianCalendar(), true);
1670: }
1671: return currentDateTime;
1672: }
1673:
1674: /////////////////////////////////////////
1675: // Methods for handling dynamic context
1676: /////////////////////////////////////////
1677:
1678: /**
1679: * Make an XPathContext object for expression evaluation.
1680: * <p>
1681: * This method is intended for internal use.
1682: *
1683: * @return the new XPathContext
1684: */
1685:
1686: public XPathContextMajor newXPathContext() {
1687: return new XPathContextMajor(this );
1688: }
1689:
1690: /**
1691: * Set the last remembered node, for node numbering purposes.
1692: * <p>
1693: * This method is strictly for internal use only.
1694: *
1695: * @param node the node in question
1696: * @param number the number of this node
1697: */
1698:
1699: public void setRememberedNumber(NodeInfo node, int number) {
1700: lastRememberedNode = node;
1701: lastRememberedNumber = number;
1702: }
1703:
1704: /**
1705: * Get the number of a node if it is the last remembered one.
1706: * <p>
1707: * This method is strictly for internal use only.
1708: *
1709: * @param node the node for which remembered information is required
1710: * @return the number of this node if known, else -1.
1711: */
1712:
1713: public int getRememberedNumber(NodeInfo node) {
1714: if (lastRememberedNode == node) {
1715: return lastRememberedNumber;
1716: }
1717: return -1;
1718: }
1719:
1720: /**
1721: * Get diagnostic information about this context.
1722: * <p>
1723: * This method is intended for internal use.
1724: */
1725:
1726: public InstructionInfo getInstructionInfo() {
1727: InstructionDetails details = new InstructionDetails();
1728: details.setConstructType(Location.CONTROLLER);
1729: return details;
1730: }
1731:
1732: /**
1733: * Set a ClassLoader to be used when loading external classes. Examples of classes that are
1734: * loaded include SAX parsers, localization modules for formatting numbers and dates,
1735: * extension functions, external object models. In an environment such as Eclipse that uses
1736: * its own ClassLoader, this ClassLoader should be nominated to ensure that any class loaded
1737: * by Saxon is identical to a class of the same name loaded by the external environment.
1738: * <p>
1739: * This method is for application use, but is experimental and subject to change.
1740: *
1741: * @param loader the ClassLoader to be used.
1742: */
1743:
1744: public void setClassLoader(ClassLoader loader) {
1745: this .classLoader = loader;
1746: }
1747:
1748: /**
1749: * Get the ClassLoader supplied using the method {@link #setClassLoader}.
1750: * If none has been supplied, return null.
1751: * <p>
1752: * This method is for application use, but is experimental and subject to change.
1753: *
1754: * @return the ClassLoader in use.
1755: */
1756:
1757: public ClassLoader getClassLoader() {
1758: return classLoader;
1759: }
1760:
1761: /**
1762: * Allocate a local document number: that is, a document number for a document
1763: * that is used only locally within this query or transformation (a temporary tree).
1764: * Local document numbers are allocated by the controller to avoid the synchronization
1765: * overhead of allocating a global document number from the NamePool. Local document
1766: * numbers are always negative, global document numbers are positive.
1767: */
1768:
1769: // public int allocateLocalDocumentNumber() {
1770: // return nextLocalDocumentNumber--;
1771: // }
1772:
1773: }
1774:
1775: //
1776: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1777: // you may not use this file except in compliance with the License. You may obtain a copy of the
1778: // License at http://www.mozilla.org/MPL/
1779: //
1780: // Software distributed under the License is distributed on an "AS IS" basis,
1781: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
1782: // See the License for the specific language governing rights and limitations under the License.
1783: //
1784: // The Original Code is: all this file.
1785: //
1786: // The Initial Developer of the Original Code is Michael H. Kay.
1787: //
1788: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1789: //
1790: // Contributor(s):
1791: // Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
1792: //
|