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

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