aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/events
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/org/apache/fop/events')
-rw-r--r--src/java/org/apache/fop/events/CompositeEventListener.java69
-rw-r--r--src/java/org/apache/fop/events/DefaultEventBroadcaster.java160
-rw-r--r--src/java/org/apache/fop/events/Event.java150
-rw-r--r--src/java/org/apache/fop/events/EventBroadcaster.java61
-rw-r--r--src/java/org/apache/fop/events/EventExceptionManager.java84
-rw-r--r--src/java/org/apache/fop/events/EventFormatter.java196
-rw-r--r--src/java/org/apache/fop/events/EventFormatter.xml101
-rw-r--r--src/java/org/apache/fop/events/EventFormatter_de.xml23
-rw-r--r--src/java/org/apache/fop/events/EventListener.java37
-rw-r--r--src/java/org/apache/fop/events/EventProducer.java31
-rw-r--r--src/java/org/apache/fop/events/FOPEventListenerProxy.java73
-rw-r--r--src/java/org/apache/fop/events/FOPEventModelFactory.java37
-rw-r--r--src/java/org/apache/fop/events/LoggingEventListener.java92
-rw-r--r--src/java/org/apache/fop/events/PropertyExceptionFactory.java47
-rw-r--r--src/java/org/apache/fop/events/ResourceEventProducer.java136
-rw-r--r--src/java/org/apache/fop/events/UnsupportedOperationExceptionFactory.java43
-rw-r--r--src/java/org/apache/fop/events/ValidationExceptionFactory.java51
-rw-r--r--src/java/org/apache/fop/events/model/AbstractEventModelFactory.java61
-rw-r--r--src/java/org/apache/fop/events/model/EventMethodModel.java198
-rw-r--r--src/java/org/apache/fop/events/model/EventModel.java135
-rw-r--r--src/java/org/apache/fop/events/model/EventModelFactory.java33
-rw-r--r--src/java/org/apache/fop/events/model/EventModelParser.java140
-rw-r--r--src/java/org/apache/fop/events/model/EventProducerModel.java105
-rw-r--r--src/java/org/apache/fop/events/model/EventSeverity.java82
24 files changed, 2145 insertions, 0 deletions
diff --git a/src/java/org/apache/fop/events/CompositeEventListener.java b/src/java/org/apache/fop/events/CompositeEventListener.java
new file mode 100644
index 000000000..a65728b71
--- /dev/null
+++ b/src/java/org/apache/fop/events/CompositeEventListener.java
@@ -0,0 +1,69 @@
+/*
+ * 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.List;
+
+/**
+ * EventListener implementation forwards events to possibly multiple other EventListeners.
+ */
+public class CompositeEventListener implements EventListener {
+
+ private List listeners = new java.util.ArrayList();
+
+ /**
+ * 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);
+ }
+
+ private synchronized int getListenerCount() {
+ return this.listeners.size();
+ }
+
+ /**
+ * Indicates whether any listeners have been registered with the broadcaster.
+ * @return true if listeners are present, false otherwise
+ */
+ public boolean hasEventListeners() {
+ return (getListenerCount() > 0);
+ }
+
+ /** {@inheritDoc} */
+ public synchronized void processEvent(Event event) {
+ for (int i = 0, c = getListenerCount(); i < c; i++) {
+ EventListener listener = (EventListener)this.listeners.get(i);
+ listener.processEvent(event);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/events/DefaultEventBroadcaster.java b/src/java/org/apache/fop/events/DefaultEventBroadcaster.java
new file mode 100644
index 000000000..bb1752a72
--- /dev/null
+++ b/src/java/org/apache/fop/events/DefaultEventBroadcaster.java
@@ -0,0 +1,160 @@
+/*
+ * 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.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 org.apache.xmlgraphics.util.Service;
+
+import org.apache.fop.events.model.EventMethodModel;
+import org.apache.fop.events.model.EventModel;
+import org.apache.fop.events.model.EventModelFactory;
+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();
+
+ static {
+ Iterator iter = Service.providers(EventModelFactory.class, true);
+ while (iter.hasNext()) {
+ EventModelFactory factory = (EventModelFactory)iter.next();
+ addEventModel(factory.createEventModel());
+ }
+ }
+
+ /**
+ * Adds a new {@link EventModel} to the list of registered event models.
+ * @param eventModel the event model instance
+ */
+ public static void addEventModel(EventModel eventModel) {
+ eventModels.add(eventModel);
+ }
+
+ /** {@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;
+ }
+
+ private 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;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 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/src/java/org/apache/fop/events/Event.java b/src/java/org/apache/fop/events/Event.java
new file mode 100644
index 000000000..d3da1809e
--- /dev/null
+++ b/src/java/org/apache/fop/events/Event.java
@@ -0,0 +1,150 @@
+/*
+ * 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.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 and a map of name/value pairs.
+ */
+public class Event extends EventObject {
+
+ private static final long serialVersionUID = -1310594422868258083L;
+
+ private String eventID;
+ private EventSeverity severity;
+ private Map 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 params the event parameters (a map of name/value pairs)
+ */
+ public Event(Object source, String eventID, EventSeverity severity, Map params) {
+ super(source);
+ this.eventID = eventID;
+ setSeverity(severity);
+ this.params = params;
+ }
+
+ /**
+ * Returns the event identifier.
+ * @return the event identifier
+ */
+ public String getEventID() {
+ return this.eventID;
+ }
+
+ /**
+ * Returns the event group identifier.
+ * @return the event group identifier (or null if there is no group identifier)
+ */
+ public String getEventGroupID() {
+ int pos = this.eventID.lastIndexOf('.');
+ if (pos > 0) {
+ return this.eventID.substring(0, pos);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 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 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 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 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();
+ }
+ this.params.put(name, value);
+ return this;
+ }
+
+ /**
+ * Returns the accumulated parameter map.
+ * @return the accumulated parameter map
+ */
+ public Map build() {
+ return this.params;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/events/EventBroadcaster.java b/src/java/org/apache/fop/events/EventBroadcaster.java
new file mode 100644
index 000000000..6c8df7375
--- /dev/null
+++ b/src/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
+ */
+ EventProducer getEventProducerFor(Class clazz);
+
+}
diff --git a/src/java/org/apache/fop/events/EventExceptionManager.java b/src/java/org/apache/fop/events/EventExceptionManager.java
new file mode 100644
index 000000000..093ae7010
--- /dev/null
+++ b/src/java/org/apache/fop/events/EventExceptionManager.java
@@ -0,0 +1,84 @@
+/*
+ * 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 reponsible for converting events into exceptions.
+ */
+public class EventExceptionManager {
+
+ private static final Map EXCEPTION_FACTORIES = new java.util.HashMap();
+
+ static {
+ Iterator iter;
+ iter = Service.providers(ExceptionFactory.class, true);
+ 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 = (ExceptionFactory)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);
+ 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 getExceptionClass();
+ }
+}
diff --git a/src/java/org/apache/fop/events/EventFormatter.java b/src/java/org/apache/fop/events/EventFormatter.java
new file mode 100644
index 000000000..56964039b
--- /dev/null
+++ b/src/java/org/apache/fop/events/EventFormatter.java
@@ -0,0 +1,196 @@
+/*
+ * 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 ResourceBundle defaultBundle = XMLResourceBundle.getXMLBundle(
+ EventFormatter.class.getName(), EventFormatter.class.getClassLoader());
+
+ private static Log log = LogFactory.getLog(EventFormatter.class);
+
+ private EventFormatter() {
+ //utility class
+ }
+
+ /**
+ * Formats an event using the default locale.
+ * @param event the event
+ * @return the formatted message
+ */
+ public static String format(Event event) {
+ ResourceBundle bundle = null;
+ String groupID = event.getEventGroupID();
+ if (groupID != null) {
+ try {
+ bundle = XMLResourceBundle.getXMLBundle(
+ groupID,
+ EventFormatter.class.getClassLoader());
+ } catch (MissingResourceException mre) {
+ if (log.isTraceEnabled()) {
+ log.trace("No XMLResourceBundle for " + groupID + " available.");
+ }
+ }
+ }
+ if (bundle == null) {
+ bundle = defaultBundle;
+ }
+ return format(event, bundle);
+ }
+
+ /**
+ * 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) {
+ ResourceBundle bundle = null;
+ String groupID = event.getEventGroupID();
+ if (groupID != null) {
+ try {
+ bundle = XMLResourceBundle.getXMLBundle(
+ groupID, locale,
+ EventFormatter.class.getClassLoader());
+ } catch (MissingResourceException mre) {
+ if (log.isTraceEnabled()) {
+ log.trace("No XMLResourceBundle for " + groupID + " available.");
+ }
+ }
+ }
+ if (bundle == null) {
+ bundle = XMLResourceBundle.getXMLBundle(
+ EventFormatter.class.getName(),
+ locale,
+ EventFormatter.class.getClassLoader());
+ }
+ return format(event, bundle);
+ }
+
+ private static String format(Event event, ResourceBundle bundle) {
+ String template = bundle.getString(event.getEventID());
+ 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;
+ 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());
+ 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) {
+ sb.append(defaultBundle.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/src/java/org/apache/fop/events/EventFormatter.xml b/src/java/org/apache/fop/events/EventFormatter.xml
new file mode 100644
index 000000000..f17da1161
--- /dev/null
+++ b/src/java/org/apache/fop/events/EventFormatter.xml
@@ -0,0 +1,101 @@
+<?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$ -->
+<catalogue xml:lang="en">
+ <message key="locator">[ (See position {loc})| (See {#gatherContextInfo})| (No context info available)]</message>
+ <message key="rule.markerDescendantOfFlow">An fo:marker is permitted only as the descendant of an fo:flow.</message>
+ <message key="rule.retrieveMarkerDescendatOfStaticContent">An fo:retrieve-marker is permitted only as the descendant of an fo:static-content.</message>
+ <message key="rule.bidiOverrideContent">An fo:bidi-override that is a descendant of an fo:leader or of the fo:inline child of an fo:footnote may not have block-level children, unless it has a nearer ancestor that is an fo:inline-container.</message>
+ <message key="rule.inlineContent">An fo:inline that is a descendant of an fo:leader or fo:footnote may not have block-level children, unless it has a nearer ancestor that is an fo:inline-container.</message>
+ <message key="rule.childOfSPM">The element must be a child of fo:simple-page-master.</message>
+ <message key="rule.childOfDeclarations">The element must be a child of fo:declarations.</message>
+ <message key="rule.childOfSPMorDeclarations">The element must be a child of fo:declarations or fo:simple-page-master.</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.tooManyNodes">For "{elementName}", only one "{offendingNode}" may be declared.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.nodeOutOfOrder">For "{elementName}", "{tooLateNode}" must be declared before "{tooEarlyNode}"!{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.invalidChild">"{offendingNode}" is not a valid child element of "{elementName}"![ {ruleViolated,lookup}]{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.missingChildElement">"{elementName}" is missing child elements.[
+Required content model: {contentModel}]{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.missingProperty">Element "{elementName}" is missing required property "{propertyName}"!{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.idNotUnique">Property ID "{id}" (found on "{elementName}") previously used; ID values must be unique within a document!{severity,equals,EventSeverity:FATAL,,
+Any reference to it will be considered a reference to the first occurrence in the document.}{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.markerNotInitialChild">fo:marker must be an initial child: {mcname}{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.markerNotUniqueForSameParent">fo:marker "marker-class-name" must be unique for same parent: {mcname}{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.invalidProperty">Invalid property encountered on "{elementName}": {attr}{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.invalidPropertyValue">Invalid property value encountered in {propName}="{propValue}"[: {e}]{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.unimplementedFeature">The following feature isn't implemented by Apache FOP, yet: {feature} (on {elementName}){{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.missingLinkDestination">Missing attribute on {elementName}: Either external-destination or internal-destination must be specified.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.markerCloningFailed">Unable to clone subtree of fo:marker (marker-class-name="{markerClassName}") for fo:retrieve-marker.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.colorProfileNameNotUnique">Duplicate color profile profile name: {name}{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.regionNameMappedToMultipleRegionClasses">Region-name ("{regionName}") is being mapped to multiple region-classes ({defaultRegionClass1} and {defaultRegionClass2}).{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.masterNameNotUnique">The page master name ("{name}") must be unique across page-masters and page-sequence-masters.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.duplicateFlowNameInPageSequence">Duplicate flow-name "{flowName}" found within {elementName}.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.flowNameNotMapped">The flow-name "{flowName}" on {elementName} could not be mapped to a region-name in the layout-master-set.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.masterNotFound">The master-reference "{masterReference}" on {elementName} matches no simple-page-master or page-sequence-master.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.illegalRegionName">The region-name "{regionName}" for {elementName} is not permitted.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.nonZeroBorderPaddingOnRegion">Border and padding for {elementName} "{regionName}" must be '0' (See 6.4.13 in XSL 1.0).{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.columnCountErrorOnRegionBodyOverflowScroll">If overflow property is set to "scroll" on {elementName}, a column-count other than "1" may not be specified.{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.invalidFORoot">First element must be the fo:root formatting object. Found {elementName} instead. Please make sure you're producing a valid XSL-FO document.</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.emptyDocument">Document is empty (something might be wrong with your XSLT stylesheet).</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.unknownFormattingObject">Unknown formatting object "{offendingNode}" encountered (a child of {elementName}}.{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.nonAutoBPDOnTable">Only a value of "auto" for block-progression-dimension has a well-specified behavior on fo:table. Falling back to "auto".{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.noTablePaddingWithCollapsingBorderModel">In collapsing border model a table does not have padding (see http://www.w3.org/TR/REC-CSS2/tables.html#collapsing-borders), but a non-zero value for padding was found. The padding will be ignored.{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.noMixRowsAndCells">Either fo:table-rows or fo:table-cells may be children of an {elementName} but not both.{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.footerOrderCannotRecover">This table uses the collapsing border model. In order to resolve borders in an efficient way the table-footer must be known before any table-body is parsed. Either put the footer at the correct place or switch to the separate border model.{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.startEndRowUnderTableRowWarning">starts-row/ends-row for fo:table-cells non-applicable for children of an fo:table-row.{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.tooManyCells">The column-number or number of cells in the row overflows the number of fo:table-columns specified for the table.{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.valueMustBeBiggerGtEqOne">{propName} must be 1 or bigger, but got {actualValue}{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.warnImplicitColumns">table-layout=\"fixed\" and column-width unspecified =&gt; falling back to proportional-column-width(1){{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.paddingNotApplicable">padding-* properties are not applicable to {elementName}, but a non-zero value for padding was found.{{locator}}</message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.cellOverlap">{elementName} overlaps in column {column}.<!-- no locator here, exception will be wrapped --></message>
+ <message key="org.apache.fop.fo.flow.table.TableEventProducer.breakIgnoredDueToRowSpanning">{breakBefore,if,break-before,break-after} ignored on {elementName} because of row spanning in progress (See XSL 1.1, {breakBefore,if,7.20.2,7.20.1}){{locator}}</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.imageNotFound">Image not found.[ URI: {uri}.]{{locator}}</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.imageError">Image not available.[ URI: {uri}.] Reason:[ {reason}][ {e}]{{locator}}</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.imageIOError">I/O error while loading image.[ URI: {uri}.][ Reason: {ioe}]{{locator}}</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.ifoNoIntrinsicSize">The intrinsic dimensions of an instream-foreign-object could not be determined.{{locator}}</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.uriError">Error while handling URI: {uri}. Reason: {e}{{locator}}</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.foreignXMLProcessingError">Some XML content will be ignored. Could not render XML in namespace "{namespaceURI}".[ Reason: {e}]</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.foreignXMLNoHandler">Some XML content will be ignored. No handler defined for XML with namespace "{namespaceURI}".</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.imageWritingError">Error while writing an image to the target file.[ Reason: {e}]</message>
+ <message key="org.apache.fop.events.ResourceEventProducer.cannotDeleteTempFile">Temporary file could not be deleted: {tempFile}</message>
+ <message key="org.apache.fop.layoutmgr.inline.InlineLevelEventProducer.leaderWithoutContent">fo:leader is set to "use-content" but has no content.{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.inline.InlineLevelEventProducer.lineOverflows">Line {line} of a paragraph overflows the available area by {overflowLength,choice,50000#{overflowLength} millipoints|50000&lt;more than 50 points}.{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.rowTooTall">The contents of table-row {row} are taller than they should be (there is a block-progression-dimension or height constraint on the indicated row). Due to its contents the row grows to {effCellBPD} millipoints, but the row shouldn't get any taller than {maxCellBPD} millipoints.{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.tableFixedAutoWidthNotSupported">table-layout="fixed" and width="auto", but auto-layout not supported =&gt; assuming width="100%".{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.objectTooWide">The extent in inline-progression-direction (width) of a {elementName} is bigger than the available space ({effIPD}mpt &gt; {maxIPD}mpt).{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.overconstrainedAdjustEndIndent">Adjusting end-indent based on overconstrained geometry rules for {elementName}.{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.viewportOverflow">Content overflows the viewport of an {elementName} in block-progression direction by {amount} millipoints.{clip,if, Content will be clipped.}{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.regionOverflow">Content overflows the viewport of the {elementName} on page {page} in block-progression direction by {amount} millipoints.{clip,if, Content will be clipped.}{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.flowNotMappingToRegionBody">Flow "{flowName}" does not map to the region-body in page-master "{masterName}". FOP presently does not support this.{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.pageSequenceMasterExhausted">Subsequences exhausted in page-sequence-master "{pageSequenceMasterName}", {canRecover,if,using previous subsequence,cannot recover}.{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.missingSubsequencesInPageSequenceMaster">No subsequences in page-sequence-master "{pageSequenceMasterName}".{{locator}}</message>
+ <message key="org.apache.fop.layoutmgr.BlockLevelEventProducer.noMatchingPageMaster">No simple-page-master matching "{pageMasterName}" in page-sequence-master "{pageSequenceMasterName}".{{locator}}</message>
+ <message key="org.apache.fop.svg.SVGEventProducer.error">SVG error: {message}</message>
+ <message key="org.apache.fop.svg.SVGEventProducer.alert">SVG alert: {message}</message>
+ <message key="org.apache.fop.svg.SVGEventProducer.info">SVG info: {message}</message>
+ <message key="org.apache.fop.svg.SVGEventProducer.svgNotBuilt">SVG graphic could not be built. Reason: {e}</message>
+ <message key="org.apache.fop.svg.SVGEventProducer.svgRenderingError">SVG graphic could not be rendered. Reason: {e}</message>
+ <message key="org.apache.fop.render.RendererEventProducer.ioError">I/O error while writing to target file.[ Reason: {ioe}]</message>
+ <message key="org.apache.fop.area.AreaEventProducer.unresolvedIDReference">{type}: Unresolved ID reference "{id}" found.</message>
+ <message key="org.apache.fop.area.AreaEventProducer.unresolvedIDReferenceOnPage">Page {page}: Unresolved ID reference "{id}" found.</message>
+ <message key="org.apache.fop.area.AreaEventProducer.pageLoadError">Error while deserializing page {page}.[ Reason: {e}]</message>
+ <message key="org.apache.fop.area.AreaEventProducer.pageSaveError">Error while serializing page {page}.[ Reason: {e}]</message>
+ <message key="org.apache.fop.area.AreaEventProducer.pageRenderingError">Error while rendering page {page}.[ Reason: {e}]</message>
+ <message key="org.apache.fop.fonts.FontEventAdapter.fontSubstituted">Font "{requested}" not found. Substituting with "{effective}".</message>
+ <message key="org.apache.fop.fonts.FontEventAdapter.fontLoadingErrorAtAutoDetection">Unable to load font file: {fontURL}.[ Reason: {e}]</message>
+ <message key="org.apache.fop.fonts.FontEventAdapter.glyphNotAvailable">Glyph "{ch}" (0x{ch,hex}[, {ch,glyph-name}]) not available in font "{fontName}".</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/events/EventFormatter_de.xml b/src/java/org/apache/fop/events/EventFormatter_de.xml
new file mode 100644
index 000000000..c65d24f73
--- /dev/null
+++ b/src/java/org/apache/fop/events/EventFormatter_de.xml
@@ -0,0 +1,23 @@
+<?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$ -->
+<catalogue xml:lang="de">
+ <message key="locator">[ (Siehe Position {loc})| (Siehe {#gatherContextInfo})| (Keine Kontextinformationen verfügbar)]</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.tooManyNodes">In "{elementName}" darf nur ein einziges "{offendingNode}" vorkommen!{{locator}}</message>
+ <message key="org.apache.fop.fo.FOValidationEventProducer.missingProperty">Dem Element "{elementName}" fehlt ein verlangtes Property "{propertyName}"!{{locator}}</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/events/EventListener.java b/src/java/org/apache/fop/events/EventListener.java
new file mode 100644
index 000000000..f8293aed9
--- /dev/null
+++ b/src/java/org/apache/fop/events/EventListener.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.events;
+
+/**
+ * This interface is implemented by clients who want to listen for events.
+ */
+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 occured. This way the client can configure the behaviour of
+ * the observed application.
+ * @param event the event
+ */
+ void processEvent(Event event);
+
+}
diff --git a/src/java/org/apache/fop/events/EventProducer.java b/src/java/org/apache/fop/events/EventProducer.java
new file mode 100644
index 000000000..88da771a4
--- /dev/null
+++ b/src/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/src/java/org/apache/fop/events/FOPEventListenerProxy.java b/src/java/org/apache/fop/events/FOPEventListenerProxy.java
new file mode 100644
index 000000000..d4c237844
--- /dev/null
+++ b/src/java/org/apache/fop/events/FOPEventListenerProxy.java
@@ -0,0 +1,73 @@
+/*
+ * 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.apache.fop.apps.FOUserAgent;
+import org.apache.fop.events.model.EventSeverity;
+import org.apache.fop.fo.FOValidationEventProducer;
+import org.apache.fop.layoutmgr.BlockLevelEventProducer;
+
+/**
+ * EventListener proxy that inspects all events and adjusts severity levels where necessary.
+ * For validation events, it reacts on each event based on the strict validation setting in
+ * the user agent.
+ * For layout events, it reduces the default severity level if FOP signals that it can recover
+ * from the event.
+ */
+public class FOPEventListenerProxy implements EventListener {
+
+ private static final String FOVALIDATION_EVENT_ID_PREFIX
+ = FOValidationEventProducer.class.getName();
+
+ private static final String BLOCK_LEVEL_EVENT_ID_PREFIX
+ = BlockLevelEventProducer.class.getName();
+
+ private EventListener delegate;
+ private FOUserAgent userAgent;
+
+ /**
+ * Main constructor.
+ * @param delegate the event listener to delegate events to
+ * @param userAgent the FO user agent
+ */
+ public FOPEventListenerProxy(EventListener delegate, FOUserAgent userAgent) {
+ this.delegate = delegate;
+ this.userAgent = userAgent;
+ }
+
+ /** {@inheritDoc} */
+ public synchronized void processEvent(Event event) {
+ if (event.getEventID().startsWith(FOVALIDATION_EVENT_ID_PREFIX)) {
+ Boolean canRecover = (Boolean)event.getParam("canRecover");
+ if (Boolean.TRUE.equals(canRecover) && !userAgent.validateStrictly()) {
+ //Reduce severity if FOP can recover
+ event.setSeverity(EventSeverity.WARN);
+ }
+ } else if (event.getEventID().startsWith(BLOCK_LEVEL_EVENT_ID_PREFIX)) {
+ Boolean canRecover = (Boolean)event.getParam("canRecover");
+ if (Boolean.TRUE.equals(canRecover)) {
+ //Reduce severity if FOP can recover
+ event.setSeverity(EventSeverity.WARN);
+ }
+ }
+ this.delegate.processEvent(event);
+ }
+
+}
diff --git a/src/java/org/apache/fop/events/FOPEventModelFactory.java b/src/java/org/apache/fop/events/FOPEventModelFactory.java
new file mode 100644
index 000000000..5a75042fa
--- /dev/null
+++ b/src/java/org/apache/fop/events/FOPEventModelFactory.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.events;
+
+import org.apache.fop.events.model.AbstractEventModelFactory;
+import org.apache.fop.events.model.EventModel;
+
+/**
+ * Factory for FOP's main event model.
+ */
+public class FOPEventModelFactory extends AbstractEventModelFactory {
+
+ private static final String EVENT_MODEL_FILENAME = "event-model.xml";
+
+ /** {@inheritDoc} */
+ public EventModel createEventModel() {
+ return loadModel(getClass(), EVENT_MODEL_FILENAME);
+ }
+
+}
diff --git a/src/java/org/apache/fop/events/LoggingEventListener.java b/src/java/org/apache/fop/events/LoggingEventListener.java
new file mode 100644
index 000000000..9ba8ed2df
--- /dev/null
+++ b/src/java/org/apache/fop/events/LoggingEventListener.java
@@ -0,0 +1,92 @@
+/*
+ * 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.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.events.model.EventSeverity;
+
+/**
+ * EventListener implementation that redirects events to Commons Logging. The events are
+ * converted to localized messages.
+ */
+public class LoggingEventListener implements EventListener {
+
+ /** Default logger instance */
+ private static Log defaultLog = LogFactory.getLog(LoggingEventListener.class);
+
+ private Log log;
+ private boolean skipFatal;
+
+ /**
+ * Creates an instance logging to the default log category of this class.
+ */
+ public LoggingEventListener() {
+ this(defaultLog);
+ }
+
+ /**
+ * Creates an instance logging to a given logger. Events with fatal severity level will be
+ * skipped.
+ * @param log the target logger
+ */
+ public LoggingEventListener(Log log) {
+ this(log, true);
+ }
+
+ /**
+ * Creates an instance logging to a given logger.
+ * @param log the target logger
+ * @param skipFatal true if events with fatal severity level should be skipped (i.e. not logged)
+ */
+ public LoggingEventListener(Log log, boolean skipFatal) {
+ this.log = log;
+ this.skipFatal = skipFatal;
+ }
+
+ /**
+ * Returns the target logger for this instance.
+ * @return the target logger
+ */
+ public Log getLog() {
+ return this.log;
+ }
+
+ /** {@inheritDoc} */
+ public void processEvent(Event event) {
+ String msg = EventFormatter.format(event);
+ EventSeverity severity = event.getSeverity();
+ if (severity == EventSeverity.INFO) {
+ log.info(msg);
+ } else if (severity == EventSeverity.WARN) {
+ log.warn(msg);
+ } else if (severity == EventSeverity.ERROR) {
+ log.error(msg);
+ } else if (severity == EventSeverity.FATAL) {
+ if (!skipFatal) {
+ log.fatal(msg);
+ }
+ } else {
+ assert false;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/events/PropertyExceptionFactory.java b/src/java/org/apache/fop/events/PropertyExceptionFactory.java
new file mode 100644
index 000000000..667c4a16e
--- /dev/null
+++ b/src/java/org/apache/fop/events/PropertyExceptionFactory.java
@@ -0,0 +1,47 @@
+/*
+ * 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 org.apache.fop.events.EventExceptionManager.ExceptionFactory;
+import org.apache.fop.fo.expr.PropertyException;
+
+/**
+ * Exception factory for {@link PropertyException}.
+ */
+public class PropertyExceptionFactory implements ExceptionFactory {
+
+ /** {@inheritDoc} */
+ public Throwable createException(Event event) {
+ String msg = EventFormatter.format(event, Locale.ENGLISH);
+ PropertyException ex = new PropertyException(msg);
+ if (!Locale.ENGLISH.equals(Locale.getDefault())) {
+ ex.setLocalizedMessage(EventFormatter.format(event));
+ }
+ return ex;
+ }
+
+ /** {@inheritDoc} */
+ public Class getExceptionClass() {
+ return PropertyException.class;
+ }
+
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/events/ResourceEventProducer.java b/src/java/org/apache/fop/events/ResourceEventProducer.java
new file mode 100644
index 000000000..21da4f1d7
--- /dev/null
+++ b/src/java/org/apache/fop/events/ResourceEventProducer.java
@@ -0,0 +1,136 @@
+/*
+ * 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.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+
+import org.xml.sax.Locator;
+
+import org.apache.xmlgraphics.image.loader.ImageException;
+
+/**
+ * Event producer interface for resource events (missing images, fonts etc.).
+ */
+public interface ResourceEventProducer extends EventProducer {
+
+ /**
+ * Provider class for the event producer.
+ */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the requested event producer
+ */
+ public static ResourceEventProducer get(EventBroadcaster broadcaster) {
+ return (ResourceEventProducer)broadcaster.getEventProducerFor(
+ ResourceEventProducer.class);
+ }
+ }
+
+ /**
+ * Image not found.
+ * @param source the event source
+ * @param uri the original URI of the image
+ * @param fnfe the "file not found" exception
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void imageNotFound(Object source, String uri, FileNotFoundException fnfe, Locator loc);
+
+ /**
+ * Error while processing image.
+ * @param source the event source
+ * @param uri the original URI of the image
+ * @param e the image exception
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void imageError(Object source, String uri, ImageException e, Locator loc);
+
+ /**
+ * I/O error while loading an image.
+ * @param source the event source
+ * @param uri the original URI of the image
+ * @param ioe the I/O exception
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void imageIOError(Object source, String uri, IOException ioe, Locator loc);
+
+ /**
+ * Error while writing/serializing an image to an output format.
+ * @param source the event source
+ * @param e the original exception
+ * @event.severity ERROR
+ */
+ void imageWritingError(Object source, Exception e);
+
+ /**
+ * Error while handling a URI.
+ * @param source the event source
+ * @param uri the original URI of the image
+ * @param e the original exception
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void uriError(Object source, String uri, Exception e, Locator loc);
+
+ /**
+ * Intrinsic size of fo:instream-foreign-object could not be determined.
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void ifoNoIntrinsicSize(Object source, Locator loc);
+
+ /**
+ * Error processing foreign XML content.
+ * @param source the event source
+ * @param doc the foreign XML
+ * @param namespaceURI the namespace URI of the foreign XML
+ * @param e the original exception
+ * @event.severity ERROR
+ */
+ void foreignXMLProcessingError(Object source, Document doc, String namespaceURI, Exception e);
+
+ /**
+ * No handler for foreign XML content.
+ * @param source the event source
+ * @param doc the foreign XML
+ * @param namespaceURI the namespace URI of the foreign XML
+ * @event.severity ERROR
+ */
+ void foreignXMLNoHandler(Object source, Document doc, String namespaceURI);
+
+ /**
+ * Cannot delete a temporary file.
+ * @param source the event source
+ * @param tempFile the temporary file
+ * @event.severity ERROR
+ */
+ void cannotDeleteTempFile(Object source, File tempFile);
+
+}
diff --git a/src/java/org/apache/fop/events/UnsupportedOperationExceptionFactory.java b/src/java/org/apache/fop/events/UnsupportedOperationExceptionFactory.java
new file mode 100644
index 000000000..06ce8dd25
--- /dev/null
+++ b/src/java/org/apache/fop/events/UnsupportedOperationExceptionFactory.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.fop.events.EventExceptionManager.ExceptionFactory;
+
+/**
+ * Exception factory for {@link UnsupportedOperationException}.
+ */
+public class UnsupportedOperationExceptionFactory implements ExceptionFactory {
+
+ /** {@inheritDoc} */
+ public Throwable createException(Event event) {
+ String msg = EventFormatter.format(event, Locale.ENGLISH);
+ UnsupportedOperationException ex = new UnsupportedOperationException(msg);
+ return ex;
+ }
+
+ /** {@inheritDoc} */
+ public Class getExceptionClass() {
+ return UnsupportedOperationException.class;
+ }
+
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/events/ValidationExceptionFactory.java b/src/java/org/apache/fop/events/ValidationExceptionFactory.java
new file mode 100644
index 000000000..9dba84007
--- /dev/null
+++ b/src/java/org/apache/fop/events/ValidationExceptionFactory.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;
+
+import java.util.Locale;
+
+import org.xml.sax.Locator;
+
+import org.apache.fop.events.EventExceptionManager.ExceptionFactory;
+import org.apache.fop.fo.ValidationException;
+
+/**
+ * Exception factory for {@link ValidationException}.
+ */
+public class ValidationExceptionFactory implements ExceptionFactory {
+
+ /** {@inheritDoc} */
+ public Throwable createException(Event event) {
+ Locator loc = (Locator)event.getParam("loc");
+ String msg = EventFormatter.format(event, Locale.ENGLISH);
+ ValidationException ex = new ValidationException(msg, loc);
+ if (!Locale.ENGLISH.equals(Locale.getDefault())) {
+ ex.setLocalizedMessage(EventFormatter.format(event));
+ }
+ return ex;
+ }
+
+ /** {@inheritDoc} */
+ public Class getExceptionClass() {
+ return ValidationException.class;
+ }
+
+
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/events/model/AbstractEventModelFactory.java b/src/java/org/apache/fop/events/model/AbstractEventModelFactory.java
new file mode 100644
index 000000000..ee980f34e
--- /dev/null
+++ b/src/java/org/apache/fop/events/model/AbstractEventModelFactory.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.model;
+
+import java.io.InputStream;
+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.DefaultEventBroadcaster;
+
+/**
+ * This interface is used to instantiate (load, parse) event models.
+ */
+public abstract class AbstractEventModelFactory implements EventModelFactory {
+
+ /**
+ * Loads an event model and returns its instance.
+ * @param resourceBaseClass base class to use for loading resources
+ * @param resourceName the resource name pointing to the event model to be loaded
+ * @return the newly loaded event model.
+ */
+ public EventModel loadModel(Class resourceBaseClass, String resourceName) {
+ 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);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/events/model/EventMethodModel.java b/src/java/org/apache/fop/events/model/EventMethodModel.java
new file mode 100644
index 000000000..930cda53d
--- /dev/null
+++ b/src/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(null, "name", "name", "CDATA", getMethodName());
+ atts.addAttribute(null, "severity", "severity", "CDATA", getSeverity().getName());
+ if (getExceptionClass() != null) {
+ atts.addAttribute(null, "exception", "exception", "CDATA", getExceptionClass());
+ }
+ String elName = "method";
+ handler.startElement(null, elName, elName, atts);
+ Iterator iter = this.params.iterator();
+ while (iter.hasNext()) {
+ ((XMLizable)iter.next()).toSAX(handler);
+ }
+ handler.endElement(null, 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(null, "type", "type", "CDATA", getType().getName());
+ atts.addAttribute(null, "name", "name", "CDATA", getName());
+ String elName = "parameter";
+ handler.startElement(null, elName, elName, atts);
+ handler.endElement(null, elName, elName);
+ }
+
+ }
+}
diff --git a/src/java/org/apache/fop/events/model/EventModel.java b/src/java/org/apache/fop/events/model/EventModel.java
new file mode 100644
index 000000000..61e221b3b
--- /dev/null
+++ b/src/java/org/apache/fop/events/model/EventModel.java
@@ -0,0 +1,135 @@
+/*
+ * 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.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.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&lt;EventProducerModel&gt;)
+ */
+ 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(null, elName, elName, atts);
+ Iterator iter = getProducers();
+ while (iter.hasNext()) {
+ ((XMLizable)iter.next()).toSAX(handler);
+ }
+ handler.endElement(null, elName, elName);
+ }
+
+ private void writeXMLizable(XMLizable object, File outputFile) throws IOException {
+ Result res = new StreamResult(outputFile);
+
+ 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());
+ }
+ }
+
+ /**
+ * 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/src/java/org/apache/fop/events/model/EventModelFactory.java b/src/java/org/apache/fop/events/model/EventModelFactory.java
new file mode 100644
index 000000000..cd760501c
--- /dev/null
+++ b/src/java/org/apache/fop/events/model/EventModelFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * This interface is used to instantiate (load, parse) event models.
+ */
+public interface EventModelFactory {
+
+ /**
+ * Creates a new EventModel instance.
+ * @return the new EventModel instance
+ */
+ EventModel createEventModel();
+
+}
diff --git a/src/java/org/apache/fop/events/model/EventModelParser.java b/src/java/org/apache/fop/events/model/EventModelParser.java
new file mode 100644
index 000000000..600e495c5
--- /dev/null
+++ b/src/java/org/apache/fop/events/model/EventModelParser.java
@@ -0,0 +1,140 @@
+/*
+ * 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 class EventModelParser {
+
+ /** Logger instance */
+ protected static 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/src/java/org/apache/fop/events/model/EventProducerModel.java b/src/java/org/apache/fop/events/model/EventProducerModel.java
new file mode 100644
index 000000000..938609cd9
--- /dev/null
+++ b/src/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&lt;EventMethodModel&gt;)
+ */
+ public Iterator getMethods() {
+ return this.methods.values().iterator();
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute(null, "name", "name", "CDATA", getInterfaceName());
+ String elName = "producer";
+ handler.startElement(null, elName, elName, atts);
+ Iterator iter = getMethods();
+ while (iter.hasNext()) {
+ ((XMLizable)iter.next()).toSAX(handler);
+ }
+ handler.endElement(null, elName, elName);
+ }
+
+
+}
diff --git a/src/java/org/apache/fop/events/model/EventSeverity.java b/src/java/org/apache/fop/events/model/EventSeverity.java
new file mode 100644
index 000000000..d37c53c1e
--- /dev/null
+++ b/src/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;
+ }
+
+}