82 import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
83 import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory;
84
85 import com.sun.org.apache.xml.internal.dtm.DTMWSFilter;
86 import com.sun.org.apache.xml.internal.utils.XMLReaderManager;
87
88 import org.xml.sax.ContentHandler;
89 import org.xml.sax.InputSource;
90 import org.xml.sax.SAXException;
91 import org.xml.sax.XMLReader;
92 import org.xml.sax.ext.LexicalHandler;
93
94 /**
95 * @author Morten Jorgensen
96 * @author G. Todd Miller
97 * @author Santiago Pericas-Geertsen
98 */
99 public final class TransformerImpl extends Transformer
100 implements DOMCache, ErrorListener
101 {
102 private final static String EMPTY_STRING = "";
103 private final static String NO_STRING = "no";
104 private final static String YES_STRING = "yes";
105 private final static String XML_STRING = "xml";
106
107 private final static String LEXICAL_HANDLER_PROPERTY =
108 "http://xml.org/sax/properties/lexical-handler";
109 private static final String NAMESPACE_FEATURE =
110 "http://xml.org/sax/features/namespaces";
111
112 /**
113 * Namespace prefixes feature for {@link XMLReader}.
114 */
115 private static final String NAMESPACE_PREFIXES_FEATURE =
116 "http://xml.org/sax/features/namespace-prefixes";
117
118 /**
119 * A reference to the translet or null if the identity transform.
120 */
121 private AbstractTranslet _translet = null;
122
123 /**
124 * The output method of this transformation.
125 */
139 * An error listener for runtime errors.
140 */
141 private ErrorListener _errorListener = this;
142
143 /**
144 * A reference to a URI resolver for calls to document().
145 */
146 private URIResolver _uriResolver = null;
147
148 /**
149 * Output properties of this transformer instance.
150 */
151 private Properties _properties, _propertiesClone;
152
153 /**
154 * A reference to an output handler factory.
155 */
156 private TransletOutputHandlerFactory _tohFactory = null;
157
158 /**
159 * A reference to a internal DOM represenation of the input.
160 */
161 private DOM _dom = null;
162
163 /**
164 * Number of indent spaces to add when indentation is on.
165 */
166 private int _indentNumber;
167
168 /**
169 * A reference to the transformer factory that this templates
170 * object belongs to.
171 */
172 private TransformerFactoryImpl _tfactory = null;
173
174 /**
175 * A reference to the output stream, if we create one in our code.
176 */
177 private OutputStream _ostream = null;
178
179 /**
221 /**
222 * A hashtable to store parameters for the identity transform. These
223 * are not needed during the transformation, but we must keep track of
224 * them to be fully complaint with the JAXP API.
225 */
226 private Hashtable _parameters = null;
227
228 /**
229 * This class wraps an ErrorListener into a MessageHandler in order to
230 * capture messages reported via xsl:message.
231 */
232 static class MessageHandler
233 extends com.sun.org.apache.xalan.internal.xsltc.runtime.MessageHandler
234 {
235 private ErrorListener _errorListener;
236
237 public MessageHandler(ErrorListener errorListener) {
238 _errorListener = errorListener;
239 }
240
241 public void displayMessage(String msg) {
242 if(_errorListener == null) {
243 System.err.println(msg);
244 }
245 else {
246 try {
247 _errorListener.warning(new TransformerException(msg));
248 }
249 catch (TransformerException e) {
250 // ignored
251 }
252 }
253 }
254 }
255
256 protected TransformerImpl(Properties outputProperties, int indentNumber,
257 TransformerFactoryImpl tfactory)
258 {
259 this(null, outputProperties, indentNumber, tfactory);
260 _isIdentity = true;
306
307 /**
308 * Returns the translet wrapped inside this Transformer or
309 * null if this is the identity transform.
310 */
311 protected AbstractTranslet getTranslet() {
312 return _translet;
313 }
314
315 public boolean isIdentity() {
316 return _isIdentity;
317 }
318
319 /**
320 * Implements JAXP's Transformer.transform()
321 *
322 * @param source Contains the input XML document
323 * @param result Will contain the output from the transformation
324 * @throws TransformerException
325 */
326 public void transform(Source source, Result result)
327 throws TransformerException
328 {
329 if (!_isIdentity) {
330 if (_translet == null) {
331 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_TRANSLET_ERR);
332 throw new TransformerException(err.toString());
333 }
334 // Pass output properties to the translet
335 transferOutputProperties(_translet);
336 }
337
338 final SerializationHandler toHandler = getOutputHandler(result);
339 if (toHandler == null) {
340 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_HANDLER_ERR);
341 throw new TransformerException(err.toString());
342 }
343
344 if (_uriResolver != null && !_isIdentity) {
345 _translet.setDOMCache(this);
448 return _tohFactory.getSerializationHandler();
449 }
450
451 // or try to get an OutputStream from Result object
452 final OutputStream ostream = target.getOutputStream();
453 if (ostream != null) {
454 _tohFactory.setOutputStream(ostream);
455 return _tohFactory.getSerializationHandler();
456 }
457
458 // or try to get just a systemId string from Result object
459 String systemId = result.getSystemId();
460 if (systemId == null) {
461 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_RESULT_ERR);
462 throw new TransformerException(err.toString());
463 }
464
465 // System Id may be in one of several forms, (1) a uri
466 // that starts with 'file:', (2) uri that starts with 'http:'
467 // or (3) just a filename on the local system.
468 URL url = null;
469 if (systemId.startsWith("file:")) {
470 // if StreamResult(File) or setSystemID(File) was used,
471 // the systemId will be URI encoded as a result of File.toURI(),
472 // it must be decoded for use by URL
473 try{
474 URI uri = new URI(systemId) ;
475 systemId = "file:";
476
477 String host = uri.getHost(); // decoded String
478 String path = uri.getPath(); //decoded String
479 if (path == null) {
480 path = "";
481 }
482
483 // if host (URI authority) then file:// + host + path
484 // else just path (may be absolute or relative)
485 if (host != null) {
486 systemId += "//" + host + path;
487 } else {
488 systemId += "//" + path;
520 }
521 // If we cannot create the file specified by the SystemId
522 catch (IOException e) {
523 throw new TransformerException(e);
524 }
525 return null;
526 }
527
528 /**
529 * Set the internal DOM that will be used for the next transformation
530 */
531 protected void setDOM(DOM dom) {
532 _dom = dom;
533 }
534
535 /**
536 * Builds an internal DOM from a TrAX Source object
537 */
538 private DOM getDOM(Source source) throws TransformerException {
539 try {
540 DOM dom = null;
541
542 if (source != null) {
543 DTMWSFilter wsfilter;
544 if (_translet != null && _translet instanceof StripFilter) {
545 wsfilter = new DOMWSFilter(_translet);
546 } else {
547 wsfilter = null;
548 }
549
550 boolean hasIdCall = (_translet != null) ? _translet.hasIdCall()
551 : false;
552
553 if (_dtmManager == null) {
554 _dtmManager =
555 (XSLTCDTMManager)_tfactory.getDTMManagerClass()
556 .newInstance();
557 _dtmManager.setServicesMechnism(_useServicesMechanism);
558 }
559 dom = (DOM)_dtmManager.getDTM(source, false, wsfilter, true,
560 false, false, 0, hasIdCall);
561 } else if (_dom != null) {
562 dom = _dom;
563 _dom = null; // use only once, so reset to 'null'
564 } else {
565 return null;
566 }
567
568 if (!_isIdentity) {
569 // Give the translet the opportunity to make a prepass of
570 // the document, in case it can extract useful information early
571 _translet.prepassDocument(dom);
572 }
573
574 return dom;
575
576 }
659 }
660
661 // Hook up reader and output handler
662 try {
663 reader.setProperty(LEXICAL_HANDLER_PROPERTY, handler);
664 reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true);
665 } catch (SAXException e) {
666 // Falls through
667 }
668 reader.setContentHandler(handler);
669
670 // Start pushing SAX events
671 reader.parse(input);
672 } finally {
673 if (!userReader) {
674 _readerManager.releaseXMLReader(reader);
675 }
676 }
677 } else if (source instanceof StAXSource) {
678 final StAXSource staxSource = (StAXSource)source;
679 StAXEvent2SAX staxevent2sax = null;
680 StAXStream2SAX staxStream2SAX = null;
681 if (staxSource.getXMLEventReader() != null) {
682 final XMLEventReader xmlEventReader = staxSource.getXMLEventReader();
683 staxevent2sax = new StAXEvent2SAX(xmlEventReader);
684 staxevent2sax.setContentHandler(handler);
685 staxevent2sax.parse();
686 handler.flushPending();
687 } else if (staxSource.getXMLStreamReader() != null) {
688 final XMLStreamReader xmlStreamReader = staxSource.getXMLStreamReader();
689 staxStream2SAX = new StAXStream2SAX(xmlStreamReader);
690 staxStream2SAX.setContentHandler(handler);
691 staxStream2SAX.parse();
692 handler.flushPending();
693 }
694 } else if (source instanceof DOMSource) {
695 final DOMSource domsrc = (DOMSource) source;
696 new DOM2TO(domsrc.getNode(), handler).parse();
697 } else if (source instanceof XSLTCSource) {
698 final DOM dom = ((XSLTCSource) source).getDOM(null, _translet);
699 ((SAXImpl)dom).copy(handler);
700 } else {
753 } finally {
754 _dtmManager = null;
755 }
756
757 // If we create an output stream for the Result, we need to close it after the transformation.
758 if (_ostream != null) {
759 try {
760 _ostream.close();
761 }
762 catch (IOException e) {}
763 _ostream = null;
764 }
765 }
766
767 /**
768 * Implements JAXP's Transformer.getErrorListener()
769 * Get the error event handler in effect for the transformation.
770 *
771 * @return The error event handler currently in effect
772 */
773 public ErrorListener getErrorListener() {
774 return _errorListener;
775 }
776
777 /**
778 * Implements JAXP's Transformer.setErrorListener()
779 * Set the error event listener in effect for the transformation.
780 * Register a message handler in the translet in order to forward
781 * xsl:messages to error listener.
782 *
783 * @param listener The error event listener to use
784 * @throws IllegalArgumentException
785 */
786 public void setErrorListener(ErrorListener listener)
787 throws IllegalArgumentException {
788 if (listener == null) {
789 ErrorMsg err = new ErrorMsg(ErrorMsg.ERROR_LISTENER_NULL_ERR,
790 "Transformer");
791 throw new IllegalArgumentException(err.toString());
792 }
793 _errorListener = listener;
794
795 // Register a message handler to report xsl:messages
796 if (_translet != null)
797 _translet.setMessageHandler(new MessageHandler(_errorListener));
798 }
799
800 /**
801 * Inform TrAX error listener of an error
802 */
803 private void postErrorToListener(String message) {
804 try {
805 _errorListener.error(new TransformerException(message));
813 * Inform TrAX error listener of a warning
814 */
815 private void postWarningToListener(String message) {
816 try {
817 _errorListener.warning(new TransformerException(message));
818 }
819 catch (TransformerException e) {
820 // ignored - transformation cannot be continued
821 }
822 }
823
824 /**
825 * The translet stores all CDATA sections set in the <xsl:output> element
826 * in a Hashtable. This method will re-construct the whitespace separated
827 * list of elements given in the <xsl:output> element.
828 */
829 private String makeCDATAString(Hashtable cdata) {
830 // Return a 'null' string if no CDATA section elements were specified
831 if (cdata == null) return null;
832
833 StringBuffer result = new StringBuffer();
834
835 // Get an enumeration of all the elements in the hashtable
836 Enumeration elements = cdata.keys();
837 if (elements.hasMoreElements()) {
838 result.append((String)elements.nextElement());
839 while (elements.hasMoreElements()) {
840 String element = (String)elements.nextElement();
841 result.append(' ');
842 result.append(element);
843 }
844 }
845
846 return(result.toString());
847 }
848
849 /**
850 * Implements JAXP's Transformer.getOutputProperties().
851 * Returns a copy of the output properties for the transformation. This is
852 * a set of layered properties. The first layer contains properties set by
853 * calls to setOutputProperty() and setOutputProperties() on this class,
854 * and the output settings defined in the stylesheet's <xsl:output>
855 * element makes up the second level, while the default XSLT output
856 * settings are returned on the third level.
857 *
858 * @return Properties in effect for this Transformer
859 */
860 public Properties getOutputProperties() {
861 return (Properties) _properties.clone();
862 }
863
864 /**
865 * Implements JAXP's Transformer.getOutputProperty().
866 * Get an output property that is in effect for the transformation. The
867 * property specified may be a property that was set with setOutputProperty,
868 * or it may be a property specified in the stylesheet.
869 *
870 * @param name A non-null string that contains the name of the property
871 * @throws IllegalArgumentException if the property name is not known
872 */
873 public String getOutputProperty(String name)
874 throws IllegalArgumentException
875 {
876 if (!validOutputProperty(name)) {
877 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
878 throw new IllegalArgumentException(err.toString());
879 }
880 return _properties.getProperty(name);
881 }
882
883 /**
884 * Implements JAXP's Transformer.setOutputProperties().
885 * Set the output properties for the transformation. These properties
886 * will override properties set in the Templates with xsl:output.
887 * Unrecognised properties will be quitely ignored.
888 *
889 * @param properties The properties to use for the Transformer
890 * @throws IllegalArgumentException Never, errors are ignored
891 */
892 public void setOutputProperties(Properties properties)
893 throws IllegalArgumentException
894 {
895 if (properties != null) {
896 final Enumeration names = properties.propertyNames();
897
898 while (names.hasMoreElements()) {
899 final String name = (String) names.nextElement();
900
901 // Ignore lower layer properties
902 if (isDefaultProperty(name, properties)) continue;
903
904 if (validOutputProperty(name)) {
905 _properties.setProperty(name, properties.getProperty(name));
906 }
907 else {
908 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
909 throw new IllegalArgumentException(err.toString());
910 }
911 }
912 }
913 else {
914 _properties = _propertiesClone;
915 }
916 }
917
918 /**
919 * Implements JAXP's Transformer.setOutputProperty().
920 * Get an output property that is in effect for the transformation. The
921 * property specified may be a property that was set with
922 * setOutputProperty(), or it may be a property specified in the stylesheet.
923 *
924 * @param name The name of the property to set
925 * @param value The value to assign to the property
926 * @throws IllegalArgumentException Never, errors are ignored
927 */
928 public void setOutputProperty(String name, String value)
929 throws IllegalArgumentException
930 {
931 if (!validOutputProperty(name)) {
932 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
933 throw new IllegalArgumentException(err.toString());
934 }
935 _properties.setProperty(name, value);
936 }
937
938 /**
939 * Internal method to pass any properties to the translet prior to
940 * initiating the transformation
941 */
942 private void transferOutputProperties(AbstractTranslet translet)
943 {
944 // Return right now if no properties are set
945 if (_properties == null) return;
946
947 // Get a list of all the defined properties
1188 name.equals(OutputPropertiesFactory.ORACLE_IS_STANDALONE) ||
1189 name.charAt(0) == '{');
1190 }
1191
1192 /**
1193 * Checks if a given output property is default (2nd layer only)
1194 */
1195 private boolean isDefaultProperty(String name, Properties properties) {
1196 return (properties.get(name) == null);
1197 }
1198
1199 /**
1200 * Implements JAXP's Transformer.setParameter()
1201 * Add a parameter for the transformation. The parameter is simply passed
1202 * on to the translet - no validation is performed - so any unused
1203 * parameters are quitely ignored by the translet.
1204 *
1205 * @param name The name of the parameter
1206 * @param value The value to assign to the parameter
1207 */
1208 public void setParameter(String name, Object value) {
1209
1210 if (value == null) {
1211 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_INVALID_SET_PARAM_VALUE, name);
1212 throw new IllegalArgumentException(err.toString());
1213 }
1214
1215 if (_isIdentity) {
1216 if (_parameters == null) {
1217 _parameters = new Hashtable();
1218 }
1219 _parameters.put(name, value);
1220 }
1221 else {
1222 _translet.addParameter(name, value);
1223 }
1224 }
1225
1226 /**
1227 * Implements JAXP's Transformer.clearParameters()
1228 * Clear all parameters set with setParameter. Clears the translet's
1229 * parameter stack.
1230 */
1231 public void clearParameters() {
1232 if (_isIdentity && _parameters != null) {
1233 _parameters.clear();
1234 }
1235 else {
1236 _translet.clearParameters();
1237 }
1238 }
1239
1240 /**
1241 * Implements JAXP's Transformer.getParameter()
1242 * Returns the value of a given parameter. Note that the translet will not
1243 * keep values for parameters that were not defined in the stylesheet.
1244 *
1245 * @param name The name of the parameter
1246 * @return An object that contains the value assigned to the parameter
1247 */
1248 public final Object getParameter(String name) {
1249 if (_isIdentity) {
1250 return (_parameters != null) ? _parameters.get(name) : null;
1251 }
1252 else {
1253 return _translet.getParameter(name);
1254 }
1255 }
1256
1257 /**
1258 * Implements JAXP's Transformer.getURIResolver()
1259 * Set the object currently used to resolve URIs used in document().
1260 *
1261 * @return The URLResolver object currently in use
1262 */
1263 public URIResolver getURIResolver() {
1264 return _uriResolver;
1265 }
1266
1267 /**
1268 * Implements JAXP's Transformer.setURIResolver()
1269 * Set an object that will be used to resolve URIs used in document().
1270 *
1271 * @param resolver The URIResolver to use in document()
1272 */
1273 public void setURIResolver(URIResolver resolver) {
1274 _uriResolver = resolver;
1275 }
1276
1277 /**
1278 * This class should only be used as a DOMCache for the translet if the
1279 * URIResolver has been set.
1280 *
1281 * The method implements XSLTC's DOMCache interface, which is used to
1282 * plug in an external document loader into a translet. This method acts
1283 * as an adapter between TrAX's URIResolver interface and XSLTC's
1284 * DOMCache interface. This approach is simple, but removes the
1285 * possibility of using external document caches with XSLTC.
1286 *
1287 * @param baseURI The base URI used by the document call.
1288 * @param href The href argument passed to the document function.
1289 * @param translet A reference to the translet requesting the document
1290 */
1291 public DOM retrieveDocument(String baseURI, String href, Translet translet) {
1292 try {
1293 // Argument to document function was: document('');
1294 if (href.length() == 0) {
1295 href = baseURI;
1296 }
1297
1298 /*
1299 * Fix for bug 24188
1300 * Incase the _uriResolver.resolve(href,base) is null
1301 * try to still retrieve the document before returning null
1302 * and throwing the FileNotFoundException in
1303 * com.sun.org.apache.xalan.internal.xsltc.dom.LoadDocument
1304 *
1305 */
1306 Source resolvedSource = _uriResolver.resolve(href, baseURI);
1307 if (resolvedSource == null) {
1308 StreamSource streamSource = new StreamSource(
1309 SystemIDResolver.getAbsoluteURI(href, baseURI));
1310 return getDOM(streamSource) ;
1313 return getDOM(resolvedSource);
1314 }
1315 catch (TransformerException e) {
1316 if (_errorListener != null)
1317 postErrorToListener("File not found: " + e.getMessage());
1318 return(null);
1319 }
1320 }
1321
1322 /**
1323 * Receive notification of a recoverable error.
1324 * The transformer must continue to provide normal parsing events after
1325 * invoking this method. It should still be possible for the application
1326 * to process the document through to the end.
1327 *
1328 * @param e The warning information encapsulated in a transformer
1329 * exception.
1330 * @throws TransformerException if the application chooses to discontinue
1331 * the transformation (always does in our case).
1332 */
1333 public void error(TransformerException e)
1334 throws TransformerException
1335 {
1336 Throwable wrapped = e.getException();
1337 if (wrapped != null) {
1338 System.err.println(new ErrorMsg(ErrorMsg.ERROR_PLUS_WRAPPED_MSG,
1339 e.getMessageAndLocation(),
1340 wrapped.getMessage()));
1341 } else {
1342 System.err.println(new ErrorMsg(ErrorMsg.ERROR_MSG,
1343 e.getMessageAndLocation()));
1344 }
1345 throw e;
1346 }
1347
1348 /**
1349 * Receive notification of a non-recoverable error.
1350 * The application must assume that the transformation cannot continue
1351 * after the Transformer has invoked this method, and should continue
1352 * (if at all) only to collect addition error messages. In fact,
1353 * Transformers are free to stop reporting events once this method has
1354 * been invoked.
1355 *
1356 * @param e The warning information encapsulated in a transformer
1357 * exception.
1358 * @throws TransformerException if the application chooses to discontinue
1359 * the transformation (always does in our case).
1360 */
1361 public void fatalError(TransformerException e)
1362 throws TransformerException
1363 {
1364 Throwable wrapped = e.getException();
1365 if (wrapped != null) {
1366 System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_PLUS_WRAPPED_MSG,
1367 e.getMessageAndLocation(),
1368 wrapped.getMessage()));
1369 } else {
1370 System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_MSG,
1371 e.getMessageAndLocation()));
1372 }
1373 throw e;
1374 }
1375
1376 /**
1377 * Receive notification of a warning.
1378 * Transformers can use this method to report conditions that are not
1379 * errors or fatal errors. The default behaviour is to take no action.
1380 * After invoking this method, the Transformer must continue with the
1381 * transformation. It should still be possible for the application to
1382 * process the document through to the end.
1383 *
1384 * @param e The warning information encapsulated in a transformer
1385 * exception.
1386 * @throws TransformerException if the application chooses to discontinue
1387 * the transformation (never does in our case).
1388 */
1389 public void warning(TransformerException e)
1390 throws TransformerException
1391 {
1392 Throwable wrapped = e.getException();
1393 if (wrapped != null) {
1394 System.err.println(new ErrorMsg(ErrorMsg.WARNING_PLUS_WRAPPED_MSG,
1395 e.getMessageAndLocation(),
1396 wrapped.getMessage()));
1397 } else {
1398 System.err.println(new ErrorMsg(ErrorMsg.WARNING_MSG,
1399 e.getMessageAndLocation()));
1400 }
1401 }
1402
1403 /**
1404 * This method resets the Transformer to its original configuration
1405 * Transformer code is reset to the same state it was when it was
1406 * created
1407 * @since 1.5
1408 */
1409 public void reset() {
1410
1411 _method = null;
1412 _encoding = null;
1413 _sourceSystemId = null;
1414 _errorListener = this;
1415 _uriResolver = null;
1416 _dom = null;
1417 _parameters = null;
1418 _indentNumber = 0;
1419 setOutputProperties (null);
1420 _tohFactory = null;
1421 _ostream = null;
1422
1423 }
1424 }
|
82 import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
83 import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory;
84
85 import com.sun.org.apache.xml.internal.dtm.DTMWSFilter;
86 import com.sun.org.apache.xml.internal.utils.XMLReaderManager;
87
88 import org.xml.sax.ContentHandler;
89 import org.xml.sax.InputSource;
90 import org.xml.sax.SAXException;
91 import org.xml.sax.XMLReader;
92 import org.xml.sax.ext.LexicalHandler;
93
94 /**
95 * @author Morten Jorgensen
96 * @author G. Todd Miller
97 * @author Santiago Pericas-Geertsen
98 */
99 public final class TransformerImpl extends Transformer
100 implements DOMCache, ErrorListener
101 {
102
103 private final static String LEXICAL_HANDLER_PROPERTY =
104 "http://xml.org/sax/properties/lexical-handler";
105 private static final String NAMESPACE_FEATURE =
106 "http://xml.org/sax/features/namespaces";
107
108 /**
109 * Namespace prefixes feature for {@link XMLReader}.
110 */
111 private static final String NAMESPACE_PREFIXES_FEATURE =
112 "http://xml.org/sax/features/namespace-prefixes";
113
114 /**
115 * A reference to the translet or null if the identity transform.
116 */
117 private AbstractTranslet _translet = null;
118
119 /**
120 * The output method of this transformation.
121 */
135 * An error listener for runtime errors.
136 */
137 private ErrorListener _errorListener = this;
138
139 /**
140 * A reference to a URI resolver for calls to document().
141 */
142 private URIResolver _uriResolver = null;
143
144 /**
145 * Output properties of this transformer instance.
146 */
147 private Properties _properties, _propertiesClone;
148
149 /**
150 * A reference to an output handler factory.
151 */
152 private TransletOutputHandlerFactory _tohFactory = null;
153
154 /**
155 * A reference to a internal DOM representation of the input.
156 */
157 private DOM _dom = null;
158
159 /**
160 * Number of indent spaces to add when indentation is on.
161 */
162 private int _indentNumber;
163
164 /**
165 * A reference to the transformer factory that this templates
166 * object belongs to.
167 */
168 private TransformerFactoryImpl _tfactory = null;
169
170 /**
171 * A reference to the output stream, if we create one in our code.
172 */
173 private OutputStream _ostream = null;
174
175 /**
217 /**
218 * A hashtable to store parameters for the identity transform. These
219 * are not needed during the transformation, but we must keep track of
220 * them to be fully complaint with the JAXP API.
221 */
222 private Hashtable _parameters = null;
223
224 /**
225 * This class wraps an ErrorListener into a MessageHandler in order to
226 * capture messages reported via xsl:message.
227 */
228 static class MessageHandler
229 extends com.sun.org.apache.xalan.internal.xsltc.runtime.MessageHandler
230 {
231 private ErrorListener _errorListener;
232
233 public MessageHandler(ErrorListener errorListener) {
234 _errorListener = errorListener;
235 }
236
237 @Override
238 public void displayMessage(String msg) {
239 if(_errorListener == null) {
240 System.err.println(msg);
241 }
242 else {
243 try {
244 _errorListener.warning(new TransformerException(msg));
245 }
246 catch (TransformerException e) {
247 // ignored
248 }
249 }
250 }
251 }
252
253 protected TransformerImpl(Properties outputProperties, int indentNumber,
254 TransformerFactoryImpl tfactory)
255 {
256 this(null, outputProperties, indentNumber, tfactory);
257 _isIdentity = true;
303
304 /**
305 * Returns the translet wrapped inside this Transformer or
306 * null if this is the identity transform.
307 */
308 protected AbstractTranslet getTranslet() {
309 return _translet;
310 }
311
312 public boolean isIdentity() {
313 return _isIdentity;
314 }
315
316 /**
317 * Implements JAXP's Transformer.transform()
318 *
319 * @param source Contains the input XML document
320 * @param result Will contain the output from the transformation
321 * @throws TransformerException
322 */
323 @Override
324 public void transform(Source source, Result result)
325 throws TransformerException
326 {
327 if (!_isIdentity) {
328 if (_translet == null) {
329 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_TRANSLET_ERR);
330 throw new TransformerException(err.toString());
331 }
332 // Pass output properties to the translet
333 transferOutputProperties(_translet);
334 }
335
336 final SerializationHandler toHandler = getOutputHandler(result);
337 if (toHandler == null) {
338 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_HANDLER_ERR);
339 throw new TransformerException(err.toString());
340 }
341
342 if (_uriResolver != null && !_isIdentity) {
343 _translet.setDOMCache(this);
446 return _tohFactory.getSerializationHandler();
447 }
448
449 // or try to get an OutputStream from Result object
450 final OutputStream ostream = target.getOutputStream();
451 if (ostream != null) {
452 _tohFactory.setOutputStream(ostream);
453 return _tohFactory.getSerializationHandler();
454 }
455
456 // or try to get just a systemId string from Result object
457 String systemId = result.getSystemId();
458 if (systemId == null) {
459 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_NO_RESULT_ERR);
460 throw new TransformerException(err.toString());
461 }
462
463 // System Id may be in one of several forms, (1) a uri
464 // that starts with 'file:', (2) uri that starts with 'http:'
465 // or (3) just a filename on the local system.
466 URL url;
467 if (systemId.startsWith("file:")) {
468 // if StreamResult(File) or setSystemID(File) was used,
469 // the systemId will be URI encoded as a result of File.toURI(),
470 // it must be decoded for use by URL
471 try{
472 URI uri = new URI(systemId) ;
473 systemId = "file:";
474
475 String host = uri.getHost(); // decoded String
476 String path = uri.getPath(); //decoded String
477 if (path == null) {
478 path = "";
479 }
480
481 // if host (URI authority) then file:// + host + path
482 // else just path (may be absolute or relative)
483 if (host != null) {
484 systemId += "//" + host + path;
485 } else {
486 systemId += "//" + path;
518 }
519 // If we cannot create the file specified by the SystemId
520 catch (IOException e) {
521 throw new TransformerException(e);
522 }
523 return null;
524 }
525
526 /**
527 * Set the internal DOM that will be used for the next transformation
528 */
529 protected void setDOM(DOM dom) {
530 _dom = dom;
531 }
532
533 /**
534 * Builds an internal DOM from a TrAX Source object
535 */
536 private DOM getDOM(Source source) throws TransformerException {
537 try {
538 DOM dom;
539
540 if (source != null) {
541 DTMWSFilter wsfilter;
542 if (_translet != null && _translet instanceof StripFilter) {
543 wsfilter = new DOMWSFilter(_translet);
544 } else {
545 wsfilter = null;
546 }
547
548 boolean hasIdCall = (_translet != null) ? _translet.hasIdCall()
549 : false;
550
551 if (_dtmManager == null) {
552 _dtmManager =
553 _tfactory.createNewDTMManagerInstance();
554 _dtmManager.setServicesMechnism(_useServicesMechanism);
555 }
556 dom = (DOM)_dtmManager.getDTM(source, false, wsfilter, true,
557 false, false, 0, hasIdCall);
558 } else if (_dom != null) {
559 dom = _dom;
560 _dom = null; // use only once, so reset to 'null'
561 } else {
562 return null;
563 }
564
565 if (!_isIdentity) {
566 // Give the translet the opportunity to make a prepass of
567 // the document, in case it can extract useful information early
568 _translet.prepassDocument(dom);
569 }
570
571 return dom;
572
573 }
656 }
657
658 // Hook up reader and output handler
659 try {
660 reader.setProperty(LEXICAL_HANDLER_PROPERTY, handler);
661 reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true);
662 } catch (SAXException e) {
663 // Falls through
664 }
665 reader.setContentHandler(handler);
666
667 // Start pushing SAX events
668 reader.parse(input);
669 } finally {
670 if (!userReader) {
671 _readerManager.releaseXMLReader(reader);
672 }
673 }
674 } else if (source instanceof StAXSource) {
675 final StAXSource staxSource = (StAXSource)source;
676 StAXEvent2SAX staxevent2sax;
677 StAXStream2SAX staxStream2SAX;
678 if (staxSource.getXMLEventReader() != null) {
679 final XMLEventReader xmlEventReader = staxSource.getXMLEventReader();
680 staxevent2sax = new StAXEvent2SAX(xmlEventReader);
681 staxevent2sax.setContentHandler(handler);
682 staxevent2sax.parse();
683 handler.flushPending();
684 } else if (staxSource.getXMLStreamReader() != null) {
685 final XMLStreamReader xmlStreamReader = staxSource.getXMLStreamReader();
686 staxStream2SAX = new StAXStream2SAX(xmlStreamReader);
687 staxStream2SAX.setContentHandler(handler);
688 staxStream2SAX.parse();
689 handler.flushPending();
690 }
691 } else if (source instanceof DOMSource) {
692 final DOMSource domsrc = (DOMSource) source;
693 new DOM2TO(domsrc.getNode(), handler).parse();
694 } else if (source instanceof XSLTCSource) {
695 final DOM dom = ((XSLTCSource) source).getDOM(null, _translet);
696 ((SAXImpl)dom).copy(handler);
697 } else {
750 } finally {
751 _dtmManager = null;
752 }
753
754 // If we create an output stream for the Result, we need to close it after the transformation.
755 if (_ostream != null) {
756 try {
757 _ostream.close();
758 }
759 catch (IOException e) {}
760 _ostream = null;
761 }
762 }
763
764 /**
765 * Implements JAXP's Transformer.getErrorListener()
766 * Get the error event handler in effect for the transformation.
767 *
768 * @return The error event handler currently in effect
769 */
770 @Override
771 public ErrorListener getErrorListener() {
772 return _errorListener;
773 }
774
775 /**
776 * Implements JAXP's Transformer.setErrorListener()
777 * Set the error event listener in effect for the transformation.
778 * Register a message handler in the translet in order to forward
779 * xsl:messages to error listener.
780 *
781 * @param listener The error event listener to use
782 * @throws IllegalArgumentException
783 */
784 @Override
785 public void setErrorListener(ErrorListener listener)
786 throws IllegalArgumentException {
787 if (listener == null) {
788 ErrorMsg err = new ErrorMsg(ErrorMsg.ERROR_LISTENER_NULL_ERR,
789 "Transformer");
790 throw new IllegalArgumentException(err.toString());
791 }
792 _errorListener = listener;
793
794 // Register a message handler to report xsl:messages
795 if (_translet != null)
796 _translet.setMessageHandler(new MessageHandler(_errorListener));
797 }
798
799 /**
800 * Inform TrAX error listener of an error
801 */
802 private void postErrorToListener(String message) {
803 try {
804 _errorListener.error(new TransformerException(message));
812 * Inform TrAX error listener of a warning
813 */
814 private void postWarningToListener(String message) {
815 try {
816 _errorListener.warning(new TransformerException(message));
817 }
818 catch (TransformerException e) {
819 // ignored - transformation cannot be continued
820 }
821 }
822
823 /**
824 * The translet stores all CDATA sections set in the <xsl:output> element
825 * in a Hashtable. This method will re-construct the whitespace separated
826 * list of elements given in the <xsl:output> element.
827 */
828 private String makeCDATAString(Hashtable cdata) {
829 // Return a 'null' string if no CDATA section elements were specified
830 if (cdata == null) return null;
831
832 final StringBuilder result = new StringBuilder();
833
834 // Get an enumeration of all the elements in the hashtable
835 Enumeration elements = cdata.keys();
836 if (elements.hasMoreElements()) {
837 result.append((String)elements.nextElement());
838 while (elements.hasMoreElements()) {
839 String element = (String)elements.nextElement();
840 result.append(' ');
841 result.append(element);
842 }
843 }
844
845 return(result.toString());
846 }
847
848 /**
849 * Implements JAXP's Transformer.getOutputProperties().
850 * Returns a copy of the output properties for the transformation. This is
851 * a set of layered properties. The first layer contains properties set by
852 * calls to setOutputProperty() and setOutputProperties() on this class,
853 * and the output settings defined in the stylesheet's <xsl:output>
854 * element makes up the second level, while the default XSLT output
855 * settings are returned on the third level.
856 *
857 * @return Properties in effect for this Transformer
858 */
859 @Override
860 public Properties getOutputProperties() {
861 return (Properties) _properties.clone();
862 }
863
864 /**
865 * Implements JAXP's Transformer.getOutputProperty().
866 * Get an output property that is in effect for the transformation. The
867 * property specified may be a property that was set with setOutputProperty,
868 * or it may be a property specified in the stylesheet.
869 *
870 * @param name A non-null string that contains the name of the property
871 * @throws IllegalArgumentException if the property name is not known
872 */
873 @Override
874 public String getOutputProperty(String name)
875 throws IllegalArgumentException
876 {
877 if (!validOutputProperty(name)) {
878 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
879 throw new IllegalArgumentException(err.toString());
880 }
881 return _properties.getProperty(name);
882 }
883
884 /**
885 * Implements JAXP's Transformer.setOutputProperties().
886 * Set the output properties for the transformation. These properties
887 * will override properties set in the Templates with xsl:output.
888 * Unrecognised properties will be quitely ignored.
889 *
890 * @param properties The properties to use for the Transformer
891 * @throws IllegalArgumentException Never, errors are ignored
892 */
893 @Override
894 public void setOutputProperties(Properties properties)
895 throws IllegalArgumentException
896 {
897 if (properties != null) {
898 final Enumeration names = properties.propertyNames();
899
900 while (names.hasMoreElements()) {
901 final String name = (String) names.nextElement();
902
903 // Ignore lower layer properties
904 if (isDefaultProperty(name, properties)) continue;
905
906 if (validOutputProperty(name)) {
907 _properties.setProperty(name, properties.getProperty(name));
908 }
909 else {
910 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
911 throw new IllegalArgumentException(err.toString());
912 }
913 }
914 }
915 else {
916 _properties = _propertiesClone;
917 }
918 }
919
920 /**
921 * Implements JAXP's Transformer.setOutputProperty().
922 * Get an output property that is in effect for the transformation. The
923 * property specified may be a property that was set with
924 * setOutputProperty(), or it may be a property specified in the stylesheet.
925 *
926 * @param name The name of the property to set
927 * @param value The value to assign to the property
928 * @throws IllegalArgumentException Never, errors are ignored
929 */
930 @Override
931 public void setOutputProperty(String name, String value)
932 throws IllegalArgumentException
933 {
934 if (!validOutputProperty(name)) {
935 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
936 throw new IllegalArgumentException(err.toString());
937 }
938 _properties.setProperty(name, value);
939 }
940
941 /**
942 * Internal method to pass any properties to the translet prior to
943 * initiating the transformation
944 */
945 private void transferOutputProperties(AbstractTranslet translet)
946 {
947 // Return right now if no properties are set
948 if (_properties == null) return;
949
950 // Get a list of all the defined properties
1191 name.equals(OutputPropertiesFactory.ORACLE_IS_STANDALONE) ||
1192 name.charAt(0) == '{');
1193 }
1194
1195 /**
1196 * Checks if a given output property is default (2nd layer only)
1197 */
1198 private boolean isDefaultProperty(String name, Properties properties) {
1199 return (properties.get(name) == null);
1200 }
1201
1202 /**
1203 * Implements JAXP's Transformer.setParameter()
1204 * Add a parameter for the transformation. The parameter is simply passed
1205 * on to the translet - no validation is performed - so any unused
1206 * parameters are quitely ignored by the translet.
1207 *
1208 * @param name The name of the parameter
1209 * @param value The value to assign to the parameter
1210 */
1211 @Override
1212 public void setParameter(String name, Object value) {
1213
1214 if (value == null) {
1215 ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_INVALID_SET_PARAM_VALUE, name);
1216 throw new IllegalArgumentException(err.toString());
1217 }
1218
1219 if (_isIdentity) {
1220 if (_parameters == null) {
1221 _parameters = new Hashtable();
1222 }
1223 _parameters.put(name, value);
1224 }
1225 else {
1226 _translet.addParameter(name, value);
1227 }
1228 }
1229
1230 /**
1231 * Implements JAXP's Transformer.clearParameters()
1232 * Clear all parameters set with setParameter. Clears the translet's
1233 * parameter stack.
1234 */
1235 @Override
1236 public void clearParameters() {
1237 if (_isIdentity && _parameters != null) {
1238 _parameters.clear();
1239 }
1240 else {
1241 _translet.clearParameters();
1242 }
1243 }
1244
1245 /**
1246 * Implements JAXP's Transformer.getParameter()
1247 * Returns the value of a given parameter. Note that the translet will not
1248 * keep values for parameters that were not defined in the stylesheet.
1249 *
1250 * @param name The name of the parameter
1251 * @return An object that contains the value assigned to the parameter
1252 */
1253 @Override
1254 public final Object getParameter(String name) {
1255 if (_isIdentity) {
1256 return (_parameters != null) ? _parameters.get(name) : null;
1257 }
1258 else {
1259 return _translet.getParameter(name);
1260 }
1261 }
1262
1263 /**
1264 * Implements JAXP's Transformer.getURIResolver()
1265 * Set the object currently used to resolve URIs used in document().
1266 *
1267 * @return The URLResolver object currently in use
1268 */
1269 @Override
1270 public URIResolver getURIResolver() {
1271 return _uriResolver;
1272 }
1273
1274 /**
1275 * Implements JAXP's Transformer.setURIResolver()
1276 * Set an object that will be used to resolve URIs used in document().
1277 *
1278 * @param resolver The URIResolver to use in document()
1279 */
1280 @Override
1281 public void setURIResolver(URIResolver resolver) {
1282 _uriResolver = resolver;
1283 }
1284
1285 /**
1286 * This class should only be used as a DOMCache for the translet if the
1287 * URIResolver has been set.
1288 *
1289 * The method implements XSLTC's DOMCache interface, which is used to
1290 * plug in an external document loader into a translet. This method acts
1291 * as an adapter between TrAX's URIResolver interface and XSLTC's
1292 * DOMCache interface. This approach is simple, but removes the
1293 * possibility of using external document caches with XSLTC.
1294 *
1295 * @param baseURI The base URI used by the document call.
1296 * @param href The href argument passed to the document function.
1297 * @param translet A reference to the translet requesting the document
1298 */
1299 @Override
1300 public DOM retrieveDocument(String baseURI, String href, Translet translet) {
1301 try {
1302 // Argument to document function was: document('');
1303 if (href.length() == 0) {
1304 href = baseURI;
1305 }
1306
1307 /*
1308 * Fix for bug 24188
1309 * Incase the _uriResolver.resolve(href,base) is null
1310 * try to still retrieve the document before returning null
1311 * and throwing the FileNotFoundException in
1312 * com.sun.org.apache.xalan.internal.xsltc.dom.LoadDocument
1313 *
1314 */
1315 Source resolvedSource = _uriResolver.resolve(href, baseURI);
1316 if (resolvedSource == null) {
1317 StreamSource streamSource = new StreamSource(
1318 SystemIDResolver.getAbsoluteURI(href, baseURI));
1319 return getDOM(streamSource) ;
1322 return getDOM(resolvedSource);
1323 }
1324 catch (TransformerException e) {
1325 if (_errorListener != null)
1326 postErrorToListener("File not found: " + e.getMessage());
1327 return(null);
1328 }
1329 }
1330
1331 /**
1332 * Receive notification of a recoverable error.
1333 * The transformer must continue to provide normal parsing events after
1334 * invoking this method. It should still be possible for the application
1335 * to process the document through to the end.
1336 *
1337 * @param e The warning information encapsulated in a transformer
1338 * exception.
1339 * @throws TransformerException if the application chooses to discontinue
1340 * the transformation (always does in our case).
1341 */
1342 @Override
1343 public void error(TransformerException e)
1344 throws TransformerException
1345 {
1346 Throwable wrapped = e.getException();
1347 if (wrapped != null) {
1348 System.err.println(new ErrorMsg(ErrorMsg.ERROR_PLUS_WRAPPED_MSG,
1349 e.getMessageAndLocation(),
1350 wrapped.getMessage()));
1351 } else {
1352 System.err.println(new ErrorMsg(ErrorMsg.ERROR_MSG,
1353 e.getMessageAndLocation()));
1354 }
1355 throw e;
1356 }
1357
1358 /**
1359 * Receive notification of a non-recoverable error.
1360 * The application must assume that the transformation cannot continue
1361 * after the Transformer has invoked this method, and should continue
1362 * (if at all) only to collect addition error messages. In fact,
1363 * Transformers are free to stop reporting events once this method has
1364 * been invoked.
1365 *
1366 * @param e The warning information encapsulated in a transformer
1367 * exception.
1368 * @throws TransformerException if the application chooses to discontinue
1369 * the transformation (always does in our case).
1370 */
1371 @Override
1372 public void fatalError(TransformerException e)
1373 throws TransformerException
1374 {
1375 Throwable wrapped = e.getException();
1376 if (wrapped != null) {
1377 System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_PLUS_WRAPPED_MSG,
1378 e.getMessageAndLocation(),
1379 wrapped.getMessage()));
1380 } else {
1381 System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_MSG,
1382 e.getMessageAndLocation()));
1383 }
1384 throw e;
1385 }
1386
1387 /**
1388 * Receive notification of a warning.
1389 * Transformers can use this method to report conditions that are not
1390 * errors or fatal errors. The default behaviour is to take no action.
1391 * After invoking this method, the Transformer must continue with the
1392 * transformation. It should still be possible for the application to
1393 * process the document through to the end.
1394 *
1395 * @param e The warning information encapsulated in a transformer
1396 * exception.
1397 * @throws TransformerException if the application chooses to discontinue
1398 * the transformation (never does in our case).
1399 */
1400 @Override
1401 public void warning(TransformerException e)
1402 throws TransformerException
1403 {
1404 Throwable wrapped = e.getException();
1405 if (wrapped != null) {
1406 System.err.println(new ErrorMsg(ErrorMsg.WARNING_PLUS_WRAPPED_MSG,
1407 e.getMessageAndLocation(),
1408 wrapped.getMessage()));
1409 } else {
1410 System.err.println(new ErrorMsg(ErrorMsg.WARNING_MSG,
1411 e.getMessageAndLocation()));
1412 }
1413 }
1414
1415 /**
1416 * This method resets the Transformer to its original configuration
1417 * Transformer code is reset to the same state it was when it was
1418 * created
1419 * @since 1.5
1420 */
1421 @Override
1422 public void reset() {
1423
1424 _method = null;
1425 _encoding = null;
1426 _sourceSystemId = null;
1427 _errorListener = this;
1428 _uriResolver = null;
1429 _dom = null;
1430 _parameters = null;
1431 _indentNumber = 0;
1432 setOutputProperties (null);
1433 _tohFactory = null;
1434 _ostream = null;
1435
1436 }
1437 }
|