diff options
author | Glenn Adams <gadams@apache.org> | 2016-03-12 05:39:53 +0000 |
---|---|---|
committer | Glenn Adams <gadams@apache.org> | 2016-03-12 05:39:53 +0000 |
commit | 6a719897d6f98ba89aa08e2f97b2b801be066cbf (patch) | |
tree | bcce1bb7e9c6bde85e1167cff6218948f5be9f0a /fop-events | |
parent | 2184238c210288138b6703bd63ccb2c8c40300f0 (diff) | |
download | xmlgraphics-fop-6a719897d6f98ba89aa08e2f97b2b801be066cbf.tar.gz xmlgraphics-fop-6a719897d6f98ba89aa08e2f97b2b801be066cbf.zip |
Configure fop-{sandbox,servlet,transcoder}, checkstyle, findbugs; fixups for ant targets.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/maven@1734671 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'fop-events')
22 files changed, 2474 insertions, 0 deletions
diff --git a/fop-events/pom.xml b/fop-events/pom.xml new file mode 100644 index 000000000..548d0adf5 --- /dev/null +++ b/fop-events/pom.xml @@ -0,0 +1,147 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.apache.xmlgraphics</groupId> + <artifactId>fop-events</artifactId> + <name>Apache FOP Events</name> + <description>XML Graphics Format Object Processor Events</description> + + <parent> + <groupId>org.apache.xmlgraphics</groupId> + <artifactId>fop-parent</artifactId> + <version>2.2.0-SNAPSHOT</version> + </parent> + + <dependencies> + <!-- fop deps --> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>fop-util</artifactId> + <version>${project.version}</version> + </dependency> + <!-- external deps --> + <dependency> + <groupId>com.thoughtworks.qdox</groupId> + <artifactId>qdox</artifactId> + <version>1.12</version> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>${commons.io.version}</version> + </dependency> + <dependency> + <groupId>org.apache.ant</groupId> + <artifactId>ant</artifactId> + <version>1.8.2</version> + </dependency> + <dependency> + <groupId>org.apache.xmlgraphics</groupId> + <artifactId>xmlgraphics-commons</artifactId> + <version>${xmlgraphics.commons.version}</version> + </dependency> + <!-- test-only deps --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- generate event collector models --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <version>${antrun.plugin.version}</version> + <executions> + <execution> + <id>codegen-test-events</id> + <phase>process-test-classes</phase> + <goals> + <goal>run</goal> + </goals> + <configuration> + <target> + <taskdef name="collectEvents" classname="org.apache.fop.tools.EventProducerCollectorTask"> + <classpath> + <path refid="maven.compile.classpath"/> + <path refid="maven.test.classpath"/> + </classpath> + </taskdef> + <collectEvents destdir="${project.build.testOutputDirectory}"> + <fileset dir="${project.basedir}/src/test/java"> + <include name="**/events/TestEventProducer.java"/> + </fileset> + </collectEvents> + </target> + </configuration> + </execution> + </executions> + </plugin> + <!-- junit testing --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <includes> + <include>**/*TestCase.java</include> + </includes> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + </configuration> + </plugin> + <!-- code analysis - checkstyle --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + <version>${checkstyle.plugin.version}</version> + <configuration> + <configLocation>${project.baseUri}../fop-core/src/tools/resources/checkstyle/checkstyle.xml</configLocation> + <headerLocation>${project.baseUri}../fop-core/src/tools/resources/checkstyle/LICENSE.txt</headerLocation> + <includeResources>false</includeResources> + <includeTestResources>false</includeTestResources> + <linkXRef>false</linkXRef> + <logViolationsToConsole>true</logViolationsToConsole> + <suppressionsLocation>${project.baseUri}../fop-core/src/tools/resources/checkstyle/suppressions.xml</suppressionsLocation> + <violationSeverity>warning</violationSeverity> + </configuration> + </plugin> + <!-- code analysis - findbugs --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.plugin.version}</version> + <configuration> + <excludeFilterFile>../fop-core/src/tools/resources/findbugs/exclusions.xml</excludeFilterFile> + <effort>Max</effort> + <threshold>Low</threshold> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>src/main/resources</directory> + </resource> + <resource> + <directory>${basedir}/..</directory> + <includes> + <include>LICENSE</include> + <include>NOTICE</include> + </includes> + <targetPath>META-INF</targetPath> + </resource> + </resources> + <testResources> + <testResource> + <directory>src/test/resources</directory> + <includes> + <include>**/*</include> + </includes> + </testResource> + </testResources> + </build> + +</project> diff --git a/fop-events/src/main/java/org/apache/fop/events/CompositeEventListener.java b/fop-events/src/main/java/org/apache/fop/events/CompositeEventListener.java new file mode 100644 index 000000000..c2bef322e --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/CompositeEventListener.java @@ -0,0 +1,65 @@ +/* + * 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.events; + +import java.util.ArrayList; +import java.util.List; + +/** + * EventListener implementation forwards events to possibly multiple other EventListeners. + */ +public class CompositeEventListener implements EventListener { + + private List<EventListener> listeners = new ArrayList<EventListener>(); + + /** + * Adds an event listener to the broadcaster. It is appended to the list of previously + * registered listeners (the order of registration defines the calling order). + * @param listener the listener to be added + */ + public synchronized void addEventListener(EventListener listener) { + this.listeners.add(listener); + } + + /** + * Removes an event listener from the broadcaster. If the event listener is not registered, + * nothing happens. + * @param listener the listener to be removed + */ + public synchronized void removeEventListener(EventListener listener) { + this.listeners.remove(listener); + } + + /** + * Indicates whether any listeners have been registered with the broadcaster. + * @return true if listeners are present, false otherwise + */ + public synchronized boolean hasEventListeners() { + return !listeners.isEmpty(); + } + + /** {@inheritDoc } */ + public synchronized void processEvent(Event event) { + for (EventListener listener : listeners) { + listener.processEvent(event); + } + } + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/DefaultEventBroadcaster.java b/fop-events/src/main/java/org/apache/fop/events/DefaultEventBroadcaster.java new file mode 100644 index 000000000..596a0a86b --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/DefaultEventBroadcaster.java @@ -0,0 +1,183 @@ +/* + * 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.events; + +import java.io.InputStream; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.MissingResourceException; + +import javax.xml.transform.TransformerException; +import javax.xml.transform.stream.StreamSource; + +import org.apache.commons.io.IOUtils; + +import org.apache.fop.events.model.EventMethodModel; +import org.apache.fop.events.model.EventModel; +import org.apache.fop.events.model.EventModelParser; +import org.apache.fop.events.model.EventProducerModel; +import org.apache.fop.events.model.EventSeverity; + +/** + * Default implementation of the EventBroadcaster interface. It holds a list of event listeners + * and can provide {@link EventProducer} instances for type-safe event production. + */ +public class DefaultEventBroadcaster implements EventBroadcaster { + + /** Holds all registered event listeners */ + protected CompositeEventListener listeners = new CompositeEventListener(); + + /** {@inheritDoc} */ + public void addEventListener(EventListener listener) { + this.listeners.addEventListener(listener); + } + + /** {@inheritDoc} */ + public void removeEventListener(EventListener listener) { + this.listeners.removeEventListener(listener); + } + + /** {@inheritDoc} */ + public boolean hasEventListeners() { + return this.listeners.hasEventListeners(); + } + + /** {@inheritDoc} */ + public void broadcastEvent(Event event) { + this.listeners.processEvent(event); + } + + private static List/*<EventModel>*/ eventModels = new java.util.ArrayList(); + private Map proxies = new java.util.HashMap(); + + /** + * Loads an event model and returns its instance. + * @param resourceBaseClass base class to use for loading resources + * @return the newly loaded event model. + */ + private static EventModel loadModel(Class resourceBaseClass) { + String resourceName = "event-model.xml"; + InputStream in = resourceBaseClass.getResourceAsStream(resourceName); + if (in == null) { + throw new MissingResourceException( + "File " + resourceName + " not found", + DefaultEventBroadcaster.class.getName(), ""); + } + try { + return EventModelParser.parse(new StreamSource(in)); + } catch (TransformerException e) { + throw new MissingResourceException( + "Error reading " + resourceName + ": " + e.getMessage(), + DefaultEventBroadcaster.class.getName(), ""); + } finally { + IOUtils.closeQuietly(in); + } + } + + /** + * Adds a new {@link EventModel} to the list of registered event models. + * @param eventModel the event model instance + */ + public static synchronized void addEventModel(EventModel eventModel) { + eventModels.add(eventModel); + } + + private static synchronized EventProducerModel getEventProducerModel(Class clazz) { + for (int i = 0, c = eventModels.size(); i < c; i++) { + EventModel eventModel = (EventModel)eventModels.get(i); + EventProducerModel producerModel = eventModel.getProducer(clazz); + if (producerModel != null) { + return producerModel; + } + } + EventModel model = loadModel(clazz); + addEventModel(model); + return model.getProducer(clazz); + } + + /** {@inheritDoc} */ + public EventProducer getEventProducerFor(Class clazz) { + if (!EventProducer.class.isAssignableFrom(clazz)) { + throw new IllegalArgumentException( + "Class must be an implementation of the EventProducer interface: " + + clazz.getName()); + } + EventProducer producer; + producer = (EventProducer)this.proxies.get(clazz); + if (producer == null) { + producer = createProxyFor(clazz); + this.proxies.put(clazz, producer); + } + return producer; + } + + /** + * Creates a dynamic proxy for the given EventProducer interface that will handle the + * conversion of the method call into the broadcasting of an event instance. + * @param clazz a descendant interface of EventProducer + * @return the EventProducer instance + */ + protected EventProducer createProxyFor(Class clazz) { + final EventProducerModel producerModel = getEventProducerModel(clazz); + if (producerModel == null) { + throw new IllegalStateException("Event model doesn't contain the definition for " + + clazz.getName()); + } + return (EventProducer)Proxy.newProxyInstance(clazz.getClassLoader(), + new Class[] {clazz}, + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + String methodName = method.getName(); + EventMethodModel methodModel = producerModel.getMethod(methodName); + String eventID = producerModel.getInterfaceName() + "." + methodName; + if (methodModel == null) { + throw new IllegalStateException( + "Event model isn't consistent" + + " with the EventProducer interface. Please rebuild FOP!" + + " Affected method: " + + eventID); + } + Map params = new java.util.HashMap(); + int i = 1; + Iterator iter = methodModel.getParameters().iterator(); + while (iter.hasNext()) { + EventMethodModel.Parameter param + = (EventMethodModel.Parameter)iter.next(); + params.put(param.getName(), args[i]); + i++; + } + Event ev = new Event(args[0], eventID, methodModel.getSeverity(), params); + broadcastEvent(ev); + + if (ev.getSeverity() == EventSeverity.FATAL) { + EventExceptionManager.throwException(ev, + methodModel.getExceptionClass()); + } + return null; + } + }); + } + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/Event.java b/fop-events/src/main/java/org/apache/fop/events/Event.java new file mode 100644 index 000000000..7c5759e6b --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/Event.java @@ -0,0 +1,190 @@ +/* + * 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.events; + +import java.util.Collections; +import java.util.EventObject; +import java.util.Locale; +import java.util.Map; + +import org.apache.fop.events.model.EventSeverity; + +/** + * This is the default event class used by this package. Each event has a unique event identifier + * (a String), a severity indicator, a locale (for formatting event messages), and a map of + * name/value pairs. + */ +public class Event extends EventObject { + + private static final long serialVersionUID = -1310594422868258083L; + + private String eventGroupID; + private String eventKey; + private EventSeverity severity; + private Locale locale; + private Map<String, Object> params; + + /** + * Creates a new Event using default locale. + * @param source the object that creates the event + * @param eventID the unique identifier of the event + * @param severity the severity level + * @param params the event parameters (a map of name/value pairs) + */ + public Event(Object source, String eventID, EventSeverity severity, Map<String, Object> params) + { + this (source, eventID, severity, Locale.getDefault(), params); + } + + /** + * Creates a new Event. + * @param source the object that creates the event + * @param eventID the unique identifier of the event + * @param severity the severity level + * @param locale to use when formatting event (or null, which means use default locale) + * @param params the event parameters (a map of name/value pairs) + */ + public Event(Object source, String eventID, EventSeverity severity, Locale locale, Map<String, Object> params) + { + super(source); + int pos = eventID.lastIndexOf('.'); + if (pos < 0 || pos == eventID.length() - 1) { + eventKey = eventID; + } else { + eventGroupID = eventID.substring(0, pos); + eventKey = eventID.substring(pos + 1); + } + setSeverity(severity); + this.locale = locale; + this.params = params; + } + + /** + * Returns the event identifier. + * @return the event identifier + */ + public String getEventID() { + if (eventGroupID == null) { + return eventKey; + } else { + return eventGroupID + '.' + eventKey; + } + } + + /** + * Returns the event group identifier. + * @return the event group identifier (or null if there is no group identifier) + */ + public String getEventGroupID() { + return eventGroupID; + } + + /** + * Returns the event key. + * @return the event key + */ + public String getEventKey() { + return eventKey; + } + + /** + * Returns the severity level. + * @return the severity level + */ + public EventSeverity getSeverity() { + return this.severity; + } + + /** + * Sets the event's severity level. This method can be used to increase or decrease the + * severity level in a listener. + * @param severity the new event severity + */ + public void setSeverity(EventSeverity severity) { + this.severity = severity; + } + + /** + * Returns the locale. + * @return the locale + */ + public Locale getLocale() { + return this.locale; + } + + /** + * Returns a parameter. + * @param key the key to the parameter + * @return the parameter value or null if no value with this key is found + */ + public Object getParam(String key) { + if (this.params != null) { + return this.params.get(key); + } else { + return null; + } + } + + /** + * Returns an unmodifiable {@link java.util.Map} with all event parameters. + * @return the parameter map + */ + public Map<String, Object> getParams() { + return Collections.unmodifiableMap(this.params); + } + + /** + * Creates and returns a fluent builder object for building up the parameter map. + * @return the parameter builder + */ + public static ParamsBuilder paramsBuilder() { + return new ParamsBuilder(); + } + + /** + * This class is a fluent builder class for building up the parameter map. + */ + public static class ParamsBuilder { + private Map<String, Object> params; + + /** + * Adds a new parameter (a name/value pair). + * @param name the name of the parameter + * @param value the value of the parameter + * @return this instance + */ + public ParamsBuilder param(String name, Object value) { + if (this.params == null) { + this.params = new java.util.HashMap<String, Object>(); + } + this.params.put(name, value); + return this; + } + + /** + * Returns the accumulated parameter map. + * @return the accumulated parameter map + */ + public Map<String, Object> build() { + return this.params; + } + } + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/EventBroadcaster.java b/fop-events/src/main/java/org/apache/fop/events/EventBroadcaster.java new file mode 100644 index 000000000..57f30c9cf --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/EventBroadcaster.java @@ -0,0 +1,61 @@ +/* + * 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.events; + +/** + * The EventBroadcaster is the central relay point for events. It receives events from various + * parts of the application and forwards them to any registered EventListener. + */ +public interface EventBroadcaster { + + /** + * Adds an event listener to the broadcaster. It is appended to the list of previously + * registered listeners (the order of registration defines the calling order). + * @param listener the listener to be added + */ + void addEventListener(EventListener listener); + + /** + * Removes an event listener from the broadcaster. If the event listener is not registered, + * nothing happens. + * @param listener the listener to be removed + */ + void removeEventListener(EventListener listener); + + /** + * Indicates whether any listeners have been registered with the broadcaster. + * @return true if listeners are present, false otherwise + */ + boolean hasEventListeners(); + + /** + * Broadcasts an event. This method is usually called from within the observed component. + * @param event the event to be broadcast + */ + void broadcastEvent(Event event); + + /** + * Returns an event producer instance for the given interface class. + * @param clazz the Class object identifying an {@link EventProducer} interface + * @return the event producer instance + */ + <T extends EventProducer> T getEventProducerFor(Class<T> clazz); + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/EventExceptionManager.java b/fop-events/src/main/java/org/apache/fop/events/EventExceptionManager.java new file mode 100644 index 000000000..e9208fbc6 --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/EventExceptionManager.java @@ -0,0 +1,102 @@ +/* + * 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.events; + +import java.util.Iterator; +import java.util.Map; + +import org.apache.xmlgraphics.util.Service; + +/** + * This class is responsible for converting events into exceptions. + */ +public final class EventExceptionManager { + + private EventExceptionManager() { + } + + private static final Map<String, ExceptionFactory> EXCEPTION_FACTORIES + = new java.util.HashMap<String, ExceptionFactory>(); + + static { + Iterator<Object> iter; + iter = Service.providers(ExceptionFactory.class); + while (iter.hasNext()) { + ExceptionFactory factory = (ExceptionFactory)iter.next(); + EXCEPTION_FACTORIES.put(factory.getExceptionClass().getName(), factory); + } + } + + /** + * Converts an event into an exception and throws that. If the exception class is null, + * a {@link RuntimeException} will be thrown. + * @param event the event to be converted + * @param exceptionClass the exception class to be thrown + * @throws Throwable this happens always + */ + public static void throwException(Event event, String exceptionClass) throws Throwable { + if (exceptionClass != null) { + ExceptionFactory factory = EXCEPTION_FACTORIES.get(exceptionClass); + if (factory != null) { + throw factory.createException(event); + } else { + throw new IllegalArgumentException( + "No such ExceptionFactory available: " + exceptionClass); + } + } else { + String msg = EventFormatter.format(event); + //Get original exception as cause if it is given as one of the parameters + Throwable t = null; + Iterator<Object> iter = event.getParams().values().iterator(); + while (iter.hasNext()) { + Object o = iter.next(); + if (o instanceof Throwable) { + t = (Throwable)o; + break; + } + } + if (t != null) { + throw new RuntimeException(msg, t); + } else { + throw new RuntimeException(msg); + } + } + } + + /** + * This interface is implementation by exception factories that can create exceptions from + * events. + */ + public interface ExceptionFactory { + + /** + * Creates an exception from an event. + * @param event the event + * @return the newly created exception + */ + Throwable createException(Event event); + + /** + * Returns the {@link Exception} class created by this factory. + * @return the exception class + */ + Class<? extends Exception> getExceptionClass(); + } +} diff --git a/fop-events/src/main/java/org/apache/fop/events/EventFormatter.java b/fop-events/src/main/java/org/apache/fop/events/EventFormatter.java new file mode 100644 index 000000000..7f56c8396 --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/EventFormatter.java @@ -0,0 +1,189 @@ +/* + * 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.events; + +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.util.XMLResourceBundle; +import org.apache.fop.util.text.AdvancedMessageFormat; +import org.apache.fop.util.text.AdvancedMessageFormat.Part; +import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory; + +/** + * Converts events into human-readable, localized messages. + */ +public final class EventFormatter { + + private static final Pattern INCLUDES_PATTERN = Pattern.compile("\\{\\{.+\\}\\}"); + + private static Log log = LogFactory.getLog(EventFormatter.class); + + private EventFormatter() { + //utility class + } + + private static ResourceBundle getBundle(String groupID, Locale locale) { + ResourceBundle bundle; + String baseName = (groupID != null) ? groupID : EventFormatter.class.getName(); + try { + ClassLoader classLoader = EventFormatter.class.getClassLoader(); + bundle = XMLResourceBundle.getXMLBundle(baseName, locale, classLoader); + } catch (MissingResourceException e) { + if (log.isTraceEnabled()) { + log.trace("No XMLResourceBundle for " + baseName + " available."); + } + bundle = null; + } + return bundle; + } + + /** + * Formats an event using the default locale. + * @param event the event + * @return the formatted message + */ + public static String format(Event event) { + return format(event, event.getLocale()); + } + + /** + * Formats an event using a given locale. + * @param event the event + * @param locale the locale + * @return the formatted message + */ + public static String format(Event event, Locale locale) { + return format(event, getBundle(event.getEventGroupID(), locale)); + } + + private static String format(Event event, ResourceBundle bundle) { + assert event != null; + String key = event.getEventKey(); + String template; + if (bundle != null) { + template = bundle.getString(key); + } else { + template = "Missing bundle. Can't lookup event key: '" + key + "'."; + } + return format(event, processIncludes(template, bundle)); + } + + private static String processIncludes(String template, ResourceBundle bundle) { + CharSequence input = template; + int replacements; + StringBuffer sb; + do { + sb = new StringBuffer(Math.max(16, input.length())); + replacements = processIncludesInner(input, sb, bundle); + input = sb; + } while (replacements > 0); + String s = sb.toString(); + return s; + } + + private static int processIncludesInner(CharSequence template, StringBuffer sb, + ResourceBundle bundle) { + int replacements = 0; + if (bundle != null) { + Matcher m = INCLUDES_PATTERN.matcher(template); + while (m.find()) { + String include = m.group(); + include = include.substring(2, include.length() - 2); + m.appendReplacement(sb, bundle.getString(include)); + replacements++; + } + m.appendTail(sb); + } + return replacements; + } + + /** + * Formats the event using a given pattern. The pattern needs to be compatible with + * {@link AdvancedMessageFormat}. + * @param event the event + * @param pattern the pattern (compatible with {@link AdvancedMessageFormat}) + * @return the formatted message + */ + public static String format(Event event, String pattern) { + AdvancedMessageFormat format = new AdvancedMessageFormat(pattern); + Map params = new java.util.HashMap(event.getParams()); + params.put("source", event.getSource()); + params.put("severity", event.getSeverity()); + params.put("groupID", event.getEventGroupID()); + params.put("locale", event.getLocale()); + return format.format(params); + } + + private static class LookupFieldPart implements Part { + + private String fieldName; + + public LookupFieldPart(String fieldName) { + this.fieldName = fieldName; + } + + public boolean isGenerated(Map params) { + return getKey(params) != null; + } + + public void write(StringBuffer sb, Map params) { + String groupID = (String) params.get("groupID"); + Locale locale = (Locale) params.get("locale"); + ResourceBundle bundle = getBundle(groupID, locale); + if (bundle != null) { + sb.append(bundle.getString(getKey(params))); + } + } + + private String getKey(Map params) { + return (String)params.get(fieldName); + } + + /** {@inheritDoc} */ + public String toString() { + return "{" + this.fieldName + ", lookup}"; + } + + } + + /** PartFactory for lookups. */ + public static class LookupFieldPartFactory implements PartFactory { + + /** {@inheritDoc} */ + public Part newPart(String fieldName, String values) { + return new LookupFieldPart(fieldName); + } + + /** {@inheritDoc} */ + public String getFormat() { + return "lookup"; + } + + } + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/EventListener.java b/fop-events/src/main/java/org/apache/fop/events/EventListener.java new file mode 100644 index 000000000..fa1bb5beb --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/EventListener.java @@ -0,0 +1,38 @@ +/* + * 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.events; + +/** + * This interface is implemented by clients who want to listen for events. + */ +// @SuppressFBWarnings("NM_SAME_SIMPLE_NAME_AS_INTERFACE") +public interface EventListener extends java.util.EventListener { + + /** + * This method is called for each event that is generated. With the event's ID it is possible + * to react to certain events. Events can also simply be recorded and presented to a user. + * It is possible to throw an (unchecked) exception if the processing needs to be aborted + * because some special event occurred. This way the client can configure the behaviour of + * the observed application. + * @param event the event + */ + void processEvent(Event event); + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/EventProducer.java b/fop-events/src/main/java/org/apache/fop/events/EventProducer.java new file mode 100644 index 000000000..88da771a4 --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/EventProducer.java @@ -0,0 +1,31 @@ +/* + * 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.events; + +/** + * This is a marker interface which all event producer interfaces need to extend. These interfaces + * must agree to the following convention: + * <ul> + * <li>The first parameter of each method must be: <code>Object source</code> + * </ul> + */ +public interface EventProducer { + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/model/EventMethodModel.java b/fop-events/src/main/java/org/apache/fop/events/model/EventMethodModel.java new file mode 100644 index 000000000..b9c62f8ab --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/model/EventMethodModel.java @@ -0,0 +1,198 @@ +/* + * 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.events.model; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.xmlgraphics.util.XMLizable; + +/** + * Represents an event method. Each method in an event producer interface will result in one + * instance of <code>EventMethodModel</code>. + */ +public class EventMethodModel implements Serializable, XMLizable { + + private static final long serialVersionUID = -7548882973341444354L; + + private String methodName; + private EventSeverity severity; + private List params = new java.util.ArrayList(); + private String exceptionClass; + + /** + * Creates an new instance. + * @param methodName the event method's name + * @param severity the event severity + */ + public EventMethodModel(String methodName, EventSeverity severity) { + this.methodName = methodName; + this.severity = severity; + } + + /** + * Adds a method parameter. + * @param param the method parameter + */ + public void addParameter(Parameter param) { + this.params.add(param); + } + + /** + * Adds a method parameter. + * @param type the type of the parameter + * @param name the name of the parameter + * @return the resulting Parameter instance + */ + public Parameter addParameter(Class type, String name) { + Parameter param = new Parameter(type, name); + addParameter(param); + return param; + } + + /** + * Sets the event method name. + * @param name the event name + */ + public void setMethodName(String name) { + this.methodName = name; + } + + /** + * Returns the event method name + * @return the event name + */ + public String getMethodName() { + return this.methodName; + } + + /** + * Sets the event's severity level. + * @param severity the severity + */ + public void setSeverity(EventSeverity severity) { + this.severity = severity; + } + + /** + * Returns the event's severity level. + * @return the severity + */ + public EventSeverity getSeverity() { + return this.severity; + } + + /** + * Returns an unmodifiable list of parameters for this event method. + * @return the list of parameters + */ + public List getParameters() { + return Collections.unmodifiableList(this.params); + } + + /** + * Sets the primary exception class for this event method. Note: Not all event methods throw + * exceptions! + * @param exceptionClass the exception class + */ + public void setExceptionClass(String exceptionClass) { + this.exceptionClass = exceptionClass; + } + + /** + * Returns the primary exception class for this event method. This method returns null if + * the event is only informational or just a warning. + * @return the primary exception class or null + */ + public String getExceptionClass() { + return this.exceptionClass; + } + + /** {@inheritDoc} */ + public void toSAX(ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute("", "name", "name", "CDATA", getMethodName()); + atts.addAttribute("", "severity", "severity", "CDATA", getSeverity().getName()); + if (getExceptionClass() != null) { + atts.addAttribute("", "exception", "exception", "CDATA", getExceptionClass()); + } + String elName = "method"; + handler.startElement("", elName, elName, atts); + Iterator iter = this.params.iterator(); + while (iter.hasNext()) { + ((XMLizable)iter.next()).toSAX(handler); + } + handler.endElement("", elName, elName); + } + + /** + * Represents an event parameter. + */ + public static class Parameter implements Serializable, XMLizable { + + private static final long serialVersionUID = 6062500277953887099L; + + private Class type; + private String name; + + /** + * Creates a new event parameter. + * @param type the parameter type + * @param name the parameter name + */ + public Parameter(Class type, String name) { + this.type = type; + this.name = name; + } + + /** + * Returns the parameter type. + * @return the parameter type + */ + public Class getType() { + return this.type; + } + + /** + * Returns the parameter name. + * @return the parameter name + */ + public String getName() { + return this.name; + } + + /** {@inheritDoc} */ + public void toSAX(ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute("", "type", "type", "CDATA", getType().getName()); + atts.addAttribute("", "name", "name", "CDATA", getName()); + String elName = "parameter"; + handler.startElement("", elName, elName, atts); + handler.endElement("", elName, elName); + } + + } +} diff --git a/fop-events/src/main/java/org/apache/fop/events/model/EventModel.java b/fop-events/src/main/java/org/apache/fop/events/model/EventModel.java new file mode 100644 index 000000000..9c11b79a3 --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/model/EventModel.java @@ -0,0 +1,146 @@ +/* + * 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.events.model; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.Iterator; +import java.util.Map; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; + +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.commons.io.IOUtils; + +import org.apache.xmlgraphics.util.XMLizable; + +/** + * Represents a whole event model that supports multiple event producers. + */ +public class EventModel implements Serializable, XMLizable { + + private static final long serialVersionUID = 7468592614934605082L; + + private Map producers = new java.util.LinkedHashMap(); + + /** + * Creates a new, empty event model + */ + public EventModel() { + } + + /** + * Adds the model of an event producer to the event model. + * @param producer the event producer model + */ + public void addProducer(EventProducerModel producer) { + this.producers.put(producer.getInterfaceName(), producer); + } + + /** + * Returns an iterator over the contained event producer models. + * @return an iterator (Iterator<EventProducerModel>) + */ + public Iterator getProducers() { + return this.producers.values().iterator(); + } + + /** + * Returns the model of an event producer with the given interface name. + * @param interfaceName the fully qualified name of the event producer + * @return the model instance for the event producer (or null if it wasn't found) + */ + public EventProducerModel getProducer(String interfaceName) { + return (EventProducerModel)this.producers.get(interfaceName); + } + + /** + * Returns the model of an event producer with the given interface. + * @param clazz the interface of the event producer + * @return the model instance for the event producer (or null if it wasn't found) + */ + public EventProducerModel getProducer(Class clazz) { + return getProducer(clazz.getName()); + } + + /** {@inheritDoc} */ + public void toSAX(ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + String elName = "event-model"; + handler.startElement("", elName, elName, atts); + Iterator iter = getProducers(); + while (iter.hasNext()) { + ((XMLizable)iter.next()).toSAX(handler); + } + handler.endElement("", elName, elName); + } + + private void writeXMLizable(XMLizable object, File outputFile) throws IOException { + //These two approaches do not seem to work in all environments: + //Result res = new StreamResult(outputFile); + //Result res = new StreamResult(outputFile.toURI().toURL().toExternalForm()); + //With an old Xalan version: file:/C:/.... --> file:\C:\..... + OutputStream out = new java.io.FileOutputStream(outputFile); + out = new java.io.BufferedOutputStream(out); + Result res = new StreamResult(out); + + try { + SAXTransformerFactory tFactory + = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + TransformerHandler handler = tFactory.newTransformerHandler(); + Transformer transformer = handler.getTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + handler.setResult(res); + handler.startDocument(); + object.toSAX(handler); + handler.endDocument(); + } catch (TransformerConfigurationException e) { + throw new IOException(e.getMessage()); + } catch (TransformerFactoryConfigurationError e) { + throw new IOException(e.getMessage()); + } catch (SAXException e) { + throw new IOException(e.getMessage()); + } finally { + IOUtils.closeQuietly(out); + } + } + + /** + * Saves this event model to an XML file. + * @param modelFile the target file + * @throws IOException if an I/O error occurs + */ + public void saveToXML(File modelFile) throws IOException { + writeXMLizable(this, modelFile); + } + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/model/EventModelParser.java b/fop-events/src/main/java/org/apache/fop/events/model/EventModelParser.java new file mode 100644 index 000000000..5441d1c4f --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/model/EventModelParser.java @@ -0,0 +1,143 @@ +/* + * 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.events.model; + +import java.util.Stack; + +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXTransformerFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.util.DefaultErrorListener; + +/** + * This is a parser for the event model XML. + */ +public final class EventModelParser { + + private EventModelParser() { + } + + /** Logger instance */ + private static final Log LOG = LogFactory.getLog(EventModelParser.class); + + private static SAXTransformerFactory tFactory + = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + + /** + * Parses an event model file into an EventModel instance. + * @param src the Source instance pointing to the XML file + * @return the created event model structure + * @throws TransformerException if an error occurs while parsing the XML file + */ + public static EventModel parse(Source src) + throws TransformerException { + Transformer transformer = tFactory.newTransformer(); + transformer.setErrorListener(new DefaultErrorListener(LOG)); + + EventModel model = new EventModel(); + SAXResult res = new SAXResult(getContentHandler(model)); + + transformer.transform(src, res); + return model; + } + + /** + * Creates a new ContentHandler instance that you can send the event model XML to. The parsed + * content is accumulated in the model structure. + * @param model the EventModel + * @return the ContentHandler instance to receive the SAX stream from the XML file + */ + public static ContentHandler getContentHandler(EventModel model) { + return new Handler(model); + } + + private static class Handler extends DefaultHandler { + + private EventModel model; + private Stack objectStack = new Stack(); + + public Handler(EventModel model) { + this.model = model; + } + + /** {@inheritDoc} */ + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + try { + if ("event-model".equals(localName)) { + if (objectStack.size() > 0) { + throw new SAXException("event-model must be the root element"); + } + objectStack.push(model); + } else if ("producer".equals(localName)) { + EventProducerModel producer = new EventProducerModel( + attributes.getValue("name")); + EventModel parent = (EventModel)objectStack.peek(); + parent.addProducer(producer); + objectStack.push(producer); + } else if ("method".equals(localName)) { + EventSeverity severity = EventSeverity.valueOf(attributes.getValue("severity")); + String ex = attributes.getValue("exception"); + EventMethodModel method = new EventMethodModel( + attributes.getValue("name"), severity); + if (ex != null && ex.length() > 0) { + method.setExceptionClass(ex); + } + EventProducerModel parent = (EventProducerModel)objectStack.peek(); + parent.addMethod(method); + objectStack.push(method); + } else if ("parameter".equals(localName)) { + String className = attributes.getValue("type"); + Class type; + try { + type = Class.forName(className); + } catch (ClassNotFoundException e) { + throw new SAXException("Could not find Class for: " + className, e); + } + String name = attributes.getValue("name"); + EventMethodModel parent = (EventMethodModel)objectStack.peek(); + objectStack.push(parent.addParameter(type, name)); + } else { + throw new SAXException("Invalid element: " + qName); + } + } catch (ClassCastException cce) { + throw new SAXException("XML format error: " + qName, cce); + } + } + + /** {@inheritDoc} */ + public void endElement(String uri, String localName, String qName) throws SAXException { + objectStack.pop(); + } + + } + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/model/EventProducerModel.java b/fop-events/src/main/java/org/apache/fop/events/model/EventProducerModel.java new file mode 100644 index 000000000..b0d334e99 --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/model/EventProducerModel.java @@ -0,0 +1,105 @@ +/* + * 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.events.model; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Map; + +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.xmlgraphics.util.XMLizable; + +/** + * Represents the model of an event producer with multiple event methods. + */ +public class EventProducerModel implements Serializable, XMLizable { + + private static final long serialVersionUID = 122267104123721902L; + + private String interfaceName; + private Map methods = new java.util.LinkedHashMap(); + + /** + * Creates a new instance. + * @param interfaceName the fully qualified interface name of the event producer + */ + public EventProducerModel(String interfaceName) { + this.interfaceName = interfaceName; + } + + /** + * Returns the fully qualified interface name of the event producer. + * @return the fully qualified interface name + */ + public String getInterfaceName() { + return this.interfaceName; + } + + /** + * Sets the fully qualified interface name of the event producer. + * @param name the fully qualified interface name + */ + public void setInterfaceName(String name) { + this.interfaceName = name; + } + + /** + * Adds a model instance of an event method. + * @param method the event method model + */ + public void addMethod(EventMethodModel method) { + this.methods.put(method.getMethodName(), method); + } + + /** + * Returns the model instance of an event method for the given method name. + * @param methodName the method name + * @return the model instance (or null if no method with the given name exists) + */ + public EventMethodModel getMethod(String methodName) { + return (EventMethodModel)this.methods.get(methodName); + } + + /** + * Returns an iterator over the contained event producer methods. + * @return an iterator (Iterator<EventMethodModel>) + */ + public Iterator getMethods() { + return this.methods.values().iterator(); + } + + /** {@inheritDoc} */ + public void toSAX(ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute("", "name", "name", "CDATA", getInterfaceName()); + String elName = "producer"; + handler.startElement("", elName, elName, atts); + Iterator iter = getMethods(); + while (iter.hasNext()) { + ((XMLizable)iter.next()).toSAX(handler); + } + handler.endElement("", elName, elName); + } + + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/model/EventSeverity.java b/fop-events/src/main/java/org/apache/fop/events/model/EventSeverity.java new file mode 100644 index 000000000..c0d886154 --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/model/EventSeverity.java @@ -0,0 +1,82 @@ +/* + * 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.events.model; + +import java.io.ObjectStreamException; +import java.io.Serializable; + +/** Enumeration class for event severities. */ +public final class EventSeverity implements Serializable { + + private static final long serialVersionUID = 4108175215810759243L; + + /** info level */ + public static final EventSeverity INFO = new EventSeverity("INFO"); + /** warning level */ + public static final EventSeverity WARN = new EventSeverity("WARN"); + /** error level */ + public static final EventSeverity ERROR = new EventSeverity("ERROR"); + /** fatal error */ + public static final EventSeverity FATAL = new EventSeverity("FATAL"); + + private String name; + + /** + * Constructor to add a new named item. + * @param name Name of the item. + */ + private EventSeverity(String name) { + this.name = name; + } + + /** @return the name of the enumeration */ + public String getName() { + return this.name; + } + + /** + * Returns the enumeration/singleton object based on its name. + * @param name the name of the enumeration value + * @return the enumeration object + */ + public static EventSeverity valueOf(String name) { + if (INFO.getName().equalsIgnoreCase(name)) { + return INFO; + } else if (WARN.getName().equalsIgnoreCase(name)) { + return WARN; + } else if (ERROR.getName().equalsIgnoreCase(name)) { + return ERROR; + } else if (FATAL.getName().equalsIgnoreCase(name)) { + return FATAL; + } else { + throw new IllegalArgumentException("Illegal value for enumeration: " + name); + } + } + + private Object readResolve() throws ObjectStreamException { + return valueOf(getName()); + } + + /** {@inheritDoc} */ + public String toString() { + return "EventSeverity:" + name; + } + +} diff --git a/fop-events/src/main/java/org/apache/fop/events/model/package.html b/fop-events/src/main/java/org/apache/fop/events/model/package.html new file mode 100644 index 000000000..4d915721b --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/events/model/package.html @@ -0,0 +1,23 @@ +<!-- + 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$ --> +<HTML> +<TITLE>org.apache.fop.events.model Package</TITLE> +<BODY> +<P>Model classes for the event subsystem.</P> +</BODY> +</HTML>
\ No newline at end of file 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 new file mode 100644 index 000000000..363850b95 --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/tools/EventConventionException.java @@ -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.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 new file mode 100644 index 000000000..7151f5a9c --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/tools/EventProducerCollector.java @@ -0,0 +1,203 @@ +/* + * 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 (int i = 0, c = classes.length; i < c; i++) { + JavaClass clazz = classes[i]; + 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 (int i = 0, c = classes.length; i < c; i++) { + JavaClass cl = classes[i]; + 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 (int i = 0, c = methods.length; i < c; i++) { + JavaMethod method = methods[i]; + 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 new file mode 100644 index 000000000..85c10f09a --- /dev/null +++ b/fop-events/src/main/java/org/apache/fop/tools/EventProducerCollectorTask.java @@ -0,0 +1,287 @@ +/* + * 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("out1.xml")); + generator.execute(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/fop-events/src/main/resources/org/apache/fop/tools/merge-translation.xsl b/fop-events/src/main/resources/org/apache/fop/tools/merge-translation.xsl new file mode 100644 index 000000000..d15d22fd3 --- /dev/null +++ b/fop-events/src/main/resources/org/apache/fop/tools/merge-translation.xsl @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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$ --> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> + <xsl:output indent="yes" method="xml" encoding="UTF-8"/> + + <xsl:param name="generated-url" select="''"/> + + <xsl:template match="catalogue"> + <catalogue> + <xsl:attribute name="xml:lang"><xsl:value-of select="@xml:lang"/></xsl:attribute> + <xsl:apply-templates/> + <xsl:if test="$generated-url != ''"> + <xsl:variable name="generated" select="document($generated-url)"/> + <xsl:call-template name="add-new-messages"> + <xsl:with-param name="existing" select="."/> + <xsl:with-param name="new" select="$generated/catalogue"/> + </xsl:call-template> + </xsl:if> + </catalogue> + </xsl:template> + + <xsl:template name="add-new-messages"> + <xsl:param name="existing"/> + <xsl:param name="new"/> + <xsl:for-each select="$new/message"> + <xsl:variable name="k" select="@key"/> + <xsl:if test="not(boolean($existing/message[@key = $k]))"> + <xsl:apply-templates select="."></xsl:apply-templates> + </xsl:if> + </xsl:for-each> + </xsl:template> + + <xsl:template match="@*|node()"> + <xsl:copy> + <xsl:apply-templates select="@*|node()"/> + </xsl:copy> + </xsl:template> + +</xsl:stylesheet> diff --git a/fop-events/src/main/resources/org/apache/fop/tools/model2translation.xsl b/fop-events/src/main/resources/org/apache/fop/tools/model2translation.xsl new file mode 100644 index 000000000..a1cf404ee --- /dev/null +++ b/fop-events/src/main/resources/org/apache/fop/tools/model2translation.xsl @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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$ --> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> + <xsl:output indent="yes" method="xml" encoding="UTF-8"/> + + <xsl:template match="event-model"> + <catalogue> + <xsl:attribute name="xml:lang">en</xsl:attribute> + <xsl:apply-templates select="//method"></xsl:apply-templates> + </catalogue> + </xsl:template> + + <xsl:template match="method"> + <message> + <xsl:attribute name="key"><xsl:value-of select="../@name"/>.<xsl:value-of select="@name"/></xsl:attribute> + </message> + </xsl:template> + +</xsl:stylesheet> diff --git a/fop-events/src/test/java/org/apache/fop/events/BasicEventTestCase.java b/fop-events/src/test/java/org/apache/fop/events/BasicEventTestCase.java new file mode 100644 index 000000000..39e3fec98 --- /dev/null +++ b/fop-events/src/test/java/org/apache/fop/events/BasicEventTestCase.java @@ -0,0 +1,103 @@ +/* + * 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.events; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.apache.fop.events.model.EventSeverity; + +public class BasicEventTestCase { + + @Test + public void testBasics() throws Exception { + + MyEventListener listener = new MyEventListener(); + + EventBroadcaster broadcaster = new DefaultEventBroadcaster(); + broadcaster.addEventListener(listener); + assertTrue(broadcaster.hasEventListeners()); + + Event ev = new Event(this, "123", EventSeverity.INFO, + Event.paramsBuilder() + .param("reason", "I'm tired") + .param("blah", new Integer(23)) + .build()); + broadcaster.broadcastEvent(ev); + + ev = listener.event; + assertNotNull(ev); + assertEquals("123", listener.event.getEventID()); + assertEquals(EventSeverity.INFO, listener.event.getSeverity()); + assertEquals("I'm tired", ev.getParam("reason")); + assertEquals(new Integer(23), ev.getParam("blah")); + + broadcaster.removeEventListener(listener); + assertFalse(broadcaster.hasEventListeners()); + + //Just check that there are no NPEs + broadcaster.broadcastEvent(ev); + } + + @Test + public void testEventProducer() throws Exception { + MyEventListener listener = new MyEventListener(); + + EventBroadcaster broadcaster = new DefaultEventBroadcaster(); + broadcaster.addEventListener(listener); + assertTrue(broadcaster.hasEventListeners()); + + + TestEventProducer producer = TestEventProducer.Provider.get(broadcaster); + producer.complain(this, "I'm tired", 23); + + Event ev = listener.event; + assertNotNull(ev); + assertEquals("org.apache.fop.events.TestEventProducer.complain", + listener.event.getEventID()); + assertEquals(EventSeverity.WARN, listener.event.getSeverity()); + assertEquals("I'm tired", ev.getParam("reason")); + assertEquals(new Integer(23), ev.getParam("blah")); + + broadcaster.removeEventListener(listener); + assertFalse(broadcaster.hasEventListeners()); + + //Just check that there are no NPEs + broadcaster.broadcastEvent(ev); + } + + private class MyEventListener implements EventListener { + + private Event event; + + public void processEvent(Event event) { + if (this.event != null) { + fail("Multiple events received"); + } + this.event = event; + } + } + +} diff --git a/fop-events/src/test/java/org/apache/fop/events/TestEventProducer.java b/fop-events/src/test/java/org/apache/fop/events/TestEventProducer.java new file mode 100644 index 000000000..ff19c7ea6 --- /dev/null +++ b/fop-events/src/test/java/org/apache/fop/events/TestEventProducer.java @@ -0,0 +1,51 @@ +/* + * 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.events; + +public interface TestEventProducer extends EventProducer { + + /** + * Complain about something. + * @param source the event source + * @param reason the reason for the complaint + * @param blah the complaint + * @event.severity WARN + */ + void complain(Object source, String reason, int blah); + + /** + * Express joy about something. + * @param source the event source + * @param what the cause for the joy + * @event.severity INFO + */ + void enjoy(Object source, String what); + + public static final class Provider { + + private Provider() { + } + + public static TestEventProducer get(EventBroadcaster broadcaster) { + return (TestEventProducer)broadcaster.getEventProducerFor(TestEventProducer.class); + } + } + +} |