ExpandCreoleXmls.java
001 package gate.util.ant;
002 
003 import java.io.File;
004 import java.io.FileOutputStream;
005 import java.io.PrintWriter;
006 import java.io.StringWriter;
007 import java.util.ArrayList;
008 import java.util.Iterator;
009 import java.util.List;
010 
011 import gate.Gate;
012 import gate.creole.CreoleAnnotationHandler;
013 import gate.creole.metadata.CreoleResource;
014 import gate.util.CreoleXmlUpperCaseFilter;
015 import gate.util.GateException;
016 
017 import org.apache.tools.ant.BuildException;
018 import org.apache.tools.ant.DirectoryScanner;
019 import org.apache.tools.ant.Project;
020 import org.apache.tools.ant.Task;
021 import org.apache.tools.ant.types.FileSet;
022 import org.jdom.Document;
023 import org.jdom.Element;
024 import org.jdom.filter.ElementFilter;
025 import org.jdom.filter.Filter;
026 import org.jdom.input.SAXBuilder;
027 import org.jdom.output.XMLOutputter;
028 
029 /**
030  * Ant task to take a bunch of creole.xml files, process the
031  {@link CreoleResource} annotations on their resources, and write the
032  * augmented XML to a target directory.  If the "classesOnly" attribute is
033  * set to "true" (or "1", "yes" or "on" in the normal Ant way) then the task
034  * performs only the first JAR scanning step, adding
035  <pre>
036  * &lt;RESOURCE&gt;
037  *   &lt;CLASS&gt;com.example.ClassName&lt;/CLASS&gt;
038  * &lt;/RESOURCE&gt;
039  </pre>
040  * for each annotated class in the JAR, but does not process the annotation
041  * fully.  This limited expansion is useful in cases where you will be using
042  * the plugin in a way that does not allow JAR scanning to take place at
043  * runtime, for example if you will be loading the plugin directly from a
044  <code>jar:<code> URL (with the plugin's JAR files placed on your
045  * application's classpath).
046  */
047 public class ExpandCreoleXmls extends Task {
048 
049   private List<FileSet> srcFiles = new ArrayList<FileSet>();
050   
051   private File toDir;
052   
053   private File gateHome = null;
054   
055   private File pluginsHome = null;
056 
057   private boolean classesOnly = false;
058   
059   private SAXBuilder builder;
060   
061   private XMLOutputter outputter = new XMLOutputter();
062 
063   public ExpandCreoleXmls() {
064     builder = new SAXBuilder(false);
065     builder.setXMLFilter(new CreoleXmlUpperCaseFilter());
066   }
067   
068   public void addFileset(FileSet fs) {
069     srcFiles.add(fs);
070   }
071   
072   public void setTodir(File toDir) {
073     this.toDir = toDir;
074   }
075 
076   public void setClassesOnly(boolean classesOnly) {
077     this.classesOnly = classesOnly;
078   }
079   
080   @Override
081   public void execute() throws BuildException {
082     if(toDir == null) {
083       throw new BuildException("Please specify a destination directory using todir", getLocation());
084     }
085     if(toDir.isFile()) {
086       throw new BuildException("Destination already exists and is not a directory", getLocation());
087     }
088 
089     if(Gate.isInitialised()) {
090       log("GATE already initialised, gatehome and pluginshome attributes ignored", Project.MSG_VERBOSE);
091     else {
092       try {
093         Gate.setGateHome(gateHome);
094         Gate.setPluginsHome(pluginsHome);
095         Gate.init();
096       }
097       catch(GateException e) {
098         throw new BuildException("Error initialising GATE", e, getLocation());
099       }
100     }
101     for(FileSet fs : srcFiles) {
102       DirectoryScanner ds = fs.getDirectoryScanner(getProject());
103       for(String f : ds.getIncludedFiles()) {
104         File creoleFile = new File(ds.getBasedir(), f);
105         try {
106           File plugin = creoleFile.getParentFile();
107           File destFile = new File(toDir, f);
108           File destPlugin = destFile.getParentFile();
109 
110           log("Expanding " + creoleFile + " to " + destFile, Project.MSG_VERBOSE);
111           Gate.addKnownPlugin(plugin.toURI().toURL());
112           CreoleAnnotationHandler annotationHandler = new CreoleAnnotationHandler(creoleFile.toURI().toURL());
113           Document creoleDoc = builder.build(creoleFile);
114           annotationHandler.createResourceElementsForDirInfo(creoleDoc);
115           if(!classesOnly) {
116             annotationHandler.addJarsToClassLoader(Gate.getClassLoader().getDisposableClassLoader(plugin.toURI().toURL().toString()), creoleDoc);
117             annotationHandler.processAnnotations(creoleDoc);
118           }
119           
120           // strip out SCAN="true" attributes as scanning has now been done
121           @SuppressWarnings({"unchecked""serial"})
122           Iterator<Element> scannedJars = creoleDoc.getDescendants(new ElementFilter("JAR").and(new Filter() {
123             @Override
124             public boolean matches(Object o) {
125               return "true".equalsIgnoreCase(((Element)o).getAttributeValue("SCAN"));
126             }
127           }));
128           while(scannedJars.hasNext()) {
129             scannedJars.next().removeAttribute("SCAN");
130           }
131           
132           destPlugin.mkdirs();
133           FileOutputStream fos = new FileOutputStream(destFile);
134           try {
135             outputter.output(creoleDoc, fos);
136           }
137           finally {
138             fos.close();
139           }
140         }
141         catch(Throwable e) {
142           log("Error processing " + creoleFile + ", skipped", Project.MSG_WARN);
143           StringWriter sw = new StringWriter();
144           PrintWriter pw = new PrintWriter(sw);
145           e.printStackTrace(pw);
146           log(sw.toString(), Project.MSG_VERBOSE);
147         }
148       }
149     }
150   }
151 
152   public void setGateHome(File gateHome) {
153     this.gateHome = gateHome;
154   }
155   
156   public void setPluginsHome(File pluginsHome) {
157     this.pluginsHome = pluginsHome;
158   }
159 
160 }