1. /*
  2. * Copyright 2000-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. package org.apache.tools.ant.taskdefs;
  18. import java.io.File;
  19. import java.util.Enumeration;
  20. import java.util.Vector;
  21. import org.apache.tools.ant.AntClassLoader;
  22. import org.apache.tools.ant.BuildException;
  23. import org.apache.tools.ant.DirectoryScanner;
  24. import org.apache.tools.ant.DynamicConfigurator;
  25. import org.apache.tools.ant.Project;
  26. import org.apache.tools.ant.types.Mapper;
  27. import org.apache.tools.ant.types.Path;
  28. import org.apache.tools.ant.types.Reference;
  29. import org.apache.tools.ant.types.XMLCatalog;
  30. import org.apache.tools.ant.util.FileNameMapper;
  31. import org.apache.tools.ant.util.FileUtils;
  32. /**
  33. * Processes a set of XML documents via XSLT. This is
  34. * useful for building views of XML based documentation.
  35. *
  36. * @version $Revision: 1.78.2.7 $
  37. *
  38. * @since Ant 1.1
  39. *
  40. * @ant.task name="xslt" category="xml"
  41. */
  42. public class XSLTProcess extends MatchingTask implements XSLTLogger {
  43. /** destination directory */
  44. private File destDir = null;
  45. /** where to find the source XML file, default is the project's basedir */
  46. private File baseDir = null;
  47. /** XSL stylesheet */
  48. private String xslFile = null;
  49. /** extension of the files produced by XSL processing */
  50. private String targetExtension = ".html";
  51. /** additional parameters to be passed to the stylesheets */
  52. private Vector params = new Vector();
  53. /** Input XML document to be used */
  54. private File inFile = null;
  55. /** Output file */
  56. private File outFile = null;
  57. /** The name of the XSL processor to use */
  58. private String processor;
  59. /** Classpath to use when trying to load the XSL processor */
  60. private Path classpath = null;
  61. /** The Liason implementation to use to communicate with the XSL
  62. * processor */
  63. private XSLTLiaison liaison;
  64. /** Flag which indicates if the stylesheet has been loaded into
  65. * the processor */
  66. private boolean stylesheetLoaded = false;
  67. /** force output of target files even if they already exist */
  68. private boolean force = false;
  69. /** Utilities used for file operations */
  70. private FileUtils fileUtils;
  71. /** XSL output properties to be used */
  72. private Vector outputProperties = new Vector();
  73. /** for resolving entities such as dtds */
  74. private XMLCatalog xmlCatalog = new XMLCatalog();
  75. /** Name of the TRAX Liaison class */
  76. private static final String TRAX_LIAISON_CLASS =
  77. "org.apache.tools.ant.taskdefs.optional.TraXLiaison";
  78. /** Name of the now-deprecated XSLP Liaison class */
  79. private static final String XSLP_LIAISON_CLASS =
  80. "org.apache.tools.ant.taskdefs.optional.XslpLiaison";
  81. /** Name of the now-deprecated Xalan liaison class */
  82. private static final String XALAN_LIAISON_CLASS =
  83. "org.apache.tools.ant.taskdefs.optional.XalanLiaison";
  84. /**
  85. * Whether to style all files in the included directories as well.
  86. *
  87. * @since Ant 1.5
  88. */
  89. private boolean performDirectoryScan = true;
  90. /**
  91. * factory element for TraX processors only
  92. * @since Ant 1.6
  93. */
  94. private Factory factory = null;
  95. /**
  96. * whether to reuse Transformer if transforming multiple files.
  97. * @since 1.5.2
  98. */
  99. private boolean reuseLoadedStylesheet = true;
  100. /**
  101. * AntClassLoader for the nested <classpath> - if set.
  102. *
  103. * <p>We keep this here in order to reset the context classloader
  104. * in execute. We can't use liaison.getClass().getClassLoader()
  105. * since the actual liaison class may have been loaded by a loader
  106. * higher up (system classloader, for example).</p>
  107. *
  108. * @since Ant 1.6.2
  109. */
  110. private AntClassLoader loader = null;
  111. /**
  112. * Mapper to use when a set of files gets processed.
  113. *
  114. * @since Ant 1.6.2
  115. */
  116. private Mapper mapperElement = null;
  117. /**
  118. * Creates a new XSLTProcess Task.
  119. */
  120. public XSLTProcess() {
  121. fileUtils = FileUtils.newFileUtils();
  122. } //-- XSLTProcess
  123. /**
  124. * Whether to style all files in the included directories as well;
  125. * optional, default is true.
  126. *
  127. * @param b true if files in included directories are processed.
  128. * @since Ant 1.5
  129. */
  130. public void setScanIncludedDirectories(boolean b) {
  131. performDirectoryScan = b;
  132. }
  133. /**
  134. * Controls whether the stylesheet is reloaded for every transform.
  135. *
  136. * <p>Setting this to true may get around a bug in certain
  137. * Xalan-J versions, default is false.</p>
  138. *
  139. * @since Ant 1.5.2
  140. */
  141. public void setReloadStylesheet(boolean b) {
  142. reuseLoadedStylesheet = !b;
  143. }
  144. /**
  145. * Defines the mapper to map source to destination files.
  146. * @exception BuildException if more than one mapper is defined
  147. * @since Ant 1.6.2
  148. */
  149. public void addMapper(Mapper mapper) {
  150. if (mapperElement != null) {
  151. throw new BuildException("Cannot define more than one mapper",
  152. getLocation());
  153. }
  154. mapperElement = mapper;
  155. }
  156. /**
  157. * Executes the task.
  158. *
  159. * @exception BuildException if there is an execution problem.
  160. * @todo validate that if either in or our is defined, then both are
  161. */
  162. public void execute() throws BuildException {
  163. File savedBaseDir = baseDir;
  164. DirectoryScanner scanner;
  165. String[] list;
  166. String[] dirs;
  167. if (xslFile == null) {
  168. throw new BuildException("no stylesheet specified", getLocation());
  169. }
  170. if (inFile != null && !inFile.exists()) {
  171. throw new BuildException("input file " + inFile.toString() + " does not exist", getLocation());
  172. }
  173. try {
  174. if (baseDir == null) {
  175. baseDir = getProject().resolveFile(".");
  176. }
  177. liaison = getLiaison();
  178. // check if liaison wants to log errors using us as logger
  179. if (liaison instanceof XSLTLoggerAware) {
  180. ((XSLTLoggerAware) liaison).setLogger(this);
  181. }
  182. log("Using " + liaison.getClass().toString(), Project.MSG_VERBOSE);
  183. File stylesheet = getProject().resolveFile(xslFile);
  184. if (!stylesheet.exists()) {
  185. stylesheet = fileUtils.resolveFile(baseDir, xslFile);
  186. /*
  187. * shouldn't throw out deprecation warnings before we know,
  188. * the wrong version has been used.
  189. */
  190. if (stylesheet.exists()) {
  191. log("DEPRECATED - the style attribute should be relative "
  192. + "to the project\'s");
  193. log(" basedir, not the tasks\'s basedir.");
  194. }
  195. }
  196. // if we have an in file and out then process them
  197. if (inFile != null && outFile != null) {
  198. process(inFile, outFile, stylesheet);
  199. return;
  200. }
  201. /*
  202. * if we get here, in and out have not been specified, we are
  203. * in batch processing mode.
  204. */
  205. //-- make sure Source directory exists...
  206. if (destDir == null) {
  207. String msg = "destdir attributes must be set!";
  208. throw new BuildException(msg);
  209. }
  210. scanner = getDirectoryScanner(baseDir);
  211. log("Transforming into " + destDir, Project.MSG_INFO);
  212. // Process all the files marked for styling
  213. list = scanner.getIncludedFiles();
  214. for (int i = 0; i < list.length; ++i) {
  215. process(baseDir, list[i], destDir, stylesheet);
  216. }
  217. if (performDirectoryScan) {
  218. // Process all the directories marked for styling
  219. dirs = scanner.getIncludedDirectories();
  220. for (int j = 0; j < dirs.length; ++j) {
  221. list = new File(baseDir, dirs[j]).list();
  222. for (int i = 0; i < list.length; ++i) {
  223. process(baseDir, list[i], destDir, stylesheet);
  224. }
  225. }
  226. }
  227. } finally {
  228. if (loader != null) {
  229. loader.resetThreadContextLoader();
  230. loader = null;
  231. }
  232. liaison = null;
  233. stylesheetLoaded = false;
  234. baseDir = savedBaseDir;
  235. }
  236. }
  237. /**
  238. * Set whether to check dependencies, or always generate;
  239. * optional, default is false.
  240. *
  241. * @param force true if always generate.
  242. */
  243. public void setForce(boolean force) {
  244. this.force = force;
  245. }
  246. /**
  247. * Set the base directory;
  248. * optional, default is the project's basedir.
  249. *
  250. * @param dir the base directory
  251. **/
  252. public void setBasedir(File dir) {
  253. baseDir = dir;
  254. }
  255. /**
  256. * Set the destination directory into which the XSL result
  257. * files should be copied to;
  258. * required, unless <tt>in</tt> and <tt>out</tt> are
  259. * specified.
  260. * @param dir the name of the destination directory
  261. **/
  262. public void setDestdir(File dir) {
  263. destDir = dir;
  264. }
  265. /**
  266. * Set the desired file extension to be used for the target;
  267. * optional, default is html.
  268. * @param name the extension to use
  269. **/
  270. public void setExtension(String name) {
  271. targetExtension = name;
  272. }
  273. /**
  274. * Name of the stylesheet to use - given either relative
  275. * to the project's basedir or as an absolute path; required.
  276. *
  277. * @param xslFile the stylesheet to use
  278. */
  279. public void setStyle(String xslFile) {
  280. this.xslFile = xslFile;
  281. }
  282. /**
  283. * Set the optional classpath to the XSL processor
  284. *
  285. * @param classpath the classpath to use when loading the XSL processor
  286. */
  287. public void setClasspath(Path classpath) {
  288. createClasspath().append(classpath);
  289. }
  290. /**
  291. * Set the optional classpath to the XSL processor
  292. *
  293. * @return a path instance to be configured by the Ant core.
  294. */
  295. public Path createClasspath() {
  296. if (classpath == null) {
  297. classpath = new Path(getProject());
  298. }
  299. return classpath.createPath();
  300. }
  301. /**
  302. * Set the reference to an optional classpath to the XSL processor
  303. *
  304. * @param r the id of the Ant path instance to act as the classpath
  305. * for loading the XSL processor
  306. */
  307. public void setClasspathRef(Reference r) {
  308. createClasspath().setRefid(r);
  309. }
  310. /**
  311. * Set the name of the XSL processor to use; optional, default trax.
  312. * Other values are "xalan" for Xalan1 and "xslp" for XSL:P, though the
  313. * later is strongly deprecated.
  314. *
  315. * @param processor the name of the XSL processor
  316. */
  317. public void setProcessor(String processor) {
  318. this.processor = processor;
  319. }
  320. /**
  321. * Add the catalog to our internal catalog
  322. *
  323. * @param xmlCatalog the XMLCatalog instance to use to look up DTDs
  324. */
  325. public void addConfiguredXMLCatalog(XMLCatalog xmlCatalog) {
  326. this.xmlCatalog.addConfiguredXMLCatalog(xmlCatalog);
  327. }
  328. /**
  329. * Load processor here instead of in setProcessor - this will be
  330. * called from within execute, so we have access to the latest
  331. * classpath.
  332. *
  333. * @param proc the name of the processor to load.
  334. * @exception Exception if the processor cannot be loaded.
  335. */
  336. private void resolveProcessor(String proc) throws Exception {
  337. if (proc.equals("trax")) {
  338. final Class clazz = loadClass(TRAX_LIAISON_CLASS);
  339. liaison = (XSLTLiaison) clazz.newInstance();
  340. } else if (proc.equals("xslp")) {
  341. log("DEPRECATED - xslp processor is deprecated. Use trax "
  342. + "instead.");
  343. final Class clazz = loadClass(XSLP_LIAISON_CLASS);
  344. liaison = (XSLTLiaison) clazz.newInstance();
  345. } else if (proc.equals("xalan")) {
  346. log("DEPRECATED - xalan processor is deprecated. Use trax "
  347. + "instead.");
  348. final Class clazz = loadClass(XALAN_LIAISON_CLASS);
  349. liaison = (XSLTLiaison) clazz.newInstance();
  350. } else {
  351. liaison = (XSLTLiaison) loadClass(proc).newInstance();
  352. }
  353. }
  354. /**
  355. * Load named class either via the system classloader or a given
  356. * custom classloader.
  357. *
  358. * @param classname the name of the class to load.
  359. * @return the requested class.
  360. * @exception Exception if the class could not be loaded.
  361. */
  362. private Class loadClass(String classname) throws Exception {
  363. if (classpath == null) {
  364. return Class.forName(classname);
  365. } else {
  366. loader = getProject().createClassLoader(classpath);
  367. loader.setThreadContextLoader();
  368. Class c = Class.forName(classname, true, loader);
  369. return c;
  370. }
  371. }
  372. /**
  373. * Specifies the output name for the styled result from the
  374. * <tt>in</tt> attribute; required if <tt>in</tt> is set
  375. *
  376. * @param outFile the output File instance.
  377. */
  378. public void setOut(File outFile) {
  379. this.outFile = outFile;
  380. }
  381. /**
  382. * specifies a single XML document to be styled. Should be used
  383. * with the <tt>out</tt> attribute; ; required if <tt>out</tt> is set
  384. *
  385. * @param inFile the input file
  386. */
  387. public void setIn(File inFile) {
  388. this.inFile = inFile;
  389. }
  390. /**
  391. * Processes the given input XML file and stores the result
  392. * in the given resultFile.
  393. *
  394. * @param baseDir the base directory for resolving files.
  395. * @param xmlFile the input file
  396. * @param destDir the destination directory
  397. * @param stylesheet the stylesheet to use.
  398. * @exception BuildException if the processing fails.
  399. */
  400. private void process(File baseDir, String xmlFile, File destDir,
  401. File stylesheet)
  402. throws BuildException {
  403. File outFile = null;
  404. File inFile = null;
  405. try {
  406. long styleSheetLastModified = stylesheet.lastModified();
  407. inFile = new File(baseDir, xmlFile);
  408. if (inFile.isDirectory()) {
  409. log("Skipping " + inFile + " it is a directory.",
  410. Project.MSG_VERBOSE);
  411. return;
  412. }
  413. FileNameMapper mapper = null;
  414. if (mapperElement != null) {
  415. mapper = mapperElement.getImplementation();
  416. } else {
  417. mapper = new StyleMapper();
  418. }
  419. String[] outFileName = mapper.mapFileName(xmlFile);
  420. if (outFileName == null || outFileName.length == 0) {
  421. log("Skipping " + inFile + " it cannot get mapped to output.",
  422. Project.MSG_VERBOSE);
  423. return;
  424. } else if (outFileName == null || outFileName.length > 1) {
  425. log("Skipping " + inFile + " its mapping is ambiguos.",
  426. Project.MSG_VERBOSE);
  427. return;
  428. }
  429. outFile = new File(destDir, outFileName[0]);
  430. if (force
  431. || inFile.lastModified() > outFile.lastModified()
  432. || styleSheetLastModified > outFile.lastModified()) {
  433. ensureDirectoryFor(outFile);
  434. log("Processing " + inFile + " to " + outFile);
  435. configureLiaison(stylesheet);
  436. liaison.transform(inFile, outFile);
  437. }
  438. } catch (Exception ex) {
  439. // If failed to process document, must delete target document,
  440. // or it will not attempt to process it the second time
  441. log("Failed to process " + inFile, Project.MSG_INFO);
  442. if (outFile != null) {
  443. outFile.delete();
  444. }
  445. throw new BuildException(ex);
  446. }
  447. } //-- processXML
  448. /**
  449. * Process the input file to the output file with the given stylesheet.
  450. *
  451. * @param inFile the input file to process.
  452. * @param outFile the destination file.
  453. * @param stylesheet the stylesheet to use.
  454. * @exception BuildException if the processing fails.
  455. */
  456. private void process(File inFile, File outFile, File stylesheet)
  457. throws BuildException {
  458. try {
  459. long styleSheetLastModified = stylesheet.lastModified();
  460. log("In file " + inFile + " time: " + inFile.lastModified(),
  461. Project.MSG_DEBUG);
  462. log("Out file " + outFile + " time: " + outFile.lastModified(),
  463. Project.MSG_DEBUG);
  464. log("Style file " + xslFile + " time: " + styleSheetLastModified,
  465. Project.MSG_DEBUG);
  466. if (force || inFile.lastModified() >= outFile.lastModified()
  467. || styleSheetLastModified >= outFile.lastModified()) {
  468. ensureDirectoryFor(outFile);
  469. log("Processing " + inFile + " to " + outFile,
  470. Project.MSG_INFO);
  471. configureLiaison(stylesheet);
  472. liaison.transform(inFile, outFile);
  473. } else {
  474. log("Skipping input file " + inFile
  475. + " because it is older than output file " + outFile
  476. + " and so is the stylesheet " + stylesheet, Project.MSG_DEBUG);
  477. }
  478. } catch (Exception ex) {
  479. log("Failed to process " + inFile, Project.MSG_INFO);
  480. if (outFile != null) {
  481. outFile.delete();
  482. }
  483. throw new BuildException(ex);
  484. }
  485. }
  486. /**
  487. * Ensure the directory exists for a given file
  488. *
  489. * @param targetFile the file for which the directories are required.
  490. * @exception BuildException if the directories cannot be created.
  491. */
  492. private void ensureDirectoryFor(File targetFile)
  493. throws BuildException {
  494. File directory = fileUtils.getParentFile(targetFile);
  495. if (!directory.exists()) {
  496. if (!directory.mkdirs()) {
  497. throw new BuildException("Unable to create directory: "
  498. + directory.getAbsolutePath());
  499. }
  500. }
  501. }
  502. /**
  503. * Get the factory instance configured for this processor
  504. *
  505. * @return the factory instance in use
  506. */
  507. public Factory getFactory() {
  508. return factory;
  509. }
  510. /**
  511. * Get the XML catalog containing entity definitions
  512. *
  513. * @return the XML catalog for the task.
  514. */
  515. public XMLCatalog getXMLCatalog() {
  516. return xmlCatalog;
  517. }
  518. public Enumeration getOutputProperties() {
  519. return outputProperties.elements();
  520. }
  521. /**
  522. * Get the Liason implementation to use in processing.
  523. *
  524. * @return an instance of the XSLTLiason interface.
  525. */
  526. protected XSLTLiaison getLiaison() {
  527. // if processor wasn't specified, see if TraX is available. If not,
  528. // default it to xslp or xalan, depending on which is in the classpath
  529. if (liaison == null) {
  530. if (processor != null) {
  531. try {
  532. resolveProcessor(processor);
  533. } catch (Exception e) {
  534. throw new BuildException(e);
  535. }
  536. } else {
  537. try {
  538. resolveProcessor("trax");
  539. } catch (Throwable e1) {
  540. try {
  541. resolveProcessor("xalan");
  542. } catch (Throwable e2) {
  543. try {
  544. resolveProcessor("xslp");
  545. } catch (Throwable e3) {
  546. e3.printStackTrace();
  547. e2.printStackTrace();
  548. throw new BuildException(e1);
  549. }
  550. }
  551. }
  552. }
  553. }
  554. return liaison;
  555. }
  556. /**
  557. * Create an instance of an XSL parameter for configuration by Ant.
  558. *
  559. * @return an instance of the Param class to be configured.
  560. */
  561. public Param createParam() {
  562. Param p = new Param();
  563. params.addElement(p);
  564. return p;
  565. }
  566. /**
  567. * The Param inner class used to store XSL parameters
  568. */
  569. public static class Param {
  570. /** The parameter name */
  571. private String name = null;
  572. /** The parameter's value */
  573. private String expression = null;
  574. private String ifProperty;
  575. private String unlessProperty;
  576. private Project project;
  577. /**
  578. * Set the current project
  579. *
  580. * @param project the current project
  581. */
  582. public void setProject(Project project) {
  583. this.project = project;
  584. }
  585. /**
  586. * Set the parameter name.
  587. *
  588. * @param name the name of the parameter.
  589. */
  590. public void setName(String name) {
  591. this.name = name;
  592. }
  593. /**
  594. * The parameter value
  595. * NOTE : was intended to be an XSL expression.
  596. * @param expression the parameter's value.
  597. */
  598. public void setExpression(String expression) {
  599. this.expression = expression;
  600. }
  601. /**
  602. * Get the parameter name
  603. *
  604. * @return the parameter name
  605. * @exception BuildException if the name is not set.
  606. */
  607. public String getName() throws BuildException {
  608. if (name == null) {
  609. throw new BuildException("Name attribute is missing.");
  610. }
  611. return name;
  612. }
  613. /**
  614. * Get the parameter's value
  615. *
  616. * @return the parameter value
  617. * @exception BuildException if the value is not set.
  618. */
  619. public String getExpression() throws BuildException {
  620. if (expression == null) {
  621. throw new BuildException("Expression attribute is missing.");
  622. }
  623. return expression;
  624. }
  625. /**
  626. * Set whether this param should be used. It will be
  627. * used if the property has been set, otherwise it won't.
  628. * @param ifProperty name of property
  629. */
  630. public void setIf(String ifProperty) {
  631. this.ifProperty = ifProperty;
  632. }
  633. /**
  634. * Set whether this param should NOT be used. It
  635. * will not be used if the property has been set, otherwise it
  636. * will be used.
  637. * @param unlessProperty name of property
  638. */
  639. public void setUnless(String unlessProperty) {
  640. this.unlessProperty = unlessProperty;
  641. }
  642. /**
  643. * Ensures that the param passes the conditions placed
  644. * on it with <code>if</code> and <code>unless</code> properties.
  645. */
  646. public boolean shouldUse() {
  647. if (ifProperty != null && project.getProperty(ifProperty) == null) {
  648. return false;
  649. } else if (unlessProperty != null
  650. && project.getProperty(unlessProperty) != null) {
  651. return false;
  652. }
  653. return true;
  654. }
  655. } // Param
  656. /**
  657. * Create an instance of an output property to be configured.
  658. * @return the newly created output property.
  659. * @since Ant 1.5
  660. */
  661. public OutputProperty createOutputProperty() {
  662. OutputProperty p = new OutputProperty();
  663. outputProperties.addElement(p);
  664. return p;
  665. }
  666. /**
  667. * Specify how the result tree should be output as specified
  668. * in the <a href="http://www.w3.org/TR/xslt#output">
  669. * specification</a>.
  670. * @since Ant 1.5
  671. */
  672. public static class OutputProperty {
  673. /** output property name */
  674. private String name;
  675. /** output property value */
  676. private String value;
  677. /**
  678. * @return the output property name.
  679. */
  680. public String getName() {
  681. return name;
  682. }
  683. /**
  684. * set the name for this property
  685. * @param name A non-null String that specifies an
  686. * output property name, which may be namespace qualified.
  687. */
  688. public void setName(String name) {
  689. this.name = name;
  690. }
  691. /**
  692. * @return the output property value.
  693. */
  694. public String getValue() {
  695. return value;
  696. }
  697. /**
  698. * set the value for this property
  699. * @param value The non-null string value of the output property.
  700. */
  701. public void setValue(String value) {
  702. this.value = value;
  703. }
  704. }
  705. /**
  706. * Initialize internal instance of XMLCatalog
  707. */
  708. public void init() throws BuildException {
  709. super.init();
  710. xmlCatalog.setProject(getProject());
  711. }
  712. /**
  713. * Loads the stylesheet and set xsl:param parameters.
  714. *
  715. * @param stylesheet the file form which to load the stylesheet.
  716. * @exception BuildException if the stylesheet cannot be loaded.
  717. */
  718. protected void configureLiaison(File stylesheet) throws BuildException {
  719. if (stylesheetLoaded && reuseLoadedStylesheet) {
  720. return;
  721. }
  722. stylesheetLoaded = true;
  723. try {
  724. log("Loading stylesheet " + stylesheet, Project.MSG_INFO);
  725. liaison.setStylesheet(stylesheet);
  726. for (Enumeration e = params.elements(); e.hasMoreElements();) {
  727. Param p = (Param) e.nextElement();
  728. if (p.shouldUse()) {
  729. liaison.addParam(p.getName(), p.getExpression());
  730. }
  731. }
  732. if (liaison instanceof XSLTLiaison2) {
  733. ((XSLTLiaison2) liaison).configure(this);
  734. }
  735. } catch (Exception ex) {
  736. log("Failed to transform using stylesheet " + stylesheet,
  737. Project.MSG_INFO);
  738. throw new BuildException(ex);
  739. }
  740. }
  741. /**
  742. * Create the factory element to configure a trax liaison.
  743. * @return the newly created factory element.
  744. * @throws BuildException if the element is created more than one time.
  745. */
  746. public Factory createFactory() throws BuildException {
  747. if (factory != null) {
  748. throw new BuildException("'factory' element must be unique");
  749. }
  750. factory = new Factory();
  751. return factory;
  752. }
  753. /**
  754. * The factory element to configure a transformer factory
  755. * @since Ant 1.6
  756. */
  757. public static class Factory {
  758. /** the factory class name to use for TraXLiaison */
  759. private String name;
  760. /**
  761. * the list of factory attributes to use for TraXLiaison
  762. */
  763. private Vector attributes = new Vector();
  764. /**
  765. * @return the name of the factory.
  766. */
  767. public String getName() {
  768. return name;
  769. }
  770. /**
  771. * Set the name of the factory
  772. * @param name the name of the factory.
  773. */
  774. public void setName(String name) {
  775. this.name = name;
  776. }
  777. /**
  778. * Create an instance of a factory attribute.
  779. * the newly created factory attribute
  780. */
  781. public void addAttribute(Attribute attr) {
  782. attributes.addElement(attr);
  783. }
  784. /**
  785. * return the attribute elements.
  786. * @return the enumeration of attributes
  787. */
  788. public Enumeration getAttributes() {
  789. return attributes.elements();
  790. }
  791. /**
  792. * A JAXP factory attribute. This is mostly processor specific, for
  793. * example for Xalan 2.3+, the following attributes could be set:
  794. * <ul>
  795. * <li>http://xml.apache.org/xalan/features/optimize (true|false) </li>
  796. * <li>http://xml.apache.org/xalan/features/incremental (true|false) </li>
  797. * </ul>
  798. */
  799. public static class Attribute implements DynamicConfigurator {
  800. /** attribute name, mostly processor specific */
  801. private String name;
  802. /** attribute value, often a boolean string */
  803. private Object value;
  804. /**
  805. * @return the attribute name.
  806. */
  807. public String getName() {
  808. return name;
  809. }
  810. /**
  811. * @return the output property value.
  812. */
  813. public Object getValue() {
  814. return value;
  815. }
  816. public Object createDynamicElement(String name) throws BuildException {
  817. return null;
  818. }
  819. public void setDynamicAttribute(String name, String value)
  820. throws BuildException {
  821. // only 'name' and 'value' exist.
  822. if ("name".equalsIgnoreCase(name)) {
  823. this.name = value;
  824. } else if ("value".equalsIgnoreCase(name)) {
  825. // a value must be of a given type
  826. // say boolean|integer|string that are mostly used.
  827. if ("true".equalsIgnoreCase(value)
  828. || "false".equalsIgnoreCase(value)) {
  829. this.value = new Boolean(value);
  830. } else {
  831. try {
  832. this.value = new Integer(value);
  833. } catch (NumberFormatException e) {
  834. this.value = value;
  835. }
  836. }
  837. } else {
  838. throw new BuildException("Unsupported attribute: " + name);
  839. }
  840. }
  841. } // -- class Attribute
  842. } // -- class Factory
  843. /**
  844. * Mapper implementation of the "traditional" way <xslt>
  845. * mapped filenames.
  846. *
  847. * <p>If the file has an extension, chop it off. Append whatever
  848. * the user has specified as extension or ".html".</p>
  849. *
  850. * @since Ant 1.6.2
  851. */
  852. private class StyleMapper implements FileNameMapper {
  853. public void setFrom(String from) {}
  854. public void setTo(String to) {}
  855. public String[] mapFileName(String xmlFile) {
  856. int dotPos = xmlFile.lastIndexOf('.');
  857. if (dotPos > 0) {
  858. xmlFile = xmlFile.substring(0, dotPos);
  859. }
  860. return new String[] {xmlFile + targetExtension};
  861. }
  862. }
  863. }