1 18 19 package org.apache.tools.ant.taskdefs; 20 21 import java.io.File ; 22 import java.util.Enumeration ; 23 import java.util.Iterator ; 24 import java.util.Vector ; 25 import org.apache.tools.ant.AntClassLoader; 26 import org.apache.tools.ant.BuildException; 27 import org.apache.tools.ant.DirectoryScanner; 28 import org.apache.tools.ant.DynamicConfigurator; 29 import org.apache.tools.ant.Project; 30 import org.apache.tools.ant.types.Mapper; 31 import org.apache.tools.ant.types.Path; 32 import org.apache.tools.ant.types.Reference; 33 import org.apache.tools.ant.types.Resource; 34 import org.apache.tools.ant.types.ResourceCollection; 35 import org.apache.tools.ant.types.XMLCatalog; 36 import org.apache.tools.ant.types.resources.FileResource; 37 import org.apache.tools.ant.types.resources.Resources; 38 import org.apache.tools.ant.types.resources.Union; 39 import org.apache.tools.ant.util.FileNameMapper; 40 import org.apache.tools.ant.util.FileUtils; 41 42 51 52 public class XSLTProcess extends MatchingTask implements XSLTLogger { 53 54 private File destDir = null; 55 56 57 private File baseDir = null; 58 59 60 private String xslFile = null; 61 62 63 private Resource xslResource = null; 64 65 66 private String targetExtension = ".html"; 67 68 69 private String fileNameParameter = null; 70 71 72 private String fileDirParameter = null; 73 74 75 private Vector params = new Vector (); 76 77 78 private File inFile = null; 79 80 81 private File outFile = null; 82 83 84 private String processor; 85 86 87 private Path classpath = null; 88 89 91 private XSLTLiaison liaison; 92 93 95 private boolean stylesheetLoaded = false; 96 97 98 private boolean force = false; 99 100 101 private Vector outputProperties = new Vector (); 102 103 104 private XMLCatalog xmlCatalog = new XMLCatalog(); 105 106 107 private static final String TRAX_LIAISON_CLASS = 108 "org.apache.tools.ant.taskdefs.optional.TraXLiaison"; 109 110 111 private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); 112 113 118 private boolean performDirectoryScan = true; 119 120 124 private Factory factory = null; 125 126 130 private boolean reuseLoadedStylesheet = true; 131 132 142 private AntClassLoader loader = null; 143 144 149 private Mapper mapperElement = null; 150 151 156 private Union resources = new Union(); 157 158 163 private boolean useImplicitFileset = true; 164 165 169 public static final String PROCESSOR_TRAX = "trax"; 170 171 174 public XSLTProcess() { 175 } 177 184 public void setScanIncludedDirectories(boolean b) { 185 performDirectoryScan = b; 186 } 187 188 196 public void setReloadStylesheet(boolean b) { 197 reuseLoadedStylesheet = !b; 198 } 199 200 206 public void addMapper(Mapper mapper) { 207 if (mapperElement != null) { 208 throw new BuildException("Cannot define more than one mapper", 209 getLocation()); 210 } 211 mapperElement = mapper; 212 } 213 214 221 public void add(ResourceCollection rc) { 222 resources.add(rc); 223 } 224 225 230 public void addConfiguredStyle(Resources rc) { 231 if (rc.size() != 1) { 232 throw new BuildException("The style element must be specified" 233 + " with exactly one nested resource."); 234 } 235 setXslResource((Resource) rc.iterator().next()); 236 } 237 238 243 public void setXslResource(Resource xslResource) { 244 this.xslResource = xslResource; 245 } 246 247 253 public void add(FileNameMapper fileNameMapper) throws BuildException { 254 Mapper mapper = new Mapper(getProject()); 255 mapper.add(fileNameMapper); 256 addMapper(mapper); 257 } 258 259 265 public void execute() throws BuildException { 266 if ("style".equals(getTaskType())) { 267 log("Warning: the task name <style> is deprecated. Use <xslt> instead.", 268 Project.MSG_WARN); 269 } 270 271 File savedBaseDir = baseDir; 272 273 DirectoryScanner scanner; 274 String [] list; 275 String [] dirs; 276 277 if (xslResource == null && xslFile == null) { 278 throw new BuildException("specify the " 279 + "stylesheet either as a filename in style " 280 + "attribute or as a nested resource", getLocation()); 281 282 } 283 if (xslResource != null && xslFile != null) { 284 throw new BuildException("specify the " 285 + "stylesheet either as a filename in style " 286 + "attribute or as a nested resource but not " 287 + "as both", getLocation()); 288 } 289 290 if (inFile != null && !inFile.exists()) { 291 throw new BuildException( 292 "input file " + inFile.toString() + " does not exist", getLocation()); 293 } 294 295 try { 296 if (baseDir == null) { 297 baseDir = getProject().resolveFile("."); 298 } 299 300 liaison = getLiaison(); 301 302 if (liaison instanceof XSLTLoggerAware) { 304 ((XSLTLoggerAware) liaison).setLogger(this); 305 } 306 307 log("Using " + liaison.getClass().toString(), Project.MSG_VERBOSE); 308 309 if (xslFile != null) { 310 File stylesheet = getProject().resolveFile(xslFile); 313 if (!stylesheet.exists()) { 314 stylesheet = FILE_UTILS.resolveFile(baseDir, xslFile); 315 319 if (stylesheet.exists()) { 320 log("DEPRECATED - the 'style' attribute should be relative " 321 + "to the project's"); 322 log(" basedir, not the tasks's basedir."); 323 } 324 } 325 FileResource fr = new FileResource(); 326 fr.setProject(getProject()); 327 fr.setFile(stylesheet); 328 xslResource = fr; 329 } 330 331 if (inFile != null && outFile != null) { 333 process(inFile, outFile, xslResource); 334 return; 335 } 336 337 341 342 checkDest(); 344 345 if (useImplicitFileset) { 346 scanner = getDirectoryScanner(baseDir); 347 log("Transforming into " + destDir, Project.MSG_INFO); 348 349 list = scanner.getIncludedFiles(); 351 for (int i = 0; i < list.length; ++i) { 352 process(baseDir, list[i], destDir, xslResource); 353 } 354 if (performDirectoryScan) { 355 dirs = scanner.getIncludedDirectories(); 357 for (int j = 0; j < dirs.length; ++j) { 358 list = new File (baseDir, dirs[j]).list(); 359 for (int i = 0; i < list.length; ++i) { 360 process(baseDir, dirs[j] + File.separator + list[i], 361 destDir, xslResource); 362 } 363 } 364 } 365 } else { if (resources.size() == 0) { 367 throw new BuildException("no resources specified"); 368 } 369 } 370 processResources(xslResource); 371 } finally { 372 if (loader != null) { 373 loader.resetThreadContextLoader(); 374 loader.cleanup(); 375 loader = null; 376 } 377 liaison = null; 378 stylesheetLoaded = false; 379 baseDir = savedBaseDir; 380 } 381 } 382 383 389 public void setForce(boolean force) { 390 this.force = force; 391 } 392 393 399 public void setBasedir(File dir) { 400 baseDir = dir; 401 } 402 403 410 public void setDestdir(File dir) { 411 destDir = dir; 412 } 413 414 419 public void setExtension(String name) { 420 targetExtension = name; 421 } 422 423 429 public void setStyle(String xslFile) { 430 this.xslFile = xslFile; 431 } 432 433 438 public void setClasspath(Path classpath) { 439 createClasspath().append(classpath); 440 } 441 442 447 public Path createClasspath() { 448 if (classpath == null) { 449 classpath = new Path(getProject()); 450 } 451 return classpath.createPath(); 452 } 453 454 460 public void setClasspathRef(Reference r) { 461 createClasspath().setRefid(r); 462 } 463 464 470 public void setProcessor(String processor) { 471 this.processor = processor; 472 } 473 474 482 public void setUseImplicitFileset(boolean useimplicitfileset) { 483 useImplicitFileset = useimplicitfileset; 484 } 485 486 491 public void addConfiguredXMLCatalog(XMLCatalog xmlCatalog) { 492 this.xmlCatalog.addConfiguredXMLCatalog(xmlCatalog); 493 } 494 495 502 public void setFileNameParameter(String fileNameParameter) { 503 this.fileNameParameter = fileNameParameter; 504 } 505 506 513 public void setFileDirParameter(String fileDirParameter) { 514 this.fileDirParameter = fileDirParameter; 515 } 516 517 525 private void resolveProcessor(String proc) throws Exception { 526 String classname; 527 if (proc.equals(PROCESSOR_TRAX)) { 528 classname = TRAX_LIAISON_CLASS; 529 } else { 530 classname = proc; 532 } 533 Class clazz = loadClass(classname); 534 liaison = (XSLTLiaison) clazz.newInstance(); 535 } 536 537 546 private Class loadClass(String classname) throws Exception { 547 if (classpath == null) { 548 return Class.forName(classname); 549 } else { 550 loader = getProject().createClassLoader(classpath); 551 loader.setThreadContextLoader(); 552 Class c = Class.forName(classname, true, loader); 553 return c; 554 } 555 } 556 557 563 public void setOut(File outFile) { 564 this.outFile = outFile; 565 } 566 567 573 public void setIn(File inFile) { 574 this.inFile = inFile; 575 } 576 577 582 private void checkDest() { 583 if (destDir == null) { 584 String msg = "destdir attributes must be set!"; 585 throw new BuildException(msg); 586 } 587 } 588 589 594 private void processResources(Resource stylesheet) { 595 Iterator iter = resources.iterator(); 596 while (iter.hasNext()) { 597 Resource r = (Resource) iter.next(); 598 if (!r.isExists()) { 599 continue; 600 } 601 File base = baseDir; 602 String name = r.getName(); 603 if (r instanceof FileResource) { 604 FileResource f = (FileResource) r; 605 base = f.getBaseDir(); 606 if (base == null) { 607 name = f.getFile().getAbsolutePath(); 608 } 609 } 610 process(base, name, destDir, stylesheet); 611 } 612 } 613 614 624 private void process(File baseDir, String xmlFile, File destDir, 625 Resource stylesheet) 626 throws BuildException { 627 628 File outF = null; 629 File inF = null; 630 631 try { 632 long styleSheetLastModified = stylesheet.getLastModified(); 633 inF = new File (baseDir, xmlFile); 634 635 if (inF.isDirectory()) { 636 log("Skipping " + inF + " it is a directory.", 637 Project.MSG_VERBOSE); 638 return; 639 } 640 641 FileNameMapper mapper = null; 642 if (mapperElement != null) { 643 mapper = mapperElement.getImplementation(); 644 } else { 645 mapper = new StyleMapper(); 646 } 647 648 String [] outFileName = mapper.mapFileName(xmlFile); 649 if (outFileName == null || outFileName.length == 0) { 650 log("Skipping " + inFile + " it cannot get mapped to output.", 651 Project.MSG_VERBOSE); 652 return; 653 } else if (outFileName == null || outFileName.length > 1) { 654 log("Skipping " + inFile + " its mapping is ambiguos.", 655 Project.MSG_VERBOSE); 656 return; 657 } 658 659 outF = new File (destDir, outFileName[0]); 660 661 if (force 662 || inF.lastModified() > outF.lastModified() 663 || styleSheetLastModified > outF.lastModified()) { 664 ensureDirectoryFor(outF); 665 log("Processing " + inF + " to " + outF); 666 667 configureLiaison(stylesheet); 668 setLiaisonDynamicFileParameters(liaison, inF); 669 liaison.transform(inF, outF); 670 } 671 } catch (Exception ex) { 672 log("Failed to process " + inFile, Project.MSG_INFO); 675 if (outF != null) { 676 outF.delete(); 677 } 678 679 throw new BuildException(ex); 680 } 681 682 } 684 692 private void process(File inFile, File outFile, Resource stylesheet) 693 throws BuildException { 694 try { 695 long styleSheetLastModified = stylesheet.getLastModified(); 696 log("In file " + inFile + " time: " + inFile.lastModified(), 697 Project.MSG_DEBUG); 698 log("Out file " + outFile + " time: " + outFile.lastModified(), 699 Project.MSG_DEBUG); 700 log("Style file " + xslFile + " time: " + styleSheetLastModified, 701 Project.MSG_DEBUG); 702 if (force || inFile.lastModified() >= outFile.lastModified() 703 || styleSheetLastModified >= outFile.lastModified()) { 704 ensureDirectoryFor(outFile); 705 log("Processing " + inFile + " to " + outFile, 706 Project.MSG_INFO); 707 configureLiaison(stylesheet); 708 setLiaisonDynamicFileParameters(liaison, inFile); 709 liaison.transform(inFile, outFile); 710 } else { 711 log("Skipping input file " + inFile 712 + " because it is older than output file " + outFile 713 + " and so is the stylesheet " + stylesheet, Project.MSG_DEBUG); 714 } 715 } catch (Exception ex) { 716 log("Failed to process " + inFile, Project.MSG_INFO); 717 if (outFile != null) { 718 outFile.delete(); 719 } 720 throw new BuildException(ex); 721 } 722 } 723 724 730 private void ensureDirectoryFor(File targetFile) 731 throws BuildException { 732 File directory = targetFile.getParentFile(); 733 if (!directory.exists()) { 734 if (!directory.mkdirs()) { 735 throw new BuildException("Unable to create directory: " 736 + directory.getAbsolutePath()); 737 } 738 } 739 } 740 741 746 public Factory getFactory() { 747 return factory; 748 } 749 750 755 public XMLCatalog getXMLCatalog() { 756 xmlCatalog.setProject(getProject()); 757 return xmlCatalog; 758 } 759 760 764 public Enumeration getOutputProperties() { 765 return outputProperties.elements(); 766 } 767 768 773 protected XSLTLiaison getLiaison() { 774 if (liaison == null) { 777 if (processor != null) { 778 try { 779 resolveProcessor(processor); 780 } catch (Exception e) { 781 throw new BuildException(e); 782 } 783 } else { 784 try { 785 resolveProcessor(PROCESSOR_TRAX); 786 } catch (Throwable e1) { 787 e1.printStackTrace(); 788 throw new BuildException(e1); 789 } 790 } 791 } 792 return liaison; 793 } 794 795 800 public Param createParam() { 801 Param p = new Param(); 802 params.addElement(p); 803 return p; 804 } 805 806 809 public static class Param { 810 811 private String name = null; 812 813 814 private String expression = null; 815 816 private String ifProperty; 817 private String unlessProperty; 818 private Project project; 819 820 825 public void setProject(Project project) { 826 this.project = project; 827 } 828 829 834 public void setName(String name) { 835 this.name = name; 836 } 837 838 843 public void setExpression(String expression) { 844 this.expression = expression; 845 } 846 847 853 public String getName() throws BuildException { 854 if (name == null) { 855 throw new BuildException("Name attribute is missing."); 856 } 857 return name; 858 } 859 860 866 public String getExpression() throws BuildException { 867 if (expression == null) { 868 throw new BuildException("Expression attribute is missing."); 869 } 870 return expression; 871 } 872 873 878 public void setIf(String ifProperty) { 879 this.ifProperty = ifProperty; 880 } 881 882 888 public void setUnless(String unlessProperty) { 889 this.unlessProperty = unlessProperty; 890 } 891 896 public boolean shouldUse() { 897 if (ifProperty != null && project.getProperty(ifProperty) == null) { 898 return false; 899 } else if (unlessProperty != null 900 && project.getProperty(unlessProperty) != null) { 901 return false; 902 } 903 904 return true; 905 } 906 } 908 909 914 public OutputProperty createOutputProperty() { 915 OutputProperty p = new OutputProperty(); 916 outputProperties.addElement(p); 917 return p; 918 } 919 920 921 927 public static class OutputProperty { 928 929 private String name; 930 931 932 private String value; 933 934 937 public String getName() { 938 return name; 939 } 940 941 946 public void setName(String name) { 947 this.name = name; 948 } 949 950 953 public String getValue() { 954 return value; 955 } 956 957 961 public void setValue(String value) { 962 this.value = value; 963 } 964 } 965 966 970 public void init() throws BuildException { 971 super.init(); 972 xmlCatalog.setProject(getProject()); 973 } 974 975 982 protected void configureLiaison(File stylesheet) throws BuildException { 983 FileResource fr = new FileResource(); 984 fr.setProject(getProject()); 985 fr.setFile(stylesheet); 986 configureLiaison(fr); 987 } 988 995 protected void configureLiaison(Resource stylesheet) throws BuildException { 996 if (stylesheetLoaded && reuseLoadedStylesheet) { 997 return; 998 } 999 stylesheetLoaded = true; 1000 1001 try { 1002 log("Loading stylesheet " + stylesheet, Project.MSG_INFO); 1003 if (liaison instanceof XSLTLiaison2) { 1006 ((XSLTLiaison2) liaison).configure(this); 1007 } 1008 1009 if (liaison instanceof XSLTLiaison3) { 1010 ((XSLTLiaison3) liaison).setStylesheet(stylesheet); 1013 } else { 1014 if (stylesheet instanceof FileResource) { 1018 liaison.setStylesheet( 1019 ((FileResource) stylesheet).getFile()); 1020 } else { 1021 throw new BuildException(liaison.getClass().toString() 1022 + " accepts the stylesheet only as a file", 1023 getLocation()); 1024 } 1025 } 1026 for (Enumeration e = params.elements(); e.hasMoreElements();) { 1027 Param p = (Param) e.nextElement(); 1028 if (p.shouldUse()) { 1029 liaison.addParam(p.getName(), p.getExpression()); 1030 } 1031 } 1032 } catch (Exception ex) { 1033 log("Failed to transform using stylesheet " + stylesheet, 1034 Project.MSG_INFO); 1035 throw new BuildException(ex); 1036 } 1037 } 1038 1039 1049 private void setLiaisonDynamicFileParameters( 1050 XSLTLiaison liaison, 1051 File inFile 1052 ) throws Exception { 1053 if (fileNameParameter != null) { 1054 liaison.addParam(fileNameParameter, inFile.getName()); 1055 } 1056 if (fileDirParameter != null) { 1057 String fileName = FileUtils.getRelativePath(baseDir, inFile); 1058 File file = new File (fileName); 1059 liaison.addParam( 1062 fileDirParameter, 1063 (file.getParent() != null) 1064 ? file.getParent().replace('\\', '/') : "."); 1065 } 1066 } 1067 1068 1073 public Factory createFactory() throws BuildException { 1074 if (factory != null) { 1075 throw new BuildException("'factory' element must be unique"); 1076 } 1077 factory = new Factory(); 1078 return factory; 1079 } 1080 1081 1085 public static class Factory { 1086 1087 1088 private String name; 1089 1090 1093 private Vector attributes = new Vector (); 1094 1095 1098 public String getName() { 1099 return name; 1100 } 1101 1102 1106 public void setName(String name) { 1107 this.name = name; 1108 } 1109 1110 1114 public void addAttribute(Attribute attr) { 1115 attributes.addElement(attr); 1116 } 1117 1118 1122 public Enumeration getAttributes() { 1123 return attributes.elements(); 1124 } 1125 1126 1134 public static class Attribute implements DynamicConfigurator { 1135 1136 1137 private String name; 1138 1139 1140 private Object value; 1141 1142 1145 public String getName() { 1146 return name; 1147 } 1148 1149 1152 public Object getValue() { 1153 return value; 1154 } 1155 1156 1162 public Object createDynamicElement(String name) throws BuildException { 1163 return null; 1164 } 1165 1166 1173 public void setDynamicAttribute(String name, String value) 1174 throws BuildException { 1175 if ("name".equalsIgnoreCase(name)) { 1177 this.name = value; 1178 } else if ("value".equalsIgnoreCase(name)) { 1179 if ("true".equalsIgnoreCase(value)) { 1182 this.value = Boolean.TRUE; 1183 } else if ("false".equalsIgnoreCase(value)) { 1184 this.value = Boolean.FALSE; 1185 } else { 1186 try { 1187 this.value = new Integer (value); 1188 } catch (NumberFormatException e) { 1189 this.value = value; 1190 } 1191 } 1192 } else { 1193 throw new BuildException("Unsupported attribute: " + name); 1194 } 1195 } 1196 } 1198 } 1200 1209 private class StyleMapper implements FileNameMapper { 1210 public void setFrom(String from) { 1211 } 1212 public void setTo(String to) { 1213 } 1214 public String [] mapFileName(String xmlFile) { 1215 int dotPos = xmlFile.lastIndexOf('.'); 1216 if (dotPos > 0) { 1217 xmlFile = xmlFile.substring(0, dotPos); 1218 } 1219 return new String [] {xmlFile + targetExtension}; 1220 } 1221 } 1222 1223} 1224 | Popular Tags |