</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"/>
</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"/>
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-
-}
+++ /dev/null
-/*
- * 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();
- }
- }
-}
<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}"/>