]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-3160: Rename tools to avoid conflicts with core module
authorSimon Steiner <ssteiner@apache.org>
Tue, 5 Mar 2024 14:31:47 +0000 (14:31 +0000)
committerSimon Steiner <ssteiner@apache.org>
Tue, 5 Mar 2024 14:31:47 +0000 (14:31 +0000)
fop-core/pom.xml
fop-events/pom.xml
fop-events/src/main/java/org/apache/fop/eventtools/EventConventionException.java [new file with mode: 0644]
fop-events/src/main/java/org/apache/fop/eventtools/EventProducerCollector.java [new file with mode: 0644]
fop-events/src/main/java/org/apache/fop/eventtools/EventProducerCollectorTask.java [new file with mode: 0644]
fop-events/src/main/java/org/apache/fop/tools/EventConventionException.java [deleted file]
fop-events/src/main/java/org/apache/fop/tools/EventProducerCollector.java [deleted file]
fop-events/src/main/java/org/apache/fop/tools/EventProducerCollectorTask.java [deleted file]
fop/build.xml

index a041e727da26aa1f2d62d77812f98d2cd0c481f0..19a6fa90db0bae9f590ecb10e2243047dc4bc18b 100644 (file)
             </goals>
             <configuration>
               <target>
-                <taskdef name="collectEvents" classname="org.apache.fop.tools.EventProducerCollectorTask" classpathref="maven.compile.classpath"/>
+                <taskdef name="collectEvents" classname="org.apache.fop.eventtools.EventProducerCollectorTask" classpathref="maven.compile.classpath"/>
                 <collectEvents destdir="${project.build.outputDirectory}">
                   <fileset dir="${project.basedir}/src/main/java">
                     <include name="**/accessibility/AccessibilityEventProducer.java"/>
index 15e4e994778d020e60acf89a9a79ea4c72a728c3..1eeea6db0818f66700a7d7ce7b4879cc5fa5b2a0 100644 (file)
@@ -78,7 +78,7 @@
             </goals>
             <configuration>
               <target>
-                <taskdef name="collectEvents" classname="org.apache.fop.tools.EventProducerCollectorTask">
+                <taskdef name="collectEvents" classname="org.apache.fop.eventtools.EventProducerCollectorTask">
                   <classpath>
                     <path refid="maven.compile.classpath"/>
                     <path refid="maven.test.classpath"/>
diff --git a/fop-events/src/main/java/org/apache/fop/eventtools/EventConventionException.java b/fop-events/src/main/java/org/apache/fop/eventtools/EventConventionException.java
new file mode 100644 (file)
index 0000000..63aceb2
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.eventtools;
+
+/**
+ * This exception is used to indicate a violation of the conventions for event producers.
+ */
+public class EventConventionException extends Exception {
+
+    private static final long serialVersionUID = 117244726033986628L;
+
+    /**
+     * Creates a new EventConventionException
+     * @param message the error message
+     */
+    public EventConventionException(String message) {
+        super(message);
+    }
+
+}
diff --git a/fop-events/src/main/java/org/apache/fop/eventtools/EventProducerCollector.java b/fop-events/src/main/java/org/apache/fop/eventtools/EventProducerCollector.java
new file mode 100644 (file)
index 0000000..fc72ffc
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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.eventtools;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.events.model.EventMethodModel;
+import org.apache.fop.events.model.EventModel;
+import org.apache.fop.events.model.EventProducerModel;
+import org.apache.fop.events.model.EventSeverity;
+
+import com.thoughtworks.qdox.JavaDocBuilder;
+import com.thoughtworks.qdox.model.DefaultDocletTagFactory;
+import com.thoughtworks.qdox.model.DocletTag;
+import com.thoughtworks.qdox.model.DocletTagFactory;
+import com.thoughtworks.qdox.model.JavaClass;
+import com.thoughtworks.qdox.model.JavaMethod;
+import com.thoughtworks.qdox.model.JavaParameter;
+import com.thoughtworks.qdox.model.Type;
+
+/**
+ * Finds EventProducer interfaces and builds the event model for them.
+ */
+class EventProducerCollector {
+
+    private static final String CLASSNAME_EVENT_PRODUCER = EventProducer.class.getName();
+    private static final Map<String, Class<?>> PRIMITIVE_MAP;
+
+    static {
+        Map<String, Class<?>> m = new java.util.HashMap<String, Class<?>>();
+        m.put("boolean", Boolean.class);
+        m.put("byte", Byte.class);
+        m.put("char", Character.class);
+        m.put("short", Short.class);
+        m.put("int", Integer.class);
+        m.put("long", Long.class);
+        m.put("float", Float.class);
+        m.put("double", Double.class);
+        PRIMITIVE_MAP = Collections.unmodifiableMap(m);
+    }
+
+    private DocletTagFactory tagFactory;
+    private List<EventModel> models = new java.util.ArrayList<EventModel>();
+
+    /**
+     * Creates a new EventProducerCollector.
+     */
+    EventProducerCollector() {
+        this.tagFactory = createDocletTagFactory();
+    }
+
+    /**
+     * Creates the {@link DocletTagFactory} to be used by the collector.
+     * @return the doclet tag factory
+     */
+    protected DocletTagFactory createDocletTagFactory() {
+        return new DefaultDocletTagFactory();
+    }
+
+    /**
+     * Scans a file and processes it if it extends the {@link EventProducer} interface.
+     * @param src the source file (a Java source file)
+     * @return true if the file contained an EventProducer interface
+     * @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
+     */
+    public boolean scanFile(File src)
+            throws IOException, EventConventionException, ClassNotFoundException {
+        JavaDocBuilder builder = new JavaDocBuilder(this.tagFactory);
+        builder.addSource(src);
+        JavaClass[] classes = builder.getClasses();
+        boolean eventProducerFound = false;
+        for (JavaClass clazz : classes) {
+            if (clazz.isInterface() && implementsInterface(clazz, CLASSNAME_EVENT_PRODUCER)) {
+                processEventProducerInterface(clazz);
+                eventProducerFound = true;
+            }
+        }
+        return eventProducerFound;
+    }
+
+    private boolean implementsInterface(JavaClass clazz, String intf) {
+        JavaClass[] classes = clazz.getImplementedInterfaces();
+        for (JavaClass cl : classes) {
+            if (cl.getFullyQualifiedName().equals(intf)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Processes an EventProducer interface and creates an EventProducerModel from it.
+     * @param clazz the EventProducer interface
+     * @throws EventConventionException if the event producer conventions are violated
+     * @throws ClassNotFoundException if a required class cannot be found
+     */
+    protected void processEventProducerInterface(JavaClass clazz)
+                throws EventConventionException, ClassNotFoundException {
+        EventProducerModel prodMeta = new EventProducerModel(clazz.getFullyQualifiedName());
+        JavaMethod[] methods = clazz.getMethods(true);
+        for (JavaMethod method : methods) {
+            EventMethodModel methodMeta = createMethodModel(method);
+            prodMeta.addMethod(methodMeta);
+        }
+        EventModel model = new EventModel();
+        model.addProducer(prodMeta);
+        models.add(model);
+    }
+
+    private EventMethodModel createMethodModel(JavaMethod method)
+            throws EventConventionException, ClassNotFoundException {
+        JavaClass clazz = method.getParentClass();
+        //Check EventProducer conventions
+        if (!method.getReturnType().isVoid()) {
+            throw new EventConventionException("All methods of interface "
+                    + clazz.getFullyQualifiedName() + " must have return type 'void'!");
+        }
+        String methodSig = clazz.getFullyQualifiedName() + "." + method.getCallSignature();
+        JavaParameter[] params = method.getParameters();
+        if (params.length < 1) {
+            throw new EventConventionException("The method " + methodSig
+                    + " must have at least one parameter: 'Object source'!");
+        }
+        Type firstType = params[0].getType();
+        if (firstType.isPrimitive() || !"source".equals(params[0].getName())) {
+            throw new EventConventionException("The first parameter of the method " + methodSig
+                    + " must be: 'Object source'!");
+        }
+
+        //build method model
+        DocletTag tag = method.getTagByName("event.severity");
+        EventSeverity severity;
+        if (tag != null) {
+            severity = EventSeverity.valueOf(tag.getValue());
+        } else {
+            severity = EventSeverity.INFO;
+        }
+        EventMethodModel methodMeta = new EventMethodModel(
+                method.getName(), severity);
+        if (params.length > 1) {
+            for (int j = 1, cj = params.length; j < cj; j++) {
+                JavaParameter p = params[j];
+                Class<?> type;
+                JavaClass pClass = p.getType().getJavaClass();
+                if (p.getType().isPrimitive()) {
+                    type = PRIMITIVE_MAP.get(pClass.getName());
+                    if (type == null) {
+                        throw new UnsupportedOperationException(
+                                "Primitive datatype not supported: " + pClass.getName());
+                    }
+                } else {
+                    String className = pClass.getFullyQualifiedName();
+                    type = Class.forName(className);
+                }
+                methodMeta.addParameter(type, p.getName());
+            }
+        }
+        Type[] exceptions = method.getExceptions();
+        if (exceptions != null && exceptions.length > 0) {
+            //We only use the first declared exception because that is always thrown
+            JavaClass cl = exceptions[0].getJavaClass();
+            methodMeta.setExceptionClass(cl.getFullyQualifiedName());
+            methodMeta.setSeverity(EventSeverity.FATAL); //In case it's not set in the comments
+        }
+        return methodMeta;
+    }
+
+    /**
+     * Returns the event model that has been accumulated.
+     * @return the event model.
+     */
+    public List<EventModel> getModels() {
+        return this.models;
+    }
+
+}
diff --git a/fop-events/src/main/java/org/apache/fop/eventtools/EventProducerCollectorTask.java b/fop-events/src/main/java/org/apache/fop/eventtools/EventProducerCollectorTask.java
new file mode 100644 (file)
index 0000000..5dc6475
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * 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.eventtools;
+
+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 (EventModel model : collector.getModels()) {
+                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;
+        for (FileSet fs : filesets) {
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            String[] srcFiles = ds.getIncludedFiles();
+            File directory = fs.getDir(getProject());
+            for (String filename : srcFiles) {
+                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("out1.xml"));
+            generator.execute();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/fop-events/src/main/java/org/apache/fop/tools/EventConventionException.java b/fop-events/src/main/java/org/apache/fop/tools/EventConventionException.java
deleted file mode 100644 (file)
index 363850b..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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;
-
-/**
- * This exception is used to indicate a violation of the conventions for event producers.
- */
-public class EventConventionException extends Exception {
-
-    private static final long serialVersionUID = 117244726033986628L;
-
-    /**
-     * Creates a new EventConventionException
-     * @param message the error message
-     */
-    public EventConventionException(String message) {
-        super(message);
-    }
-
-}
diff --git a/fop-events/src/main/java/org/apache/fop/tools/EventProducerCollector.java b/fop-events/src/main/java/org/apache/fop/tools/EventProducerCollector.java
deleted file mode 100644 (file)
index 2351fc8..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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.IOException;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.fop.events.EventProducer;
-import org.apache.fop.events.model.EventMethodModel;
-import org.apache.fop.events.model.EventModel;
-import org.apache.fop.events.model.EventProducerModel;
-import org.apache.fop.events.model.EventSeverity;
-
-import com.thoughtworks.qdox.JavaDocBuilder;
-import com.thoughtworks.qdox.model.DefaultDocletTagFactory;
-import com.thoughtworks.qdox.model.DocletTag;
-import com.thoughtworks.qdox.model.DocletTagFactory;
-import com.thoughtworks.qdox.model.JavaClass;
-import com.thoughtworks.qdox.model.JavaMethod;
-import com.thoughtworks.qdox.model.JavaParameter;
-import com.thoughtworks.qdox.model.Type;
-
-/**
- * Finds EventProducer interfaces and builds the event model for them.
- */
-class EventProducerCollector {
-
-    private static final String CLASSNAME_EVENT_PRODUCER = EventProducer.class.getName();
-    private static final Map<String, Class<?>> PRIMITIVE_MAP;
-
-    static {
-        Map<String, Class<?>> m = new java.util.HashMap<String, Class<?>>();
-        m.put("boolean", Boolean.class);
-        m.put("byte", Byte.class);
-        m.put("char", Character.class);
-        m.put("short", Short.class);
-        m.put("int", Integer.class);
-        m.put("long", Long.class);
-        m.put("float", Float.class);
-        m.put("double", Double.class);
-        PRIMITIVE_MAP = Collections.unmodifiableMap(m);
-    }
-
-    private DocletTagFactory tagFactory;
-    private List<EventModel> models = new java.util.ArrayList<EventModel>();
-
-    /**
-     * Creates a new EventProducerCollector.
-     */
-    EventProducerCollector() {
-        this.tagFactory = createDocletTagFactory();
-    }
-
-    /**
-     * Creates the {@link DocletTagFactory} to be used by the collector.
-     * @return the doclet tag factory
-     */
-    protected DocletTagFactory createDocletTagFactory() {
-        return new DefaultDocletTagFactory();
-    }
-
-    /**
-     * Scans a file and processes it if it extends the {@link EventProducer} interface.
-     * @param src the source file (a Java source file)
-     * @return true if the file contained an EventProducer interface
-     * @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
-     */
-    public boolean scanFile(File src)
-            throws IOException, EventConventionException, ClassNotFoundException {
-        JavaDocBuilder builder = new JavaDocBuilder(this.tagFactory);
-        builder.addSource(src);
-        JavaClass[] classes = builder.getClasses();
-        boolean eventProducerFound = false;
-        for (JavaClass clazz : classes) {
-            if (clazz.isInterface() && implementsInterface(clazz, CLASSNAME_EVENT_PRODUCER)) {
-                processEventProducerInterface(clazz);
-                eventProducerFound = true;
-            }
-        }
-        return eventProducerFound;
-    }
-
-    private boolean implementsInterface(JavaClass clazz, String intf) {
-        JavaClass[] classes = clazz.getImplementedInterfaces();
-        for (JavaClass cl : classes) {
-            if (cl.getFullyQualifiedName().equals(intf)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Processes an EventProducer interface and creates an EventProducerModel from it.
-     * @param clazz the EventProducer interface
-     * @throws EventConventionException if the event producer conventions are violated
-     * @throws ClassNotFoundException if a required class cannot be found
-     */
-    protected void processEventProducerInterface(JavaClass clazz)
-                throws EventConventionException, ClassNotFoundException {
-        EventProducerModel prodMeta = new EventProducerModel(clazz.getFullyQualifiedName());
-        JavaMethod[] methods = clazz.getMethods(true);
-        for (JavaMethod method : methods) {
-            EventMethodModel methodMeta = createMethodModel(method);
-            prodMeta.addMethod(methodMeta);
-        }
-        EventModel model = new EventModel();
-        model.addProducer(prodMeta);
-        models.add(model);
-    }
-
-    private EventMethodModel createMethodModel(JavaMethod method)
-            throws EventConventionException, ClassNotFoundException {
-        JavaClass clazz = method.getParentClass();
-        //Check EventProducer conventions
-        if (!method.getReturnType().isVoid()) {
-            throw new EventConventionException("All methods of interface "
-                    + clazz.getFullyQualifiedName() + " must have return type 'void'!");
-        }
-        String methodSig = clazz.getFullyQualifiedName() + "." + method.getCallSignature();
-        JavaParameter[] params = method.getParameters();
-        if (params.length < 1) {
-            throw new EventConventionException("The method " + methodSig
-                    + " must have at least one parameter: 'Object source'!");
-        }
-        Type firstType = params[0].getType();
-        if (firstType.isPrimitive() || !"source".equals(params[0].getName())) {
-            throw new EventConventionException("The first parameter of the method " + methodSig
-                    + " must be: 'Object source'!");
-        }
-
-        //build method model
-        DocletTag tag = method.getTagByName("event.severity");
-        EventSeverity severity;
-        if (tag != null) {
-            severity = EventSeverity.valueOf(tag.getValue());
-        } else {
-            severity = EventSeverity.INFO;
-        }
-        EventMethodModel methodMeta = new EventMethodModel(
-                method.getName(), severity);
-        if (params.length > 1) {
-            for (int j = 1, cj = params.length; j < cj; j++) {
-                JavaParameter p = params[j];
-                Class<?> type;
-                JavaClass pClass = p.getType().getJavaClass();
-                if (p.getType().isPrimitive()) {
-                    type = PRIMITIVE_MAP.get(pClass.getName());
-                    if (type == null) {
-                        throw new UnsupportedOperationException(
-                                "Primitive datatype not supported: " + pClass.getName());
-                    }
-                } else {
-                    String className = pClass.getFullyQualifiedName();
-                    type = Class.forName(className);
-                }
-                methodMeta.addParameter(type, p.getName());
-            }
-        }
-        Type[] exceptions = method.getExceptions();
-        if (exceptions != null && exceptions.length > 0) {
-            //We only use the first declared exception because that is always thrown
-            JavaClass cl = exceptions[0].getJavaClass();
-            methodMeta.setExceptionClass(cl.getFullyQualifiedName());
-            methodMeta.setSeverity(EventSeverity.FATAL); //In case it's not set in the comments
-        }
-        return methodMeta;
-    }
-
-    /**
-     * Returns the event model that has been accumulated.
-     * @return the event model.
-     */
-    public List<EventModel> getModels() {
-        return this.models;
-    }
-
-}
diff --git a/fop-events/src/main/java/org/apache/fop/tools/EventProducerCollectorTask.java b/fop-events/src/main/java/org/apache/fop/tools/EventProducerCollectorTask.java
deleted file mode 100644 (file)
index 8796280..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * 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 (EventModel model : collector.getModels()) {
-                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;
-        for (FileSet fs : filesets) {
-            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
-            String[] srcFiles = ds.getIncludedFiles();
-            File directory = fs.getDir(getProject());
-            for (String filename : srcFiles) {
-                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("out1.xml"));
-            generator.execute();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
index 781128dd83998bb300779c559ee9b7a9860f868b..45a45af633f1399a591828137645fce7b01ba519 100644 (file)
@@ -325,7 +325,7 @@ list of possible build targets.
         <include name="**/tools/*.xsl"/>
       </fileset>
     </copy>
-    <taskdef name="eventResourceGenerator" classname="org.apache.fop.tools.EventProducerCollectorTask">
+    <taskdef name="eventResourceGenerator" classname="org.apache.fop.eventtools.EventProducerCollectorTask">
       <classpath>
         <path refid="libs-tools-build-classpath"/>
         <pathelement location="${build.classes.dir}"/>