001: package net.sf.saxon;
002:
003: import net.sf.saxon.event.PIGrabber;
004: import net.sf.saxon.event.Sender;
005: import net.sf.saxon.om.NamespaceConstant;
006: import net.sf.saxon.trans.XPathException;
007: import org.xml.sax.InputSource;
008: import org.xml.sax.XMLFilter;
009:
010: import javax.xml.transform.*;
011: import javax.xml.transform.dom.DOMResult;
012: import javax.xml.transform.dom.DOMSource;
013: import javax.xml.transform.sax.*;
014: import javax.xml.transform.stream.StreamResult;
015: import javax.xml.transform.stream.StreamSource;
016: import java.io.StringReader;
017: import java.util.List;
018:
019: /**
020: * A TransformerFactoryImpl instance can be used to create Transformer and Template
021: * objects.
022: *
023: * <p>The system property that determines which Factory implementation
024: * to create is named "javax.xml.transform.TransformerFactory". This
025: * property names a concrete subclass of the TransformerFactory abstract
026: * class. If the property is not defined, a platform default is be used.</p>
027: *
028: * <p>This implementation class implements the abstract methods on both the
029: * javax.xml.transform.TransformerFactory and javax.xml.transform.sax.SAXTransformerFactory
030: * classes.
031: */
032:
033: public class TransformerFactoryImpl extends SAXTransformerFactory {
034:
035: private Configuration config;
036:
037: /**
038: * Default constructor.
039: */
040: public TransformerFactoryImpl() {
041: config = new Configuration();
042: }
043:
044: /**
045: * Construct a TransformerFactory using an existing Configuration.
046: */
047: public TransformerFactoryImpl(Configuration config) {
048: this .config = config;
049: }
050:
051: /**
052: * Set the configuration (en bloc)
053: */
054:
055: public void setConfiguration(Configuration config) {
056: this .config = config;
057: }
058:
059: /**
060: * Get the configuration (en bloc)
061: */
062:
063: public Configuration getConfiguration() {
064: return config;
065: }
066:
067: /**
068: * Process the Source into a Transformer object. Care must
069: * be given not to use this object in multiple threads running concurrently.
070: * Different TransformerFactories can be used concurrently by different
071: * threads.
072: *
073: * @param source An object that holds a URI, input stream, etc.
074: *
075: * @return A Transformer object that may be used to perform a transformation
076: * in a single thread, never null.
077: *
078: * @exception TransformerConfigurationException May throw this during the parse
079: * when it is constructing the Templates object and fails.
080: */
081:
082: public Transformer newTransformer(Source source)
083: throws TransformerConfigurationException {
084: Templates templates = newTemplates(source);
085: Transformer trans = templates.newTransformer();
086: return trans;
087: }
088:
089: /**
090: * Create a new Transformer object that performs a copy
091: * of the source to the result.
092: *
093: * @return A Transformer object that may be used to perform a transformation
094: * in a single thread, never null.
095: *
096: * @exception TransformerConfigurationException May throw this during
097: * the parse when it is constructing the
098: * Templates object and fails.
099: */
100:
101: public Transformer newTransformer()
102: throws TransformerConfigurationException {
103:
104: return new IdentityTransformer(config);
105: }
106:
107: /**
108: * Process the Source into a Templates object, which is a
109: * a compiled representation of the source. This Templates object
110: * may then be used concurrently across multiple threads. Creating
111: * a Templates object allows the TransformerFactory to do detailed
112: * performance optimization of transformation instructions, without
113: * penalizing runtime transformation.
114: *
115: * @param source An object that holds a URL, input stream, etc.
116: *
117: * @return A Templates object capable of being used for transformation purposes,
118: * never null.
119: *
120: * @exception TransformerConfigurationException May throw this during the parse when it
121: * is constructing the Templates object and fails.
122: */
123:
124: public Templates newTemplates(Source source)
125: throws TransformerConfigurationException {
126:
127: PreparedStylesheet pss = new PreparedStylesheet(config);
128: pss.prepare(source);
129: return pss;
130: }
131:
132: /**
133: * Get the stylesheet specification(s) associated
134: * via the xml-stylesheet processing instruction (see
135: * http://www.w3.org/TR/xml-stylesheet/) with the document
136: * document specified in the source parameter, and that match
137: * the given criteria. Note that it is possible to return several
138: * stylesheets, in which case they are applied as if they were
139: * a list of imports or cascades.
140: *
141: * @param source The XML source document.
142: * @param media The media attribute to be matched. May be null, in which
143: * case the prefered templates will be used (i.e. alternate = no).
144: * @param title The value of the title attribute to match. May be null.
145: * @param charset The value of the charset attribute to match. May be null.
146: *
147: * @return A Source object suitable for passing to the TransformerFactory.
148: *
149: * @throws TransformerConfigurationException if any problems occur
150: */
151:
152: public Source getAssociatedStylesheet(Source source, String media,
153: String title, String charset)
154: throws TransformerConfigurationException {
155:
156: PIGrabber grabber = new PIGrabber();
157: grabber.setFactory(config);
158: grabber.setCriteria(media, title, charset);
159: grabber.setBaseURI(source.getSystemId());
160: grabber.setURIResolver(config.getURIResolver());
161:
162: try {
163: new Sender(config.makePipelineConfiguration()).send(source,
164: grabber);
165: // this parse will be aborted when the first start tag is found
166: } catch (XPathException err) {
167: if (grabber.isTerminated()) {
168: // do nothing
169: } else {
170: throw new TransformerConfigurationException(
171: "Failed while looking for xml-stylesheet PI",
172: err);
173: }
174: }
175:
176: try {
177: Source[] sources = grabber.getAssociatedStylesheets();
178: if (sources == null) {
179: throw new TransformerConfigurationException(
180: "No matching <?xml-stylesheet?> processing instruction found");
181: }
182: return compositeStylesheet(source.getSystemId(), sources);
183: } catch (TransformerException err) {
184: if (err instanceof TransformerConfigurationException) {
185: throw (TransformerConfigurationException) err;
186: } else {
187: throw new TransformerConfigurationException(err);
188: }
189: }
190: }
191:
192: /**
193: * Process a series of stylesheet inputs, treating them in import or cascade
194: * order. This is mainly for support of the getAssociatedStylesheets
195: * method, but may be useful for other purposes.
196: *
197: * @param sources An array of Source objects representing individual stylesheets.
198: * @return A Source object representing a composite stylesheet.
199: */
200:
201: private Source compositeStylesheet(String baseURI, Source[] sources)
202: throws TransformerConfigurationException {
203:
204: if (sources.length == 1) {
205: return sources[0];
206: } else if (sources.length == 0) {
207: throw new TransformerConfigurationException(
208: "No stylesheets were supplied");
209: }
210:
211: // create a new top-level stylesheet that imports all the others
212:
213: StringBuffer sb = new StringBuffer(250);
214: sb.append("<xsl:stylesheet version='1.0' ");
215: sb.append(" xmlns:xsl='" + NamespaceConstant.XSLT + "'>");
216: for (int i = 0; i < sources.length; i++) {
217: sb.append("<xsl:import href='" + sources[i].getSystemId()
218: + "'/>");
219: }
220: sb.append("</xsl:stylesheet>");
221: InputSource composite = new InputSource();
222: composite.setSystemId(baseURI);
223: composite.setCharacterStream(new StringReader(sb.toString()));
224: return new SAXSource(config.getSourceParser(), composite);
225: }
226:
227: /**
228: * Set an object that is used by default during the transformation
229: * to resolve URIs used in xsl:import, or xsl:include.
230: *
231: * @param resolver An object that implements the URIResolver interface,
232: * or null.
233: */
234:
235: public void setURIResolver(URIResolver resolver) {
236: config.setURIResolver(resolver);
237: }
238:
239: /**
240: * Get the object that is used by default during the transformation
241: * to resolve URIs used in document(), xsl:import, or xsl:include.
242: *
243: * @return The URIResolver that was set with setURIResolver.
244: */
245:
246: public URIResolver getURIResolver() {
247: return config.getURIResolver();
248: }
249:
250: //======= CONFIGURATION METHODS =======
251:
252: private static final String FEATURE_SECURE_PROCESSING =
253: //javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING;
254: "http://javax.xml.XMLConstants/feature/secure-processing";
255:
256: // Avoid reference to this JDK 1.5 constant
257:
258: /**
259: * Look up the value of a feature.
260: *
261: * <p>The feature name is any absolute URI.</p>
262: * @param name The feature name, which is an absolute URI.
263: * @return The current state of the feature (true or false).
264: */
265:
266: public boolean getFeature(String name) {
267: if (name.equals(SAXSource.FEATURE))
268: return true;
269: if (name.equals(SAXResult.FEATURE))
270: return true;
271: if (name.equals(DOMSource.FEATURE))
272: return isDOMAvailable();
273: if (name.equals(DOMResult.FEATURE))
274: return isDOMAvailable();
275: if (name.equals(StreamSource.FEATURE))
276: return true;
277: if (name.equals(StreamResult.FEATURE))
278: return true;
279: if (name.equals(SAXTransformerFactory.FEATURE))
280: return true;
281: if (name.equals(SAXTransformerFactory.FEATURE_XMLFILTER))
282: return true;
283: if (name.equals(FEATURE_SECURE_PROCESSING)) {
284: return !config.isAllowExternalFunctions();
285: }
286: throw new IllegalArgumentException("Unknown feature " + name);
287: }
288:
289: /**
290: * Test whether DOM processing is available
291: */
292:
293: private boolean isDOMAvailable() {
294: List models = config.getExternalObjectModels();
295: for (int i = 0; i < models.size(); i++) {
296: if (models.get(i).getClass().getName().equals(
297: "net.sf.saxon.dom.DOMObjectModel")) {
298: return true;
299: }
300: }
301: return false;
302: }
303:
304: /**
305: * Allows the user to set specific attributes on the underlying
306: * implementation. An attribute in this context is defined to
307: * be an option that the implementation provides.
308: *
309: * @param name The name of the attribute. This must be one of the constants
310: * defined in class {@link net.sf.saxon.FeatureKeys}.
311: * @param value The value of the attribute.
312: * @throws IllegalArgumentException thrown if Saxon
313: * doesn't recognize the attribute.
314: * @see net.sf.saxon.FeatureKeys
315: */
316:
317: public void setAttribute(String name, Object value)
318: throws IllegalArgumentException {
319: config.setConfigurationProperty(name, value);
320: }
321:
322: /**
323: * Allows the user to retrieve specific attributes on the underlying
324: * implementation.
325: * @param name The name of the attribute.
326: * @return value The value of the attribute.
327: * @throws IllegalArgumentException thrown if the underlying
328: * implementation doesn't recognize the attribute.
329: */
330: public Object getAttribute(String name)
331: throws IllegalArgumentException {
332: return config.getConfigurationProperty(name);
333: }
334:
335: /**
336: * Set the error event listener for the TransformerFactory, which
337: * is used for the processing of transformation instructions,
338: * and not for the transformation itself.
339: *
340: * @param listener The new error listener.
341: * @throws IllegalArgumentException if listener is null.
342: */
343:
344: public void setErrorListener(ErrorListener listener)
345: throws IllegalArgumentException {
346: config.setErrorListener(listener);
347: }
348:
349: /**
350: * Get the error event handler for the TransformerFactory.
351: * @return The current error listener, which should never be null.
352: */
353: public ErrorListener getErrorListener() {
354: return config.getErrorListener();
355: }
356:
357: ///////////////////////////////////////////////////////////////////////////////
358: // Methods defined in class javax.xml.transform.sax.SAXTransformerFactory
359: ///////////////////////////////////////////////////////////////////////////////
360:
361: /**
362: * Get a TransformerHandler object that can process SAX
363: * ContentHandler events into a Result, based on the transformation
364: * instructions specified by the argument.
365: *
366: * @param src The Source of the transformation instructions.
367: *
368: * @return TransformerHandler ready to transform SAX events.
369: *
370: * @throws TransformerConfigurationException If for some reason the
371: * TransformerHandler can not be created.
372: */
373:
374: public TransformerHandler newTransformerHandler(Source src)
375: throws TransformerConfigurationException {
376: Templates tmpl = newTemplates(src);
377: return newTransformerHandler(tmpl);
378: }
379:
380: /**
381: * Get a TransformerHandler object that can process SAX
382: * ContentHandler events into a Result, based on the Templates argument.
383: *
384: * @param templates The compiled transformation instructions.
385: *
386: * @return TransformerHandler ready to transform SAX events.
387: *
388: * @throws TransformerConfigurationException If for some reason the
389: * TransformerHandler can not be created.
390: */
391:
392: public TransformerHandler newTransformerHandler(Templates templates)
393: throws TransformerConfigurationException {
394: if (!(templates instanceof PreparedStylesheet)) {
395: throw new TransformerConfigurationException(
396: "Templates object was not created by Saxon");
397: }
398: Controller controller = (Controller) templates.newTransformer();
399: TransformerHandlerImpl handler = new TransformerHandlerImpl(
400: controller);
401: return handler;
402: }
403:
404: /**
405: * Get a TransformerHandler object that can process SAX
406: * ContentHandler events into a Result. The transformation
407: * is defined as an identity (or copy) transformation, for example
408: * to copy a series of SAX parse events into a DOM tree.
409: *
410: * @return A non-null reference to a TransformerHandler, that may
411: * be used as a ContentHandler for SAX parse events.
412: *
413: * @throws TransformerConfigurationException If for some reason the
414: * TransformerHandler cannot be created.
415: */
416:
417: public TransformerHandler newTransformerHandler()
418: throws TransformerConfigurationException {
419: Controller controller = new IdentityTransformer(config);
420: return new IdentityTransformerHandler(controller);
421: }
422:
423: /**
424: * Get a TemplatesHandler object that can process SAX
425: * ContentHandler events into a Templates object.
426: *
427: * @return A non-null reference to a TransformerHandler, that may
428: * be used as a ContentHandler for SAX parse events.
429: *
430: * @throws TransformerConfigurationException If for some reason the
431: * TemplatesHandler cannot be created.
432: */
433:
434: public TemplatesHandler newTemplatesHandler()
435: throws TransformerConfigurationException {
436: return new TemplatesHandlerImpl(config);
437: }
438:
439: /**
440: * Create an XMLFilter that uses the given Source as the
441: * transformation instructions.
442: *
443: * @param src The Source of the transformation instructions.
444: *
445: * @return An XMLFilter object, or null if this feature is not supported.
446: *
447: * @throws TransformerConfigurationException If for some reason the
448: * XMLFilter cannot be created.
449: */
450:
451: public XMLFilter newXMLFilter(Source src)
452: throws TransformerConfigurationException {
453: Templates tmpl = newTemplates(src);
454: return newXMLFilter(tmpl);
455: }
456:
457: /**
458: * Create an XMLFilter, based on the Templates argument..
459: *
460: * @param templates The compiled transformation instructions.
461: *
462: * @return An XMLFilter object, or null if this feature is not supported.
463: *
464: * @throws TransformerConfigurationException If for some reason the
465: * XMLFilter cannot be created.
466: */
467:
468: public XMLFilter newXMLFilter(Templates templates)
469: throws TransformerConfigurationException {
470: if (!(templates instanceof PreparedStylesheet)) {
471: throw new TransformerConfigurationException(
472: "Supplied Templates object was not created using Saxon");
473: }
474: Controller controller = (Controller) templates.newTransformer();
475: return new Filter(controller);
476: }
477:
478: /**
479: * <p>Set a feature for this <code>TransformerFactory</code> and <code>Transformer</code>s
480: * or <code>Template</code>s created by this factory.</p>
481: * <p/>
482: * <p/>
483: * Feature names are fully qualified {@link java.net.URI}s.
484: * Implementations may define their own features.
485: * An {@link javax.xml.transform.TransformerConfigurationException} is thrown if this <code>TransformerFactory</code> or the
486: * <code>Transformer</code>s or <code>Template</code>s it creates cannot support the feature.
487: * It is possible for an <code>TransformerFactory</code> to expose a feature value but be unable to change its state.
488: * </p>
489: * <p/>
490: * <p>All implementations are required to support the FEATURE_SECURE_PROCESSING feature.
491: * When the feature is:</p>
492: * <ul>
493: * <li>
494: * <code>true</code>: the implementation will limit XML processing to conform to implementation limits
495: * and behave in a secure fashion as defined by the implementation.
496: * Examples include resolving user defined style sheets and functions.
497: * If XML processing is limited for security reasons, it will be reported via a call to the registered
498: * {@link javax.xml.transform.ErrorListener#fatalError(javax.xml.transform.TransformerException exception)}.
499: * See {@link #setErrorListener(javax.xml.transform.ErrorListener listener)}. In the Saxon implementation,
500: * this option causes calls on extension functions and extensions instructions to be disabled, and also
501: * disables the use of xsl:result-document to write to secondary output destinations.
502: * </li>
503: * <li>
504: * <code>false</code>: the implementation will processing XML according to the XML specifications without
505: * regard to possible implementation limits.
506: * </li>
507: * </ul>
508: *
509: * @param name Feature name.
510: * @param value Is feature state <code>true</code> or <code>false</code>.
511: * @throws javax.xml.transform.TransformerConfigurationException
512: * if this <code>TransformerFactory</code>
513: * or the <code>Transformer</code>s or <code>Template</code>s it creates cannot support this feature.
514: * @throws NullPointerException If the <code>name</code> parameter is null.
515: */
516:
517: public void setFeature(String name, boolean value)
518: throws TransformerConfigurationException {
519: if (name.equals(FEATURE_SECURE_PROCESSING)) {
520: config.setAllowExternalFunctions(!value);
521: } else {
522: throw new TransformerConfigurationException(
523: "Unsupported TransformerFactory feature: " + name);
524: }
525: }
526:
527: }
528:
529: //
530: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
531: // you may not use this file except in compliance with the License. You may obtain a copy of the
532: // License at http://www.mozilla.org/MPL/
533: //
534: // Software distributed under the License is distributed on an "AS IS" basis,
535: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
536: // See the License for the specific language governing rights and limitations under the License.
537: //
538: // The Original Code is: all this file.
539: //
540: // The Initial Developer of the Original Code is Michael H. Kay
541: //
542: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
543: //
544: // Contributor(s): none.
545: //
|