| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* $Id$ */ |
| |
| package org.apache.fop.tools; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import javax.xml.transform.Result; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.URIResolver; |
| import javax.xml.transform.dom.DOMResult; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.sax.SAXTransformerFactory; |
| import javax.xml.transform.stream.StreamResult; |
| import javax.xml.transform.stream.StreamSource; |
| |
| import org.w3c.dom.Node; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.DirectoryScanner; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.types.FileSet; |
| import org.apache.tools.ant.types.selectors.FilenameSelector; |
| |
| import org.apache.fop.events.model.EventModel; |
| import org.apache.fop.events.model.EventProducerModel; |
| |
| /** |
| * Ant task which inspects a file set for Java interfaces which extend the |
| * {@link org.apache.fop.events.EventProducer} interface. For all such interfaces an event model |
| * file and a translation file for the human-readable messages generated by the events is |
| * created and/or updated. |
| */ |
| public class EventProducerCollectorTask extends Task { |
| |
| private List<FileSet> filesets = new java.util.ArrayList<FileSet>(); |
| private File destDir; |
| private File translationFile; |
| |
| /** {@inheritDoc} */ |
| public void execute() throws BuildException { |
| try { |
| EventProducerCollector collector = new EventProducerCollector(); |
| long lastModified = processFileSets(collector); |
| for (Iterator iter = collector.getModels().iterator(); iter.hasNext();) { |
| EventModel model = (EventModel) iter.next(); |
| File parentDir = getParentDir(model); |
| if (!parentDir.exists() && !parentDir.mkdirs()) { |
| throw new BuildException( |
| "Could not create target directory for event model file: " + parentDir); |
| } |
| File modelFile = new File(parentDir, "event-model.xml"); |
| if (!modelFile.exists() || lastModified > modelFile.lastModified()) { |
| model.saveToXML(modelFile); |
| log("Event model written to " + modelFile); |
| } |
| if (getTranslationFile() != null) { |
| // TODO Remove translation file creation facility? |
| if (!getTranslationFile().exists() |
| || lastModified > getTranslationFile().lastModified()) { |
| updateTranslationFile(modelFile); |
| } |
| } |
| } |
| } catch (ClassNotFoundException e) { |
| throw new BuildException(e); |
| } catch (EventConventionException ece) { |
| throw new BuildException(ece); |
| } catch (IOException ioe) { |
| throw new BuildException(ioe); |
| } |
| } |
| |
| private static final String MODEL2TRANSLATION = "model2translation.xsl"; |
| private static final String MERGETRANSLATION = "merge-translation.xsl"; |
| |
| private File getParentDir(EventModel model) { |
| Iterator iter = model.getProducers(); |
| assert iter.hasNext(); |
| EventProducerModel producer = (EventProducerModel) iter.next(); |
| assert !iter.hasNext(); |
| String interfaceName = producer.getInterfaceName(); |
| int startLocalName = interfaceName.lastIndexOf("."); |
| if (startLocalName < 0) { |
| return destDir; |
| } else { |
| String dirname = interfaceName.substring(0, startLocalName); |
| dirname = dirname.replace('.', File.separatorChar); |
| return new File(destDir, dirname); |
| } |
| } |
| |
| /** |
| * Updates the translation file with new entries for newly found event producer methods. |
| * @param modelFile the model file to use |
| * @throws IOException if an I/O error occurs |
| */ |
| protected void updateTranslationFile(File modelFile) throws IOException { |
| try { |
| boolean resultExists = getTranslationFile().exists(); |
| SAXTransformerFactory tFactory |
| = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); |
| |
| //Generate fresh generated translation file as template |
| Source src = new StreamSource(modelFile.toURI().toURL().toExternalForm()); |
| StreamSource xslt1 = new StreamSource( |
| getClass().getResourceAsStream(MODEL2TRANSLATION)); |
| if (xslt1.getInputStream() == null) { |
| throw new FileNotFoundException(MODEL2TRANSLATION + " not found"); |
| } |
| DOMResult domres = new DOMResult(); |
| Transformer transformer = tFactory.newTransformer(xslt1); |
| transformer.transform(src, domres); |
| final Node generated = domres.getNode(); |
| |
| Node sourceDocument; |
| if (resultExists) { |
| //Load existing translation file into memory (because we overwrite it later) |
| src = new StreamSource(getTranslationFile().toURI().toURL().toExternalForm()); |
| domres = new DOMResult(); |
| transformer = tFactory.newTransformer(); |
| transformer.transform(src, domres); |
| sourceDocument = domres.getNode(); |
| } else { |
| //Simply use generated as source document |
| sourceDocument = generated; |
| } |
| |
| //Generate translation file (with potentially new translations) |
| src = new DOMSource(sourceDocument); |
| |
| //The following triggers a bug in older Xalan versions |
| //Result res = new StreamResult(getTranslationFile()); |
| OutputStream out = new java.io.FileOutputStream(getTranslationFile()); |
| out = new java.io.BufferedOutputStream(out); |
| Result res = new StreamResult(out); |
| try { |
| StreamSource xslt2 = new StreamSource( |
| getClass().getResourceAsStream(MERGETRANSLATION)); |
| if (xslt2.getInputStream() == null) { |
| throw new FileNotFoundException(MERGETRANSLATION + " not found"); |
| } |
| transformer = tFactory.newTransformer(xslt2); |
| transformer.setURIResolver(new URIResolver() { |
| public Source resolve(String href, String base) throws TransformerException { |
| if ("my:dom".equals(href)) { |
| return new DOMSource(generated); |
| } |
| return null; |
| } |
| }); |
| if (resultExists) { |
| transformer.setParameter("generated-url", "my:dom"); |
| } |
| transformer.transform(src, res); |
| if (resultExists) { |
| log("Translation file updated: " + getTranslationFile()); |
| } else { |
| log("Translation file generated: " + getTranslationFile()); |
| } |
| } finally { |
| IOUtils.closeQuietly(out); |
| } |
| } catch (TransformerException te) { |
| throw new IOException(te.getMessage()); |
| } |
| } |
| |
| /** |
| * Processes the file sets defined for the task. |
| * @param collector the collector to use for collecting the event producers |
| * @return the time of the latest modification of any of the files inspected |
| * @throws IOException if an I/O error occurs |
| * @throws EventConventionException if the EventProducer conventions are violated |
| * @throws ClassNotFoundException if a required class cannot be found |
| */ |
| protected long processFileSets(EventProducerCollector collector) |
| throws IOException, EventConventionException, ClassNotFoundException { |
| long lastModified = 0; |
| Iterator<FileSet> iter = filesets.iterator(); |
| while (iter.hasNext()) { |
| FileSet fs = (FileSet)iter.next(); |
| DirectoryScanner ds = fs.getDirectoryScanner(getProject()); |
| String[] srcFiles = ds.getIncludedFiles(); |
| File directory = fs.getDir(getProject()); |
| for (int i = 0, c = srcFiles.length; i < c; i++) { |
| String filename = srcFiles[i]; |
| File src = new File(directory, filename); |
| boolean eventProducerFound = collector.scanFile(src); |
| if (eventProducerFound) { |
| lastModified = Math.max(lastModified, src.lastModified()); |
| } |
| } |
| } |
| return lastModified; |
| } |
| |
| /** |
| * Adds a file set. |
| * @param set the file set |
| */ |
| public void addFileset(FileSet set) { |
| filesets.add(set); |
| } |
| |
| /** |
| * Sets the destination directory for the event models. |
| * |
| * @param destDir the destination directory |
| */ |
| public void setDestDir(File destDir) { |
| if (!destDir.isDirectory()) { |
| throw new IllegalArgumentException("destDir must be a directory"); |
| } |
| this.destDir = destDir; |
| } |
| |
| /** |
| * Sets the translation file for the event producer methods. |
| * @param f the translation file |
| */ |
| public void setTranslationFile(File f) { |
| this.translationFile = f; |
| } |
| |
| /** |
| * Returns the translation file for the event producer methods. |
| * @return the translation file |
| */ |
| public File getTranslationFile() { |
| return this.translationFile; |
| } |
| |
| /** |
| * Command-line interface for testing purposes. |
| * @param args the command-line arguments |
| */ |
| public static void main(String[] args) { |
| try { |
| Project project = new Project(); |
| |
| EventProducerCollectorTask generator = new EventProducerCollectorTask(); |
| generator.setProject(project); |
| project.setName("Test"); |
| FileSet fileset = new FileSet(); |
| fileset.setDir(new File("test/java")); |
| |
| FilenameSelector selector = new FilenameSelector(); |
| selector.setName("**/*.java"); |
| fileset.add(selector); |
| generator.addFileset(fileset); |
| |
| File targetDir = new File("build/codegen1"); |
| targetDir.mkdirs(); |
| |
| generator.setTranslationFile(new File("D:/out1.xml")); |
| generator.execute(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |