1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: TransformerFactoryImpl.java,v 1.73 2004/02/23 10:29:36 aruny Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.trax;
  20. import java.io.File;
  21. import java.io.FileInputStream;
  22. import java.io.FileNotFoundException;
  23. import java.io.FilenameFilter;
  24. import java.io.IOException;
  25. import java.io.InputStream;
  26. import java.net.MalformedURLException;
  27. import java.net.URL;
  28. import java.util.Enumeration;
  29. import java.util.Hashtable;
  30. import java.util.Properties;
  31. import java.util.Vector;
  32. import java.util.zip.ZipEntry;
  33. import java.util.zip.ZipFile;
  34. import javax.xml.XMLConstants;
  35. import javax.xml.parsers.SAXParserFactory;
  36. import javax.xml.parsers.SAXParser;
  37. import javax.xml.parsers.ParserConfigurationException;
  38. import javax.xml.transform.ErrorListener;
  39. import javax.xml.transform.Source;
  40. import javax.xml.transform.Templates;
  41. import javax.xml.transform.Transformer;
  42. import javax.xml.transform.TransformerConfigurationException;
  43. import javax.xml.transform.TransformerException;
  44. import javax.xml.transform.URIResolver;
  45. import javax.xml.transform.dom.DOMResult;
  46. import javax.xml.transform.dom.DOMSource;
  47. import javax.xml.transform.sax.SAXResult;
  48. import javax.xml.transform.sax.SAXSource;
  49. import javax.xml.transform.sax.SAXTransformerFactory;
  50. import javax.xml.transform.sax.TemplatesHandler;
  51. import javax.xml.transform.sax.TransformerHandler;
  52. import javax.xml.transform.stream.StreamResult;
  53. import javax.xml.transform.stream.StreamSource;
  54. import com.sun.org.apache.xml.internal.utils.StylesheetPIHandler;
  55. import com.sun.org.apache.xml.internal.utils.StopParseException;
  56. import com.sun.org.apache.xalan.internal.xsltc.compiler.SourceLoader;
  57. import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC;
  58. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  59. import com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager;
  60. import org.xml.sax.InputSource;
  61. import org.xml.sax.XMLFilter;
  62. import org.xml.sax.XMLReader;
  63. import org.xml.sax.helpers.XMLReaderFactory;
  64. /**
  65. * Implementation of a JAXP1.1 TransformerFactory for Translets.
  66. */
  67. public class TransformerFactoryImpl
  68. extends SAXTransformerFactory implements SourceLoader, ErrorListener
  69. {
  70. /**
  71. * <p>Name of class as a constant to use for debugging.</p>
  72. */
  73. private static final String CLASS_NAME = "TransformerFactoryImpl";
  74. // Public constants for attributes supported by the XSLTC TransformerFactory.
  75. public final static String TRANSLET_NAME = "translet-name";
  76. public final static String DESTINATION_DIRECTORY = "destination-directory";
  77. public final static String PACKAGE_NAME = "package-name";
  78. public final static String JAR_NAME = "jar-name";
  79. public final static String GENERATE_TRANSLET = "generate-translet";
  80. public final static String AUTO_TRANSLET = "auto-translet";
  81. public final static String USE_CLASSPATH = "use-classpath";
  82. public final static String DEBUG = "debug";
  83. public final static String ENABLE_INLINING = "enable-inlining";
  84. public final static String INDENT_NUMBER = "indent-number";
  85. /**
  86. * This error listener is used only for this factory and is not passed to
  87. * the Templates or Transformer objects that we create.
  88. */
  89. private ErrorListener _errorListener = this;
  90. /**
  91. * This URIResolver is passed to all created Templates and Transformers
  92. */
  93. private URIResolver _uriResolver = null;
  94. /**
  95. * As Gregor Samsa awoke one morning from uneasy dreams he found himself
  96. * transformed in his bed into a gigantic insect. He was lying on his hard,
  97. * as it were armour plated, back, and if he lifted his head a little he
  98. * could see his big, brown belly divided into stiff, arched segments, on
  99. * top of which the bed quilt could hardly keep in position and was about
  100. * to slide off completely. His numerous legs, which were pitifully thin
  101. * compared to the rest of his bulk, waved helplessly before his eyes.
  102. * "What has happened to me?", he thought. It was no dream....
  103. */
  104. protected static String DEFAULT_TRANSLET_NAME = "GregorSamsa";
  105. /**
  106. * The class name of the translet
  107. */
  108. private String _transletName = DEFAULT_TRANSLET_NAME;
  109. /**
  110. * The destination directory for the translet
  111. */
  112. private String _destinationDirectory = null;
  113. /**
  114. * The package name prefix for all generated translet classes
  115. */
  116. private String _packageName = null;
  117. /**
  118. * The jar file name which the translet classes are packaged into
  119. */
  120. private String _jarFileName = null;
  121. /**
  122. * This Hashtable is used to store parameters for locating
  123. * <?xml-stylesheet ...?> processing instructions in XML docs.
  124. */
  125. private Hashtable _piParams = null;
  126. /**
  127. * Use a thread local variable to store a copy of an XML Reader.
  128. */
  129. static ThreadLocal _xmlReader = new ThreadLocal();
  130. /**
  131. * The above hashtable stores objects of this class.
  132. */
  133. private static class PIParamWrapper {
  134. public String _media = null;
  135. public String _title = null;
  136. public String _charset = null;
  137. public PIParamWrapper(String media, String title, String charset) {
  138. _media = media;
  139. _title = title;
  140. _charset = charset;
  141. }
  142. }
  143. /**
  144. * Set to <code>true</code> when debugging is enabled.
  145. */
  146. private boolean _debug = false;
  147. /**
  148. * Set to <code>true</code> when templates are inlined.
  149. */
  150. private boolean _enableInlining = false;
  151. /**
  152. * Set to <code>true</code> when we want to generate
  153. * translet classes from the stylesheet.
  154. */
  155. private boolean _generateTranslet = false;
  156. /**
  157. * If this is set to <code>true</code>, we attempt to use translet classes
  158. * for transformation if possible without compiling the stylesheet. The
  159. * translet class is only used if its timestamp is newer than the timestamp
  160. * of the stylesheet.
  161. */
  162. private boolean _autoTranslet = false;
  163. /**
  164. * If this is set to <code>true</code>, we attempt to load the translet
  165. * from the CLASSPATH.
  166. */
  167. private boolean _useClasspath = false;
  168. /**
  169. * Number of indent spaces when indentation is turned on.
  170. */
  171. private int _indentNumber = -1;
  172. /**
  173. * The provider of the XSLTC DTM Manager service. This is fixed for any
  174. * instance of this class. In order to change service providers, a new
  175. * XSLTC <code>TransformerFactory</code> must be instantiated.
  176. * @see XSLTCDTMManager#getDTMManagerClass()
  177. */
  178. private Class m_DTMManagerClass;
  179. /**
  180. * <p>State of secure processing feature.</p>
  181. */
  182. private boolean featureSecureProcessing = false;
  183. /**
  184. * javax.xml.transform.sax.TransformerFactory implementation.
  185. */
  186. public TransformerFactoryImpl() {
  187. m_DTMManagerClass = XSLTCDTMManager.getDTMManagerClass();
  188. }
  189. /**
  190. * javax.xml.transform.sax.TransformerFactory implementation.
  191. * Set the error event listener for the TransformerFactory, which is used
  192. * for the processing of transformation instructions, and not for the
  193. * transformation itself.
  194. *
  195. * @param listener The error listener to use with the TransformerFactory
  196. * @throws IllegalArgumentException
  197. */
  198. public void setErrorListener(ErrorListener listener)
  199. throws IllegalArgumentException
  200. {
  201. if (listener == null) {
  202. ErrorMsg err = new ErrorMsg(ErrorMsg.ERROR_LISTENER_NULL_ERR,
  203. "TransformerFactory");
  204. throw new IllegalArgumentException(err.toString());
  205. }
  206. _errorListener = listener;
  207. }
  208. /**
  209. * javax.xml.transform.sax.TransformerFactory implementation.
  210. * Get the error event handler for the TransformerFactory.
  211. *
  212. * @return The error listener used with the TransformerFactory
  213. */
  214. public ErrorListener getErrorListener() {
  215. return _errorListener;
  216. }
  217. /**
  218. * javax.xml.transform.sax.TransformerFactory implementation.
  219. * Returns the value set for a TransformerFactory attribute
  220. *
  221. * @param name The attribute name
  222. * @return An object representing the attribute value
  223. * @throws IllegalArgumentException
  224. */
  225. public Object getAttribute(String name)
  226. throws IllegalArgumentException
  227. {
  228. // Return value for attribute 'translet-name'
  229. if (name.equals(TRANSLET_NAME)) {
  230. return _transletName;
  231. }
  232. else if (name.equals(GENERATE_TRANSLET)) {
  233. return new Boolean(_generateTranslet);
  234. }
  235. else if (name.equals(AUTO_TRANSLET)) {
  236. return new Boolean(_autoTranslet);
  237. }
  238. // Throw an exception for all other attributes
  239. ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_INVALID_ATTR_ERR, name);
  240. throw new IllegalArgumentException(err.toString());
  241. }
  242. /**
  243. * javax.xml.transform.sax.TransformerFactory implementation.
  244. * Sets the value for a TransformerFactory attribute.
  245. *
  246. * @param name The attribute name
  247. * @param value An object representing the attribute value
  248. * @throws IllegalArgumentException
  249. */
  250. public void setAttribute(String name, Object value)
  251. throws IllegalArgumentException
  252. {
  253. // Set the default translet name (ie. class name), which will be used
  254. // for translets that cannot be given a name from their system-id.
  255. if (name.equals(TRANSLET_NAME) && value instanceof String) {
  256. _transletName = (String) value;
  257. return;
  258. }
  259. else if (name.equals(DESTINATION_DIRECTORY) && value instanceof String) {
  260. _destinationDirectory = (String) value;
  261. return;
  262. }
  263. else if (name.equals(PACKAGE_NAME) && value instanceof String) {
  264. _packageName = (String) value;
  265. return;
  266. }
  267. else if (name.equals(JAR_NAME) && value instanceof String) {
  268. _jarFileName = (String) value;
  269. return;
  270. }
  271. else if (name.equals(GENERATE_TRANSLET)) {
  272. if (value instanceof Boolean) {
  273. _generateTranslet = ((Boolean) value).booleanValue();
  274. return;
  275. }
  276. else if (value instanceof String) {
  277. _generateTranslet = ((String) value).equalsIgnoreCase("true");
  278. return;
  279. }
  280. }
  281. else if (name.equals(AUTO_TRANSLET)) {
  282. if (value instanceof Boolean) {
  283. _autoTranslet = ((Boolean) value).booleanValue();
  284. return;
  285. }
  286. else if (value instanceof String) {
  287. _autoTranslet = ((String) value).equalsIgnoreCase("true");
  288. return;
  289. }
  290. }
  291. else if (name.equals(USE_CLASSPATH)) {
  292. if (value instanceof Boolean) {
  293. _useClasspath = ((Boolean) value).booleanValue();
  294. return;
  295. }
  296. else if (value instanceof String) {
  297. _useClasspath = ((String) value).equalsIgnoreCase("true");
  298. return;
  299. }
  300. }
  301. else if (name.equals(DEBUG)) {
  302. if (value instanceof Boolean) {
  303. _debug = ((Boolean) value).booleanValue();
  304. return;
  305. }
  306. else if (value instanceof String) {
  307. _debug = ((String) value).equalsIgnoreCase("true");
  308. return;
  309. }
  310. }
  311. else if (name.equals(ENABLE_INLINING)) {
  312. if (value instanceof Boolean) {
  313. _enableInlining = ((Boolean) value).booleanValue();
  314. return;
  315. }
  316. else if (value instanceof String) {
  317. _enableInlining = ((String) value).equalsIgnoreCase("true");
  318. return;
  319. }
  320. }
  321. else if (name.equals(INDENT_NUMBER)) {
  322. if (value instanceof String) {
  323. try {
  324. _indentNumber = Integer.parseInt((String) value);
  325. return;
  326. }
  327. catch (NumberFormatException e) {
  328. // Falls through
  329. }
  330. }
  331. else if (value instanceof Integer) {
  332. _indentNumber = ((Integer) value).intValue();
  333. return;
  334. }
  335. }
  336. // Throw an exception for all other attributes
  337. final ErrorMsg err
  338. = new ErrorMsg(ErrorMsg.JAXP_INVALID_ATTR_ERR, name);
  339. throw new IllegalArgumentException(err.toString());
  340. }
  341. /**
  342. * <p>Set a feature for this <code>TransformerFactory</code> and <code>Transformer</code>s
  343. * or <code>Template</code>s created by this factory.</p>
  344. *
  345. * <p>
  346. * Feature names are fully qualified {@link java.net.URI}s.
  347. * Implementations may define their own features.
  348. * An {@link TransformerConfigurationException} is thrown if this <code>TransformerFactory</code> or the
  349. * <code>Transformer</code>s or <code>Template</code>s it creates cannot support the feature.
  350. * It is possible for an <code>TransformerFactory</code> to expose a feature value but be unable to change its state.
  351. * </p>
  352. *
  353. * <p>See {@link javax.xml.transform.TransformerFactory} for full documentation of specific features.</p>
  354. *
  355. * @param name Feature name.
  356. * @param value Is feature state <code>true</code> or <code>false</code>.
  357. *
  358. * @throws TransformerConfigurationException if this <code>TransformerFactory</code>
  359. * or the <code>Transformer</code>s or <code>Template</code>s it creates cannot support this feature.
  360. * @throws NullPointerException If the <code>name</code> parameter is null.
  361. */
  362. public void setFeature(String name, boolean value)
  363. throws TransformerConfigurationException {
  364. // feature name cannot be null
  365. if (name == null) {
  366. throw new NullPointerException(
  367. "Trying to set a feature with a null name: "
  368. + CLASS_NAME + "#setFeature(null, " + value + ")"
  369. );
  370. }
  371. // secure processing?
  372. if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
  373. featureSecureProcessing = value;
  374. // all done processing feature
  375. return;
  376. }
  377. // unknown feature
  378. throw new TransformerConfigurationException(
  379. "Trying to set the unknown feature \"" + name + "\": "
  380. + CLASS_NAME + "#setFeature(" + name + ", " + value + ")"
  381. );
  382. }
  383. /**
  384. * javax.xml.transform.sax.TransformerFactory implementation.
  385. * Look up the value of a feature (to see if it is supported).
  386. * This method must be updated as the various methods and features of this
  387. * class are implemented.
  388. *
  389. * @param name The feature name
  390. * @return 'true' if feature is supported, 'false' if not
  391. */
  392. public boolean getFeature(String name) {
  393. // All supported features should be listed here
  394. String[] features = {
  395. DOMSource.FEATURE,
  396. DOMResult.FEATURE,
  397. SAXSource.FEATURE,
  398. SAXResult.FEATURE,
  399. StreamSource.FEATURE,
  400. StreamResult.FEATURE,
  401. SAXTransformerFactory.FEATURE,
  402. SAXTransformerFactory.FEATURE_XMLFILTER
  403. };
  404. // feature name cannot be null
  405. if (name == null) {
  406. throw new NullPointerException(
  407. "Trying to get a feature with a null name: "
  408. + CLASS_NAME
  409. + "#getFeature(null)");
  410. }
  411. // Inefficient, but array is small
  412. for (int i =0; i < features.length; i++) {
  413. if (name.equals(features[i])) {
  414. return true;
  415. }
  416. }
  417. // secure processing?
  418. if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
  419. return featureSecureProcessing;
  420. }
  421. // Feature not supported
  422. return false;
  423. }
  424. /**
  425. * javax.xml.transform.sax.TransformerFactory implementation.
  426. * Get the object that is used by default during the transformation to
  427. * resolve URIs used in document(), xsl:import, or xsl:include.
  428. *
  429. * @return The URLResolver used for this TransformerFactory and all
  430. * Templates and Transformer objects created using this factory
  431. */
  432. public URIResolver getURIResolver() {
  433. return _uriResolver;
  434. }
  435. /**
  436. * javax.xml.transform.sax.TransformerFactory implementation.
  437. * Set the object that is used by default during the transformation to
  438. * resolve URIs used in document(), xsl:import, or xsl:include. Note that
  439. * this does not affect Templates and Transformers that are already
  440. * created with this factory.
  441. *
  442. * @param resolver The URLResolver used for this TransformerFactory and all
  443. * Templates and Transformer objects created using this factory
  444. */
  445. public void setURIResolver(URIResolver resolver) {
  446. _uriResolver = resolver;
  447. }
  448. /**
  449. * javax.xml.transform.sax.TransformerFactory implementation.
  450. * Get the stylesheet specification(s) associated via the xml-stylesheet
  451. * processing instruction (see http://www.w3.org/TR/xml-stylesheet/) with
  452. * the document document specified in the source parameter, and that match
  453. * the given criteria.
  454. *
  455. * @param source The XML source document.
  456. * @param media The media attribute to be matched. May be null, in which
  457. * case the prefered templates will be used (i.e. alternate = no).
  458. * @param title The value of the title attribute to match. May be null.
  459. * @param charset The value of the charset attribute to match. May be null.
  460. * @return A Source object suitable for passing to the TransformerFactory.
  461. * @throws TransformerConfigurationException
  462. */
  463. public Source getAssociatedStylesheet(Source source, String media,
  464. String title, String charset)
  465. throws TransformerConfigurationException {
  466. String baseId;
  467. XMLReader reader = null;
  468. InputSource isource = null;
  469. /**
  470. * Fix for bugzilla bug 24187
  471. */
  472. StylesheetPIHandler _stylesheetPIHandler = new StylesheetPIHandler(null,media,title,charset);
  473. try {
  474. if (source instanceof DOMSource ) {
  475. final DOMSource domsrc = (DOMSource) source;
  476. baseId = domsrc.getSystemId();
  477. final org.w3c.dom.Node node = domsrc.getNode();
  478. final DOM2SAX dom2sax = new DOM2SAX(node);
  479. _stylesheetPIHandler.setBaseId(baseId);
  480. dom2sax.setContentHandler( _stylesheetPIHandler);
  481. dom2sax.parse();
  482. } else {
  483. isource = SAXSource.sourceToInputSource(source);
  484. baseId = isource.getSystemId();
  485. SAXParserFactory factory = SAXParserFactory.newInstance();
  486. factory.setNamespaceAware(true);
  487. SAXParser jaxpParser = factory.newSAXParser();
  488. reader = jaxpParser.getXMLReader();
  489. if (reader == null) {
  490. reader = XMLReaderFactory.createXMLReader();
  491. }
  492. _stylesheetPIHandler.setBaseId(baseId);
  493. reader.setContentHandler(_stylesheetPIHandler);
  494. reader.parse(isource);
  495. }
  496. if (_uriResolver != null ) {
  497. _stylesheetPIHandler.setURIResolver(_uriResolver);
  498. }
  499. } catch (StopParseException e ) {
  500. // startElement encountered so do not parse further
  501. } catch (javax.xml.parsers.ParserConfigurationException e) {
  502. throw new TransformerConfigurationException(
  503. "getAssociatedStylesheets failed", e);
  504. } catch (org.xml.sax.SAXException se) {
  505. throw new TransformerConfigurationException(
  506. "getAssociatedStylesheets failed", se);
  507. } catch (IOException ioe ) {
  508. throw new TransformerConfigurationException(
  509. "getAssociatedStylesheets failed", ioe);
  510. }
  511. return _stylesheetPIHandler.getAssociatedStylesheet();
  512. }
  513. /**
  514. * javax.xml.transform.sax.TransformerFactory implementation.
  515. * Create a Transformer object that copies the input document to the result.
  516. *
  517. * @return A Transformer object that simply copies the source to the result.
  518. * @throws TransformerConfigurationException
  519. */
  520. public Transformer newTransformer()
  521. throws TransformerConfigurationException
  522. {
  523. TransformerImpl result = new TransformerImpl(new Properties(),
  524. _indentNumber, this);
  525. if (_uriResolver != null) {
  526. result.setURIResolver(_uriResolver);
  527. }
  528. return result;
  529. }
  530. /**
  531. * javax.xml.transform.sax.TransformerFactory implementation.
  532. * Process the Source into a Templates object, which is a a compiled
  533. * representation of the source. Note that this method should not be
  534. * used with XSLTC, as the time-consuming compilation is done for each
  535. * and every transformation.
  536. *
  537. * @return A Templates object that can be used to create Transformers.
  538. * @throws TransformerConfigurationException
  539. */
  540. public Transformer newTransformer(Source source) throws
  541. TransformerConfigurationException
  542. {
  543. final Templates templates = newTemplates(source);
  544. final Transformer transformer = templates.newTransformer();
  545. if (_uriResolver != null) {
  546. transformer.setURIResolver(_uriResolver);
  547. }
  548. return(transformer);
  549. }
  550. /**
  551. * Pass warning messages from the compiler to the error listener
  552. */
  553. private void passWarningsToListener(Vector messages)
  554. throws TransformerException
  555. {
  556. if (_errorListener == null || messages == null) {
  557. return;
  558. }
  559. // Pass messages to listener, one by one
  560. final int count = messages.size();
  561. for (int pos = 0; pos < count; pos++) {
  562. String message = messages.elementAt(pos).toString();
  563. _errorListener.error(
  564. new TransformerConfigurationException(message));
  565. }
  566. }
  567. /**
  568. * Pass error messages from the compiler to the error listener
  569. */
  570. private void passErrorsToListener(Vector messages) {
  571. try {
  572. if (_errorListener == null || messages == null) {
  573. return;
  574. }
  575. // Pass messages to listener, one by one
  576. final int count = messages.size();
  577. for (int pos = 0; pos < count; pos++) {
  578. String message = messages.elementAt(pos).toString();
  579. _errorListener.error(new TransformerException(message));
  580. }
  581. }
  582. catch (TransformerException e) {
  583. // nada
  584. }
  585. }
  586. /**
  587. * javax.xml.transform.sax.TransformerFactory implementation.
  588. * Process the Source into a Templates object, which is a a compiled
  589. * representation of the source.
  590. *
  591. * @param stylesheet The input stylesheet - DOMSource not supported!!!
  592. * @return A Templates object that can be used to create Transformers.
  593. * @throws TransformerConfigurationException
  594. */
  595. public Templates newTemplates(Source source)
  596. throws TransformerConfigurationException
  597. {
  598. // If the _useClasspath attribute is true, try to load the translet from
  599. // the CLASSPATH and create a template object using the loaded
  600. // translet.
  601. if (_useClasspath) {
  602. String transletName = getTransletBaseName(source);
  603. if (_packageName != null)
  604. transletName = _packageName + "." + transletName;
  605. try {
  606. final Class clazz = ObjectFactory.findProviderClass(
  607. transletName, ObjectFactory.findClassLoader(), true);
  608. resetTransientAttributes();
  609. return new TemplatesImpl(new Class[]{clazz}, transletName, null, _indentNumber, this);
  610. }
  611. catch (ClassNotFoundException cnfe) {
  612. ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, transletName);
  613. throw new TransformerConfigurationException(err.toString());
  614. }
  615. catch (Exception e) {
  616. ErrorMsg err = new ErrorMsg(
  617. new ErrorMsg(ErrorMsg.RUNTIME_ERROR_KEY)
  618. + e.getMessage());
  619. throw new TransformerConfigurationException(err.toString());
  620. }
  621. }
  622. // If _autoTranslet is true, we will try to load the bytecodes
  623. // from the translet classes without compiling the stylesheet.
  624. if (_autoTranslet) {
  625. byte[][] bytecodes = null;
  626. String transletClassName = getTransletBaseName(source);
  627. if (_packageName != null)
  628. transletClassName = _packageName + "." + transletClassName;
  629. if (_jarFileName != null)
  630. bytecodes = getBytecodesFromJar(source, transletClassName);
  631. else
  632. bytecodes = getBytecodesFromClasses(source, transletClassName);
  633. if (bytecodes != null) {
  634. if (_debug) {
  635. if (_jarFileName != null)
  636. System.err.println(new ErrorMsg(
  637. ErrorMsg.TRANSFORM_WITH_JAR_STR, transletClassName, _jarFileName));
  638. else
  639. System.err.println(new ErrorMsg(
  640. ErrorMsg.TRANSFORM_WITH_TRANSLET_STR, transletClassName));
  641. }
  642. // Reset the per-session attributes to their default values
  643. // after each newTemplates() call.
  644. resetTransientAttributes();
  645. return new TemplatesImpl(bytecodes, transletClassName, null, _indentNumber, this);
  646. }
  647. }
  648. // Create and initialize a stylesheet compiler
  649. final XSLTC xsltc = new XSLTC();
  650. if (_debug) xsltc.setDebug(true);
  651. if (_enableInlining) xsltc.setTemplateInlining(true);
  652. xsltc.init();
  653. // Set a document loader (for xsl:include/import) if defined
  654. if (_uriResolver != null) {
  655. xsltc.setSourceLoader(this);
  656. }
  657. // Pass parameters to the Parser to make sure it locates the correct
  658. // <?xml-stylesheet ...?> PI in an XML input document
  659. if ((_piParams != null) && (_piParams.get(source) != null)) {
  660. // Get the parameters for this Source object
  661. PIParamWrapper p = (PIParamWrapper)_piParams.get(source);
  662. // Pass them on to the compiler (which will pass then to the parser)
  663. if (p != null) {
  664. xsltc.setPIParameters(p._media, p._title, p._charset);
  665. }
  666. }
  667. // Set the attributes for translet generation
  668. int outputType = XSLTC.BYTEARRAY_OUTPUT;
  669. if (_generateTranslet || _autoTranslet) {
  670. // Set the translet name
  671. xsltc.setClassName(getTransletBaseName(source));
  672. if (_destinationDirectory != null)
  673. xsltc.setDestDirectory(_destinationDirectory);
  674. else {
  675. String xslName = getStylesheetFileName(source);
  676. if (xslName != null) {
  677. File xslFile = new File(xslName);
  678. String xslDir = xslFile.getParent();
  679. if (xslDir != null)
  680. xsltc.setDestDirectory(xslDir);
  681. }
  682. }
  683. if (_packageName != null)
  684. xsltc.setPackageName(_packageName);
  685. if (_jarFileName != null) {
  686. xsltc.setJarFileName(_jarFileName);
  687. outputType = XSLTC.BYTEARRAY_AND_JAR_OUTPUT;
  688. }
  689. else
  690. outputType = XSLTC.BYTEARRAY_AND_FILE_OUTPUT;
  691. }
  692. // Compile the stylesheet
  693. final InputSource input = Util.getInputSource(xsltc, source);
  694. byte[][] bytecodes = xsltc.compile(null, input, outputType);
  695. final String transletName = xsltc.getClassName();
  696. // Output to the jar file if the jar file name is set.
  697. if ((_generateTranslet || _autoTranslet)
  698. && bytecodes != null && _jarFileName != null) {
  699. try {
  700. xsltc.outputToJar();
  701. }
  702. catch (java.io.IOException e) { }
  703. }
  704. // Reset the per-session attributes to their default values
  705. // after each newTemplates() call.
  706. resetTransientAttributes();
  707. // Pass compiler warnings to the error listener
  708. if (_errorListener != this) {
  709. try {
  710. passWarningsToListener(xsltc.getWarnings());
  711. }
  712. catch (TransformerException e) {
  713. throw new TransformerConfigurationException(e);
  714. }
  715. }
  716. else {
  717. xsltc.printWarnings();
  718. }
  719. // Check that the transformation went well before returning
  720. if (bytecodes == null) {
  721. ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR);
  722. TransformerConfigurationException exc = new TransformerConfigurationException(err.toString());
  723. // Pass compiler errors to the error listener
  724. if (_errorListener != null) {
  725. passErrorsToListener(xsltc.getErrors());
  726. // As required by TCK 1.2, send a fatalError to the
  727. // error listener because compilation of the stylesheet
  728. // failed and no further processing will be possible.
  729. try {
  730. _errorListener.fatalError(exc);
  731. } catch (TransformerException te) {
  732. // well, we tried.
  733. }
  734. }
  735. else {
  736. xsltc.printErrors();
  737. }
  738. throw exc;
  739. }
  740. return new TemplatesImpl(bytecodes, transletName,
  741. xsltc.getOutputProperties(), _indentNumber, this);
  742. }
  743. /**
  744. * javax.xml.transform.sax.SAXTransformerFactory implementation.
  745. * Get a TemplatesHandler object that can process SAX ContentHandler
  746. * events into a Templates object.
  747. *
  748. * @return A TemplatesHandler object that can handle SAX events
  749. * @throws TransformerConfigurationException
  750. */
  751. public TemplatesHandler newTemplatesHandler()
  752. throws TransformerConfigurationException
  753. {
  754. final TemplatesHandlerImpl handler =
  755. new TemplatesHandlerImpl(_indentNumber, this);
  756. if (_uriResolver != null) {
  757. handler.setURIResolver(_uriResolver);
  758. }
  759. return handler;
  760. }
  761. /**
  762. * javax.xml.transform.sax.SAXTransformerFactory implementation.
  763. * Get a TransformerHandler object that can process SAX ContentHandler
  764. * events into a Result. This method will return a pure copy transformer.
  765. *
  766. * @return A TransformerHandler object that can handle SAX events
  767. * @throws TransformerConfigurationException
  768. */
  769. public TransformerHandler newTransformerHandler()
  770. throws TransformerConfigurationException
  771. {
  772. final Transformer transformer = newTransformer();
  773. if (_uriResolver != null) {
  774. transformer.setURIResolver(_uriResolver);
  775. }
  776. return new TransformerHandlerImpl((TransformerImpl) transformer);
  777. }
  778. /**
  779. * javax.xml.transform.sax.SAXTransformerFactory implementation.
  780. * Get a TransformerHandler object that can process SAX ContentHandler
  781. * events into a Result, based on the transformation instructions
  782. * specified by the argument.
  783. *
  784. * @param src The source of the transformation instructions.
  785. * @return A TransformerHandler object that can handle SAX events
  786. * @throws TransformerConfigurationException
  787. */
  788. public TransformerHandler newTransformerHandler(Source src)
  789. throws TransformerConfigurationException
  790. {
  791. final Transformer transformer = newTransformer(src);
  792. if (_uriResolver != null) {
  793. transformer.setURIResolver(_uriResolver);
  794. }
  795. return new TransformerHandlerImpl((TransformerImpl) transformer);
  796. }
  797. /**
  798. * javax.xml.transform.sax.SAXTransformerFactory implementation.
  799. * Get a TransformerHandler object that can process SAX ContentHandler
  800. * events into a Result, based on the transformation instructions
  801. * specified by the argument.
  802. *
  803. * @param templates Represents a pre-processed stylesheet
  804. * @return A TransformerHandler object that can handle SAX events
  805. * @throws TransformerConfigurationException
  806. */
  807. public TransformerHandler newTransformerHandler(Templates templates)
  808. throws TransformerConfigurationException
  809. {
  810. final Transformer transformer = templates.newTransformer();
  811. final TransformerImpl internal = (TransformerImpl)transformer;
  812. return new TransformerHandlerImpl(internal);
  813. }
  814. /**
  815. * javax.xml.transform.sax.SAXTransformerFactory implementation.
  816. * Create an XMLFilter that uses the given source as the
  817. * transformation instructions.
  818. *
  819. * @param src The source of the transformation instructions.
  820. * @return An XMLFilter object, or null if this feature is not supported.
  821. * @throws TransformerConfigurationException
  822. */
  823. public XMLFilter newXMLFilter(Source src)
  824. throws TransformerConfigurationException
  825. {
  826. Templates templates = newTemplates(src);
  827. if (templates == null) return null;
  828. return newXMLFilter(templates);
  829. }
  830. /**
  831. * javax.xml.transform.sax.SAXTransformerFactory implementation.
  832. * Create an XMLFilter that uses the given source as the
  833. * transformation instructions.
  834. *
  835. * @param src The source of the transformation instructions.
  836. * @return An XMLFilter object, or null if this feature is not supported.
  837. * @throws TransformerConfigurationException
  838. */
  839. public XMLFilter newXMLFilter(Templates templates)
  840. throws TransformerConfigurationException
  841. {
  842. try {
  843. return new com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter(templates);
  844. }
  845. catch (TransformerConfigurationException e1) {
  846. if (_errorListener != null) {
  847. try {
  848. _errorListener.fatalError(e1);
  849. return null;
  850. }
  851. catch (TransformerException e2) {
  852. new TransformerConfigurationException(e2);
  853. }
  854. }
  855. throw e1;
  856. }
  857. }
  858. /**
  859. * Receive notification of a recoverable error.
  860. * The transformer must continue to provide normal parsing events after
  861. * invoking this method. It should still be possible for the application
  862. * to process the document through to the end.
  863. *
  864. * @param exception The warning information encapsulated in a transformer
  865. * exception.
  866. * @throws TransformerException if the application chooses to discontinue
  867. * the transformation (always does in our case).
  868. */
  869. public void error(TransformerException e)
  870. throws TransformerException
  871. {
  872. Throwable wrapped = e.getException();
  873. if (wrapped != null) {
  874. System.err.println(new ErrorMsg(ErrorMsg.ERROR_PLUS_WRAPPED_MSG,
  875. e.getMessageAndLocation(),
  876. wrapped.getMessage()));
  877. } else {
  878. System.err.println(new ErrorMsg(ErrorMsg.ERROR_MSG,
  879. e.getMessageAndLocation()));
  880. }
  881. throw e;
  882. }
  883. /**
  884. * Receive notification of a non-recoverable error.
  885. * The application must assume that the transformation cannot continue
  886. * after the Transformer has invoked this method, and should continue
  887. * (if at all) only to collect addition error messages. In fact,
  888. * Transformers are free to stop reporting events once this method has
  889. * been invoked.
  890. *
  891. * @param exception The warning information encapsulated in a transformer
  892. * exception.
  893. * @throws TransformerException if the application chooses to discontinue
  894. * the transformation (always does in our case).
  895. */
  896. public void fatalError(TransformerException e)
  897. throws TransformerException
  898. {
  899. Throwable wrapped = e.getException();
  900. if (wrapped != null) {
  901. System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_PLUS_WRAPPED_MSG,
  902. e.getMessageAndLocation(),
  903. wrapped.getMessage()));
  904. } else {
  905. System.err.println(new ErrorMsg(ErrorMsg.FATAL_ERR_MSG,
  906. e.getMessageAndLocation()));
  907. }
  908. throw e;
  909. }
  910. /**
  911. * Receive notification of a warning.
  912. * Transformers can use this method to report conditions that are not
  913. * errors or fatal errors. The default behaviour is to take no action.
  914. * After invoking this method, the Transformer must continue with the
  915. * transformation. It should still be possible for the application to
  916. * process the document through to the end.
  917. *
  918. * @param exception The warning information encapsulated in a transformer
  919. * exception.
  920. * @throws TransformerException if the application chooses to discontinue
  921. * the transformation (never does in our case).
  922. */
  923. public void warning(TransformerException e)
  924. throws TransformerException
  925. {
  926. Throwable wrapped = e.getException();
  927. if (wrapped != null) {
  928. System.err.println(new ErrorMsg(ErrorMsg.WARNING_PLUS_WRAPPED_MSG,
  929. e.getMessageAndLocation(),
  930. wrapped.getMessage()));
  931. } else {
  932. System.err.println(new ErrorMsg(ErrorMsg.WARNING_MSG,
  933. e.getMessageAndLocation()));
  934. }
  935. }
  936. /**
  937. * This method implements XSLTC's SourceLoader interface. It is used to
  938. * glue a TrAX URIResolver to the XSLTC compiler's Input and Import classes.
  939. *
  940. * @param href The URI of the document to load
  941. * @param context The URI of the currently loaded document
  942. * @param xsltc The compiler that resuests the document
  943. * @return An InputSource with the loaded document
  944. */
  945. public InputSource loadSource(String href, String context, XSLTC xsltc) {
  946. try {
  947. if (_uriResolver != null) {
  948. final Source source = _uriResolver.resolve(href, context);
  949. if (source != null) {
  950. return Util.getInputSource(xsltc, source);
  951. }
  952. }
  953. }
  954. catch (TransformerException e) {
  955. // Falls through
  956. }
  957. return null;
  958. }
  959. /**
  960. * Reset the per-session attributes to their default values
  961. */
  962. private void resetTransientAttributes() {
  963. _transletName = DEFAULT_TRANSLET_NAME;
  964. _destinationDirectory = null;
  965. _packageName = null;
  966. _jarFileName = null;
  967. }
  968. /**
  969. * Load the translet classes from local .class files and return
  970. * the bytecode array.
  971. *
  972. * @param source The xsl source
  973. * @param fullClassName The full name of the translet
  974. * @return The bytecode array
  975. */
  976. private byte[][] getBytecodesFromClasses(Source source, String fullClassName)
  977. {
  978. if (fullClassName == null)
  979. return null;
  980. String xslFileName = getStylesheetFileName(source);
  981. File xslFile = null;
  982. if (xslFileName != null)
  983. xslFile = new File(xslFileName);
  984. // Find the base name of the translet
  985. final String transletName;
  986. int lastDotIndex = fullClassName.lastIndexOf('.');
  987. if (lastDotIndex > 0)
  988. transletName = fullClassName.substring(lastDotIndex+1);
  989. else
  990. transletName = fullClassName;
  991. // Construct the path name for the translet class file
  992. String transletPath = fullClassName.replace('.', '/');
  993. if (_destinationDirectory != null) {
  994. transletPath = _destinationDirectory + "/" + transletPath + ".class";
  995. }
  996. else {
  997. if (xslFile != null && xslFile.getParent() != null)
  998. transletPath = xslFile.getParent() + "/" + transletPath + ".class";
  999. else
  1000. transletPath = transletPath + ".class";
  1001. }
  1002. // Return null if the translet class file does not exist.
  1003. File transletFile = new File(transletPath);
  1004. if (!transletFile.exists())
  1005. return null;
  1006. // Compare the timestamps of the translet and the xsl file.
  1007. // If the translet is older than the xsl file, return null
  1008. // so that the xsl file is used for the transformation and
  1009. // the translet is regenerated.
  1010. if (xslFile != null && xslFile.exists()) {
  1011. long xslTimestamp = xslFile.lastModified();
  1012. long transletTimestamp = transletFile.lastModified();
  1013. if (transletTimestamp < xslTimestamp)
  1014. return null;
  1015. }
  1016. // Load the translet into a bytecode array.
  1017. Vector bytecodes = new Vector();
  1018. int fileLength = (int)transletFile.length();
  1019. if (fileLength > 0) {
  1020. FileInputStream input = null;
  1021. try {
  1022. input = new FileInputStream(transletFile);
  1023. }
  1024. catch (FileNotFoundException e) {
  1025. return null;
  1026. }
  1027. byte[] bytes = new byte[fileLength];
  1028. try {
  1029. readFromInputStream(bytes, input, fileLength);
  1030. input.close();
  1031. }
  1032. catch (IOException e) {
  1033. return null;
  1034. }
  1035. bytecodes.addElement(bytes);
  1036. }
  1037. else
  1038. return null;
  1039. // Find the parent directory of the translet.
  1040. String transletParentDir = transletFile.getParent();
  1041. if (transletParentDir == null)
  1042. transletParentDir = System.getProperty("user.dir");
  1043. File transletParentFile = new File(transletParentDir);
  1044. // Find all the auxiliary files which have a name pattern of "transletClass$nnn.class".
  1045. final String transletAuxPrefix = transletName + "$";
  1046. File[] auxfiles = transletParentFile.listFiles(new FilenameFilter() {
  1047. public boolean accept(File dir, String name)
  1048. {
  1049. return (name.endsWith(".class") && name.startsWith(transletAuxPrefix));
  1050. }
  1051. });
  1052. // Load the auxiliary class files and add them to the bytecode array.
  1053. for (int i = 0; i < auxfiles.length; i++)
  1054. {
  1055. File auxfile = auxfiles[i];
  1056. int auxlength = (int)auxfile.length();
  1057. if (auxlength > 0) {
  1058. FileInputStream auxinput = null;
  1059. try {
  1060. auxinput = new FileInputStream(auxfile);
  1061. }
  1062. catch (FileNotFoundException e) {
  1063. continue;
  1064. }
  1065. byte[] bytes = new byte[auxlength];
  1066. try {
  1067. readFromInputStream(bytes, auxinput, auxlength);
  1068. auxinput.close();
  1069. }
  1070. catch (IOException e) {
  1071. continue;
  1072. }
  1073. bytecodes.addElement(bytes);
  1074. }
  1075. }
  1076. // Convert the Vector of byte[] to byte[][].
  1077. final int count = bytecodes.size();
  1078. if ( count > 0) {
  1079. final byte[][] result = new byte[count][1];
  1080. for (int i = 0; i < count; i++) {
  1081. result[i] = (byte[])bytecodes.elementAt(i);
  1082. }
  1083. return result;
  1084. }
  1085. else
  1086. return null;
  1087. }
  1088. /**
  1089. * Load the translet classes from the jar file and return the bytecode.
  1090. *
  1091. * @param source The xsl source
  1092. * @param fullClassName The full name of the translet
  1093. * @return The bytecode array
  1094. */
  1095. private byte[][] getBytecodesFromJar(Source source, String fullClassName)
  1096. {
  1097. String xslFileName = getStylesheetFileName(source);
  1098. File xslFile = null;
  1099. if (xslFileName != null)
  1100. xslFile = new File(xslFileName);
  1101. // Construct the path for the jar file
  1102. String jarPath = null;
  1103. if (_destinationDirectory != null)
  1104. jarPath = _destinationDirectory + "/" + _jarFileName;
  1105. else {
  1106. if (xslFile != null && xslFile.getParent() != null)
  1107. jarPath = xslFile.getParent() + "/" + _jarFileName;
  1108. else
  1109. jarPath = _jarFileName;
  1110. }
  1111. // Return null if the jar file does not exist.
  1112. File file = new File(jarPath);
  1113. if (!file.exists())
  1114. return null;
  1115. // Compare the timestamps of the jar file and the xsl file. Return null
  1116. // if the xsl file is newer than the jar file.
  1117. if (xslFile != null && xslFile.exists()) {
  1118. long xslTimestamp = xslFile.lastModified();
  1119. long transletTimestamp = file.lastModified();
  1120. if (transletTimestamp < xslTimestamp)
  1121. return null;
  1122. }
  1123. // Create a ZipFile object for the jar file
  1124. ZipFile jarFile = null;
  1125. try {
  1126. jarFile = new ZipFile(file);
  1127. }
  1128. catch (IOException e) {
  1129. return null;
  1130. }
  1131. String transletPath = fullClassName.replace('.', '/');
  1132. String transletAuxPrefix = transletPath + "$";
  1133. String transletFullName = transletPath + ".class";
  1134. Vector bytecodes = new Vector();
  1135. // Iterate through all entries in the jar file to find the
  1136. // translet and auxiliary classes.
  1137. Enumeration entries = jarFile.entries();
  1138. while (entries.hasMoreElements())
  1139. {
  1140. ZipEntry entry = (ZipEntry)entries.nextElement();
  1141. String entryName = entry.getName();
  1142. if (entry.getSize() > 0 &&
  1143. (entryName.equals(transletFullName) ||
  1144. (entryName.endsWith(".class") &&
  1145. entryName.startsWith(transletAuxPrefix))))
  1146. {
  1147. try {
  1148. InputStream input = jarFile.getInputStream(entry);
  1149. int size = (int)entry.getSize();
  1150. byte[] bytes = new byte[size];
  1151. readFromInputStream(bytes, input, size);
  1152. input.close();
  1153. bytecodes.addElement(bytes);
  1154. }
  1155. catch (IOException e) {
  1156. return null;
  1157. }
  1158. }
  1159. }
  1160. // Convert the Vector of byte[] to byte[][].
  1161. final int count = bytecodes.size();
  1162. if (count > 0) {
  1163. final byte[][] result = new byte[count][1];
  1164. for (int i = 0; i < count; i++) {
  1165. result[i] = (byte[])bytecodes.elementAt(i);
  1166. }
  1167. return result;
  1168. }
  1169. else
  1170. return null;
  1171. }
  1172. /**
  1173. * Read a given number of bytes from the InputStream into a byte array.
  1174. *
  1175. * @param bytes The byte array to store the input content.
  1176. * @param input The input stream.
  1177. * @param size The number of bytes to read.
  1178. */
  1179. private void readFromInputStream(byte[] bytes, InputStream input, int size)
  1180. throws IOException
  1181. {
  1182. int n = 0;
  1183. int offset = 0;
  1184. int length = size;
  1185. while (length > 0 && (n = input.read(bytes, offset, length)) > 0) {
  1186. offset = offset + n;
  1187. length = length - n;
  1188. }
  1189. }
  1190. /**
  1191. * Return the base class name of the translet.
  1192. * The translet name is resolved using the following rules:
  1193. * 1. if the _transletName attribute is set and its value is not "GregorSamsa",
  1194. * then _transletName is returned.
  1195. * 2. otherwise get the translet name from the base name of the system ID
  1196. * 3. return "GregorSamsa" if the result from step 2 is null.
  1197. *
  1198. * @param source The input Source
  1199. * @return The name of the translet class
  1200. */
  1201. private String getTransletBaseName(Source source)
  1202. {
  1203. String transletBaseName = null;
  1204. if (!_transletName.equals(DEFAULT_TRANSLET_NAME))
  1205. return _transletName;
  1206. else {
  1207. String systemId = source.getSystemId();
  1208. if (systemId != null) {
  1209. String baseName = Util.baseName(systemId);
  1210. if (baseName != null) {
  1211. baseName = Util.noExtName(baseName);
  1212. transletBaseName = Util.toJavaName(baseName);
  1213. }
  1214. }
  1215. }
  1216. return (transletBaseName != null) ? transletBaseName : DEFAULT_TRANSLET_NAME;
  1217. }
  1218. /**
  1219. * Return the local file name from the systemId of the Source object
  1220. *
  1221. * @param source The Source
  1222. * @return The file name in the local filesystem, or null if the
  1223. * systemId does not represent a local file.
  1224. */
  1225. private String getStylesheetFileName(Source source)
  1226. {
  1227. String systemId = source.getSystemId();
  1228. if (systemId != null) {
  1229. File file = new File(systemId);
  1230. if (file.exists())
  1231. return systemId;
  1232. else {
  1233. URL url = null;
  1234. try {
  1235. url = new URL(systemId);
  1236. }
  1237. catch (MalformedURLException e) {
  1238. return null;
  1239. }
  1240. if ("file".equals(url.getProtocol()))
  1241. return url.getFile();
  1242. else
  1243. return null;
  1244. }
  1245. }
  1246. else
  1247. return null;
  1248. }
  1249. /**
  1250. * Returns the Class object the provides the XSLTC DTM Manager service.
  1251. */
  1252. protected Class getDTMManagerClass() {
  1253. return m_DTMManagerClass;
  1254. }
  1255. }