You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

XSLTProcess.java 31 kB

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