aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/codegen/java/org/apache/fop/tools/EventConventionException.java28
-rw-r--r--src/codegen/java/org/apache/fop/tools/EventProducerCollector.java183
-rw-r--r--src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java200
-rw-r--r--src/codegen/java/org/apache/fop/tools/merge-translation.xsl55
-rw-r--r--src/codegen/java/org/apache/fop/tools/model2translation.xsl35
-rw-r--r--src/documentation/content/xdocs/site.xml1
-rw-r--r--src/documentation/content/xdocs/trunk/events.xml422
-rw-r--r--src/java/META-INF/services/org.apache.fop.events.EventExceptionManager$ExceptionFactory4
-rw-r--r--src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory7
-rw-r--r--src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function1
-rw-r--r--src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter1
-rw-r--r--src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory6
-rw-r--r--src/java/org/apache/fop/apps/FOPException.java21
-rw-r--r--src/java/org/apache/fop/apps/FOUserAgent.java45
-rw-r--r--src/java/org/apache/fop/area/AreaEventProducer.java91
-rw-r--r--src/java/org/apache/fop/area/AreaTreeHandler.java5
-rw-r--r--src/java/org/apache/fop/area/AreaTreeObject.java10
-rw-r--r--src/java/org/apache/fop/area/AreaTreeParser.java2
-rw-r--r--src/java/org/apache/fop/area/CachedRenderPagesModel.java77
-rw-r--r--src/java/org/apache/fop/area/PageViewport.java16
-rw-r--r--src/java/org/apache/fop/area/RenderPagesModel.java45
-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
-rw-r--r--src/java/org/apache/fop/fo/ElementMapping.java3
-rw-r--r--src/java/org/apache/fop/fo/ElementMappingRegistry.java2
-rw-r--r--src/java/org/apache/fop/fo/FOElementMapping.java2
-rw-r--r--src/java/org/apache/fop/fo/FOEventHandler.java2
-rw-r--r--src/java/org/apache/fop/fo/FONode.java172
-rw-r--r--src/java/org/apache/fop/fo/FOText.java9
-rw-r--r--src/java/org/apache/fop/fo/FOTreeBuilder.java67
-rw-r--r--src/java/org/apache/fop/fo/FOValidationEventProducer.java348
-rw-r--r--src/java/org/apache/fop/fo/FObj.java77
-rw-r--r--src/java/org/apache/fop/fo/PropertyList.java67
-rw-r--r--src/java/org/apache/fop/fo/expr/FromParentFunction.java8
-rw-r--r--src/java/org/apache/fop/fo/expr/InheritedPropFunction.java5
-rw-r--r--src/java/org/apache/fop/fo/expr/NearestSpecPropFunction.java5
-rw-r--r--src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java9
-rw-r--r--src/java/org/apache/fop/fo/extensions/destination/Destination.java14
-rw-r--r--src/java/org/apache/fop/fo/flow/AbstractListItemPart.java29
-rw-r--r--src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java4
-rw-r--r--src/java/org/apache/fop/fo/flow/BasicLink.java21
-rw-r--r--src/java/org/apache/fop/fo/flow/BidiOverride.java32
-rw-r--r--src/java/org/apache/fop/fo/flow/BlockContainer.java18
-rw-r--r--src/java/org/apache/fop/fo/flow/Character.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/ExternalGraphic.java18
-rw-r--r--src/java/org/apache/fop/fo/flow/Float.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/Footnote.java8
-rw-r--r--src/java/org/apache/fop/fo/flow/FootnoteBody.java4
-rw-r--r--src/java/org/apache/fop/fo/flow/InitialPropertySet.java4
-rw-r--r--src/java/org/apache/fop/fo/flow/Inline.java33
-rw-r--r--src/java/org/apache/fop/fo/flow/InlineContainer.java18
-rw-r--r--src/java/org/apache/fop/fo/flow/InstreamForeignObject.java43
-rw-r--r--src/java/org/apache/fop/fo/flow/ListBlock.java18
-rw-r--r--src/java/org/apache/fop/fo/flow/ListItem.java32
-rw-r--r--src/java/org/apache/fop/fo/flow/Marker.java13
-rw-r--r--src/java/org/apache/fop/fo/flow/MultiCase.java3
-rw-r--r--src/java/org/apache/fop/fo/flow/MultiProperties.java9
-rw-r--r--src/java/org/apache/fop/fo/flow/MultiPropertySet.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/MultiSwitch.java11
-rw-r--r--src/java/org/apache/fop/fo/flow/MultiToggle.java11
-rw-r--r--src/java/org/apache/fop/fo/flow/PageNumber.java4
-rw-r--r--src/java/org/apache/fop/fo/flow/RetrieveMarker.java31
-rw-r--r--src/java/org/apache/fop/fo/flow/Wrapper.java25
-rw-r--r--src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java12
-rw-r--r--src/java/org/apache/fop/fo/flow/table/Table.java39
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableAndCaption.java47
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableBody.java29
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableCaption.java21
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableCell.java28
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableCellContainer.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableColumn.java21
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableEventProducer.java159
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableFObj.java29
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableRow.java13
-rw-r--r--src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java13
-rw-r--r--src/java/org/apache/fop/fo/pagination/ColorProfile.java21
-rw-r--r--src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java16
-rw-r--r--src/java/org/apache/fop/fo/pagination/Declarations.java21
-rw-r--r--src/java/org/apache/fop/fo/pagination/Flow.java39
-rw-r--r--src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java63
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageNumberGenerator.java6
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequence.java28
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java38
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequenceWrapper.java19
-rw-r--r--src/java/org/apache/fop/fo/pagination/Region.java29
-rw-r--r--src/java/org/apache/fop/fo/pagination/RegionAfter.java14
-rw-r--r--src/java/org/apache/fop/fo/pagination/RegionBA.java6
-rw-r--r--src/java/org/apache/fop/fo/pagination/RegionBefore.java15
-rw-r--r--src/java/org/apache/fop/fo/pagination/RegionBody.java24
-rw-r--r--src/java/org/apache/fop/fo/pagination/RegionEnd.java18
-rw-r--r--src/java/org/apache/fop/fo/pagination/RegionSE.java6
-rw-r--r--src/java/org/apache/fop/fo/pagination/RegionStart.java15
-rw-r--r--src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java43
-rw-r--r--src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java20
-rw-r--r--src/java/org/apache/fop/fo/pagination/Root.java20
-rw-r--r--src/java/org/apache/fop/fo/pagination/SideRegion.java6
-rw-r--r--src/java/org/apache/fop/fo/pagination/SimplePageMaster.java130
-rw-r--r--src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java16
-rw-r--r--src/java/org/apache/fop/fo/pagination/StaticContent.java18
-rw-r--r--src/java/org/apache/fop/fo/pagination/Title.java19
-rw-r--r--src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java37
-rw-r--r--src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java17
-rw-r--r--src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTree.java24
-rw-r--r--src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java2
-rw-r--r--src/java/org/apache/fop/fonts/FontEventAdapter.java82
-rw-r--r--src/java/org/apache/fop/fonts/FontEventListener.java52
-rw-r--r--src/java/org/apache/fop/fonts/FontInfo.java22
-rw-r--r--src/java/org/apache/fop/fonts/LazyFont.java1
-rw-r--r--src/java/org/apache/fop/fonts/SingleByteFont.java28
-rw-r--r--src/java/org/apache/fop/fonts/Typeface.java12
-rw-r--r--src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java38
-rw-r--r--src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java31
-rw-r--r--src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java14
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractBreaker.java8
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java44
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java168
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java10
-rw-r--r--src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java19
-rw-r--r--src/java/org/apache/fop/layoutmgr/LayoutException.java105
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreaker.java33
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java43
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageProvider.java10
-rw-r--r--src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java43
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java66
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java16
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java14
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java15
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java12
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java18
-rw-r--r--src/java/org/apache/fop/render/AbstractGenericSVGHandler.java29
-rw-r--r--src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java2
-rw-r--r--src/java/org/apache/fop/render/AbstractRenderer.java25
-rw-r--r--src/java/org/apache/fop/render/Renderer.java6
-rw-r--r--src/java/org/apache/fop/render/RendererEventProducer.java53
-rw-r--r--src/java/org/apache/fop/render/afp/AFPEventProducer.java63
-rw-r--r--src/java/org/apache/fop/render/afp/AFPEventProducer.xml4
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRenderer.java30
-rw-r--r--src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.java80
-rw-r--r--src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml6
-rw-r--r--src/java/org/apache/fop/render/bitmap/PNGRenderer.java17
-rw-r--r--src/java/org/apache/fop/render/bitmap/TIFFRenderer.java11
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DRenderer.java17
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java63
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLEventProducer.java66
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLEventProducer.xml4
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLGenerator.java12
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRenderer.java33
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLRendererContext.java3
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFEventProducer.java64
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFEventProducer.xml4
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java100
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFSVGHandler.java29
-rw-r--r--src/java/org/apache/fop/render/ps/PSEventProducer.java65
-rw-r--r--src/java/org/apache/fop/render/ps/PSEventProducer.xml4
-rw-r--r--src/java/org/apache/fop/render/ps/PSRenderer.java29
-rw-r--r--src/java/org/apache/fop/render/ps/PSSVGHandler.java68
-rw-r--r--src/java/org/apache/fop/render/ps/ResourceHandler.java8
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/AbstractPSCommentElement.java5
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionElement.java6
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionObject.java13
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSCommentAfterElement.java1
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSCommentBeforeElement.java1
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java20
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSPageSetupCodeElement.java5
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSSetPageDeviceElement.java11
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSSetupCodeElement.java5
-rw-r--r--src/java/org/apache/fop/render/rtf/RTFEventProducer.java94
-rw-r--r--src/java/org/apache/fop/render/rtf/RTFEventProducer.xml8
-rw-r--r--src/java/org/apache/fop/render/rtf/RTFHandler.java153
-rw-r--r--src/java/org/apache/fop/render/xml/XMLRenderer.java2
-rw-r--r--src/java/org/apache/fop/svg/SVGEventProducer.java89
-rw-r--r--src/java/org/apache/fop/svg/SVGUserAgent.java131
-rw-r--r--src/java/org/apache/fop/svg/SimpleSVGUserAgent.java126
-rw-r--r--src/java/org/apache/fop/util/QName.java94
-rw-r--r--src/java/org/apache/fop/util/XMLResourceBundle.java398
-rw-r--r--src/java/org/apache/fop/util/text/AdvancedMessageFormat.java487
-rw-r--r--src/java/org/apache/fop/util/text/ChoiceFieldPart.java91
-rw-r--r--src/java/org/apache/fop/util/text/EqualsFieldPart.java92
-rw-r--r--src/java/org/apache/fop/util/text/GlyphNameFieldPart.java89
-rw-r--r--src/java/org/apache/fop/util/text/HexFieldPart.java84
-rw-r--r--src/java/org/apache/fop/util/text/IfFieldPart.java116
-rw-r--r--src/java/org/apache/fop/util/text/LocatorFormatter.java42
205 files changed, 8110 insertions, 1601 deletions
diff --git a/src/codegen/java/org/apache/fop/tools/EventConventionException.java b/src/codegen/java/org/apache/fop/tools/EventConventionException.java
new file mode 100644
index 000000000..675f4a0ca
--- /dev/null
+++ b/src/codegen/java/org/apache/fop/tools/EventConventionException.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+public class EventConventionException extends Exception {
+
+ public EventConventionException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/codegen/java/org/apache/fop/tools/EventProducerCollector.java b/src/codegen/java/org/apache/fop/tools/EventProducerCollector.java
new file mode 100644
index 000000000..e42395ae7
--- /dev/null
+++ b/src/codegen/java/org/apache/fop/tools/EventProducerCollector.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.tools;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+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.
+ */
+public class EventProducerCollector {
+
+ private static final String CLASSNAME_EVENT_PRODUCER = EventProducer.class.getName();
+ private static final Map PRIMITIVE_MAP;
+
+ static {
+ Map m = new java.util.HashMap();
+ 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 EventModel model = new EventModel();
+
+ public EventProducerCollector() {
+ this.tagFactory = createDocletTagFactory();
+ }
+
+ protected DocletTagFactory createDocletTagFactory() {
+ return new DefaultDocletTagFactory();
+ }
+
+ public void scanFile(File src, String filename)
+ throws IOException, EventConventionException, ClassNotFoundException {
+ JavaDocBuilder builder = new JavaDocBuilder(this.tagFactory);
+ builder.addSource(src);
+ JavaClass[] classes = builder.getClasses();
+ for (int i = 0, c = classes.length; i < c; i++) {
+ JavaClass clazz = classes[i];
+ if (clazz.isInterface() && implementsInterface(clazz, CLASSNAME_EVENT_PRODUCER)) {
+ processEventProducerInterface(clazz, filename);
+ }
+ }
+ }
+
+ 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
+ * @param javaFilename the filename of the Java source of the interface
+ * @throws EventConventionException if the event producer conventions are violated
+ * @throws ClassNotFoundException if a required class cannot be found
+ */
+ protected void processEventProducerInterface(JavaClass clazz, String javaFilename)
+ 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);
+ }
+ this.model.addProducer(prodMeta);
+ }
+
+ private EventMethodModel createMethodModel(JavaMethod method)
+ throws EventConventionException, ClassNotFoundException {
+ JavaClass clazz = method.getParentClass();
+ //Check EventProducer conventions
+ if (!method.getReturns().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 = (Class)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;
+ }
+
+ public EventModel getModel() {
+ return this.model;
+ }
+
+ public void saveModelToXML(File modelFile) throws IOException {
+ getModel().saveToXML(modelFile);
+ }
+
+}
diff --git a/src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java b/src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java
new file mode 100644
index 000000000..755e3da0b
--- /dev/null
+++ b/src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.tools;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+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.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;
+
+public class EventProducerCollectorTask extends Task {
+
+ private List filesets = new java.util.ArrayList();
+ private File modelFile;
+ private File translationFile;
+
+ /** {@inheritDoc} */
+ public void execute() throws BuildException {
+ try {
+ EventProducerCollector collector = new EventProducerCollector();
+ processFileSets(collector);
+ getModelFile().getParentFile().mkdirs();
+ collector.saveModelToXML(getModelFile());
+ log("Event model written to " + getModelFile());
+ if (getTranslationFile() != null) {
+ updateTranslationFile();
+ }
+ } 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";
+
+ protected void updateTranslationFile() throws IOException {
+ try {
+ boolean resultExists = getTranslationFile().exists();
+ SAXTransformerFactory tFactory
+ = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+ //Generate fresh generated translation file as template
+ Source src = new StreamSource(getModelFile());
+ 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());
+ 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);
+ Result res = new StreamResult(getTranslationFile());
+ 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());
+ }
+ } catch (TransformerException te) {
+ throw new IOException(te.getMessage());
+ }
+ }
+
+ protected void processFileSets(EventProducerCollector collector)
+ throws IOException, EventConventionException, ClassNotFoundException {
+ Iterator 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);
+ collector.scanFile(src, filename);
+ }
+ }
+ }
+
+ public void addFileset(FileSet set) {
+ filesets.add(set);
+ }
+
+ public void setModelFile(File f) {
+ this.modelFile = f;
+ }
+
+ public File getModelFile() {
+ return this.modelFile;
+ }
+
+ public void setTranslationFile(File f) {
+ this.translationFile = f;
+ }
+
+ public File getTranslationFile() {
+ return this.translationFile;
+ }
+
+ 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.setModelFile(new File("D:/out.xml"));
+ generator.setTranslationFile(new File("D:/out1.xml"));
+ generator.execute();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/codegen/java/org/apache/fop/tools/merge-translation.xsl b/src/codegen/java/org/apache/fop/tools/merge-translation.xsl
new file mode 100644
index 000000000..d15d22fd3
--- /dev/null
+++ b/src/codegen/java/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/src/codegen/java/org/apache/fop/tools/model2translation.xsl b/src/codegen/java/org/apache/fop/tools/model2translation.xsl
new file mode 100644
index 000000000..a1cf404ee
--- /dev/null
+++ b/src/codegen/java/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/src/documentation/content/xdocs/site.xml b/src/documentation/content/xdocs/site.xml
index 18716903c..b0a3214e7 100644
--- a/src/documentation/content/xdocs/site.xml
+++ b/src/documentation/content/xdocs/site.xml
@@ -155,6 +155,7 @@
<fonts label="Fonts" href="fonts.html"/>
<hyphenation label="Hyphenation" href="hyphenation.html"/>
<extensions label="Extensions" href="extensions.html"/>
+ <events label="Events" href="events.html"/>
</features>
</trunk>
diff --git a/src/documentation/content/xdocs/trunk/events.xml b/src/documentation/content/xdocs/trunk/events.xml
new file mode 100644
index 000000000..d2fe5318c
--- /dev/null
+++ b/src/documentation/content/xdocs/trunk/events.xml
@@ -0,0 +1,422 @@
+<?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$ -->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V2.0//EN" "http://forrest.apache.org/dtd/document-v20.dtd">
+<document>
+ <header>
+ <title>Events/Processing Feedback</title>
+ <version>$Revision: 634267 $</version>
+ </header>
+ <body>
+ <section id="introduction">
+ <title>Introduction</title>
+ <p>
+ In versions until 0.20.5, FOP used
+ <a href="http://excalibur.apache.org/framework/index.html">Avalon-style Logging</a> where
+ it was possible to supply a logger per processing run. During the redesign
+ the logging infrastructure was switched over to
+ <a href="http://commons.apache.org/logging/">Commons Logging</a> which is (like Log4J or
+ java.util.logging) a "static" logging framework (the logger is accessed through static
+ variables). This made it very difficult in a multi-threaded system to retrieve information
+ for a single processing run.
+ </p>
+ <p>
+ With FOP's event subsystem, we'd like to close this gap again and even go further. The
+ first point is to realize that we have two kinds of "logging". Firstly, we have the logging
+ infrastructure for the (FOP) developer who needs to be able to enable finer log messages
+ for certain parts of FOP to track down a certain problem. Secondly, we have the user who
+ would like to be informed about missing images, overflowing lines or substituted fonts.
+ These messages (or events) are targeted at less technical people and may ideally be
+ localized (translated). Furthermore, tool and solution builders would like to integrate
+ FOP into their own solutions. For example, an FO editor should be able to point the user
+ to the right place where a particular problem occurred while developing a document template.
+ Finally, some integrators would like to abort processing if a resource (an image or a font)
+ has not been found, while others would simply continue. The event system allows to
+ react on these events.
+ </p>
+ <p>
+ On this page, we won't discuss logging as such. We will show how the event subsystem can
+ be used for various tasks. We'll first look at the event subsystem from the consumer side.
+ Finally, the production of events inside FOP will be discussed (this is mostly interesting
+ for FOP developers only).
+ </p>
+ </section>
+ <section id="consumer">
+ <title>The consumer side</title>
+ <p>
+ The event subsystem is located in the <code>org.apache.fop.events</code> package and its
+ base is the <code>Event</code> class. An instance is created for each event and is sent
+ to a set of <code>EventListener</code> instances by the <code>EventBroadcaster</code>.
+ An <code>Event</code> contains:
+ </p>
+ <ul>
+ <li>an event ID,</li>
+ <li>a source object (which generated the event),</li>
+ <li>a severity level (Info, Warning, Error and Fatal Error) and</li>
+ <li>a map of named parameters.</li>
+ </ul>
+ <p>
+ The <code>EventFormatter</code> class can be used to translate the events into
+ human-readable, localized messages.
+ </p>
+ <p>
+ A full example of what is shown here can be found in the
+ <code>examples/embedding/java/embedding/events</code> directory in the FOP distribution.
+ The example can also be accessed
+ <a href="http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/examples/embedding/java/embedding/events/">via the web</a>.
+ </p>
+ <section id="write-listener">
+ <title>Writing an EventListener</title>
+ <p>
+ The following code sample shows a very simple EventListener. It basically just sends
+ all events to System.out (stdout) or System.err (stderr) depending on the event severity.
+ </p>
+ <source><![CDATA[import org.apache.fop.events.Event;
+import org.apache.fop.events.EventFormatter;
+import org.apache.fop.events.EventListener;
+import org.apache.fop.events.model.EventSeverity;
+
+/** A simple event listener that writes the events to stdout and stderr. */
+public class SysOutEventListener implements EventListener {
+
+ /** {@inheritDoc} */
+ public void processEvent(Event event) {
+ String msg = EventFormatter.format(event);
+ EventSeverity severity = event.getSeverity();
+ if (severity == EventSeverity.INFO) {
+ System.out.println("[INFO ] " + msg);
+ } else if (severity == EventSeverity.WARN) {
+ System.out.println("[WARN ] " + msg);
+ } else if (severity == EventSeverity.ERROR) {
+ System.err.println("[ERROR] " + msg);
+ } else if (severity == EventSeverity.FATAL) {
+ System.err.println("[FATAL] " + msg);
+ } else {
+ assert false;
+ }
+ }
+}]]></source>
+ <p>
+ You can see that for every event the method <code>processEvent</code> of the
+ <code>EventListener</code> will be called. Inside this method you can do whatever
+ processing you would like including throwing a <code>RuntimeException</code>, if you want
+ to abort the current processing run.
+ </p>
+ <p>
+ The code above also shows how you can turn an event into a human-readable, localized
+ message that can be presented to a user. The <code>EventFormatter</code> class does
+ this for you. It provides additional methods if you'd like to explicitly specify
+ the locale.
+ </p>
+ <p>
+ It is possible to gather all events for a whole processing run so they can be
+ evaluated afterwards. However, care should be taken about memory consumption since
+ the events provide references to objects inside FOP which may themselves have
+ references to other objects. So holding on to these objects may mean that whole
+ object trees cannot be released!
+ </p>
+ </section>
+ <section id="add-listener">
+ <title>Adding an EventListener</title>
+ <p>
+ To register the event listener with FOP, get the <code>EventBroadcaster</code> which
+ is associated with the user agent (<code>FOUserAgent</code>) and add it there:
+ </p>
+ <source><![CDATA[FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
+foUserAgent.getEventBroadcaster().addEventListener(new SysOutEventListener());]]></source>
+ <p>
+ Please note that this is done separately for each processing run, i.e. for each
+ new user agent.
+ </p>
+ </section>
+ <section id="listener-example1">
+ <title>An additional listener example</title>
+ <p>
+ Here's an additional example of an event listener:
+ </p>
+ <p>
+ By default, FOP continues processing even if an image wasn't found. If you have
+ more strict requirements and want FOP to stop if an image is not available, you can
+ do something like the following:
+ </p>
+ <source><![CDATA[public class MyEventListener implements EventListener {
+
+ public void processEvent(Event event) {
+ if ("org.apache.fop.events.ResourceEventProducer.imageNotFound"
+ .equals(event.getEventID())) {
+
+ //Get the FileNotFoundException that's part of the event's parameters
+ FileNotFoundException fnfe = (FileNotFoundException)event.getParam("fnfe");
+
+ throw new RuntimeException(EventFormatter.format(event), fnfe);
+ } else {
+ //ignore all other events (or do something of your choice)
+ }
+ }
+
+}]]></source>
+ <p>
+ This throws a <code>RuntimeException</code> with the <code>FileNotFoundException</code>
+ as the cause. Further processing effectively stops in FOP. You can catch the exception
+ in your code and react as you see necessary.
+ </p>
+ </section>
+ </section>
+ <section id="producer">
+ <title>The producer side (for FOP developers)</title>
+ <p>
+ This section is primarily for FOP and FOP plug-in developers. It describes how to use
+ the event subsystem for producing events.
+ </p>
+ <note>
+ The event package has been designed in order to be theoretically useful for use cases
+ outside FOP. If you think this is interesting independently from FOP, please talk to
+ <a href="mailto:fop-dev@xmlgraphics.apache.org">us</a>.
+ </note>
+ <section id="basic-event-production">
+ <title>Producing and sending an event</title>
+ <p>
+ The basics are very simple. Just instantiate an <code>Event</code> object and fill
+ it with the necessary parameters. Then pass it to the <code>EventBroadcaster</code>
+ which distributes the events to the interested listeneners. Here's a code example:
+ </p>
+ <source><![CDATA[Event ev = new Event(this, "complain", EventSeverity.WARN,
+ Event.paramsBuilder()
+ .param("reason", "I'm tired")
+ .param("blah", new Integer(23))
+ .build());
+EventBroadcaster broadcaster = [get it from somewhere];
+broadcaster.broadcastEvent(ev);
+]]></source>
+ <p>
+ The <code>Event.paramsBuilder()</code> is a
+ <a href="http://en.wikipedia.org/wiki/Fluent_interface">fluent interface</a>
+ to help with the build-up of the parameters. You could just as well instantiate a
+ <code>Map</code> (<code>Map&lt;String, Object&gt;</code>) and fill it with values.
+ </p>
+ </section>
+ <section id="event-producer">
+ <title>The EventProducer interface</title>
+ <p>
+ To simplify event production, the event subsystem provides the <code>EventProducer</code>
+ interface. You can create interfaces which extend <code>EventProducer</code>. These
+ interfaces will contain one method per event to be generated. By contract, each event
+ method must have as its first parameter a parameter named "source" (Type Object) which
+ indicates the object that generated the event. After that come an arbitrary number of
+ parameters of any type as needed by the event.
+ </p>
+ <p>
+ The event producer interface does not need to have any implementation. The implementation
+ is produced at runtime by a dynamic proxy created by <code>DefaultEventBroadcaster</code>.
+ The dynamic proxy creates <code>Event</code> instances for each method call against
+ the event producer interface. Each parameter (except "source") is added to the event's
+ parameter map.
+ </p>
+ <p>
+ To simplify the code needed to get an instance of the event producer interface it is
+ suggested to create a public inner provider class inside the interface.
+ </p>
+ <p>
+ Here's an example of such an event producer interface:
+ </p>
+ <source><![CDATA[public interface MyEventProducer extends EventProducer {
+
+ public class Provider {
+
+ public static MyEventProducer get(EventBroadcaster broadcaster) {
+ return (MyEventProducer)broadcaster.getEventProducerFor(MyEventProducer.class);
+ }
+ }
+
+ /**
+ * 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);
+
+}]]></source>
+ <p>
+ To produce the same event as in the first example above, you'd use the following code:
+ </p>
+ <source><![CDATA[EventBroadcaster broadcaster = [get it from somewhere];
+TestEventProducer producer = TestEventProducer.Provider.get(broadcaster);
+producer.complain(this, "I'm tired", 23);]]></source>
+ </section>
+ <section id="event-model">
+ <title>The event model</title>
+ <p>
+ Inside an invocation handler for a dynamic proxy, there's no information about
+ the names of each parameter. The JVM doesn't provide it. The only thing you know is
+ the interface and method name. In order to properly fill the <code>Event</code>'s
+ parameter map we need to know the parameter names. These are retrieved from an
+ event object model. This is found in the <code>org.apache.fop.events.model</code>
+ package. The data for the object model is retrieved from an XML representation of the
+ event model that is loaded as a resource. The XML representation is generated using an
+ Ant task at build time (<code>ant resourcegen</code>). The Ant task (found in
+ <code>src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java</code>)
+ scans FOP's sources for descendants of the <code>EventProducer</code> interface and
+ uses <a href="http://qdox.codehaus.org/">QDox</a> to parse these interfaces.
+ </p>
+ <p>
+ Primarily, the QDox-based collector task records the parameters' names and types.
+ Furthermore, it extracts additional attributes embedded as Javadoc comments from
+ the methods. At the moment, the only such attribute is "@event.severity" which indicates
+ the default event severity (which can be changed by event listeners). The example event
+ producer above shows the Javadocs for an event method.
+ </p>
+ <p>
+ There's one more information that is extracted from the event producer information for
+ the event model: an optional primary exception. The first exception in the "throws"
+ declaration of an event method is noted. It is used to throw an exception from
+ the invocation handler if the event has an event severity of "FATAL" when all
+ listeners have been called (listeners can update the event severity). Please note
+ that an implementation of
+ <code>org.apache.fop.events.EventExceptionManager$ExceptionFactory</code> has to be
+ registered for the <code>EventExceptionManager</code> to be able to construct the
+ exception from an event.
+ </p>
+ <p>
+ For a given application, there can be multiple event models active at the same time.
+ In FOP, each renderer is considered to be a plug-in and provides its own specific
+ event model. The individual event models are provided through an
+ <code>EventModelFactory</code>. This interface is implemented for each event model
+ and registered through the service provider mechanism
+ (see the <a href="#plug-ins">plug-ins section</a> for details).
+ </p>
+ </section>
+ <section id="event-severity">
+ <title>Event severity</title>
+ <p>
+ Four different levels of severity for events has been defined:
+ </p>
+ <ol>
+ <li>INFO: informational only</li>
+ <li>WARN: a Warning</li>
+ <li>ERROR: an error condition from which FOP can recover. FOP will continue processing.</li>
+ <li>FATAL: a fatal error which causes an exception in the end and FOP will stop processing.</li>
+ </ol>
+ <p>
+ Event listeners can choose to ignore certain events based on their event severity.
+ Please note that you may recieve an event "twice" in a specific case: if there is
+ a fatal error an event is generated and sent to the listeners. After that an exception
+ is thrown with the same information and processing stops. If the fatal event is
+ shown to the user and the following exception is equally presented to the user it
+ may appear that the event is duplicated. Of course, the same information is just
+ published through two different channels.
+ </p>
+ </section>
+ <section id="plug-ins">
+ <title>Plug-ins to the event subsystem</title>
+ <p>
+ The event subsystem is extensible. There are a number of extension points:
+ </p>
+ <ul>
+ <li>
+ <strong><code>org.apache.fop.events.model.EventModelFactory</code>:</strong> Provides
+ an event model to the event subsystem.
+ </li>
+ <li>
+ <strong><code>org.apache.fop.events.EventExceptionManager$ExceptionFactory</code>:</strong>
+ Creates exceptions for events, i.e. turns an event into a specific exception.
+ </li>
+ </ul>
+ <p>
+ The names in bold above are used as filenames for the service provider files that
+ are placed in the <code>META-INF/services</code> directory. That way, they are
+ automatically detected. This is a mechanism defined by the
+ <a href="http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider">JAR file specification</a>.
+ </p>
+ </section>
+ <section id="l10n">
+ <title>Localization (L10n)</title>
+ <p>
+ One goal of the event subsystem was to have localized (translated) event messages.
+ The <code>EventFormatter</code> class can be used to convert an event to a
+ human-readable message. Each <code>EventProducer</code> can provide its own XML-based
+ translation file. If there is none, a central translation file is used, called
+ "EventFormatter.xml" (found in the same directory as the <code>EventFormatter</code>
+ class).
+ </p>
+ <p>
+ The XML format used by the <code>EventFormatter</code> is the same as
+ <a href="ext:cocoon">Apache Cocoon's</a> catalog format. Here's an example:
+ </p>
+ <source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="locator">
+ [ (See position {loc})| (See {#gatherContextInfo})| (No context info available)]
+ </message>
+ <message key="org.apache.fop.render.rtf.RTFEventProducer.explicitTableColumnsRequired">
+ RTF output requires that all table-columns for a table are defined. Output will be incorrect.{{locator}}
+ </message>
+ <message key="org.apache.fop.render.rtf.RTFEventProducer.ignoredDeferredEvent">
+ Ignored deferred event for {node} ({start,if,start,end}).{{locator}}
+ </message>
+</catalogue>
+]]></source>
+ <p>
+ The example (extracted from the RTF handler's event producer) has message templates for
+ two event methods. The class used to do variable replacement in the templates is
+ <code>org.apache.fop.util.text.AdvancedMessageFormat</code> which is more powerful
+ than the <code>MessageFormat</code> classes provided by the Java class library
+ (<code>java.util.text</code> package).
+ </p>
+ <p>
+ "locator" is a template that is reused by the other message templates
+ by referencing it through "{{locator}}". This is some kind of include command.
+ </p>
+ <p>
+ Normal event parameters are accessed by name inside single curly braces, for example:
+ "{node}". For objects, this format just uses the <code>toString()</code> method to turn
+ the object into a string, unless there is an <code>ObjectFormatter</code> registered
+ for that type (there's an example for <code>org.xml.sax.Locator</code>).
+ </p>
+ <p>
+ The single curly braces pattern supports additional features. For example, it is possible
+ to do this: "{start,if,start,end}". "if" here is a special field modifier that evaluates
+ "start" as a boolean and if that is true returns the text right after the second comma
+ ("start"). Otherwise it returns the text after the third comma ("end"). The "equals"
+ modifier is similar to "if" but it takes as an additional (comma-separated) parameter
+ right after the "equals" modifier, a string that is compared to the value of the variable.
+ An example: {severity,equals,EventSeverity:FATAL,,some text} (this adds "some text" if
+ the severity is not FATAL).
+ </p>
+ <p>
+ Additional such modifiers can be added by implementing the
+ <code>AdvancedMessageFormat$Part</code> and <code>AdvancedMessageFormat$PartFactory</code>
+ interfaces.
+ </p>
+ <p>
+ Square braces can be used to specify optional template sections. The whole section will
+ be omitted if any of the variables used within are unavailable. Pipe (|) characters can
+ be used to specify alternative sub-templates (see "locator" above for an example).
+ </p>
+ <p>
+ Developers can also register a function (in the above example:
+ <code>{#gatherContextInfo})</code>
+ to do more complex information rendering. These functions are implementations of the
+ <code>AdvancedMessageFormat$Function</code> interface. Please take care that this is
+ done in a locale-independent way as there is no locale information available, yet.
+ </p>
+ </section>
+ </section>
+ </body>
+</document>
diff --git a/src/java/META-INF/services/org.apache.fop.events.EventExceptionManager$ExceptionFactory b/src/java/META-INF/services/org.apache.fop.events.EventExceptionManager$ExceptionFactory
new file mode 100644
index 000000000..9fa7b8d04
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.fop.events.EventExceptionManager$ExceptionFactory
@@ -0,0 +1,4 @@
+org.apache.fop.events.ValidationExceptionFactory
+org.apache.fop.events.PropertyExceptionFactory
+org.apache.fop.events.UnsupportedOperationExceptionFactory
+org.apache.fop.layoutmgr.LayoutException$LayoutExceptionFactory
diff --git a/src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory b/src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory
new file mode 100644
index 000000000..8dc13dbfd
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.fop.events.model.EventModelFactory
@@ -0,0 +1,7 @@
+org.apache.fop.events.FOPEventModelFactory
+org.apache.fop.render.afp.AFPEventProducer$EventModelFactory
+org.apache.fop.render.bitmap.BitmapRendererEventProducer$EventModelFactory
+org.apache.fop.render.pcl.PCLEventProducer$EventModelFactory
+org.apache.fop.render.pdf.PDFEventProducer$EventModelFactory
+org.apache.fop.render.ps.PSEventProducer$EventModelFactory
+org.apache.fop.render.rtf.RTFEventProducer$EventModelFactory
diff --git a/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function b/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function
new file mode 100644
index 000000000..375130f2a
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function
@@ -0,0 +1 @@
+org.apache.fop.fo.FONode$GatherContextInfoFunction
diff --git a/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter b/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter
new file mode 100644
index 000000000..9e3860b31
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter
@@ -0,0 +1 @@
+org.apache.fop.util.text.LocatorFormatter
diff --git a/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory b/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory
new file mode 100644
index 000000000..1647fb8d5
--- /dev/null
+++ b/src/java/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory
@@ -0,0 +1,6 @@
+org.apache.fop.util.text.IfFieldPart$Factory
+org.apache.fop.util.text.EqualsFieldPart$Factory
+org.apache.fop.util.text.ChoiceFieldPart$Factory
+org.apache.fop.util.text.HexFieldPart$Factory
+org.apache.fop.util.text.GlyphNameFieldPart$Factory
+org.apache.fop.events.EventFormatter$LookupFieldPartFactory
diff --git a/src/java/org/apache/fop/apps/FOPException.java b/src/java/org/apache/fop/apps/FOPException.java
index 3e1c180e9..851712b09 100644
--- a/src/java/org/apache/fop/apps/FOPException.java
+++ b/src/java/org/apache/fop/apps/FOPException.java
@@ -32,6 +32,8 @@ public class FOPException extends SAXException {
private String systemId;
private int line;
private int column;
+
+ private String localizedMessage;
/**
* Constructs a new FOP exception with the specified detail message.
@@ -210,5 +212,24 @@ public class FOPException extends SAXException {
}
}
}
+
+ /**
+ * Sets the localized message for this exception.
+ * @param msg the localized message
+ */
+ public void setLocalizedMessage(String msg) {
+ this.localizedMessage = msg;
+ }
+
+ /** {@inheritDoc} */
+ public String getLocalizedMessage() {
+ if (this.localizedMessage != null) {
+ return this.localizedMessage;
+ } else {
+ return super.getLocalizedMessage();
+ }
+ }
+
+
}
diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java
index a7405b466..307087f74 100644
--- a/src/java/org/apache/fop/apps/FOUserAgent.java
+++ b/src/java/org/apache/fop/apps/FOUserAgent.java
@@ -36,6 +36,12 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
import org.apache.fop.Version;
+import org.apache.fop.events.DefaultEventBroadcaster;
+import org.apache.fop.events.Event;
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.events.FOPEventListenerProxy;
+import org.apache.fop.events.LoggingEventListener;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.RendererFactory;
@@ -89,6 +95,7 @@ public class FOUserAgent {
private Renderer rendererOverride = null;
private FOEventHandler foEventHandlerOverride = null;
private boolean locatorEnabled = true; // true by default (for error messages).
+ private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster();
/** Producer: Metadata element for the system/software that produces
* the document. (Some renderers can store this in the document.)
@@ -530,5 +537,43 @@ public class FOUserAgent {
return locatorEnabled;
}
+ /**
+ * Returns the event broadcaster that control events sent inside a processing run. Clients
+ * can register event listeners with the event broadcaster to listen for events that occur
+ * while a document is being processed.
+ * @return the event broadcaster.
+ */
+ public EventBroadcaster getEventBroadcaster() {
+ return this.eventBroadcaster;
+ }
+
+ private class FOPEventBroadcaster extends DefaultEventBroadcaster {
+
+ private FOPEventListenerProxy rootListener;
+
+ public FOPEventBroadcaster() {
+ this.rootListener = new FOPEventListenerProxy(
+ this.listeners, FOUserAgent.this);
+ }
+
+ /** {@inheritDoc} */
+ public void broadcastEvent(Event event) {
+ rootListener.processEvent(event);
+ }
+
+ /** {@inheritDoc} */
+ protected EventProducer createProxyFor(Class clazz) {
+ if (!this.listeners.hasEventListeners()) {
+ //Backwards-compatibility: Make sure at least the LoggingEventListener is plugged
+ //in so no events are just silently swallowed.
+ addEventListener(
+ new LoggingEventListener(LogFactory.getLog(FOUserAgent.class)));
+
+ }
+ return super.createProxyFor(clazz);
+ }
+
+ }
+
}
diff --git a/src/java/org/apache/fop/area/AreaEventProducer.java b/src/java/org/apache/fop/area/AreaEventProducer.java
new file mode 100644
index 000000000..7747d2d79
--- /dev/null
+++ b/src/java/org/apache/fop/area/AreaEventProducer.java
@@ -0,0 +1,91 @@
+/*
+ * 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.area;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+
+/**
+ * Event producer interface for events related to the area tree.
+ */
+public interface AreaEventProducer extends EventProducer {
+
+ /**
+ * Provider class for the event producer.
+ */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static AreaEventProducer get(EventBroadcaster broadcaster) {
+ return (AreaEventProducer)broadcaster.getEventProducerFor(
+ AreaEventProducer.class);
+ }
+ }
+
+ /**
+ * An unresolved ID reference was encountered.
+ * @param source the event source
+ * @param type the type of reference
+ * @param id the unresolved ID
+ * @event.severity WARN
+ */
+ void unresolvedIDReference(Object source, String type, String id);
+
+ /**
+ * An unresolved ID reference was encountered on a page.
+ * @param source the event source
+ * @param page the page the ID reference was found on
+ * @param id the unresolved ID
+ * @event.severity WARN
+ */
+ void unresolvedIDReferenceOnPage(Object source, String page, String id);
+
+ /**
+ * A page could not be loaded/deserialized from a file.
+ * @param source the event source
+ * @param page the page to be loaded
+ * @param e the original exception
+ * @event.severity ERROR
+ */
+ void pageLoadError(Object source, String page, Exception e);
+
+ /**
+ * A page could not be saved/serialized to a file.
+ * @param source the event source
+ * @param page the page to be serialized
+ * @param e the original exception
+ * @event.severity ERROR
+ */
+ void pageSaveError(Object source, String page, Exception e);
+
+ /**
+ * A page could not be rendered.
+ * @param source the event source
+ * @param page the page to be serialized
+ * @param e the original exception
+ * @event.severity ERROR
+ */
+ void pageRenderingError(Object source, String page, Exception e);
+
+}
diff --git a/src/java/org/apache/fop/area/AreaTreeHandler.java b/src/java/org/apache/fop/area/AreaTreeHandler.java
index 7454f4667..d3ea41554 100644
--- a/src/java/org/apache/fop/area/AreaTreeHandler.java
+++ b/src/java/org/apache/fop/area/AreaTreeHandler.java
@@ -325,8 +325,9 @@ public class AreaTreeHandler extends FOEventHandler {
if (pageVPList != null) {
res.resolveIDRef(ids[count], pageVPList);
} else {
- log.warn(odi.getName() + ": Unresolved id reference \""
- + ids[count] + "\" found.");
+ AreaEventProducer eventProducer = AreaEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.unresolvedIDReference(this, odi.getName(), ids[count]);
idTracker.addUnresolvedIDRef(ids[count], res);
}
}
diff --git a/src/java/org/apache/fop/area/AreaTreeObject.java b/src/java/org/apache/fop/area/AreaTreeObject.java
index 94250deba..1c2269a01 100644
--- a/src/java/org/apache/fop/area/AreaTreeObject.java
+++ b/src/java/org/apache/fop/area/AreaTreeObject.java
@@ -23,7 +23,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
-import org.apache.fop.util.QName;
+import org.apache.xmlgraphics.util.QName;
/**
* Abstract base class for all area tree objects.
@@ -53,12 +53,12 @@ public abstract class AreaTreeObject {
if (atts.size() == 0) {
return;
}
- Iterator iter = atts.keySet().iterator();
+ Iterator iter = atts.entrySet().iterator();
while (iter.hasNext()) {
- QName qName = (QName)iter.next();
- String value = (String)atts.get(qName);
+ Map.Entry entry = (Map.Entry)iter.next();
+ String value = (String)entry.getValue();
//The casting is only to ensure type safety (too bad we can't use generics, yet)
- setForeignAttribute(qName, value);
+ setForeignAttribute((QName)entry.getKey(), value);
}
}
diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java
index fafb99ed6..19edd3d5e 100644
--- a/src/java/org/apache/fop/area/AreaTreeParser.java
+++ b/src/java/org/apache/fop/area/AreaTreeParser.java
@@ -50,6 +50,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.util.QName;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.Trait.Background;
@@ -78,7 +79,6 @@ import org.apache.fop.util.ColorUtil;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactoryRegistry;
import org.apache.fop.util.DefaultErrorListener;
-import org.apache.fop.util.QName;
/**
* This is a parser for the area tree XML (intermediate format) which is used to reread an area
diff --git a/src/java/org/apache/fop/area/CachedRenderPagesModel.java b/src/java/org/apache/fop/area/CachedRenderPagesModel.java
index 363fa02d1..b34a7e8d1 100644
--- a/src/java/org/apache/fop/area/CachedRenderPagesModel.java
+++ b/src/java/org/apache/fop/area/CachedRenderPagesModel.java
@@ -19,24 +19,27 @@
package org.apache.fop.area;
-import org.apache.commons.io.IOUtils;
-import org.apache.fop.apps.FOPException;
-import org.apache.fop.apps.FOUserAgent;
-import org.apache.fop.fonts.FontInfo;
-import org.xml.sax.SAXException;
-
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Iterator;
-
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.FileInputStream;
-import java.io.ObjectOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.OutputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedInputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.xml.sax.SAXException;
+
+import org.apache.commons.io.IOUtils;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.events.ResourceEventProducer;
+import org.apache.fop.fonts.FontInfo;
/**
* A simple cached render pages model.
@@ -69,46 +72,39 @@ public class CachedRenderPagesModel extends RenderPagesModel {
*/
protected boolean checkPreparedPages(PageViewport newpage, boolean renderUnresolved) {
for (Iterator iter = prepared.iterator(); iter.hasNext();) {
- PageViewport p = (PageViewport)iter.next();
- if (p.isResolved() || renderUnresolved) {
- if (p != newpage) {
+ PageViewport pageViewport = (PageViewport)iter.next();
+ if (pageViewport.isResolved() || renderUnresolved) {
+ if (pageViewport != newpage) {
try {
// load page from cache
- String name = (String)pageMap.get(p);
+ String name = (String)pageMap.get(pageViewport);
File tempFile = new File(baseDir, name);
log.debug("Loading page from: " + tempFile);
ObjectInputStream in = new ObjectInputStream(
new BufferedInputStream(
new FileInputStream(tempFile)));
try {
- p.loadPage(in);
+ pageViewport.loadPage(in);
} finally {
IOUtils.closeQuietly(in);
}
if (!tempFile.delete()) {
- log.warn("Temporary file could not be deleted: " + tempFile);
+ ResourceEventProducer eventProducer
+ = ResourceEventProducer.Provider.get(
+ renderer.getUserAgent().getEventBroadcaster());
+ eventProducer.cannotDeleteTempFile(this, tempFile);
}
- pageMap.remove(p);
+ pageMap.remove(pageViewport);
} catch (Exception e) {
- log.error(e);
+ AreaEventProducer eventProducer
+ = AreaEventProducer.Provider.get(
+ renderer.getUserAgent().getEventBroadcaster());
+ eventProducer.pageLoadError(this, pageViewport.getPageNumberString(), e);
}
}
- try {
- renderer.renderPage(p);
- if (!p.isResolved()) {
- String[] idrefs = p.getIDRefs();
- for (int count = 0; count < idrefs.length; count++) {
- log.warn("Page " + p.getPageNumberString()
- + ": Unresolved id reference \"" + idrefs[count]
- + "\" found.");
- }
- }
- } catch (Exception e) {
- // use error handler to handle this FOP or IO Exception
- log.error(e);
- }
- p.clear();
+ renderPage(pageViewport);
+ pageViewport.clear();
iter.remove();
} else {
if (!renderer.supportsOutOfOrder()) {
@@ -147,8 +143,11 @@ public class CachedRenderPagesModel extends RenderPagesModel {
if (log.isDebugEnabled()) {
log.debug("Page saved to temporary file: " + tempFile);
}
- } catch (Exception e) {
- log.error(e);
+ } catch (IOException ioe) {
+ AreaEventProducer eventProducer
+ = AreaEventProducer.Provider.get(
+ renderer.getUserAgent().getEventBroadcaster());
+ eventProducer.pageSaveError(this, page.getPageNumberString(), ioe);
}
}
diff --git a/src/java/org/apache/fop/area/PageViewport.java b/src/java/org/apache/fop/area/PageViewport.java
index af557ade9..da7ef1def 100644
--- a/src/java/org/apache/fop/area/PageViewport.java
+++ b/src/java/org/apache/fop/area/PageViewport.java
@@ -21,14 +21,15 @@ package org.apache.fop.area;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
-import java.io.ObjectOutputStream;
+import java.io.IOException;
import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
-import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -516,9 +517,9 @@ public class PageViewport extends AreaTreeObject implements Resolvable, Cloneabl
* The map of unresolved references are set on the page so that
* the resolvers can be properly serialized and reloaded.
* @param out the object output stream to write the contents
- * @throws Exception if there is a problem saving the page
+ * @throws IOException in case of an I/O error while serializing the page
*/
- public void savePage(ObjectOutputStream out) throws Exception {
+ public void savePage(ObjectOutputStream out) throws IOException {
// set the unresolved references so they are serialized
page.setUnresolvedReferences(unresolvedIDRefs);
out.writeObject(page);
@@ -531,9 +532,10 @@ public class PageViewport extends AreaTreeObject implements Resolvable, Cloneabl
* if there are any unresolved references that were resolved
* while saved they will be resolved on the page contents.
* @param in the object input stream to read the page from
- * @throws Exception if there is an error loading the page
+ * @throws ClassNotFoundException if a class was not found while loading the page
+ * @throws IOException if an I/O error occurred while loading the page
*/
- public void loadPage(ObjectInputStream in) throws Exception {
+ public void loadPage(ObjectInputStream in) throws IOException, ClassNotFoundException {
page = (Page) in.readObject();
unresolvedIDRefs = page.getUnresolvedReferences();
if (unresolvedIDRefs != null && pendingResolved != null) {
diff --git a/src/java/org/apache/fop/area/RenderPagesModel.java b/src/java/org/apache/fop/area/RenderPagesModel.java
index e080e9cbe..b21566902 100644
--- a/src/java/org/apache/fop/area/RenderPagesModel.java
+++ b/src/java/org/apache/fop/area/RenderPagesModel.java
@@ -156,23 +156,7 @@ public class RenderPagesModel extends AreaTreeModel {
&& pageViewport.getPageSequence().isFirstPage(pageViewport)) {
renderer.startPageSequence(getCurrentPageSequence());
}
- try {
- renderer.renderPage(pageViewport);
- if (!pageViewport.isResolved()) {
- String[] idrefs = pageViewport.getIDRefs();
- for (int count = 0; count < idrefs.length; count++) {
- log.warn("Page " + pageViewport.getPageNumberString()
- + ": Unresolved id reference \"" + idrefs[count]
- + "\" found.");
- }
- }
- } catch (Exception e) {
- // use error handler to handle this FOP or IO Exception
- log.error("Error while rendering page " + pageViewport.getPageIndex(), e);
- if (e instanceof RuntimeException) {
- throw (RuntimeException)e;
- }
- }
+ renderPage(pageViewport);
pageViewport.clear();
iter.remove();
} else {
@@ -186,6 +170,33 @@ public class RenderPagesModel extends AreaTreeModel {
}
/**
+ * Renders the given page and notified about unresolved IDs if any.
+ * @param pageViewport the page to be rendered.
+ */
+ protected void renderPage(PageViewport pageViewport) {
+ try {
+ renderer.renderPage(pageViewport);
+ if (!pageViewport.isResolved()) {
+ String[] idrefs = pageViewport.getIDRefs();
+ for (int count = 0; count < idrefs.length; count++) {
+ AreaEventProducer eventProducer = AreaEventProducer.Provider.get(
+ renderer.getUserAgent().getEventBroadcaster());
+ eventProducer.unresolvedIDReferenceOnPage(this,
+ pageViewport.getPageNumberString(), idrefs[count]);
+ }
+ }
+ } catch (Exception e) {
+ AreaEventProducer eventProducer = AreaEventProducer.Provider.get(
+ renderer.getUserAgent().getEventBroadcaster());
+ eventProducer.pageRenderingError(this,
+ pageViewport.getPageNumberString(), e);
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException)e;
+ }
+ }
+ }
+
+ /**
* Prepare a page.
* An unresolved page can be prepared if the renderer supports
* it and the page will be rendered later.
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;
+ }
+
+}
diff --git a/src/java/org/apache/fop/fo/ElementMapping.java b/src/java/org/apache/fop/fo/ElementMapping.java
index 0f436ae28..a47e6affa 100644
--- a/src/java/org/apache/fop/fo/ElementMapping.java
+++ b/src/java/org/apache/fop/fo/ElementMapping.java
@@ -24,9 +24,10 @@ import java.util.HashMap;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import org.apache.fop.util.QName;
import org.w3c.dom.DOMImplementation;
+import org.apache.xmlgraphics.util.QName;
+
/**
* Abstract base class for Element Mappings (including FO Element Mappings)
* which provide the framework of valid elements and attibutes for a given
diff --git a/src/java/org/apache/fop/fo/ElementMappingRegistry.java b/src/java/org/apache/fop/fo/ElementMappingRegistry.java
index 2ba142203..abc4ec834 100644
--- a/src/java/org/apache/fop/fo/ElementMappingRegistry.java
+++ b/src/java/org/apache/fop/fo/ElementMappingRegistry.java
@@ -23,6 +23,7 @@ import java.util.Iterator;
import java.util.Map;
import org.w3c.dom.DOMImplementation;
+
import org.xml.sax.Locator;
import org.apache.commons.logging.Log;
@@ -144,7 +145,6 @@ public class ElementMappingRegistry {
+ "No element mapping definition found for "
+ FONode.getNodeString(namespaceURI, localName), locator);
} else {
- log.warn("Unknown formatting object " + namespaceURI + "^" + localName);
fobjMaker = new UnknownXMLObj.Maker(namespaceURI);
}
}
diff --git a/src/java/org/apache/fop/fo/FOElementMapping.java b/src/java/org/apache/fop/fo/FOElementMapping.java
index 51ec85d21..62721afeb 100644
--- a/src/java/org/apache/fop/fo/FOElementMapping.java
+++ b/src/java/org/apache/fop/fo/FOElementMapping.java
@@ -22,7 +22,7 @@ package org.apache.fop.fo;
// Java
import java.util.HashMap;
-import org.apache.fop.util.QName;
+import org.apache.xmlgraphics.util.QName;
/**
* Element mapping class for all XSL-FO elements.
diff --git a/src/java/org/apache/fop/fo/FOEventHandler.java b/src/java/org/apache/fop/fo/FOEventHandler.java
index d226cfb6c..5a9a5bb9d 100644
--- a/src/java/org/apache/fop/fo/FOEventHandler.java
+++ b/src/java/org/apache/fop/fo/FOEventHandler.java
@@ -48,6 +48,7 @@ import org.apache.fop.fo.flow.table.TableColumn;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.fo.pagination.PageSequence;
+import org.apache.fop.fonts.FontEventAdapter;
import org.apache.fop.fonts.FontInfo;
/**
@@ -101,6 +102,7 @@ public abstract class FOEventHandler {
public FOEventHandler(FOUserAgent foUserAgent) {
this.foUserAgent = foUserAgent;
this.fontInfo = new FontInfo();
+ this.fontInfo.setEventListener(new FontEventAdapter(foUserAgent.getEventBroadcaster()));
}
/**
diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java
index f0422e414..197a2482d 100644
--- a/src/java/org/apache/fop/fo/FONode.java
+++ b/src/java/org/apache/fop/fo/FONode.java
@@ -21,6 +21,8 @@ package org.apache.fop.fo;
// Java
import java.util.ListIterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
@@ -29,6 +31,8 @@ import org.xml.sax.helpers.LocatorImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.extensions.ExtensionAttachment;
@@ -37,6 +41,7 @@ import org.apache.fop.fo.extensions.svg.SVGElementMapping;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.util.CharUtilities;
import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.text.AdvancedMessageFormat.Function;
/**
* Base class for nodes in the XML tree
@@ -143,6 +148,10 @@ public abstract class FONode implements Cloneable {
return parent.getFOEventHandler();
}
+ /**
+ * Indicates whether this node is a child of an fo:marker.
+ * @return true if this node is a child of an fo:marker
+ */
protected boolean inMarker() {
return getFOEventHandler().inMarker();
}
@@ -239,7 +248,7 @@ public abstract class FONode implements Cloneable {
* @param start starting array element to add
* @param end ending array element to add
* @param pList currently applicable PropertyList
- * @param locator location in fo source file.
+ * @param locator location in the XSL-FO source file.
* @throws FOPException if there's a problem during processing
*/
protected void addCharacters(char[] data, int start, int end,
@@ -343,54 +352,50 @@ public abstract class FONode implements Cloneable {
}
/**
- * Helper function to standardize property error exceptions
- * (e.g., not specifying either an internal- or an external-destination
- * property for an FO:link)
- * @param problem text to display that indicates the problem
- * @throws ValidationException the validation error provoked by the method call
+ * Returns an instance of the FOValidationEventProducer.
+ * @return an event producer for FO validation
*/
- protected void attributeError(String problem)
- throws ValidationException {
- throw new ValidationException(errorText(locator) + getName()
- + ", " + problem, locator);
+ protected FOValidationEventProducer getFOValidationEventProducer() {
+ return FOValidationEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
}
-
+
/**
- * Helper function to standardize attribute warnings
- * (e.g., currently unsupported properties)
- * @param problem text to display that indicates the problem
+ * Helper function to standardize "too many" error exceptions
+ * (e.g., two fo:declarations within fo:root)
+ * @param loc org.xml.sax.Locator object of the error (*not* parent node)
+ * @param nsURI namespace URI of incoming invalid node
+ * @param lName local name (i.e., no prefix) of incoming node
+ * @throws ValidationException the validation error provoked by the method call
*/
- public void attributeWarning(String problem) {
- log.warn(warningText(locator) + getName() + ", " + problem);
+ protected void tooManyNodesError(Locator loc, String nsURI, String lName)
+ throws ValidationException {
+ tooManyNodesError(loc, new QName(nsURI, lName));
}
/**
* Helper function to standardize "too many" error exceptions
* (e.g., two fo:declarations within fo:root)
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
- * @param nsURI namespace URI of incoming invalid node
- * @param lName local name (i.e., no prefix) of incoming node
+ * @param offendingNode the qualified name of the offending node
* @throws ValidationException the validation error provoked by the method call
*/
- protected void tooManyNodesError(Locator loc, String nsURI, String lName)
+ protected void tooManyNodesError(Locator loc, QName offendingNode)
throws ValidationException {
- throw new ValidationException(errorText(loc) + "For " + getName()
- + ", only one " + getNodeString(nsURI, lName) + " may be declared.",
- loc);
+ getFOValidationEventProducer().tooManyNodes(this, getName(), offendingNode, loc);
}
/**
* Helper function to standardize "too many" error exceptions
* (e.g., two fo:declarations within fo:root)
- * This overrloaded method helps make the caller code better self-documenting
+ * This overloaded method helps make the caller code better self-documenting
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param offendingNode incoming node that would cause a duplication.
* @throws ValidationException the validation error provoked by the method call
*/
protected void tooManyNodesError(Locator loc, String offendingNode)
throws ValidationException {
- throw new ValidationException(errorText(loc) + "For " + getName()
- + ", only one " + offendingNode + " may be declared.", loc);
+ tooManyNodesError(loc, new QName(FO_URI, offendingNode));
}
/**
@@ -402,9 +407,23 @@ public abstract class FONode implements Cloneable {
* @throws ValidationException the validation error provoked by the method call
*/
protected void nodesOutOfOrderError(Locator loc, String tooLateNode,
- String tooEarlyNode) throws ValidationException {
- throw new ValidationException(errorText(loc) + "For " + getName() + ", " + tooLateNode
- + " must be declared before " + tooEarlyNode + ".", loc);
+ String tooEarlyNode) throws ValidationException {
+ nodesOutOfOrderError(loc, tooLateNode, tooEarlyNode, false);
+ }
+
+ /**
+ * Helper function to standardize "out of order" exceptions
+ * (e.g., fo:layout-master-set appearing after fo:page-sequence)
+ * @param loc org.xml.sax.Locator object of the error (*not* parent node)
+ * @param tooLateNode string name of node that should be earlier in document
+ * @param tooEarlyNode string name of node that should be later in document
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @throws ValidationException the validation error provoked by the method call
+ */
+ protected void nodesOutOfOrderError(Locator loc, String tooLateNode,
+ String tooEarlyNode, boolean canRecover) throws ValidationException {
+ getFOValidationEventProducer().nodeOutOfOrder(this, getName(),
+ tooLateNode, tooEarlyNode, canRecover, loc);
}
/**
@@ -417,24 +436,24 @@ public abstract class FONode implements Cloneable {
*/
protected void invalidChildError(Locator loc, String nsURI, String lName)
throws ValidationException {
- invalidChildError(loc, nsURI, lName, null);
+ invalidChildError(loc, getName(), nsURI, lName, null);
}
/**
* Helper function to return "invalid child" exceptions with more
* complex validation rules (i.e., needing more explanation of the problem)
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
+ * @param parentName the name of the parent element
* @param nsURI namespace URI of incoming invalid node
* @param lName local name (i.e., no prefix) of incoming node
- * @param ruleViolated text explanation of problem
+ * @param ruleViolated name of the rule violated (used to lookup a resource in a bundle)
* @throws ValidationException the validation error provoked by the method call
*/
- protected void invalidChildError(Locator loc, String nsURI, String lName,
+ protected void invalidChildError(Locator loc, String parentName, String nsURI, String lName,
String ruleViolated)
throws ValidationException {
- throw new ValidationException(errorText(loc) + getNodeString(nsURI, lName)
- + " is not a valid child element of " + getName()
- + ((ruleViolated != null) ? ": " + ruleViolated : "."), loc);
+ getFOValidationEventProducer().invalidChild(this, parentName,
+ new QName(nsURI, lName), ruleViolated, loc);
}
/**
@@ -446,9 +465,22 @@ public abstract class FONode implements Cloneable {
*/
protected void missingChildElementError(String contentModel)
throws ValidationException {
- throw new ValidationException(errorText(locator) + getName()
- + " is missing child elements. \nRequired Content Model: "
- + contentModel, locator);
+ getFOValidationEventProducer().missingChildElement(this, getName(),
+ contentModel, false, locator);
+ }
+
+ /**
+ * Helper function to throw an error caused by missing mandatory child elements.
+ * E.g., fo:layout-master-set not having any page-master child element.
+ * @param contentModel The XSL Content Model for the fo: object or a similar description
+ * indicating the necessary child elements.
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @throws ValidationException the validation error provoked by the method call
+ */
+ protected void missingChildElementError(String contentModel, boolean canRecover)
+ throws ValidationException {
+ getFOValidationEventProducer().missingChildElement(this, getName(),
+ contentModel, canRecover, locator);
}
/**
@@ -458,8 +490,7 @@ public abstract class FONode implements Cloneable {
*/
protected void missingPropertyError(String propertyName)
throws ValidationException {
- throw new ValidationException(errorText(locator) + getName()
- + " is missing required \"" + propertyName + "\" property.", locator);
+ getFOValidationEventProducer().missingProperty(this, getName(), propertyName, locator);
}
/**
@@ -513,9 +544,10 @@ public abstract class FONode implements Cloneable {
/**
* Returns a String containing as much context information as possible about a node. Call
- * this methods only in exceptional conditions because this method may perform quite extensive
+ * this method only in exceptional conditions because this method may perform quite extensive
* information gathering inside the FO tree.
- * @return a String containing
+ * @return a String containing context information
+ * @deprecated Not localized! Should rename getContextInfoAlt() to getContextInfo() when done!
*/
public String getContextInfo() {
StringBuffer sb = new StringBuffer();
@@ -543,6 +575,54 @@ public abstract class FONode implements Cloneable {
}
/**
+ * Returns a String containing as some context information about a node. It does not take the
+ * locator into consideration and returns null if no useful context information can be found.
+ * Call this method only in exceptional conditions because this method may perform quite
+ * extensive information gathering inside the FO tree. All text returned by this method that
+ * is not extracted from document content needs to be locale-independent.
+ * @return a String containing context information
+ */
+ protected String getContextInfoAlt() {
+ String s = gatherContextInfo();
+ if (s != null) {
+ StringBuffer sb = new StringBuffer();
+ if (getLocalName() != null) {
+ sb.append(getName());
+ sb.append(", ");
+ }
+ sb.append("\"");
+ sb.append(s);
+ sb.append("\"");
+ return sb.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /** Function for AdvancedMessageFormat to retrieve context info from an FONode. */
+ public static class GatherContextInfoFunction implements Function {
+
+ /** {@inheritDoc} */
+ public Object evaluate(Map params) {
+ Object obj = params.get("source");
+ if (obj instanceof PropertyList) {
+ PropertyList propList = (PropertyList)obj;
+ obj = propList.getFObj();
+ }
+ if (obj instanceof FONode) {
+ FONode node = (FONode)obj;
+ return node.getContextInfoAlt();
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public Object getName() {
+ return "gatherContextInfo";
+ }
+ }
+
+ /**
* Gathers context information for the getContextInfo() method.
* @return the collected context information or null, if none is available
*/
@@ -687,7 +767,7 @@ public abstract class FONode implements Cloneable {
* of child nodes
* @return the parent node
*/
- public FObj parentNode();
+ FObj parentNode();
/**
* Convenience method with return type of FONode
@@ -695,7 +775,7 @@ public abstract class FONode implements Cloneable {
* <code>(FONode) next();</code>)
* @return the next node (if any), as a type FONode
*/
- public FONode nextNode();
+ FONode nextNode();
/**
* Convenience method with return type of FONode
@@ -703,7 +783,7 @@ public abstract class FONode implements Cloneable {
* <code>(FONode) previous();</code>)
* @return the previous node (if any), as a type FONode
*/
- public FONode previousNode();
+ FONode previousNode();
/**
* Returns the first node in the list, and decreases the index,
@@ -711,7 +791,7 @@ public abstract class FONode implements Cloneable {
* @return the first node in the list
* @throws NoSuchElementException if the list is empty
*/
- public FONode firstNode();
+ FONode firstNode();
/**
* Returns the last node in the list, and advances the
@@ -720,7 +800,7 @@ public abstract class FONode implements Cloneable {
* @return the last node in the list
* @throws NoSuchElementException if the list is empty
*/
- public FONode lastNode();
+ FONode lastNode();
}
}
diff --git a/src/java/org/apache/fop/fo/FOText.java b/src/java/org/apache/fop/fo/FOText.java
index 6d1ac417f..99d37dba9 100644
--- a/src/java/org/apache/fop/fo/FOText.java
+++ b/src/java/org/apache/fop/fo/FOText.java
@@ -19,11 +19,11 @@
package org.apache.fop.fo;
-// Java
import java.awt.Color;
import java.util.NoSuchElementException;
-// FOP
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.flow.Block;
@@ -34,9 +34,6 @@ import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.SpaceProperty;
-// SAX
-import org.xml.sax.Locator;
-
/**
* A text node (PCDATA) in the formatting object tree.
*
@@ -396,7 +393,7 @@ public class FOText extends FONode {
return ca[i];
}
default:
- log.warn("Invalid text-tranform value: " + textTransform);
+ assert false; //should never happen as the property subsystem catches that case
return ca[i];
}
}
diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java
index d02a058fe..84abc4b8b 100644
--- a/src/java/org/apache/fop/fo/FOTreeBuilder.java
+++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java
@@ -31,6 +31,8 @@ import org.xml.sax.helpers.DefaultHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FormattingResults;
@@ -130,6 +132,7 @@ public class FOTreeBuilder extends DefaultHandler {
throw new IllegalStateException("FOTreeBuilder (and the Fop class) cannot be reused."
+ " Please instantiate a new instance.");
}
+
used = true;
empty = true;
rootFObj = null; // allows FOTreeBuilder to be reused
@@ -146,8 +149,10 @@ public class FOTreeBuilder extends DefaultHandler {
public void endDocument() throws SAXException {
this.delegate.endDocument();
if (this.rootFObj == null && empty) {
- throw new ValidationException(
- "Document is empty (something might be wrong with your XSLT stylesheet).");
+ FOValidationEventProducer eventProducer
+ = FOValidationEventProducer.Provider.get(
+ foEventHandler.getUserAgent().getEventBroadcaster());
+ eventProducer.emptyDocument(this);
}
rootFObj = null;
if (log.isDebugEnabled()) {
@@ -178,18 +183,6 @@ public class FOTreeBuilder extends DefaultHandler {
}
}
- /**
- * Finds the {@link Maker} used to create {@link FONode} objects of a particular type
- *
- * @param namespaceURI URI for the namespace of the element
- * @param localName name of the Element
- * @return the ElementMapping.Maker that can create an FO object for this element
- * @throws FOPException if a Maker could not be found for a bound namespace.
- */
- private Maker findFOMaker(String namespaceURI, String localName) throws FOPException {
- return elementMappingRegistry.findFOMaker(namespaceURI, localName, locator);
- }
-
/** {@inheritDoc} */
public void warning(SAXParseException e) {
log.warn(e.getLocalizedMessage());
@@ -258,22 +251,21 @@ public class FOTreeBuilder extends DefaultHandler {
if (rootFObj == null) {
empty = false;
if (!namespaceURI.equals(FOElementMapping.URI)
- || !localName.equals("root")) {
- throw new ValidationException(
- "Error: First element must be the fo:root formatting object. "
- + "Found " + FONode.getNodeString(namespaceURI, localName)
- + " instead."
- + " Please make sure you're producing a valid XSL-FO document.");
+ || !localName.equals("root")) {
+ FOValidationEventProducer eventProducer
+ = FOValidationEventProducer.Provider.get(
+ foEventHandler.getUserAgent().getEventBroadcaster());
+ eventProducer.invalidFORoot(this, FONode.getNodeString(namespaceURI, localName),
+ getEffectiveLocator());
}
} else { // check that incoming node is valid for currentFObj
- if (namespaceURI.equals(FOElementMapping.URI)
- || namespaceURI.equals(ExtensionElementMapping.URI)) {
+ if (currentFObj.getNamespaceURI().equals(FOElementMapping.URI)
+ || currentFObj.getNamespaceURI().equals(ExtensionElementMapping.URI)) {
currentFObj.validateChildNode(locator, namespaceURI, localName);
}
}
- ElementMapping.Maker fobjMaker =
- findFOMaker(namespaceURI, localName);
+ ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
try {
foNode = fobjMaker.make(currentFObj);
@@ -342,8 +334,7 @@ public class FOTreeBuilder extends DefaultHandler {
if (currentPropertyList != null
&& currentPropertyList.getFObj() == currentFObj
&& !foEventHandler.inMarker()) {
- currentPropertyList =
- currentPropertyList.getParentPropertyList();
+ currentPropertyList = currentPropertyList.getParentPropertyList();
}
if (currentFObj.getNameId() == Constants.FO_MARKER) {
@@ -373,7 +364,29 @@ public class FOTreeBuilder extends DefaultHandler {
/** {@inheritDoc} */
public void endDocument() throws SAXException {
currentFObj = null;
- }
+ }
+
+ /**
+ * Finds the {@link Maker} used to create {@link FONode} objects of a particular type
+ *
+ * @param namespaceURI URI for the namespace of the element
+ * @param localName name of the Element
+ * @return the ElementMapping.Maker that can create an FO object for this element
+ * @throws FOPException if a Maker could not be found for a bound namespace.
+ */
+ private Maker findFOMaker(String namespaceURI, String localName) throws FOPException {
+ Maker maker = elementMappingRegistry.findFOMaker(namespaceURI, localName, locator);
+ if (maker instanceof UnknownXMLObj.Maker) {
+ FOValidationEventProducer eventProducer
+ = FOValidationEventProducer.Provider.get(
+ foEventHandler.getUserAgent().getEventBroadcaster());
+ eventProducer.unknownFormattingObject(this, currentFObj.getName(),
+ new QName(namespaceURI, localName),
+ getEffectiveLocator());
+ }
+ return maker;
+ }
+
}
}
diff --git a/src/java/org/apache/fop/fo/FOValidationEventProducer.java b/src/java/org/apache/fop/fo/FOValidationEventProducer.java
new file mode 100644
index 000000000..aa7b14941
--- /dev/null
+++ b/src/java/org/apache/fop/fo/FOValidationEventProducer.java
@@ -0,0 +1,348 @@
+/*
+ * 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.fo;
+
+import org.xml.sax.Locator;
+
+import org.apache.xmlgraphics.util.QName;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.fo.expr.PropertyException;
+
+/**
+ * Event producer interface for XSL-FO validation messages.
+ */
+public interface FOValidationEventProducer extends EventProducer {
+
+ /**
+ * Provider class for the event producer.
+ */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static FOValidationEventProducer get(EventBroadcaster broadcaster) {
+ return (FOValidationEventProducer)broadcaster.getEventProducerFor(
+ FOValidationEventProducer.class);
+ }
+ }
+
+ /**
+ * Too many child nodes.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param offendingNode the offending node
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void tooManyNodes(Object source, String elementName, QName offendingNode,
+ Locator loc) throws ValidationException;
+
+ /**
+ * The node order is wrong.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param tooLateNode string name of node that should be earlier in document
+ * @param tooEarlyNode string name of node that should be later in document
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ */
+ void nodeOutOfOrder(Object source, String elementName,
+ String tooLateNode, String tooEarlyNode, boolean canRecover,
+ Locator loc) throws ValidationException;
+
+ /**
+ * An invalid child was encountered.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param offendingNode the offending node
+ * @param ruleViolated the rule that was violated or null
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ */
+ void invalidChild(Object source, String elementName, QName offendingNode, String ruleViolated,
+ Locator loc) throws ValidationException;
+
+ /**
+ * A required child element is missing.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param contentModel the expected content model
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void missingChildElement(Object source, String elementName,
+ String contentModel, boolean canRecover,
+ Locator loc) throws ValidationException;
+
+ /**
+ * An element is missing a required property.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param propertyName the name of the missing property
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void missingProperty(Object source, String elementName, String propertyName,
+ Locator loc) throws ValidationException;
+
+ /**
+ * An id was used twice in a document.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param id the id that was reused
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void idNotUnique(Object source, String elementName, String id, boolean canRecover,
+ Locator loc) throws ValidationException;
+
+ /**
+ * There are multiple color profiles defined with the same name.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param name the duplicate color profile name
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void colorProfileNameNotUnique(Object source, String elementName, String name,
+ Locator loc);
+
+ /**
+ * There are multiple page masters defined with the same name.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param name the duplicate page master name
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void masterNameNotUnique(Object source, String elementName, String name,
+ Locator loc) throws ValidationException;
+
+ /**
+ * A marker is not an initial child on a node.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param mcname the marker class name
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void markerNotInitialChild(Object source, String elementName, String mcname, Locator loc);
+
+ /**
+ * A marker class name is not unique within the same parent.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param mcname the marker class name
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void markerNotUniqueForSameParent(Object source, String elementName,
+ String mcname, Locator loc);
+
+ /**
+ * An invalid property was found.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param attr the invalid attribute
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void invalidProperty(Object source, String elementName, QName attr, boolean canRecover,
+ Locator loc) throws ValidationException;
+
+ /**
+ * An invalid property value was encountered.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param propName the property name
+ * @param propValue the property value
+ * @param e the property exception caused by the invalid value
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void invalidPropertyValue(Object source, String elementName,
+ String propName, String propValue, PropertyException e,
+ Locator loc);
+
+ /**
+ * A feature is not supported, yet.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param feature the unsupported feature
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void unimplementedFeature(Object source, String elementName, String feature,
+ Locator loc);
+
+ /**
+ * Missing internal-/external-destination on basic-link or bookmark.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void missingLinkDestination(Object source, String elementName, Locator loc)
+ throws ValidationException;
+
+ /**
+ * Indicates a problem while cloning a marker (ex. due to invalid property values).
+ * @param source the event source
+ * @param markerClassName the "marker-class-name" of the marker
+ * @param fe the FOP exception that cause this problem
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void markerCloningFailed(Object source, String markerClassName, FOPException fe, Locator loc);
+
+ /**
+ * A region name is mapped to multiple region classes.
+ * @param source the event source
+ * @param regionName the region name
+ * @param defaultRegionClass1 the first default region class
+ * @param defaultRegionClass2 the second default region class
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void regionNameMappedToMultipleRegionClasses(Object source, String regionName,
+ String defaultRegionClass1, String defaultRegionClass2, Locator loc)
+ throws ValidationException;
+
+ /**
+ * There are multiple flows with the same name.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param flowName the flow name
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void duplicateFlowNameInPageSequence(Object source, String elementName, String flowName,
+ Locator loc) throws ValidationException;
+
+ /**
+ * A flow name could not be mapped to a region.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param flowName the flow name
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void flowNameNotMapped(Object source, String elementName, String flowName,
+ Locator loc) throws ValidationException;
+
+ /**
+ * A page master could not be found.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param masterReference the page master reference
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void masterNotFound(Object source, String elementName, String masterReference,
+ Locator loc) throws ValidationException;
+
+ /**
+ * An illegal region name was used.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param regionName the region name
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void illegalRegionName(Object source, String elementName, String regionName,
+ Locator loc) throws ValidationException;
+
+ /**
+ * A non-zero border and/or padding has been encountered on a region.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param regionName the region name
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void nonZeroBorderPaddingOnRegion(Object source, String elementName, String regionName,
+ boolean canRecover, Locator loc) throws ValidationException;
+
+ /**
+ * If overflow property is set to "scroll", a column-count other than "1" may not be specified.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void columnCountErrorOnRegionBodyOverflowScroll(Object source, String elementName,
+ Locator loc) throws ValidationException;
+
+ /**
+ * fo:root must be root.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void invalidFORoot(Object source, String elementName,
+ Locator loc) throws ValidationException;
+
+ /**
+ * No FO document was found.
+ * @param source the event source
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void emptyDocument(Object source) throws ValidationException;
+
+ /**
+ * An unknown/unsupported formatting object has been encountered.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param offendingNode the offending node
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void unknownFormattingObject(Object source, String elementName,
+ QName offendingNode, Locator loc);
+
+}
diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java
index b2587df2d..a03a351e0 100644
--- a/src/java/org/apache/fop/fo/FObj.java
+++ b/src/java/org/apache/fop/fo/FObj.java
@@ -27,13 +27,15 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.properties.PropertyMaker;
-import org.apache.fop.util.QName;
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
/**
* Base class for representation of formatting objects and their processing.
@@ -171,25 +173,7 @@ public abstract class FObj extends FONode implements Constants {
if (!idrefs.contains(id)) {
idrefs.add(id);
} else {
- if (getUserAgent().validateStrictly()) {
- throw new ValidationException("Property id \"" + id
- + "\" previously used; id values must be unique"
- + " in document.", locator);
- } else {
- if (log.isWarnEnabled()) {
- StringBuffer msg = new StringBuffer();
- msg.append("Found non-unique id on ").append(getName());
- if (locator.getLineNumber() != -1) {
- msg.append(" (at ").append(locator.getLineNumber())
- .append("/").append(locator.getColumnNumber())
- .append(")");
- }
- msg.append("\nAny reference to it will be considered "
- + "a reference to the first occurrence "
- + "in the document.");
- log.warn(msg);
- }
- }
+ getFOValidationEventProducer().idNotUnique(this, getName(), id, true, locator);
}
}
}
@@ -283,17 +267,23 @@ public abstract class FObj extends FONode implements Constants {
return false;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public FONodeIterator getChildNodes() {
- if (firstChild != null) {
+ if (hasChildren()) {
return new FObjIterator(this);
}
return null;
}
/**
+ * Indicates whether this formatting object has children.
+ * @return true if there are children
+ */
+ public boolean hasChildren() {
+ return this.firstChild != null;
+ }
+
+ /**
* Return an iterator over the object's childNodes starting
* at the passed-in node (= first call to iterator.next() will
* return childNode)
@@ -348,8 +338,8 @@ public abstract class FObj extends FONode implements Constants {
if (node instanceof FObj
|| (node instanceof FOText
&& ((FOText) node).willCreateArea())) {
- log.error(
- "fo:marker must be an initial child: " + mcname);
+ getFOValidationEventProducer().markerNotInitialChild(this, getName(),
+ mcname, locator);
return;
} else if (node instanceof FOText) {
iter.remove();
@@ -363,8 +353,8 @@ public abstract class FObj extends FONode implements Constants {
if (!markers.containsKey(mcname)) {
markers.put(mcname, marker);
} else {
- log.error("fo:marker 'marker-class-name' "
- + "must be unique for same parent: " + mcname);
+ getFOValidationEventProducer().markerNotUniqueForSameParent(this, getName(),
+ mcname, locator);
}
}
@@ -383,6 +373,33 @@ public abstract class FObj extends FONode implements Constants {
}
/** {@inheritDoc} */
+ protected String getContextInfoAlt() {
+ StringBuffer sb = new StringBuffer();
+ if (getLocalName() != null) {
+ sb.append(getName());
+ sb.append(", ");
+ }
+ if (hasId()) {
+ sb.append("id=").append(getId());
+ return sb.toString();
+ }
+ String s = gatherContextInfo();
+ if (s != null) {
+ sb.append("\"");
+ if (s.length() < 32) {
+ sb.append(s);
+ } else {
+ sb.append(s.substring(0, 32));
+ sb.append("...");
+ }
+ sb.append("\"");
+ return sb.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /** {@inheritDoc} */
protected String gatherContextInfo() {
if (getLocator() != null) {
return super.gatherContextInfo();
diff --git a/src/java/org/apache/fop/fo/PropertyList.java b/src/java/org/apache/fop/fo/PropertyList.java
index 3d050efed..b6766bfe9 100644
--- a/src/java/org/apache/fop/fo/PropertyList.java
+++ b/src/java/org/apache/fop/fo/PropertyList.java
@@ -20,13 +20,13 @@
package org.apache.fop.fo;
// Java
-import java.text.MessageFormat;
-
import org.xml.sax.Attributes;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.apps.FopFactory;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
@@ -41,7 +41,6 @@ import org.apache.fop.fo.properties.CommonRelativePosition;
import org.apache.fop.fo.properties.CommonTextDecoration;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.PropertyMaker;
-import org.apache.fop.util.QName;
/**
* Class containing the collection of properties for a given FObj.
@@ -150,7 +149,7 @@ public abstract class PropertyList {
* the default value.
* @param propId The Constants ID of the property whose value is desired.
* @return the Property corresponding to that name
- * @throws PropertyException ...
+ * @throws PropertyException if there is a problem evaluating the property
*/
public Property get(int propId) throws PropertyException {
return get(propId, true, true);
@@ -166,7 +165,7 @@ public abstract class PropertyList {
* value is needed
* @param bTryDefault true when the default value may be used as a last resort
* @return the property
- * @throws PropertyException ...
+ * @throws PropertyException if there is a problem evaluating the property
*/
public Property get(int propId, boolean bTryInherit,
boolean bTryDefault) throws PropertyException {
@@ -321,20 +320,18 @@ public abstract class PropertyList {
} else if (!factory.isNamespaceIgnored(attributeNS)) {
ElementMapping mapping = factory.getElementMappingRegistry().getElementMapping(
attributeNS);
+ QName attr = new QName(attributeNS, attributeName);
if (mapping != null) {
- QName attName = new QName(attributeNS, attributeName);
- if (mapping.isAttributeProperty(attName)
+ if (mapping.isAttributeProperty(attr)
&& mapping.getStandardPrefix() != null) {
convertAttributeToProperty(attributes,
- mapping.getStandardPrefix() + ":" + attName.getLocalName(),
+ mapping.getStandardPrefix() + ":" + attr.getLocalName(),
attributeValue);
} else {
- getFObj().addForeignAttribute(attName, attributeValue);
+ getFObj().addForeignAttribute(attr, attributeValue);
}
} else {
- handleInvalidProperty(
- "Error processing foreign attribute: "
- + attributeNS + "/@" + attributeName, attributeName);
+ handleInvalidProperty(attr);
}
}
}
@@ -345,11 +342,8 @@ public abstract class PropertyList {
* @param propertyName the property name to check
* @return true if the base property name and the subproperty name (if any)
* can be correctly mapped to an id
- * @throws ValidationException in case the property name
- * is invalid for the FO namespace
*/
- protected boolean isValidPropertyName(String propertyName)
- throws ValidationException {
+ protected boolean isValidPropertyName(String propertyName) {
int propId = FOPropertyMapping.getPropertyId(
findBasePropertyName(propertyName));
@@ -359,9 +353,6 @@ public abstract class PropertyList {
if (propId == -1
|| (subpropId == -1
&& findSubPropertyName(propertyName) != null)) {
- String errorMessage = MessageFormat.format(
- "Invalid property name ''{0}''.", new Object[] {propertyName});
- handleInvalidProperty(errorMessage, propertyName);
return false;
}
return true;
@@ -382,19 +373,23 @@ public abstract class PropertyList {
if (attributeValue != null) {
- if (!isValidPropertyName(attributeName)) {
- //will log an error or throw an exception
+ if (attributeName.startsWith("xmlns:")) {
+ //Ignore namespace declarations
return;
}
- FObj parentFO = fobj.findNearestAncestorFObj();
-
/* Handle "compound" properties, ex. space-before.minimum */
String basePropertyName = findBasePropertyName(attributeName);
String subPropertyName = findSubPropertyName(attributeName);
int propId = FOPropertyMapping.getPropertyId(basePropertyName);
int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName);
+
+ if (propId == -1
+ || (subpropId == -1 && subPropertyName != null)) {
+ handleInvalidProperty(new QName(null, attributeName));
+ }
+ FObj parentFO = fobj.findNearestAncestorFObj();
PropertyMaker propertyMaker = findMaker(propId);
if (propertyMaker == null) {
@@ -417,8 +412,8 @@ public abstract class PropertyList {
}
prop = propertyMaker.make(this, attributeValue, parentFO);
} else { // e.g. "leader-length.maximum"
- Property baseProperty =
- findBaseProperty(attributes, parentFO, propId,
+ Property baseProperty
+ = findBaseProperty(attributes, parentFO, propId,
basePropertyName, propertyMaker);
prop = propertyMaker.make(baseProperty, subpropId,
this, attributeValue, parentFO);
@@ -427,8 +422,8 @@ public abstract class PropertyList {
putExplicit(propId, prop);
}
} catch (PropertyException e) {
- log.error("Ignoring property: "
- + attributeName + "=\"" + attributeValue + "\" (" + e.getMessage() + ")");
+ fobj.getFOValidationEventProducer().invalidPropertyValue(this, fobj.getName(),
+ attributeName, attributeValue, e, fobj.locator);
}
}
}
@@ -465,18 +460,16 @@ public abstract class PropertyList {
}
/**
- * @param message ...
- * @param propName ...
- * @throws ValidationException ...
+ * Handles an invalid property.
+ * @param attr the invalid attribute
+ * @throws ValidationException if an exception needs to be thrown depending on the
+ * validation settings
*/
- protected void handleInvalidProperty(String message, String propName)
+ protected void handleInvalidProperty(QName attr)
throws ValidationException {
- if (!propName.startsWith("xmlns")) {
- if (fobj.getUserAgent().validateStrictly()) {
- fobj.attributeError(message);
- } else {
- log.error(message + " Property ignored.");
- }
+ if (!attr.getQName().startsWith("xmlns")) {
+ fobj.getFOValidationEventProducer().invalidProperty(this, fobj.getName(),
+ attr, true, fobj.locator);
}
}
diff --git a/src/java/org/apache/fop/fo/expr/FromParentFunction.java b/src/java/org/apache/fop/fo/expr/FromParentFunction.java
index b5a82de56..b09d3c95f 100644
--- a/src/java/org/apache/fop/fo/expr/FromParentFunction.java
+++ b/src/java/org/apache/fop/fo/expr/FromParentFunction.java
@@ -64,7 +64,13 @@ public class FromParentFunction extends FunctionBase {
* non-inherited properties too. Perhaps the result is different for
* a property line line-height which "inherits specified"???
*/
- return pInfo.getPropertyList().getFromParent(FOPropertyMapping.getPropertyId(propName));
+ int propId = FOPropertyMapping.getPropertyId(propName);
+ if (propId < 0) {
+ throw new PropertyException(
+ "Unknown property name used with inherited-property-value function: "
+ + propName);
+ }
+ return pInfo.getPropertyList().getFromParent(propId);
}
}
diff --git a/src/java/org/apache/fop/fo/expr/InheritedPropFunction.java b/src/java/org/apache/fop/fo/expr/InheritedPropFunction.java
index 3e5cadf04..e24c78caa 100644
--- a/src/java/org/apache/fop/fo/expr/InheritedPropFunction.java
+++ b/src/java/org/apache/fop/fo/expr/InheritedPropFunction.java
@@ -58,6 +58,11 @@ public class InheritedPropFunction extends FunctionBase {
}
int propId = FOPropertyMapping.getPropertyId(propName);
+ if (propId < 0) {
+ throw new PropertyException(
+ "Unknown property name used with inherited-property-value function: "
+ + propName);
+ }
return pInfo.getPropertyList().getInherited(propId);
}
diff --git a/src/java/org/apache/fop/fo/expr/NearestSpecPropFunction.java b/src/java/org/apache/fop/fo/expr/NearestSpecPropFunction.java
index 2aab5eee9..cdde96092 100644
--- a/src/java/org/apache/fop/fo/expr/NearestSpecPropFunction.java
+++ b/src/java/org/apache/fop/fo/expr/NearestSpecPropFunction.java
@@ -60,6 +60,11 @@ public class NearestSpecPropFunction extends FunctionBase {
// NOTE: special cases for shorthand property
// Should return COMPUTED VALUE
int propId = FOPropertyMapping.getPropertyId(propName);
+ if (propId < 0) {
+ throw new PropertyException(
+ "Unknown property name used with inherited-property-value function: "
+ + propName);
+ }
return pInfo.getPropertyList().getNearestSpecified(propId);
}
diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
index de1d019f4..fc61167b2 100644
--- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
+++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
@@ -19,14 +19,15 @@
package org.apache.fop.fo.extensions;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.UnknownXMLObj;
import org.apache.fop.fo.extensions.destination.Destination;
-import org.apache.fop.util.QName;
-
-import java.util.HashMap;
-import java.util.Set;
/**
* Element mapping for FOP's proprietary extension to XSL-FO.
diff --git a/src/java/org/apache/fop/fo/extensions/destination/Destination.java b/src/java/org/apache/fop/fo/extensions/destination/Destination.java
index d1e631e42..e3a2bbac4 100644
--- a/src/java/org/apache/fop/fo/extensions/destination/Destination.java
+++ b/src/java/org/apache/fop/fo/extensions/destination/Destination.java
@@ -19,15 +19,15 @@
package org.apache.fop.fo.extensions.destination;
-import org.apache.fop.fo.ValidationException;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.pagination.Root;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
+import org.apache.fop.fo.pagination.Root;
/**
* Class for named destinations in PDF.
@@ -54,7 +54,7 @@ public class Destination extends FONode {
Attributes attlist, PropertyList pList) throws FOPException {
internalDestination = attlist.getValue("internal-destination");
if (internalDestination == null || internalDestination.length() == 0) {
- attributeError("Missing attribute: internal-destination must be specified.");
+ missingPropertyError("internal-destination");
}
}
diff --git a/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java b/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java
index 0fe6ed718..f99f9d947 100644
--- a/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java
+++ b/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java
@@ -62,14 +62,16 @@ public abstract class AbstractListItemPart extends FObj {
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockItemFound) {
- nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ }
+ } else if (!isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else {
+ blockItemFound = true;
}
- } else if (!isBlockItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else {
- blockItemFound = true;
}
}
@@ -79,17 +81,8 @@ public abstract class AbstractListItemPart extends FObj {
protected void endOfNode() throws FOPException {
if (!this.blockItemFound) {
String contentModel = "marker* (%block;)+";
- if (getUserAgent().validateStrictly()) {
- missingChildElementError(contentModel);
- } else {
- StringBuffer message = new StringBuffer(
- errorText(getLocator()));
- message.append(getName())
- .append(" is missing child elements. ")
- .append("Required Content Model: ")
- .append(contentModel);
- log.warn(message.toString());
- }
+ getFOValidationEventProducer().missingChildElement(this, getName(),
+ contentModel, true, getLocator());
}
}
diff --git a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java
index b6b827248..e9a1176d6 100644
--- a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java
+++ b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java
@@ -113,8 +113,10 @@ public abstract class AbstractPageNumberCitation extends FObj {
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/** @return the Common Font Properties. */
diff --git a/src/java/org/apache/fop/fo/flow/BasicLink.java b/src/java/org/apache/fop/fo/flow/BasicLink.java
index 61a4f8d19..b3ef48012 100644
--- a/src/java/org/apache/fop/fo/flow/BasicLink.java
+++ b/src/java/org/apache/fop/fo/flow/BasicLink.java
@@ -76,8 +76,7 @@ public class BasicLink extends Inline {
externalDestination = null;
} else if (externalDestination.length() == 0) {
// slightly stronger than spec "should be specified"
- attributeError("Missing attribute: Either external-destination or " +
- "internal-destination must be specified.");
+ getFOValidationEventProducer().missingLinkDestination(this, getName(), locator);
}
}
@@ -102,15 +101,17 @@ public class BasicLink extends Inline {
* XSL Content Model: marker* (#PCDATA|%inline;|%block;)*
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockOrInlineItemFound) {
- nodesOutOfOrderError(loc, "fo:marker", "(#PCDATA|%inline;|%block;)");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockOrInlineItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "(#PCDATA|%inline;|%block;)");
+ }
+ } else if (!isBlockOrInlineItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else {
+ blockOrInlineItemFound = true;
}
- } else if (!isBlockOrInlineItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else {
- blockOrInlineItemFound = true;
}
}
diff --git a/src/java/org/apache/fop/fo/flow/BidiOverride.java b/src/java/org/apache/fop/fo/flow/BidiOverride.java
index 2157085ad..892f4a3c5 100644
--- a/src/java/org/apache/fop/fo/flow/BidiOverride.java
+++ b/src/java/org/apache/fop/fo/flow/BidiOverride.java
@@ -19,13 +19,14 @@
package org.apache.fop.fo.flow;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObjMixed;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.properties.SpaceProperty;
-import org.xml.sax.Locator;
/**
* Class modelling the fo:bidi-override object.
@@ -96,22 +97,21 @@ public class BidiOverride extends FObjMixed {
* fo:inline-container."
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockOrInlineItemFound) {
- nodesOutOfOrderError(loc, "fo:marker",
- "(#PCDATA|%inline;|%block;)");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockOrInlineItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker",
+ "(#PCDATA|%inline;|%block;)");
+ }
+ } else if (!isBlockOrInlineItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else if (!canHaveBlockLevelChildren && isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, getParent().getName(), nsURI, getName(),
+ "rule.bidiOverrideContent");
+ } else {
+ blockOrInlineItemFound = true;
}
- } else if (!isBlockOrInlineItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else if (!canHaveBlockLevelChildren && isBlockItem(nsURI, localName)) {
- String ruleViolated = "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.";
- invalidChildError(loc, nsURI, localName, ruleViolated);
- } else {
- blockOrInlineItemFound = true;
}
}
diff --git a/src/java/org/apache/fop/fo/flow/BlockContainer.java b/src/java/org/apache/fop/fo/flow/BlockContainer.java
index a71999938..f1180ac16 100644
--- a/src/java/org/apache/fop/fo/flow/BlockContainer.java
+++ b/src/java/org/apache/fop/fo/flow/BlockContainer.java
@@ -114,15 +114,17 @@ public class BlockContainer extends FObj {
* @todo - implement above restriction if possible
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockItemFound) {
- nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ }
+ } else if (!isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else {
+ blockItemFound = true;
}
- } else if (!isBlockItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else {
- blockItemFound = true;
}
}
diff --git a/src/java/org/apache/fop/fo/flow/Character.java b/src/java/org/apache/fop/fo/flow/Character.java
index 022d54af7..aad4209f9 100644
--- a/src/java/org/apache/fop/fo/flow/Character.java
+++ b/src/java/org/apache/fop/fo/flow/Character.java
@@ -22,6 +22,8 @@ package org.apache.fop.fo.flow;
import java.awt.Color;
import java.util.NoSuchElementException;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.CharIterator;
@@ -35,7 +37,6 @@ import org.apache.fop.fo.properties.CommonHyphenation;
import org.apache.fop.fo.properties.CommonTextDecoration;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.SpaceProperty;
-import org.xml.sax.Locator;
/**
* Class modelling the fo:character object.
@@ -134,8 +135,10 @@ public class Character extends FObj {
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/**
diff --git a/src/java/org/apache/fop/fo/flow/ExternalGraphic.java b/src/java/org/apache/fop/fo/flow/ExternalGraphic.java
index fdddf3918..07f765e52 100644
--- a/src/java/org/apache/fop/fo/flow/ExternalGraphic.java
+++ b/src/java/org/apache/fop/fo/flow/ExternalGraphic.java
@@ -32,6 +32,7 @@ import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
@@ -78,11 +79,17 @@ public class ExternalGraphic extends AbstractGraphics {
try {
info = manager.getImageInfo(url, userAgent.getImageSessionContext());
} catch (ImageException e) {
- log.error("Image not available: " + e.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, url, e, getLocator());
} catch (FileNotFoundException fnfe) {
- log.error(fnfe.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageNotFound(this, url, fnfe, getLocator());
} catch (IOException ioe) {
- log.error("I/O error while loading image: " + ioe.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageIOError(this, url, ioe, getLocator());
}
if (info != null) {
this.intrinsicWidth = info.getSize().getWidthMpt();
@@ -93,7 +100,6 @@ public class ExternalGraphic extends AbstractGraphics {
= FixedLength.getInstance(-baseline);
}
}
- //TODO Report to caller so he can decide to throw an exception
}
/** {@inheritDoc} */
@@ -107,8 +113,10 @@ public class ExternalGraphic extends AbstractGraphics {
* <br>XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/** @return the "src" property */
diff --git a/src/java/org/apache/fop/fo/flow/Float.java b/src/java/org/apache/fop/fo/flow/Float.java
index 997f96c89..57d3b4ee1 100644
--- a/src/java/org/apache/fop/fo/flow/Float.java
+++ b/src/java/org/apache/fop/fo/flow/Float.java
@@ -46,7 +46,8 @@ public class Float extends FObj {
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:float is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
@@ -63,10 +64,12 @@ public class Float extends FObj {
* XSL Content Model: (%block;)+
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
if (!isBlockItem(nsURI, localName)) {
invalidChildError(loc, nsURI, localName);
}
+ }
}
/**
diff --git a/src/java/org/apache/fop/fo/flow/Footnote.java b/src/java/org/apache/fop/fo/flow/Footnote.java
index 95c9f25bc..c15a7e0ee 100644
--- a/src/java/org/apache/fop/fo/flow/Footnote.java
+++ b/src/java/org/apache/fop/fo/flow/Footnote.java
@@ -82,12 +82,13 @@ public class Footnote extends FObj {
* generates an absolutely positioned area.
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("inline")) {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("inline")) {
if (footnoteCitation != null) {
tooManyNodesError(loc, "fo:inline");
}
- } else if (FO_URI.equals(nsURI) && localName.equals("footnote-body")) {
+ } else if (localName.equals("footnote-body")) {
if (footnoteCitation == null) {
nodesOutOfOrderError(loc, "fo:inline", "fo:footnote-body");
} else if (footnoteBody != null) {
@@ -96,6 +97,7 @@ public class Footnote extends FObj {
} else {
invalidChildError(loc, nsURI, localName);
}
+ }
}
/**
diff --git a/src/java/org/apache/fop/fo/flow/FootnoteBody.java b/src/java/org/apache/fop/fo/flow/FootnoteBody.java
index bb4c9b482..967d15215 100644
--- a/src/java/org/apache/fop/fo/flow/FootnoteBody.java
+++ b/src/java/org/apache/fop/fo/flow/FootnoteBody.java
@@ -73,10 +73,12 @@ public class FootnoteBody extends FObj {
* XSL Content Model: (%block;)+
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
if (!isBlockItem(nsURI, localName)) {
invalidChildError(loc, nsURI, localName);
}
+ }
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/fo/flow/InitialPropertySet.java b/src/java/org/apache/fop/fo/flow/InitialPropertySet.java
index 63299978d..6d0e495b7 100644
--- a/src/java/org/apache/fop/fo/flow/InitialPropertySet.java
+++ b/src/java/org/apache/fop/fo/flow/InitialPropertySet.java
@@ -72,8 +72,10 @@ public class InitialPropertySet extends FObj {
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/**
diff --git a/src/java/org/apache/fop/fo/flow/Inline.java b/src/java/org/apache/fop/fo/flow/Inline.java
index 6bbd90ad1..50662d9f1 100644
--- a/src/java/org/apache/fop/fo/flow/Inline.java
+++ b/src/java/org/apache/fop/fo/flow/Inline.java
@@ -81,8 +81,8 @@ public class Inline extends InlineLevel {
int lvlInCntr = findAncestor(FO_INLINE_CONTAINER);
if (lvlLeader > 0) {
- if (lvlInCntr < 0 ||
- (lvlInCntr > 0 && lvlInCntr > lvlLeader)) {
+ if (lvlInCntr < 0
+ || (lvlInCntr > 0 && lvlInCntr > lvlLeader)) {
canHaveBlockLevelChildren = false;
}
} else if (lvlFootnote > 0) {
@@ -110,23 +110,20 @@ public class Inline extends InlineLevel {
* nearer ancestor that is an fo:inline-container." (paraphrased)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockOrInlineItemFound) {
- nodesOutOfOrderError(loc, "fo:marker",
- "(#PCDATA|%inline;|%block;)");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockOrInlineItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker",
+ "(#PCDATA|%inline;|%block;)");
+ }
+ } else if (!isBlockOrInlineItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else if (!canHaveBlockLevelChildren && isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, getParent().getName(), nsURI, getName(), "rule.inlineContent");
+ } else {
+ blockOrInlineItemFound = true;
}
- } else if (!isBlockOrInlineItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else if (!canHaveBlockLevelChildren && isBlockItem(nsURI, localName)) {
- String ruleViolated =
- " 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.";
- invalidChildError(loc, nsURI, localName, ruleViolated);
- } else {
- blockOrInlineItemFound = true;
}
}
diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java
index a8fb7858d..3c142afe9 100644
--- a/src/java/org/apache/fop/fo/flow/InlineContainer.java
+++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java
@@ -86,15 +86,17 @@ public class InlineContainer extends FObj {
* XSL Content Model: marker* (%block;)+
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockItemFound) {
- nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ }
+ } else if (!isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else {
+ blockItemFound = true;
}
- } else if (!isBlockItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else {
- blockItemFound = true;
}
}
diff --git a/src/java/org/apache/fop/fo/flow/InstreamForeignObject.java b/src/java/org/apache/fop/fo/flow/InstreamForeignObject.java
index 531bd657a..802f59c30 100644
--- a/src/java/org/apache/fop/fo/flow/InstreamForeignObject.java
+++ b/src/java/org/apache/fop/fo/flow/InstreamForeignObject.java
@@ -20,12 +20,17 @@
package org.apache.fop.fo.flow;
import java.awt.geom.Point2D;
+
+import org.xml.sax.Locator;
+
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Length;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.XMLObj;
-import org.xml.sax.Locator;
/**
* Class modelling the fo:instream-foreign-object object.
@@ -39,6 +44,7 @@ public class InstreamForeignObject extends AbstractGraphics {
//Additional value
private Point2D intrinsicDimensions;
+ private boolean instrisicSizeDetermined;
private Length intrinsicAlignmentAdjust;
@@ -68,11 +74,11 @@ public class InstreamForeignObject extends AbstractGraphics {
* XSL Content Model: one (1) non-XSL namespace child
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
} else if (firstChild != null) {
- tooManyNodesError(loc, "child element");
+ tooManyNodesError(loc, new QName(nsURI, null, localName));
}
}
@@ -81,32 +87,28 @@ public class InstreamForeignObject extends AbstractGraphics {
return "instream-foreign-object";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_INSTREAM_FOREIGN_OBJECT;
}
- /**
- * Preloads the image so the intrinsic size is available.
- */
+ /** Preloads the image so the intrinsic size is available. */
private void prepareIntrinsicSize() {
- if (intrinsicDimensions == null) {
+ if (!this.instrisicSizeDetermined) {
XMLObj child = (XMLObj) firstChild;
Point2D csize = new Point2D.Float(-1, -1);
intrinsicDimensions = child.getDimension(csize);
if (intrinsicDimensions == null) {
- log.error("Intrinsic dimensions of "
- + " instream-foreign-object could not be determined");
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.ifoNoIntrinsicSize(this, getLocator());
}
intrinsicAlignmentAdjust = child.getIntrinsicAlignmentAdjust();
+ this.instrisicSizeDetermined = true;
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getIntrinsicWidth() {
prepareIntrinsicSize();
if (intrinsicDimensions != null) {
@@ -116,9 +118,7 @@ public class InstreamForeignObject extends AbstractGraphics {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getIntrinsicHeight() {
prepareIntrinsicSize();
if (intrinsicDimensions != null) {
@@ -128,11 +128,8 @@ public class InstreamForeignObject extends AbstractGraphics {
}
}
- /**
- * {@inheritDoc}
- */
- public Length getIntrinsicAlignmentAdjust()
- {
+ /** {@inheritDoc} */
+ public Length getIntrinsicAlignmentAdjust() {
prepareIntrinsicSize();
return intrinsicAlignmentAdjust;
}
diff --git a/src/java/org/apache/fop/fo/flow/ListBlock.java b/src/java/org/apache/fop/fo/flow/ListBlock.java
index 86c581cc3..a196e92de 100644
--- a/src/java/org/apache/fop/fo/flow/ListBlock.java
+++ b/src/java/org/apache/fop/fo/flow/ListBlock.java
@@ -108,15 +108,17 @@ public class ListBlock extends FObj {
* XSL Content Model: marker* (list-item)+
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (hasListItem) {
- nodesOutOfOrderError(loc, "fo:marker", "fo:list-item");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (hasListItem) {
+ nodesOutOfOrderError(loc, "fo:marker", "fo:list-item");
+ }
+ } else if (localName.equals("list-item")) {
+ hasListItem = true;
+ } else {
+ invalidChildError(loc, nsURI, localName);
}
- } else if (FO_URI.equals(nsURI) && localName.equals("list-item")) {
- hasListItem = true;
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
diff --git a/src/java/org/apache/fop/fo/flow/ListItem.java b/src/java/org/apache/fop/fo/flow/ListItem.java
index c09313ef6..cf0e05c56 100644
--- a/src/java/org/apache/fop/fo/flow/ListItem.java
+++ b/src/java/org/apache/fop/fo/flow/ListItem.java
@@ -98,22 +98,24 @@ public class ListItem extends FObj {
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (label != null) {
- nodesOutOfOrderError(loc, "fo:marker", "fo:list-item-label");
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (label != null) {
+ nodesOutOfOrderError(loc, "fo:marker", "fo:list-item-label");
+ }
+ } else if (localName.equals("list-item-label")) {
+ if (label != null) {
+ tooManyNodesError(loc, "fo:list-item-label");
+ }
+ } else if (localName.equals("list-item-body")) {
+ if (label == null) {
+ nodesOutOfOrderError(loc, "fo:list-item-label", "fo:list-item-body");
+ } else if (body != null) {
+ tooManyNodesError(loc, "fo:list-item-body");
+ }
+ } else {
+ invalidChildError(loc, nsURI, localName);
}
- } else if (FO_URI.equals(nsURI) && localName.equals("list-item-label")) {
- if (label != null) {
- tooManyNodesError(loc, "fo:list-item-label");
- }
- } else if (FO_URI.equals(nsURI) && localName.equals("list-item-body")) {
- if (label == null) {
- nodesOutOfOrderError(loc, "fo:list-item-label", "fo:list-item-body");
- } else if (body != null) {
- tooManyNodesError(loc, "fo:list-item-body");
- }
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
diff --git a/src/java/org/apache/fop/fo/flow/Marker.java b/src/java/org/apache/fop/fo/flow/Marker.java
index 05c9862b2..168b18180 100644
--- a/src/java/org/apache/fop/fo/flow/Marker.java
+++ b/src/java/org/apache/fop/fo/flow/Marker.java
@@ -59,9 +59,8 @@ public class Marker extends FObjMixed {
*/
public void bind(PropertyList pList) throws FOPException {
if (findAncestor(FO_FLOW) < 0) {
- invalidChildError(locator, FO_URI, "marker",
- "An fo:marker is permitted only as the descendant "
- + "of an fo:flow");
+ invalidChildError(locator, getParent().getName(), FO_URI, getName(),
+ "rule.markerDescendantOfFlow");
}
markerClassName = pList.get(PR_MARKER_CLASS_NAME).getString();
@@ -112,9 +111,11 @@ public class Marker extends FObjMixed {
* @todo implement "additional" constraint, possibly within fo:retrieve-marker
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (!isBlockOrInlineItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!isBlockOrInlineItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
}
diff --git a/src/java/org/apache/fop/fo/flow/MultiCase.java b/src/java/org/apache/fop/fo/flow/MultiCase.java
index 42ec9d9d0..e568fba46 100644
--- a/src/java/org/apache/fop/fo/flow/MultiCase.java
+++ b/src/java/org/apache/fop/fo/flow/MultiCase.java
@@ -46,7 +46,8 @@ public class MultiCase extends FObj {
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-case is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
diff --git a/src/java/org/apache/fop/fo/flow/MultiProperties.java b/src/java/org/apache/fop/fo/flow/MultiProperties.java
index 00cb85dc7..bd3bd893e 100644
--- a/src/java/org/apache/fop/fo/flow/MultiProperties.java
+++ b/src/java/org/apache/fop/fo/flow/MultiProperties.java
@@ -49,7 +49,8 @@ public class MultiProperties extends FObj {
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-properties is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
@@ -69,13 +70,14 @@ public class MultiProperties extends FObj {
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("multi-property-set")) {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("multi-property-set")) {
if (hasWrapper) {
nodesOutOfOrderError(loc, "fo:multi-property-set", "fo:wrapper");
} else {
hasMultiPropertySet = true;
}
- } else if (FO_URI.equals(nsURI) && localName.equals("wrapper")) {
+ } else if (localName.equals("wrapper")) {
if (hasWrapper) {
tooManyNodesError(loc, "fo:wrapper");
} else {
@@ -84,6 +86,7 @@ public class MultiProperties extends FObj {
} else {
invalidChildError(loc, nsURI, localName);
}
+ }
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/fo/flow/MultiPropertySet.java b/src/java/org/apache/fop/fo/flow/MultiPropertySet.java
index 3c9c55b4f..caa31f7b9 100644
--- a/src/java/org/apache/fop/fo/flow/MultiPropertySet.java
+++ b/src/java/org/apache/fop/fo/flow/MultiPropertySet.java
@@ -45,7 +45,8 @@ public class MultiPropertySet extends FObj {
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-property-set is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
@@ -63,8 +64,10 @@ public class MultiPropertySet extends FObj {
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/fo/flow/MultiSwitch.java b/src/java/org/apache/fop/fo/flow/MultiSwitch.java
index 7c70a7346..03f404aa3 100644
--- a/src/java/org/apache/fop/fo/flow/MultiSwitch.java
+++ b/src/java/org/apache/fop/fo/flow/MultiSwitch.java
@@ -47,7 +47,8 @@ public class MultiSwitch extends FObj {
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-switch is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
@@ -75,9 +76,11 @@ public class MultiSwitch extends FObj {
* XSL Content Model: (multi-case+)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (!(FO_URI.equals(nsURI) && localName.equals("multi-case"))) {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!localName.equals("multi-case")) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
}
diff --git a/src/java/org/apache/fop/fo/flow/MultiToggle.java b/src/java/org/apache/fop/fo/flow/MultiToggle.java
index 80b36f9f4..66442c2a7 100644
--- a/src/java/org/apache/fop/fo/flow/MultiToggle.java
+++ b/src/java/org/apache/fop/fo/flow/MultiToggle.java
@@ -47,7 +47,8 @@ public class MultiToggle extends FObj {
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-toggle is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
@@ -65,9 +66,11 @@ public class MultiToggle extends FObj {
* XSL Content Model: (#PCDATA|%inline;|%block;)*
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (!isBlockOrInlineItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!isBlockOrInlineItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
}
diff --git a/src/java/org/apache/fop/fo/flow/PageNumber.java b/src/java/org/apache/fop/fo/flow/PageNumber.java
index 3eca1e16e..cc51dd28a 100644
--- a/src/java/org/apache/fop/fo/flow/PageNumber.java
+++ b/src/java/org/apache/fop/fo/flow/PageNumber.java
@@ -117,8 +117,10 @@ public class PageNumber extends FObj {
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/** @return the Common Font Properties. */
diff --git a/src/java/org/apache/fop/fo/flow/RetrieveMarker.java b/src/java/org/apache/fop/fo/flow/RetrieveMarker.java
index 7356bc72e..ea6b6f1c5 100644
--- a/src/java/org/apache/fop/fo/flow/RetrieveMarker.java
+++ b/src/java/org/apache/fop/fo/flow/RetrieveMarker.java
@@ -21,6 +21,8 @@ package org.apache.fop.fo.flow;
import java.util.Iterator;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FOText;
@@ -30,7 +32,6 @@ import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableFObj;
-import org.xml.sax.Locator;
/**
* Class modelling the fo:retrieve-marker object.
@@ -48,21 +49,18 @@ public class RetrieveMarker extends FObjMixed {
/**
* Create a retrieve marker object.
- *
+ * @param parent FONode that is the parent of this object
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public RetrieveMarker(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
if (findAncestor(FO_STATIC_CONTENT) < 0) {
- invalidChildError(locator, FO_URI, "retrieve-marker",
- "An fo:retrieve-marker is permitted only as the " +
- " descendant of an fo:static-content.");
+ invalidChildError(locator, getParent().getName(), FO_URI, getName(),
+ "rule.retrieveMarkerDescendatOfStaticContent");
}
retrieveClassName = pList.get(PR_RETRIEVE_CLASS_NAME).getString();
@@ -81,8 +79,10 @@ public class RetrieveMarker extends FObjMixed {
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/**
@@ -206,13 +206,12 @@ public class RetrieveMarker extends FObjMixed {
try {
cloneFromMarker(marker);
} catch (FOPException exc) {
- log.error("fo:retrieve-marker unable to clone "
- + "subtree of fo:marker (marker-class-name="
- + marker.getMarkerClassName() + ")", exc);
+ getFOValidationEventProducer().markerCloningFailed(this,
+ marker.getMarkerClassName(), exc, getLocator());
return;
}
- } else if (log.isInfoEnabled()) {
- log.info("Empty marker retrieved...");
+ } else if (log.isDebugEnabled()) {
+ log.debug("Empty marker retrieved...");
}
return;
}
@@ -222,9 +221,7 @@ public class RetrieveMarker extends FObjMixed {
return "retrieve-marker";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_RETRIEVE_MARKER;
}
diff --git a/src/java/org/apache/fop/fo/flow/Wrapper.java b/src/java/org/apache/fop/fo/flow/Wrapper.java
index adf9b2101..87582fb47 100644
--- a/src/java/org/apache/fop/fo/flow/Wrapper.java
+++ b/src/java/org/apache/fop/fo/flow/Wrapper.java
@@ -19,12 +19,13 @@
package org.apache.fop.fo.flow;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObjMixed;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
-import org.xml.sax.Locator;
/**
* Class modelling the fo:wrapper object.
@@ -66,17 +67,19 @@ public class Wrapper extends FObjMixed {
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
- if (FO_URI.equals(nsURI) && "marker".equals(localName)) {
- if (blockOrInlineItemFound) {
- nodesOutOfOrderError(loc, "fo:marker",
- "(#PCDATA|%inline;|%block;)");
+ if (FO_URI.equals(nsURI)) {
+ if ("marker".equals(localName)) {
+ if (blockOrInlineItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker",
+ "(#PCDATA|%inline;|%block;)");
+ }
+ } else if (isBlockOrInlineItem(nsURI, localName)) {
+ //delegate validation to parent
+ FONode.validateChildNode(this.parent, loc, nsURI, localName);
+ blockOrInlineItemFound = true;
+ } else {
+ invalidChildError(loc, nsURI, localName);
}
- } else if (isBlockOrInlineItem(nsURI, localName)) {
- //delegate validation to parent
- FONode.validateChildNode(this.parent, loc, nsURI, localName);
- blockOrInlineItemFound = true;
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
diff --git a/src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java b/src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java
index 28a30c6f7..0d24491d9 100644
--- a/src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java
+++ b/src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java
@@ -120,13 +120,17 @@ class FixedColRowGroupBuilder extends RowGroupBuilder {
void endTableRow() {
assert currentTableRow != null;
if (currentRowIndex > 0 && currentTableRow.getBreakBefore() != Constants.EN_AUTO) {
- currentTableRow.attributeWarning("break-before ignored because of row spanning "
- + "in progress (See XSL 1.1, 7.20.2)");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ currentTableRow.getUserAgent().getEventBroadcaster());
+ eventProducer.breakIgnoredDueToRowSpanning(this, currentTableRow.getName(), true,
+ currentTableRow.getLocator());
}
if (currentRowIndex < rows.size() - 1
&& currentTableRow.getBreakAfter() != Constants.EN_AUTO) {
- currentTableRow.attributeWarning("break-after ignored because of row spanning "
- + "in progress (See XSL 1.1, 7.20.1)");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ currentTableRow.getUserAgent().getEventBroadcaster());
+ eventProducer.breakIgnoredDueToRowSpanning(this, currentTableRow.getName(), false,
+ currentTableRow.getLocator());
}
for (Iterator iter = ((List) rows.get(currentRowIndex)).iterator(); iter.hasNext();) {
GridUnit gu = (GridUnit) iter.next();
diff --git a/src/java/org/apache/fop/fo/flow/table/Table.java b/src/java/org/apache/fop/fo/flow/table/Table.java
index 7d6611435..c1ef3857c 100644
--- a/src/java/org/apache/fop/fo/flow/table/Table.java
+++ b/src/java/org/apache/fop/fo/flow/table/Table.java
@@ -22,6 +22,8 @@ package org.apache.fop.fo.flow.table;
import java.util.ArrayList;
import java.util.List;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.ValidationPercentBaseContext;
@@ -35,7 +37,6 @@ import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fo.properties.LengthPairProperty;
import org.apache.fop.fo.properties.LengthRangeProperty;
import org.apache.fop.fo.properties.TableColLength;
-import org.xml.sax.Locator;
/**
* Class modelling the fo:table object.
@@ -126,20 +127,22 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder {
orphanContentLimit = pList.get(PR_X_ORPHAN_CONTENT_LIMIT).getLength();
if (!blockProgressionDimension.getOptimum(null).isAuto()) {
- attributeWarning("only a value of \"auto\" for block-progression-dimension has a"
- + " well-specified behavior on fo:table. Falling back to \"auto\"");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.nonAutoBPDOnTable(this, getLocator());
// Anyway, the bpd of a table is not used by the layout code
}
if (tableLayout == EN_AUTO) {
- attributeWarning("table-layout=\"auto\" is currently not supported by FOP");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ "table-layout=\"auto\"", getLocator());
}
if (!isSeparateBorderModel()
&& getCommonBorderPaddingBackground().hasPadding(
ValidationPercentBaseContext.getPseudoContext())) {
//See "17.6.2 The collapsing border model" in CSS2
- attributeWarning("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.");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.noTablePaddingWithCollapsingBorderModel(this, getLocator());
}
/* Store reference to the property list, so
@@ -163,7 +166,7 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder {
* XSL Content Model: (marker*,table-column*,table-header?,table-footer?,table-body+)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
if (FO_URI.equals(nsURI)) {
if ("marker".equals(localName)) {
if (tableColumnFound || tableHeaderFound || tableFooterFound
@@ -193,15 +196,11 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder {
} else {
tableFooterFound = true;
if (tableBodyFound) {
- if (getUserAgent().validateStrictly()) {
- nodesOutOfOrderError(loc, "fo:table-footer", "(table-body+)");
- } else if (!isSeparateBorderModel()) {
- nodesOutOfOrderError(loc, "fo:table-footer", "(table-body+)."
- + " 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");
+ nodesOutOfOrderError(loc, "fo:table-footer", "(table-body+)", true);
+ if (!isSeparateBorderModel()) {
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.footerOrderCannotRecover(this, getName(), getLocator());
}
}
}
@@ -210,8 +209,6 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder {
} else {
invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
@@ -225,6 +222,10 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder {
"(marker*,table-column*,table-header?,table-footer?"
+ ",table-body+)");
}
+ if (!hasChildren()) {
+ getParent().removeChild(this);
+ return;
+ }
if (!inMarker()) {
rowGroupBuilder.endTable();
/* clean up */
diff --git a/src/java/org/apache/fop/fo/flow/table/TableAndCaption.java b/src/java/org/apache/fop/fo/flow/table/TableAndCaption.java
index f16931cfc..6dabf37db 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableAndCaption.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableAndCaption.java
@@ -62,7 +62,8 @@ public class TableAndCaption extends FObj {
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:table-and-caption is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ "fo:table-and-caption", getLocator());
notImplementedWarningGiven = true;
}
}
@@ -83,30 +84,32 @@ public class TableAndCaption extends FObj {
* XSL Content Model: marker* table-caption? table
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (tableCaptionFound) {
- nodesOutOfOrderError(loc, "fo:marker", "fo:table-caption");
- } else if (tableFound) {
- nodesOutOfOrderError(loc, "fo:marker", "fo:table");
- }
- } else if (FO_URI.equals(nsURI) && localName.equals("table-caption")) {
- if (tableCaptionFound) {
- tooManyNodesError(loc, "fo:table-caption");
- } else if (tableFound) {
- nodesOutOfOrderError(loc, "fo:table-caption", "fo:table");
- } else {
- tableCaptionFound = true;
- }
- } else if (FO_URI.equals(nsURI) && localName.equals("table")) {
- if (tableFound) {
- tooManyNodesError(loc, "fo:table");
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (tableCaptionFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "fo:table-caption");
+ } else if (tableFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "fo:table");
+ }
+ } else if (localName.equals("table-caption")) {
+ if (tableCaptionFound) {
+ tooManyNodesError(loc, "fo:table-caption");
+ } else if (tableFound) {
+ nodesOutOfOrderError(loc, "fo:table-caption", "fo:table");
+ } else {
+ tableCaptionFound = true;
+ }
+ } else if (localName.equals("table")) {
+ if (tableFound) {
+ tooManyNodesError(loc, "fo:table");
+ } else {
+ tableFound = true;
+ }
} else {
- tableFound = true;
+ invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
diff --git a/src/java/org/apache/fop/fo/flow/table/TableBody.java b/src/java/org/apache/fop/fo/flow/table/TableBody.java
index de7bfda84..4e1673568 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableBody.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableBody.java
@@ -23,13 +23,14 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
/**
* Class modelling the fo:table-body object.
@@ -119,13 +120,8 @@ public class TableBody extends TableCellContainer {
getFOEventHandler().endBody(this);
if (!(tableRowsFound || tableCellsFound)) {
- if (getUserAgent().validateStrictly()) {
- missingChildElementError("marker* (table-row+|table-cell+)");
- } else {
- log.error("fo:table-body must not be empty. "
- + "Expected: marker* (table-row+|table-cell+)");
- getParent().removeChild(this);
- }
+ missingChildElementError("marker* (table-row+|table-cell+)", true);
+ getParent().removeChild(this);
} else {
finishLastRowGroup();
}
@@ -167,23 +163,20 @@ public class TableBody extends TableCellContainer {
} else if (localName.equals("table-row")) {
tableRowsFound = true;
if (tableCellsFound) {
- invalidChildError(loc, nsURI, localName, "Either fo:table-rows"
- + " or fo:table-cells may be children of an " + getName()
- + " but not both");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.noMixRowsAndCells(this, getName(), getLocator());
}
} else if (localName.equals("table-cell")) {
tableCellsFound = true;
if (tableRowsFound) {
- invalidChildError(loc, nsURI, localName,
- "Either fo:table-rows or fo:table-cells "
- + "may be children of an "
- + getName() + " but not both");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.noMixRowsAndCells(this, getName(), getLocator());
}
} else {
invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
diff --git a/src/java/org/apache/fop/fo/flow/table/TableCaption.java b/src/java/org/apache/fop/fo/flow/table/TableCaption.java
index 28174067c..416ef16ed 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableCaption.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableCaption.java
@@ -57,7 +57,8 @@ public class TableCaption extends FObj {
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:table-caption is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ "fo:table-caption", getLocator());
notImplementedWarningGiven = true;
}
}
@@ -83,15 +84,17 @@ public class TableCaption extends FObj {
* XSL Content Model: marker* (%block;)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockItemFound) {
- nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ }
+ } else if (!isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else {
+ blockItemFound = true;
}
- } else if (!isBlockItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else {
- blockItemFound = true;
}
}
diff --git a/src/java/org/apache/fop/fo/flow/table/TableCell.java b/src/java/org/apache/fop/fo/flow/table/TableCell.java
index 80dbe5e2a..78e35eb52 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableCell.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableCell.java
@@ -102,17 +102,13 @@ public class TableCell extends TableFObj {
*/
public void endOfNode() throws FOPException {
if (!blockItemFound) {
- if (getUserAgent().validateStrictly()) {
- missingChildElementError("marker* (%block;)+");
- } else if (firstChild != null) {
- log.warn("fo:table-cell content that is not "
- + "enclosed by a fo:block will be dropped/ignored.");
- }
+ missingChildElementError("marker* (%block;)+", true);
}
if ((startsRow() || endsRow())
&& getParent().getNameId() == FO_TABLE_ROW ) {
- log.warn("starts-row/ends-row for fo:table-cells "
- + "non-applicable for children of an fo:table-row.");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.startEndRowUnderTableRowWarning(this, getLocator());
}
getFOEventHandler().endCell(this);
}
@@ -123,14 +119,16 @@ public class TableCell extends TableFObj {
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockItemFound) {
- nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ }
+ } else if (!isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else {
+ blockItemFound = true;
}
- } else if (!isBlockItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else {
- blockItemFound = true;
}
}
diff --git a/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java b/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java
index 7c91be351..de9f271b5 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java
@@ -24,7 +24,6 @@ import java.util.List;
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.ValidationException;
/**
* A common class for fo:table-body and fo:table-row which both can contain fo:table-cell.
@@ -47,9 +46,9 @@ public abstract class TableCellContainer extends TableFObj implements ColumnNumb
Table t = getTable();
if (t.hasExplicitColumns()) {
if (colNumber + colSpan - 1 > t.getNumberOfColumns()) {
- throw new ValidationException(FONode.errorText(locator) + "column-number or "
- + "number of cells in the row overflows the number of fo:table-column "
- + "specified for the table.");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.tooManyCells(this, getLocator());
}
} else {
t.ensureColumnNumber(colNumber + colSpan - 1);
diff --git a/src/java/org/apache/fop/fo/flow/table/TableColumn.java b/src/java/org/apache/fop/fo/flow/table/TableColumn.java
index aeb401893..e6f6b420e 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableColumn.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableColumn.java
@@ -82,12 +82,16 @@ public class TableColumn extends TableFObj {
super.bind(pList);
if (numberColumnsRepeated <= 0) {
- throw new PropertyException("number-columns-repeated must be 1 or bigger, "
- + "but got " + numberColumnsRepeated);
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.valueMustBeBiggerGtEqOne(this,
+ "number-columns-repeated", numberColumnsRepeated, getLocator());
}
if (numberColumnsSpanned <= 0) {
- throw new PropertyException("number-columns-spanned must be 1 or bigger, "
- + "but got " + numberColumnsSpanned);
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.valueMustBeBiggerGtEqOne(this,
+ "number-columns-spanned", numberColumnsSpanned, getLocator());
}
/* check for unspecified width and replace with default of
@@ -96,8 +100,9 @@ public class TableColumn extends TableFObj {
*/
if (columnWidth.getEnum() == EN_AUTO) {
if (!this.implicitColumn && !getTable().isAutoLayout()) {
- log.warn("table-layout=\"fixed\" and column-width unspecified "
- + "=> falling back to proportional-column-width(1)");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.warnImplicitColumns(this, getLocator());
}
columnWidth = new TableColLength(1.0, this);
}
@@ -146,7 +151,9 @@ public class TableColumn extends TableFObj {
protected void validateChildNode(Locator loc,
String nsURI, String localName)
throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/**
@@ -240,7 +247,7 @@ public class TableColumn extends TableFObj {
*
* @param propId the id for the property to retrieve
* @return the requested Property
- * @throws PropertyException
+ * @throws PropertyException if there is a problem evaluating the property
*/
public Property getProperty(int propId) throws PropertyException {
return this.pList.get(propId);
diff --git a/src/java/org/apache/fop/fo/flow/table/TableEventProducer.java b/src/java/org/apache/fop/fo/flow/table/TableEventProducer.java
new file mode 100644
index 000000000..44ddcc038
--- /dev/null
+++ b/src/java/org/apache/fop/fo/flow/table/TableEventProducer.java
@@ -0,0 +1,159 @@
+/*
+ * 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.fo.flow.table;
+
+import org.xml.sax.Locator;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.expr.PropertyException;
+
+/**
+ * Event producer interface for table-specific XSL-FO validation messages.
+ */
+public interface TableEventProducer extends EventProducer {
+
+ /** Provider class for the event producer. */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static TableEventProducer get(EventBroadcaster broadcaster) {
+ return (TableEventProducer)broadcaster.getEventProducerFor(
+ TableEventProducer.class);
+ }
+ }
+
+ /**
+ * A value other than "auto" has been specified on fo:table.
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void nonAutoBPDOnTable(Object source, Locator loc);
+
+ /**
+ * Padding on fo:table is ignored if the collapsing border model is active.
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void noTablePaddingWithCollapsingBorderModel(Object source, Locator loc);
+
+ /**
+ * No mixing of table-rows and table-cells is allowed for direct children of table-body.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void noMixRowsAndCells(Object source, String elementName, Locator loc)
+ throws ValidationException;
+
+ /**
+ * The table-footer was found after the table-body. FOP cannot recover with collapsed border
+ * model.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void footerOrderCannotRecover(Object source, String elementName, Locator loc)
+ throws ValidationException;
+
+ /**
+ * starts-row/ends-row for fo:table-cells non-applicable for children of an fo:table-row
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void startEndRowUnderTableRowWarning(Object source, Locator loc);
+
+ /**
+ * Column-number or number of cells in the row overflows the number of fo:table-column
+ * specified for the table.
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @throws ValidationException the validation error provoked by the method call
+ * @event.severity FATAL
+ */
+ void tooManyCells(Object source, Locator loc) throws ValidationException;
+
+ /**
+ * Property value must be 1 or bigger.
+ * @param source the event source
+ * @param propName the property name
+ * @param actualValue the actual value
+ * @param loc the location of the error or null
+ * @throws PropertyException the property error provoked by the method call
+ * @event.severity FATAL
+ */
+ void valueMustBeBiggerGtEqOne(Object source, String propName,
+ int actualValue, Locator loc) throws PropertyException;
+
+ /**
+ * table-layout=\"fixed\" and column-width unspecified
+ * => falling back to proportional-column-width(1)
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void warnImplicitColumns(Object source, Locator loc);
+
+ /**
+ * padding-* properties are not applicable.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void paddingNotApplicable(Object source, String elementName, Locator loc);
+
+ /**
+ * Cell overlap.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param column the column index of the overlapping cell
+ * @param loc the location of the error or null
+ * @throws PropertyException the property error provoked by the method call
+ * @event.severity FATAL
+ */
+ void cellOverlap(Object source, String elementName, int column,
+ Locator loc) throws PropertyException;
+
+ /**
+ * Break ignored due to row spanning.
+ * @param source the event source
+ * @param elementName the name of the context node
+ * @param breakBefore true for "break-before", false for "break-after"
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void breakIgnoredDueToRowSpanning(Object source, String elementName, boolean breakBefore,
+ Locator loc);
+
+
+}
diff --git a/src/java/org/apache/fop/fo/flow/table/TableFObj.java b/src/java/org/apache/fop/fo/flow/table/TableFObj.java
index 9618d7ff4..24528f622 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableFObj.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableFObj.java
@@ -58,9 +58,7 @@ public abstract class TableFObj extends FObj {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
borderAfterPrecedence = pList.get(PR_BORDER_AFTER_PRECEDENCE).getNumeric();
@@ -71,9 +69,9 @@ public abstract class TableFObj extends FObj {
&& getNameId() != FO_TABLE_CELL
&& getCommonBorderPaddingBackground().hasPadding(
ValidationPercentBaseContext.getPseudoContext())) {
- attributeWarning(
- "padding-* properties are not applicable to " + getName()
- + ", but a non-zero value for padding was found.");
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.paddingNotApplicable(this, getName(), getLocator());
}
}
@@ -156,17 +154,19 @@ public abstract class TableFObj extends FObj {
ColumnNumberManager columnIndexManager = parent.getColumnNumberManager();
int columnIndex = p.getNumeric().getValue();
if (columnIndex <= 0) {
+ /* No warning necessary as the spec clearly defines how to handle these cases.
log.warn("Specified negative or zero value for "
+ "column-number on " + fo.getName() + ": "
+ columnIndex + " forced to "
- + columnIndexManager.getCurrentColumnNumber());
+ + columnIndexManager.getCurrentColumnNumber());*/
return NumberProperty.getInstance(columnIndexManager.getCurrentColumnNumber());
} else {
double tmpIndex = p.getNumeric().getNumericValue();
if (tmpIndex - columnIndex > 0.0) {
columnIndex = (int) Math.round(tmpIndex);
+ /* No warning necessary as the spec clearly defines how to handle these cases.
log.warn("Rounding specified column-number of "
- + tmpIndex + " to " + columnIndex);
+ + tmpIndex + " to " + columnIndex);*/
p = NumberProperty.getInstance(columnIndex);
}
}
@@ -179,16 +179,9 @@ public abstract class TableFObj extends FObj {
/* if column-number is already in use by another
* cell/column => error!
*/
- StringBuffer errorMessage = new StringBuffer();
- errorMessage.append(fo.getName() + " overlaps in column ")
- .append(columnIndex + i);
- org.xml.sax.Locator loc = fo.getLocator();
- if (loc != null && loc.getLineNumber() != -1) {
- errorMessage.append(" (line #")
- .append(loc.getLineNumber()).append(", column #")
- .append(loc.getColumnNumber()).append(")");
- }
- throw new PropertyException(errorMessage.toString());
+ TableEventProducer eventProducer = TableEventProducer.Provider.get(
+ fo.getUserAgent().getEventBroadcaster());
+ eventProducer.cellOverlap(this, fo.getName(), columnIndex + 1, fo.getLocator());
}
}
diff --git a/src/java/org/apache/fop/fo/flow/table/TableRow.java b/src/java/org/apache/fop/fo/flow/table/TableRow.java
index a025f92fd..e5261614b 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableRow.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableRow.java
@@ -19,6 +19,9 @@
package org.apache.fop.fo.flow.table;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.FONode;
@@ -27,8 +30,6 @@ import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fo.properties.LengthRangeProperty;
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
/**
* Class modelling the fo:table-row object.
@@ -122,9 +123,11 @@ public class TableRow extends TableCellContainer {
*/
protected void validateChildNode(Locator loc, String nsURI,
String localName)
- throws ValidationException {
- if (!(FO_URI.equals(nsURI) && localName.equals("table-cell"))) {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!localName.equals("table-cell")) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
}
diff --git a/src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java b/src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java
index 70a09b720..578d74c4d 100644
--- a/src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java
+++ b/src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java
@@ -32,7 +32,9 @@ import org.apache.fop.fo.PropertyList;
public abstract class AbstractPageSequence extends FObj {
// The value of properties relevant for fo:page-sequence.
+ /** the initial-page-number value */
protected Numeric initialPageNumber;
+ /** the force-page-count value */
protected int forcePageCount;
private String format;
private int letterValue;
@@ -43,6 +45,7 @@ public abstract class AbstractPageSequence extends FObj {
private PageNumberGenerator pageNumberGenerator;
+ /** the first page number generated by the page sequence */
protected int startingPageNumber = 0;
/**
@@ -54,9 +57,7 @@ public abstract class AbstractPageSequence extends FObj {
super(parent);
}
- /**
- * @see org.apache.fop.fo.FObj#bind(PropertyList)
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
initialPageNumber = pList.get(PR_INITIAL_PAGE_NUMBER).getNumeric();
@@ -68,16 +69,14 @@ public abstract class AbstractPageSequence extends FObj {
referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric();
}
- /**
- * @see org.apache.fop.fo.FONode#startOfNode()
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
this.pageNumberGenerator = new PageNumberGenerator(
format, groupingSeparator, groupingSize, letterValue);
}
- /** @see org.apache.fop.fo.FONode#endOfNode() */
+ /** {@inheritDoc} */
protected void endOfNode() throws FOPException {
}
diff --git a/src/java/org/apache/fop/fo/pagination/ColorProfile.java b/src/java/org/apache/fop/fo/pagination/ColorProfile.java
index 9318a8896..6067b55c5 100644
--- a/src/java/org/apache/fop/fo/pagination/ColorProfile.java
+++ b/src/java/org/apache/fop/fo/pagination/ColorProfile.java
@@ -19,14 +19,14 @@
package org.apache.fop.fo.pagination;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
-import org.xml.sax.Locator;
-
/**
* The fo:color-profile formatting object.
* This loads the color profile when needed and resolves a requested color.
@@ -39,15 +39,15 @@ public class ColorProfile extends FObj {
// End of property values
/**
+ * Creates a new color-profile element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public ColorProfile(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
src = pList.get(PR_SRC).getString();
colorProfileName = pList.get(PR_COLOR_PROFILE_NAME).getString();
@@ -59,12 +59,15 @@ public class ColorProfile extends FObj {
XSL 1.0/FOP: EMPTY (no child nodes permitted)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
/**
* Return the "color-profile-name" property.
+ * @return the color-profile-name property
*/
public String getColorProfileName() {
return colorProfileName;
@@ -75,9 +78,7 @@ public class ColorProfile extends FObj {
return "color-profile";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_COLOR_PROFILE;
}
diff --git a/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java b/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java
index dfb4ba70b..a13808324 100644
--- a/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java
+++ b/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java
@@ -45,15 +45,15 @@ public class ConditionalPageMasterReference extends FObj {
// End of property values
/**
+ * Creates a new conditional-page-master-reference element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public ConditionalPageMasterReference(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
masterReference = pList.get(PR_MASTER_REFERENCE).getString();
pagePosition = pList.get(PR_PAGE_POSITION).getEnum();
@@ -65,9 +65,7 @@ public class ConditionalPageMasterReference extends FObj {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
getConcreteParent().addConditionalPageMasterReference(this);
}
@@ -81,8 +79,10 @@ public class ConditionalPageMasterReference extends FObj {
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
/**
diff --git a/src/java/org/apache/fop/fo/pagination/Declarations.java b/src/java/org/apache/fop/fo/pagination/Declarations.java
index 9dc282d07..3eec2897a 100644
--- a/src/java/org/apache/fop/fo/pagination/Declarations.java
+++ b/src/java/org/apache/fop/fo/pagination/Declarations.java
@@ -42,6 +42,7 @@ public class Declarations extends FObj {
private Map colorProfiles = null;
/**
+ * Creates a new declarations element.
* @param parent FONode that is the parent of this object
*/
public Declarations(FONode parent) {
@@ -49,9 +50,7 @@ public class Declarations extends FObj {
((Root) parent).setDeclarations(this);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
// No properties defined for fo:declarations
}
@@ -73,6 +72,7 @@ public class Declarations extends FObj {
/**
* At the end of this element sort out the children into
* a hashmap of color profiles and a list of extension attachments.
+ * @throws FOPException if there's a problem during processing
*/
protected void endOfNode() throws FOPException {
if (firstChild != null) {
@@ -83,7 +83,8 @@ public class Declarations extends FObj {
if (!"".equals(cp.getColorProfileName())) {
addColorProfile(cp);
} else {
- log.warn("color-profile-name required for color profile");
+ getFOValidationEventProducer().missingProperty(this,
+ cp.getName(), "color-profile-name", locator);
}
} else {
log.debug("Ignoring element " + node.getName()
@@ -100,22 +101,18 @@ public class Declarations extends FObj {
}
if (colorProfiles.get(cp.getColorProfileName()) != null) {
// duplicate names
- log.warn("Duplicate fo:color-profile profile name: "
- + cp.getColorProfileName());
+ getFOValidationEventProducer().colorProfileNameNotUnique(this,
+ cp.getName(), cp.getColorProfileName(), locator);
}
colorProfiles.put(cp.getColorProfileName(), cp);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public String getLocalName() {
return "declarations";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_DECLARATIONS;
}
diff --git a/src/java/org/apache/fop/fo/pagination/Flow.java b/src/java/org/apache/fop/fo/pagination/Flow.java
index fc5d605cc..2ee77ff0a 100644
--- a/src/java/org/apache/fop/fo/pagination/Flow.java
+++ b/src/java/org/apache/fop/fo/pagination/Flow.java
@@ -45,16 +45,12 @@ public class Flow extends FObj {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
flowName = pList.get(PR_FLOW_NAME).getString();
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
if (flowName == null || flowName.equals("")) {
missingPropertyError("flow-name");
@@ -80,10 +76,7 @@ public class Flow extends FObj {
getFOEventHandler().startFlow(this);
}
- /**
- * Make sure content model satisfied, if so then tell the
- * FOEventHandler that we are at the end of the flow.
- */
+ /** {@inheritDoc} */
protected void endOfNode() throws FOPException {
if (!blockItemFound) {
missingChildElementError("marker* (%block;)+");
@@ -96,21 +89,21 @@ public class Flow extends FObj {
* XSL Content Model: marker* (%block;)+
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("marker")) {
- if (blockItemFound) {
- nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("marker")) {
+ if (blockItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ }
+ } else if (!isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ } else {
+ blockItemFound = true;
}
- } else if (!isBlockItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
- } else {
- blockItemFound = true;
}
}
- /**
- * @return true (Flow can generate reference areas)
- */
+ /** {@inheritDoc} */
public boolean generatesReferenceAreas() {
return true;
}
@@ -125,9 +118,7 @@ public class Flow extends FObj {
return "flow";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_FLOW;
}
diff --git a/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java b/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java
index 54eb29744..1b57be57d 100644
--- a/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java
+++ b/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java
@@ -46,31 +46,27 @@ public class LayoutMasterSet extends FObj {
private Map pageSequenceMasters;
/**
+ * Creates a new layout-master-set element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public LayoutMasterSet(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
// No properties in layout-master-set.
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
getRoot().setLayoutMasterSet(this);
simplePageMasters = new java.util.HashMap();
pageSequenceMasters = new java.util.HashMap();
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void endOfNode() throws FOPException {
if (firstChild == null) {
missingChildElementError("(simple-page-master|page-sequence-master)+");
@@ -83,14 +79,12 @@ public class LayoutMasterSet extends FObj {
XSL/FOP: (simple-page-master|page-sequence-master)+
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
if (FO_URI.equals(nsURI)) {
if (!localName.equals("simple-page-master")
&& !localName.equals("page-sequence-master")) {
invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
@@ -104,23 +98,20 @@ public class LayoutMasterSet extends FObj {
Map allRegions = new java.util.HashMap();
for (Iterator spm = simplePageMasters.values().iterator();
spm.hasNext();) {
- SimplePageMaster simplePageMaster =
- (SimplePageMaster)spm.next();
+ SimplePageMaster simplePageMaster
+ = (SimplePageMaster)spm.next();
Map spmRegions = simplePageMaster.getRegions();
for (Iterator e = spmRegions.values().iterator();
e.hasNext();) {
Region region = (Region) e.next();
if (allRegions.containsKey(region.getRegionName())) {
- String defaultRegionName =
- (String) allRegions.get(region.getRegionName());
+ String defaultRegionName
+ = (String) allRegions.get(region.getRegionName());
if (!defaultRegionName.equals(region.getDefaultRegionName())) {
- throw new ValidationException("Region-name ("
- + region.getRegionName()
- + ") is being mapped to multiple "
- + "region-classes ("
- + defaultRegionName + " and "
- + region.getDefaultRegionName()
- + ")", locator);
+ getFOValidationEventProducer().regionNameMappedToMultipleRegionClasses(this,
+ region.getRegionName(),
+ defaultRegionName,
+ region.getDefaultRegionName(), getLocator());
}
}
allRegions.put(region.getRegionName(),
@@ -141,21 +132,16 @@ public class LayoutMasterSet extends FObj {
// check for duplication of master-name
String masterName = sPM.getMasterName();
if (existsName(masterName)) {
- throw new ValidationException("'master-name' ("
- + masterName
- + ") must be unique "
- + "across page-masters and page-sequence-masters", sPM.getLocator());
+ getFOValidationEventProducer().masterNameNotUnique(this,
+ getName(),
+ masterName, sPM.getLocator());
}
this.simplePageMasters.put(masterName, sPM);
}
private boolean existsName(String masterName) {
- if (simplePageMasters.containsKey(masterName)
- || pageSequenceMasters.containsKey(masterName)) {
- return true;
- } else {
- return false;
- }
+ return (simplePageMasters.containsKey(masterName)
+ || pageSequenceMasters.containsKey(masterName));
}
/**
@@ -181,10 +167,9 @@ public class LayoutMasterSet extends FObj {
throws ValidationException {
// check against duplication of master-name
if (existsName(masterName)) {
- throw new ValidationException("'master-name' ("
- + masterName
- + ") must be unique "
- + "across page-masters and page-sequence-masters", pSM.getLocator());
+ getFOValidationEventProducer().masterNameNotUnique(this,
+ getName(),
+ masterName, pSM.getLocator());
}
this.pageSequenceMasters.put(masterName, pSM);
}
@@ -220,9 +205,7 @@ public class LayoutMasterSet extends FObj {
return "layout-master-set";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_LAYOUT_MASTER_SET;
}
diff --git a/src/java/org/apache/fop/fo/pagination/PageNumberGenerator.java b/src/java/org/apache/fop/fo/pagination/PageNumberGenerator.java
index 4289076ea..50620f678 100644
--- a/src/java/org/apache/fop/fo/pagination/PageNumberGenerator.java
+++ b/src/java/org/apache/fop/fo/pagination/PageNumberGenerator.java
@@ -43,7 +43,7 @@ public class PageNumberGenerator {
private int minPadding = 0; // for decimal formats
// preloaded strings of zeros
- private String zeros[] = {
+ private String[] zeros = {
"", "0", "00", "000", "0000", "00000"
};
@@ -128,10 +128,10 @@ public class PageNumberGenerator {
}
private String makeRoman(int num) {
- int arabic[] = {
+ int[] arabic = {
1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1
};
- String roman[] = {
+ String[] roman = {
"m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv",
"i"
};
diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java
index 91649fbc5..3d155a1da 100644
--- a/src/java/org/apache/fop/fo/pagination/PageSequence.java
+++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java
@@ -78,9 +78,7 @@ public class PageSequence extends AbstractPageSequence {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
country = pList.get(PR_COUNTRY).getString();
@@ -93,9 +91,7 @@ public class PageSequence extends AbstractPageSequence {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
super.startOfNode();
flowMap = new java.util.HashMap();
@@ -105,9 +101,8 @@ public class PageSequence extends AbstractPageSequence {
this.pageSequenceMaster
= getRoot().getLayoutMasterSet().getPageSequenceMaster(masterReference);
if (this.pageSequenceMaster == null) {
- throw new ValidationException("master-reference '" + masterReference
- + "' for fo:page-sequence matches no"
- + " simple-page-master or page-sequence-master", locator);
+ getFOValidationEventProducer().masterNotFound(this, getName(),
+ masterReference, getLocator());
}
}
@@ -128,7 +123,7 @@ public class PageSequence extends AbstractPageSequence {
XSL Content Model: (title?,static-content*,flow)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
if (FO_URI.equals(nsURI)) {
if (localName.equals("title")) {
if (titleFO != null) {
@@ -149,8 +144,6 @@ public class PageSequence extends AbstractPageSequence {
} else {
invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
@@ -184,18 +177,15 @@ public class PageSequence extends AbstractPageSequence {
String flowName = flow.getFlowName();
if (hasFlowName(flowName)) {
- throw new ValidationException("duplicate flow-name \""
- + flowName
- + "\" found within fo:page-sequence", flow.getLocator());
+ getFOValidationEventProducer().duplicateFlowNameInPageSequence(this, flow.getName(),
+ flowName, flow.getLocator());
}
if (!getRoot().getLayoutMasterSet().regionNameExists(flowName)
&& !flowName.equals("xsl-before-float-separator")
&& !flowName.equals("xsl-footnote-separator")) {
- throw new ValidationException("flow-name \""
- + flowName
- + "\" could not be mapped to a region-name in the"
- + " layout-master-set", flow.getLocator());
+ getFOValidationEventProducer().flowNameNotMapped(this, flow.getName(),
+ flowName, flow.getLocator());
}
}
diff --git a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
index 34ad299bd..4258a1139 100644
--- a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
+++ b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
@@ -29,6 +29,7 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
+import org.apache.fop.layoutmgr.BlockLevelEventProducer;
/**
* The page-sequence-master formatting object.
@@ -53,6 +54,8 @@ public class PageSequenceMaster extends FObj {
// but the actual FO's are MasterReferences.
/**
+ * Creates a new page-sequence-master element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public PageSequenceMaster(FONode parent) {
@@ -95,20 +98,18 @@ public class PageSequenceMaster extends FObj {
* repeatable-page-master-alternatives)+
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
if (FO_URI.equals(nsURI)) {
if (!localName.equals("single-page-master-reference")
&& !localName.equals("repeatable-page-master-reference")
&& !localName.equals("repeatable-page-master-alternatives")) {
invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
/**
- * Adds a new suqsequence specifier to the page sequence master.
+ * Adds a new subsequence specifier to the page sequence master.
* @param pageMasterReference the subsequence to add
*/
protected void addSubsequenceSpecifier(SubSequenceSpecifier pageMasterReference) {
@@ -199,8 +200,10 @@ public class PageSequenceMaster extends FObj {
if (currentSubSequence == null) {
currentSubSequence = getNextSubSequence();
if (currentSubSequence == null) {
- throw new FOPException("no subsequences in page-sequence-master '"
- + masterName + "'");
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.missingSubsequencesInPageSequenceMaster(this,
+ masterName, getLocator());
}
}
String pageMasterName = currentSubSequence
@@ -209,14 +212,10 @@ public class PageSequenceMaster extends FObj {
while (pageMasterName == null) {
SubSequenceSpecifier nextSubSequence = getNextSubSequence();
if (nextSubSequence == null) {
- if (!canRecover) {
- throw new FOPException("subsequences exhausted in page-sequence-master '"
- + masterName
- + "', cannot recover");
- }
- log.warn("subsequences exhausted in page-sequence-master '"
- + masterName
- + "', using previous subsequence");
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.pageSequenceMasterExhausted(this,
+ masterName, canRecover, getLocator());
currentSubSequence.reset();
canRecover = false;
} else {
@@ -228,9 +227,10 @@ public class PageSequenceMaster extends FObj {
SimplePageMaster pageMaster = this.layoutMasterSet
.getSimplePageMaster(pageMasterName);
if (pageMaster == null) {
- throw new FOPException("No simple-page-master matching '"
- + pageMasterName + "' in page-sequence-master '"
- + masterName + "'");
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.noMatchingPageMaster(this,
+ masterName, pageMasterName, getLocator());
}
return pageMaster;
}
@@ -240,9 +240,7 @@ public class PageSequenceMaster extends FObj {
return "page-sequence-master";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_PAGE_SEQUENCE_MASTER;
}
diff --git a/src/java/org/apache/fop/fo/pagination/PageSequenceWrapper.java b/src/java/org/apache/fop/fo/pagination/PageSequenceWrapper.java
index 94c0314a3..0b3cff276 100644
--- a/src/java/org/apache/fop/fo/pagination/PageSequenceWrapper.java
+++ b/src/java/org/apache/fop/fo/pagination/PageSequenceWrapper.java
@@ -22,8 +22,8 @@ package org.apache.fop.fo.pagination;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.FObj;
import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
@@ -39,15 +39,14 @@ public class PageSequenceWrapper extends FObj {
// End of property values
/**
+ * Creates a new page-sequence-wrapper element.
* @param parent FONode that is the parent of this object
*/
public PageSequenceWrapper(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
indexClass = pList.get(PR_INDEX_CLASS).getString();
@@ -59,10 +58,12 @@ public class PageSequenceWrapper extends FObj {
XSL/FOP: (bookmark+)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (!(FO_URI.equals(nsURI) && (localName.equals("page-sequence") ||
- localName.equals("page-sequence-wrapper")))) {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!(localName.equals("page-sequence")
+ || localName.equals("page-sequence-wrapper"))) {
invalidChildError(loc, nsURI, localName);
+ }
}
}
@@ -81,9 +82,7 @@ public class PageSequenceWrapper extends FObj {
return "page-sequence-wrapper";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_PAGE_SEQUENCE_WRAPPER;
}
diff --git a/src/java/org/apache/fop/fo/pagination/Region.java b/src/java/org/apache/fop/fo/pagination/Region.java
index 2516f90d8..ded86514b 100644
--- a/src/java/org/apache/fop/fo/pagination/Region.java
+++ b/src/java/org/apache/fop/fo/pagination/Region.java
@@ -30,7 +30,6 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
-import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
/**
@@ -50,6 +49,8 @@ public abstract class Region extends FObj {
private SimplePageMaster layoutMaster;
/**
+ * Creates a new Region.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
protected Region(FONode parent) {
@@ -57,9 +58,7 @@ public abstract class Region extends FObj {
layoutMaster = (SimplePageMaster) parent;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps();
// clip = pList.get(PR_CLIP);
@@ -76,18 +75,16 @@ public abstract class Region extends FObj {
// check that name is OK. Not very pretty.
if (isReserved(getRegionName())
&& !getRegionName().equals(getDefaultRegionName())) {
- throw new ValidationException("region-name '" + regionName
- + "' for " + this.getName()
- + " is not permitted.", locator);
+ getFOValidationEventProducer().illegalRegionName(this, getName(),
+ regionName, getLocator());
}
}
//TODO do we need context for getBPPaddingAndBorder() and getIPPaddingAndBorder()?
- if (getUserAgent().validateStrictly()
- && (getCommonBorderPaddingBackground().getBPPaddingAndBorder(false, null) != 0
+ if ((getCommonBorderPaddingBackground().getBPPaddingAndBorder(false, null) != 0
|| getCommonBorderPaddingBackground().getIPPaddingAndBorder(false, null) != 0)) {
- throw new PropertyException("Border and padding for region \""
- + regionName + "\" must be '0' (See 6.4.13 in XSL 1.0).");
+ getFOValidationEventProducer().nonZeroBorderPaddingOnRegion(this, getName(),
+ regionName, true, getLocator());
}
}
@@ -96,8 +93,10 @@ public abstract class Region extends FObj {
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/**
@@ -121,7 +120,7 @@ public abstract class Region extends FObj {
* @param name a region name to check
* @return true if the name parameter is a reserved region name
*/
- protected boolean isReserved(String name) /*throws FOPException*/ {
+ protected boolean isReserved(String name) {
return (name.equals("xsl-region-before")
|| name.equals("xsl-region-start")
|| name.equals("xsl-region-end")
@@ -130,9 +129,7 @@ public abstract class Region extends FObj {
|| name.equals("xsl-footnote-separator"));
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public boolean generatesReferenceAreas() {
return true;
}
diff --git a/src/java/org/apache/fop/fo/pagination/RegionAfter.java b/src/java/org/apache/fop/fo/pagination/RegionAfter.java
index 9459a6637..2852358b5 100644
--- a/src/java/org/apache/fop/fo/pagination/RegionAfter.java
+++ b/src/java/org/apache/fop/fo/pagination/RegionAfter.java
@@ -34,15 +34,15 @@ import org.apache.fop.datatypes.SimplePercentBaseContext;
public class RegionAfter extends RegionBA {
/**
+ * Creates a new region-after element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public RegionAfter(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public Rectangle getViewportRectangle (FODimension reldims, SimplePageMaster spm) {
/* Special rules apply to resolving extent as values are resolved relative
* to the page size and reference orientation.
@@ -82,9 +82,7 @@ public class RegionAfter extends RegionBA {
return vpRect;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-after";
}
@@ -94,9 +92,7 @@ public class RegionAfter extends RegionBA {
return "region-after";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_AFTER;
}
diff --git a/src/java/org/apache/fop/fo/pagination/RegionBA.java b/src/java/org/apache/fop/fo/pagination/RegionBA.java
index 768af2489..279164a96 100644
--- a/src/java/org/apache/fop/fo/pagination/RegionBA.java
+++ b/src/java/org/apache/fop/fo/pagination/RegionBA.java
@@ -36,15 +36,15 @@ public abstract class RegionBA extends SideRegion {
// End of property values
/**
+ * Creates a new region (before or after).
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
protected RegionBA(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
precedence = pList.get(PR_PRECEDENCE).getEnum();
diff --git a/src/java/org/apache/fop/fo/pagination/RegionBefore.java b/src/java/org/apache/fop/fo/pagination/RegionBefore.java
index 6115d8dd8..076737252 100644
--- a/src/java/org/apache/fop/fo/pagination/RegionBefore.java
+++ b/src/java/org/apache/fop/fo/pagination/RegionBefore.java
@@ -32,23 +32,22 @@ import org.apache.fop.fo.FONode;
* The fo:region-before element.
*/
public class RegionBefore extends RegionBA {
+
/**
+ * Creates a new region-before element.
+ * @param parent the parent element
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public RegionBefore(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-before";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public Rectangle getViewportRectangle (FODimension reldims, SimplePageMaster spm) {
/* Special rules apply to resolving extent as values are resolved relative
* to the page size and reference orientation.
@@ -91,9 +90,7 @@ public class RegionBefore extends RegionBA {
return "region-before";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_BEFORE;
}
diff --git a/src/java/org/apache/fop/fo/pagination/RegionBody.java b/src/java/org/apache/fop/fo/pagination/RegionBody.java
index 4de2dd1b4..9700e72fc 100644
--- a/src/java/org/apache/fop/fo/pagination/RegionBody.java
+++ b/src/java/org/apache/fop/fo/pagination/RegionBody.java
@@ -43,29 +43,29 @@ public class RegionBody extends Region {
// End of property values
/**
+ * Creates a new region-body element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public RegionBody(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
commonMarginBlock = pList.getMarginBlockProps();
columnCount = pList.get(PR_COLUMN_COUNT).getNumeric();
columnGap = pList.get(PR_COLUMN_GAP).getLength();
- if ((getColumnCount() > 1) && (getOverflow() == EN_SCROLL)) {
+ if ((getColumnCount() != 1) && (getOverflow() == EN_SCROLL)) {
/* This is an error (See XSL Rec, fo:region-body description).
* The Rec allows for acting as if "1" is chosen in
* these cases, but we will need to be able to change Numeric
* values in order to do this.
*/
- attributeError("If overflow property is set to \"scroll\","
- + " a column-count other than \"1\" may not be specified.");
+ getFOValidationEventProducer().columnCountErrorOnRegionBodyOverflowScroll(this,
+ getName(), getLocator());
}
}
@@ -93,9 +93,7 @@ public class RegionBody extends Region {
return columnGap.getValue();
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public Rectangle getViewportRectangle (FODimension reldims, SimplePageMaster spm) {
/* Special rules apply to resolving margins in the page context.
* Contrary to normal margins in this case top and bottom margin
@@ -143,9 +141,7 @@ public class RegionBody extends Region {
reldims.bpd - before - after);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-body";
}
@@ -155,9 +151,7 @@ public class RegionBody extends Region {
return "region-body";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_BODY;
}
diff --git a/src/java/org/apache/fop/fo/pagination/RegionEnd.java b/src/java/org/apache/fop/fo/pagination/RegionEnd.java
index 912be9097..13f65d71a 100644
--- a/src/java/org/apache/fop/fo/pagination/RegionEnd.java
+++ b/src/java/org/apache/fop/fo/pagination/RegionEnd.java
@@ -22,26 +22,26 @@ package org.apache.fop.fo.pagination;
// Java
import java.awt.Rectangle;
-// FOP
-import org.apache.fop.fo.FONode;
import org.apache.fop.datatypes.FODimension;
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.datatypes.SimplePercentBaseContext;
+import org.apache.fop.fo.FONode;
/**
* The fo:region-end element.
*/
public class RegionEnd extends RegionSE {
+
/**
+ * Creates a new region-end element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public RegionEnd(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public Rectangle getViewportRectangle (FODimension reldims, SimplePageMaster spm) {
/* Special rules apply to resolving extent as values are resolved relative
* to the page size and reference orientation.
@@ -80,9 +80,7 @@ public class RegionEnd extends RegionSE {
return vpRect;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-end";
}
@@ -92,9 +90,7 @@ public class RegionEnd extends RegionSE {
return "region-end";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_END;
}
diff --git a/src/java/org/apache/fop/fo/pagination/RegionSE.java b/src/java/org/apache/fop/fo/pagination/RegionSE.java
index 735623352..183b44342 100644
--- a/src/java/org/apache/fop/fo/pagination/RegionSE.java
+++ b/src/java/org/apache/fop/fo/pagination/RegionSE.java
@@ -35,15 +35,15 @@ public abstract class RegionSE extends SideRegion {
// End of property values
/**
+ * Creates a new region (start or end).
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
protected RegionSE(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
}
diff --git a/src/java/org/apache/fop/fo/pagination/RegionStart.java b/src/java/org/apache/fop/fo/pagination/RegionStart.java
index d78b19c3d..7a69cfdac 100644
--- a/src/java/org/apache/fop/fo/pagination/RegionStart.java
+++ b/src/java/org/apache/fop/fo/pagination/RegionStart.java
@@ -32,16 +32,17 @@ import org.apache.fop.datatypes.SimplePercentBaseContext;
* The fo:region-start element.
*/
public class RegionStart extends RegionSE {
+
/**
+ * Creates a new region-start element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public RegionStart(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public Rectangle getViewportRectangle (FODimension reldims, SimplePageMaster spm) {
/* Special rules apply to resolving extent as values are resolved relative
* to the page size and reference orientation.
@@ -77,9 +78,7 @@ public class RegionStart extends RegionSE {
return vpRect;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-start";
}
@@ -89,9 +88,7 @@ public class RegionStart extends RegionSE {
return "region-start";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_START;
}
diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
index 9d2fe652c..509b81f21 100644
--- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
+++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
@@ -52,38 +52,29 @@ public class RepeatablePageMasterAlternatives extends FObj
private boolean hasPagePositionOnly = false;
/**
+ * Creates a new repeatable-page-master-alternatives element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public RepeatablePageMasterAlternatives(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
maximumRepeats = pList.get(PR_MAXIMUM_REPEATS);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
conditionalPageMasterRefs = new java.util.ArrayList();
- if (parent.getName().equals("fo:page-sequence-master")) {
- PageSequenceMaster pageSequenceMaster = (PageSequenceMaster)parent;
- pageSequenceMaster.addSubsequenceSpecifier(this);
- } else {
- throw new ValidationException("fo:repeatable-page-master-alternatives "
- + "must be child of fo:page-sequence-master, not "
- + parent.getName(), locator);
- }
+ assert parent.getName().equals("fo:page-sequence-master"); //Validation by the parent
+ PageSequenceMaster pageSequenceMaster = (PageSequenceMaster)parent;
+ pageSequenceMaster.addSubsequenceSpecifier(this);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void endOfNode() throws FOPException {
if (firstChild == null) {
missingChildElementError("(conditional-page-master-reference+)");
@@ -95,10 +86,11 @@ public class RepeatablePageMasterAlternatives extends FObj
XSL/FOP: (conditional-page-master-reference+)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (!(FO_URI.equals(nsURI)
- && localName.equals("conditional-page-master-reference"))) {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!localName.equals("conditional-page-master-reference")) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
}
@@ -117,11 +109,7 @@ public class RepeatablePageMasterAlternatives extends FObj
}
}
- /**
- * Get the next matching page master from the conditional
- * page master references.
- * @see org.apache.fop.fo.pagination.SubSequenceSpecifier
- */
+ /** {@inheritDoc} */
public String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
boolean isLastPage,
@@ -183,12 +171,11 @@ public class RepeatablePageMasterAlternatives extends FObj
}
/** {@inheritDoc} */
- /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#hasPagePositionOnly() */
public boolean hasPagePositionOnly() {
return this.hasPagePositionOnly;
}
- /** @see org.apache.fop.fo.FONode#getLocalName() */
+ /** {@inheritDoc} */
public String getLocalName() {
return "repeatable-page-master-alternatives";
}
diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
index 172324232..87dc248c0 100644
--- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
+++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
@@ -47,15 +47,15 @@ public class RepeatablePageMasterReference extends FObj
private int numberConsumed = 0;
/**
+ * Creates a new repeatable-page-master-reference element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public RepeatablePageMasterReference(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
masterReference = pList.get(PR_MASTER_REFERENCE).getString();
maximumRepeats = pList.get(PR_MAXIMUM_REPEATS);
@@ -65,9 +65,7 @@ public class RepeatablePageMasterReference extends FObj
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
PageSequenceMaster pageSequenceMaster = (PageSequenceMaster) parent;
@@ -83,13 +81,13 @@ public class RepeatablePageMasterReference extends FObj
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
boolean isLastPage,
diff --git a/src/java/org/apache/fop/fo/pagination/Root.java b/src/java/org/apache/fop/fo/pagination/Root.java
index 6e079cf47..1cff9c3d6 100644
--- a/src/java/org/apache/fop/fo/pagination/Root.java
+++ b/src/java/org/apache/fop/fo/pagination/Root.java
@@ -25,7 +25,6 @@ import java.util.List;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
@@ -62,7 +61,9 @@ public class Root extends FObj {
*/
private FOEventHandler foEventHandler = null;
- /**
+ /**
+ * Creates a new root element.
+ * @param parent the parent node (must be null)
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public Root(FONode parent) {
@@ -137,13 +138,6 @@ public class Root extends FObj {
}
- /** @inheritDoc */
- protected void validateChildNode(Locator loc, FONode child) throws ValidationException {
- if (child instanceof AbstractPageSequence) {
- pageSequenceFound = true;
- }
- }
-
/**
* Sets the FOEventHandler object that this Root is attached to
* @param foEventHandler the FOEventHandler object
@@ -291,9 +285,7 @@ public class Root extends FObj {
return bookmarkTree;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public Root getRoot() {
return this;
}
@@ -303,9 +295,7 @@ public class Root extends FObj {
return "root";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_ROOT;
}
diff --git a/src/java/org/apache/fop/fo/pagination/SideRegion.java b/src/java/org/apache/fop/fo/pagination/SideRegion.java
index 14328aa9b..552ca871b 100644
--- a/src/java/org/apache/fop/fo/pagination/SideRegion.java
+++ b/src/java/org/apache/fop/fo/pagination/SideRegion.java
@@ -31,7 +31,11 @@ public abstract class SideRegion extends Region {
private Length extent;
- /** @see org.apache.fop.fo.FONode#FONode(FONode) */
+ /**
+ * Creates a new side region.
+ * @param parent the parent node
+ * @see org.apache.fop.fo.FONode#FONode(FONode)
+ */
protected SideRegion(FONode parent) {
super(parent);
}
diff --git a/src/java/org/apache/fop/fo/pagination/SimplePageMaster.java b/src/java/org/apache/fop/fo/pagination/SimplePageMaster.java
index ba1c0a6af..85a5081c8 100644
--- a/src/java/org/apache/fop/fo/pagination/SimplePageMaster.java
+++ b/src/java/org/apache/fop/fo/pagination/SimplePageMaster.java
@@ -63,15 +63,15 @@ public class SimplePageMaster extends FObj {
private boolean hasRegionEnd = false;
/**
+ * Creates a new simple-page-master element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public SimplePageMaster(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
commonMarginBlock = pList.getMarginBlockProps();
masterName = pList.get(PR_MASTER_NAME).getString();
@@ -85,9 +85,7 @@ public class SimplePageMaster extends FObj {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
LayoutMasterSet layoutMasterSet = (LayoutMasterSet) parent;
@@ -101,9 +99,7 @@ public class SimplePageMaster extends FObj {
regions = new HashMap(5);
}
- /**
- * Make sure content model satisfied.
- */
+ /** {@inheritDoc} */
protected void endOfNode() throws FOPException {
if (!hasRegionBody) {
missingChildElementError(
@@ -116,72 +112,70 @@ public class SimplePageMaster extends FObj {
* XSL Content Model: (region-body,region-before?,region-after?,region-start?,region-end?)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("region-body")) {
- if (hasRegionBody) {
- tooManyNodesError(loc, "fo:region-body");
- } else {
- hasRegionBody = true;
- }
- } else if (FO_URI.equals(nsURI) && localName.equals("region-before")) {
- if (!hasRegionBody) {
- nodesOutOfOrderError(loc, "fo:region-body", "fo:region-before");
- } else if (hasRegionBefore) {
- tooManyNodesError(loc, "fo:region-before");
- } else if (hasRegionAfter) {
- nodesOutOfOrderError(loc, "fo:region-before", "fo:region-after");
- } else if (hasRegionStart) {
- nodesOutOfOrderError(loc, "fo:region-before", "fo:region-start");
- } else if (hasRegionEnd) {
- nodesOutOfOrderError(loc, "fo:region-before", "fo:region-end");
- } else {
- hasRegionBody = true;
- }
- } else if (FO_URI.equals(nsURI) && localName.equals("region-after")) {
- if (!hasRegionBody) {
- nodesOutOfOrderError(loc, "fo:region-body", "fo:region-after");
- } else if (hasRegionAfter) {
- tooManyNodesError(loc, "fo:region-after");
- } else if (hasRegionStart) {
- nodesOutOfOrderError(loc, "fo:region-after", "fo:region-start");
- } else if (hasRegionEnd) {
- nodesOutOfOrderError(loc, "fo:region-after", "fo:region-end");
- } else {
- hasRegionAfter = true;
- }
- } else if (FO_URI.equals(nsURI) && localName.equals("region-start")) {
- if (!hasRegionBody) {
- nodesOutOfOrderError(loc, "fo:region-body", "fo:region-start");
- } else if (hasRegionStart) {
- tooManyNodesError(loc, "fo:region-start");
- } else if (hasRegionEnd) {
- nodesOutOfOrderError(loc, "fo:region-start", "fo:region-end");
- } else {
- hasRegionStart = true;
- }
- } else if (FO_URI.equals(nsURI) && localName.equals("region-end")) {
- if (!hasRegionBody) {
- nodesOutOfOrderError(loc, "fo:region-body", "fo:region-end");
- } else if (hasRegionEnd) {
- tooManyNodesError(loc, "fo:region-end");
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("region-body")) {
+ if (hasRegionBody) {
+ tooManyNodesError(loc, "fo:region-body");
+ } else {
+ hasRegionBody = true;
+ }
+ } else if (localName.equals("region-before")) {
+ if (!hasRegionBody) {
+ nodesOutOfOrderError(loc, "fo:region-body", "fo:region-before");
+ } else if (hasRegionBefore) {
+ tooManyNodesError(loc, "fo:region-before");
+ } else if (hasRegionAfter) {
+ nodesOutOfOrderError(loc, "fo:region-before", "fo:region-after");
+ } else if (hasRegionStart) {
+ nodesOutOfOrderError(loc, "fo:region-before", "fo:region-start");
+ } else if (hasRegionEnd) {
+ nodesOutOfOrderError(loc, "fo:region-before", "fo:region-end");
+ } else {
+ hasRegionBody = true;
+ }
+ } else if (localName.equals("region-after")) {
+ if (!hasRegionBody) {
+ nodesOutOfOrderError(loc, "fo:region-body", "fo:region-after");
+ } else if (hasRegionAfter) {
+ tooManyNodesError(loc, "fo:region-after");
+ } else if (hasRegionStart) {
+ nodesOutOfOrderError(loc, "fo:region-after", "fo:region-start");
+ } else if (hasRegionEnd) {
+ nodesOutOfOrderError(loc, "fo:region-after", "fo:region-end");
+ } else {
+ hasRegionAfter = true;
+ }
+ } else if (localName.equals("region-start")) {
+ if (!hasRegionBody) {
+ nodesOutOfOrderError(loc, "fo:region-body", "fo:region-start");
+ } else if (hasRegionStart) {
+ tooManyNodesError(loc, "fo:region-start");
+ } else if (hasRegionEnd) {
+ nodesOutOfOrderError(loc, "fo:region-start", "fo:region-end");
+ } else {
+ hasRegionStart = true;
+ }
+ } else if (localName.equals("region-end")) {
+ if (!hasRegionBody) {
+ nodesOutOfOrderError(loc, "fo:region-body", "fo:region-end");
+ } else if (hasRegionEnd) {
+ tooManyNodesError(loc, "fo:region-end");
+ } else {
+ hasRegionEnd = true;
+ }
} else {
- hasRegionEnd = true;
+ invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public boolean generatesReferenceAreas() {
return true;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void addChildNode(FONode child) throws FOPException {
if (child instanceof Region) {
addRegion((Region)child);
@@ -268,9 +262,7 @@ public class SimplePageMaster extends FObj {
return "simple-page-master";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_SIMPLE_PAGE_MASTER;
}
diff --git a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
index 43d8e40dc..119ec409e 100644
--- a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
+++ b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
@@ -46,6 +46,8 @@ public class SinglePageMasterReference extends FObj
private int state;
/**
+ * Creates a new single-page-master-reference element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public SinglePageMasterReference(FONode parent) {
@@ -53,9 +55,7 @@ public class SinglePageMasterReference extends FObj
this.state = FIRST;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
masterReference = pList.get(PR_MASTER_REFERENCE).getString();
@@ -64,9 +64,7 @@ public class SinglePageMasterReference extends FObj
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
PageSequenceMaster pageSequenceMaster = (PageSequenceMaster) parent;
pageSequenceMaster.addSubsequenceSpecifier(this);
@@ -77,8 +75,10 @@ public class SinglePageMasterReference extends FObj
* XSL Content Model: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/fo/pagination/StaticContent.java b/src/java/org/apache/fop/fo/pagination/StaticContent.java
index 62d73e56d..184438b6f 100644
--- a/src/java/org/apache/fop/fo/pagination/StaticContent.java
+++ b/src/java/org/apache/fop/fo/pagination/StaticContent.java
@@ -27,24 +27,22 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.ValidationException;
/**
- * Class modelling the fo:static-content object.
+ * Class modeling the fo:static-content object.
*/
public class StaticContent extends Flow {
/**
+ * Creates a new static-content element.
* @param parent FONode that is the parent of this object
*/
public StaticContent(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
if (getFlowName() == null || getFlowName().equals("")) {
- throw new ValidationException("A 'flow-name' is required for "
- + getName() + ".", locator);
+ missingPropertyError("flow-name");
}
getFOEventHandler().startFlow(this);
}
@@ -66,9 +64,11 @@ public class StaticContent extends Flow {
* XSL Content Model: (%block;)+
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (!isBlockItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!isBlockItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
}
diff --git a/src/java/org/apache/fop/fo/pagination/Title.java b/src/java/org/apache/fop/fo/pagination/Title.java
index 398424152..f6f625ea8 100644
--- a/src/java/org/apache/fop/fo/pagination/Title.java
+++ b/src/java/org/apache/fop/fo/pagination/Title.java
@@ -27,7 +27,7 @@ import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.flow.InlineLevel;
/**
- * Class modelling the fo:title object.
+ * Class modeling the fo:title object.
*/
public class Title extends InlineLevel {
// The value of properties relevant for fo:title.
@@ -35,6 +35,7 @@ public class Title extends InlineLevel {
// End of property values
/**
+ * Creates a new title element.
* @param parent FONode that is the parent of this object
*/
public Title(FONode parent) {
@@ -46,22 +47,20 @@ public class Title extends InlineLevel {
XSL/FOP: (#PCDATA|%inline;)*
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (!isInlineItem(nsURI, localName)) {
- invalidChildError(loc, nsURI, localName);
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!isInlineItem(nsURI, localName)) {
+ invalidChildError(loc, nsURI, localName);
+ }
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public String getLocalName() {
return "title";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_TITLE;
}
diff --git a/src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java b/src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java
index e588bb3f8..7f55ec51b 100644
--- a/src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java
+++ b/src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java
@@ -20,10 +20,13 @@
package org.apache.fop.fo.pagination.bookmarks;
import java.util.ArrayList;
+import java.util.List;
+
import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.FObj;
import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
@@ -68,10 +71,10 @@ public class Bookmark extends FObj {
externalDestination = null;
} else if (externalDestination.length() == 0) {
// slightly stronger than spec "should be specified"
- attributeError("Missing attribute: Either external-destination or " +
- "internal-destination must be specified.");
+ getFOValidationEventProducer().missingLinkDestination(this, getName(), locator);
} else {
- attributeWarning("external-destination property not currently supported");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ "external-destination", getLocator());
}
}
@@ -80,18 +83,20 @@ public class Bookmark extends FObj {
XSL/FOP: (bookmark-title, bookmark*)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (FO_URI.equals(nsURI) && localName.equals("bookmark-title")) {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (localName.equals("bookmark-title")) {
if (bookmarkTitle != null) {
tooManyNodesError(loc, "fo:bookmark-title");
}
- } else if (FO_URI.equals(nsURI) && localName.equals("bookmark")) {
+ } else if (localName.equals("bookmark")) {
if (bookmarkTitle == null) {
nodesOutOfOrderError(loc, "fo:bookmark-title", "fo:bookmark");
}
} else {
invalidChildError(loc, nsURI, localName);
}
+ }
}
/**
@@ -123,10 +128,18 @@ public class Bookmark extends FObj {
return bookmarkTitle == null ? "" : bookmarkTitle.getTitle();
}
+ /**
+ * Returns the value of the internal-destination property.
+ * @return the internal-destination
+ */
public String getInternalDestination() {
return internalDestination;
}
+ /**
+ * Returns the value of the external-destination property.
+ * @return the external-destination
+ */
public String getExternalDestination() {
return externalDestination;
}
@@ -141,7 +154,11 @@ public class Bookmark extends FObj {
return bShow;
}
- public ArrayList getChildBookmarks() {
+ /**
+ * Returns a list of child bookmarks.
+ * @return the list of child bookmarks
+ */
+ public List getChildBookmarks() {
return childBookmarks;
}
@@ -150,9 +167,7 @@ public class Bookmark extends FObj {
return "bookmark";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_BOOKMARK;
}
diff --git a/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java b/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java
index f31aad1cb..c7024f2aa 100644
--- a/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java
+++ b/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java
@@ -21,8 +21,8 @@ package org.apache.fop.fo.pagination.bookmarks;
import org.xml.sax.Locator;
-import org.apache.fop.fo.FObj;
import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
@@ -45,15 +45,16 @@ public class BookmarkTitle extends FObj {
/**
* Add the characters to this BookmarkTitle.
- * The text data inside the BookmarkTitle xml element
+ * The text data inside the BookmarkTitle XML element
* is used for the BookmarkTitle string.
*
* @param data the character data
* @param start the start position in the data array
* @param end the end position in the character array
- * @param locator location in fo source file.
+ * @param pList the currently valid property list
+ * @param locator location in FO source file.
*/
- protected void addCharacters(char data[], int start, int end,
+ protected void addCharacters(char[] data, int start, int end,
PropertyList pList,
Locator locator) {
title += new String(data, start, end - start);
@@ -64,8 +65,10 @@ public class BookmarkTitle extends FObj {
XSL/FOP: empty
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/**
@@ -82,9 +85,7 @@ public class BookmarkTitle extends FObj {
return "bookmark-title";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_BOOKMARK_TITLE;
}
diff --git a/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTree.java b/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTree.java
index 6190fa22a..0f1d8a8b7 100644
--- a/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTree.java
+++ b/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTree.java
@@ -21,6 +21,7 @@ package org.apache.fop.fo.pagination.bookmarks;
// Java
import java.util.ArrayList;
+import java.util.List;
import org.xml.sax.Locator;
@@ -39,15 +40,15 @@ public class BookmarkTree extends FObj {
private ArrayList bookmarks = new ArrayList();
/**
+ * Creates a new bookmark-tree element.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public BookmarkTree(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void addChildNode(FONode obj) {
if (obj instanceof Bookmark) {
bookmarks.add(obj);
@@ -69,14 +70,19 @@ public class BookmarkTree extends FObj {
XSL/FOP: (bookmark+)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
- throws ValidationException {
- if (!(FO_URI.equals(nsURI) &&
- localName.equals("bookmark"))) {
+ throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
+ if (!localName.equals("bookmark")) {
invalidChildError(loc, nsURI, localName);
+ }
}
}
- public ArrayList getBookmarks() {
+ /**
+ * Returns the root bookmarks.
+ * @return the root bookmarks
+ */
+ public List getBookmarks() {
return bookmarks;
}
@@ -85,9 +91,7 @@ public class BookmarkTree extends FObj {
return "bookmark-tree";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_BOOKMARK_TREE;
}
diff --git a/src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java b/src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java
index 69ca372e5..5a5cf95c5 100644
--- a/src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java
+++ b/src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
-/* $Id:$ */
+/* $Id$ */
package org.apache.fop.fo.properties;
diff --git a/src/java/org/apache/fop/fonts/FontEventAdapter.java b/src/java/org/apache/fop/fonts/FontEventAdapter.java
new file mode 100644
index 000000000..516999f70
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/FontEventAdapter.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.fonts;
+
+import java.util.Map;
+
+import org.apache.fop.events.Event;
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.model.EventSeverity;
+
+/**
+ * Event listener interface for font-related events. This interface extends FontEventListener
+ * and EventProducer for integration into FOP's event subsystem.
+ */
+public class FontEventAdapter implements FontEventListener {
+
+ private EventBroadcaster eventBroadcaster;
+
+ /**
+ * Creates a new FontEventAdapter.
+ * @param broadcaster the event broadcaster to send the generated events to
+ */
+ public FontEventAdapter(EventBroadcaster broadcaster) {
+ this.eventBroadcaster = broadcaster;
+ }
+
+ /**
+ * Returns the event group ID.
+ * @return the event group ID
+ */
+ protected String getEventGroupID() {
+ return getClass().getName();
+ }
+
+ /** {@inheritDoc} */
+ public void fontSubstituted(Object source, FontTriplet requested, FontTriplet effective) {
+ Map params = new java.util.HashMap();
+ params.put("requested", requested);
+ params.put("effective", effective);
+ Event ev = new Event(source, getEventGroupID() + ".fontSubstituted",
+ EventSeverity.WARN, params);
+ this.eventBroadcaster.broadcastEvent(ev);
+ }
+
+ /** {@inheritDoc} */
+ public void fontLoadingErrorAtAutoDetection(Object source, String fontURL, Exception e) {
+ Map params = new java.util.HashMap();
+ params.put("fontURL", fontURL);
+ params.put("e", e);
+ Event ev = new Event(source, getEventGroupID() + ".fontLoadingErrorAtAutoDetection",
+ EventSeverity.WARN, params);
+ this.eventBroadcaster.broadcastEvent(ev);
+ }
+
+ /** {@inheritDoc} */
+ public void glyphNotAvailable(Object source, char ch, String fontName) {
+ Map params = new java.util.HashMap();
+ params.put("ch", new Character(ch));
+ params.put("fontName", fontName);
+ Event ev = new Event(source, getEventGroupID() + ".glyphNotAvailable",
+ EventSeverity.WARN, params);
+ this.eventBroadcaster.broadcastEvent(ev);
+ }
+
+}
diff --git a/src/java/org/apache/fop/fonts/FontEventListener.java b/src/java/org/apache/fop/fonts/FontEventListener.java
new file mode 100644
index 000000000..512df0ac0
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/FontEventListener.java
@@ -0,0 +1,52 @@
+/*
+ * 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.fonts;
+
+
+/**
+ * Event listener interface for font-related events.
+ */
+public interface FontEventListener {
+
+ /**
+ * Notifies about a font being substituted as the requested one isn't available.
+ * @param source the event source
+ * @param requested the requested font triplet
+ * @param effective the effective font triplet
+ */
+ void fontSubstituted(Object source, FontTriplet requested, FontTriplet effective);
+
+ /**
+ * An error occurred while loading a font for auto-detection.
+ * @param source the event source
+ * @param fontURL the font URL
+ * @param e the original exception
+ */
+ void fontLoadingErrorAtAutoDetection(Object source, String fontURL, Exception e);
+
+ /**
+ * A glyph has been requested that is not available in the font.
+ * @param source the event source
+ * @param ch the character for which the glyph isn't available
+ * @param fontName the name of the font
+ */
+ void glyphNotAvailable(Object source, char ch, String fontName);
+
+}
diff --git a/src/java/org/apache/fop/fonts/FontInfo.java b/src/java/org/apache/fop/fonts/FontInfo.java
index 218734f15..950134eb6 100644
--- a/src/java/org/apache/fop/fonts/FontInfo.java
+++ b/src/java/org/apache/fop/fonts/FontInfo.java
@@ -67,6 +67,8 @@ public class FontInfo {
/** Cache for Font instances. */
private Map fontInstanceCache = new java.util.HashMap();
+ private FontEventListener eventListener;
+
/**
* Main constructor
*/
@@ -78,6 +80,15 @@ public class FontInfo {
}
/**
+ * Sets the font event listener that can be used to receive events about particular events
+ * in this class.
+ * @param listener the font event listener
+ */
+ public void setEventListener(FontEventListener listener) {
+ this.eventListener = listener;
+ }
+
+ /**
* Checks if the font setup is valid (At least the ultimate fallback font
* must be registered.)
* @return True if valid
@@ -158,6 +169,9 @@ public class FontInfo {
public void addMetrics(String name, FontMetrics metrics) {
// add the given metrics as a font with the given name
+ if (metrics instanceof Typeface) {
+ ((Typeface)metrics).setEventListener(this.eventListener);
+ }
this.fonts.put(name, metrics);
}
@@ -364,8 +378,12 @@ public class FontInfo {
}
if (!loggedFontKeys.contains(replacedKey)) {
loggedFontKeys.add(replacedKey);
- log.warn("Font '" + replacedKey + "' not found. "
- + "Substituting with '" + newKey + "'.");
+ if (this.eventListener != null) {
+ this.eventListener.fontSubstituted(this, replacedKey, newKey);
+ } else {
+ log.warn("Font '" + replacedKey + "' not found. "
+ + "Substituting with '" + newKey + "'.");
+ }
}
}
diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java
index 07b5be305..5490e13f1 100644
--- a/src/java/org/apache/fop/fonts/LazyFont.java
+++ b/src/java/org/apache/fop/fonts/LazyFont.java
@@ -141,6 +141,7 @@ public class LazyFont extends Typeface implements FontDescriptor {
throw new RuntimeException(ioex.getMessage());
}
}
+ realFont.setEventListener(this.eventListener);
isMetricsLoaded = true;
}
}
diff --git a/src/java/org/apache/fop/fonts/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java
index ac12b7615..0a47d52ab 100644
--- a/src/java/org/apache/fop/fonts/SingleByteFont.java
+++ b/src/java/org/apache/fop/fonts/SingleByteFont.java
@@ -117,18 +117,22 @@ public class SingleByteFont extends CustomFont {
}
//Give up, character is not available
- Character ch = new Character(c);
- if (warnedChars == null) {
- warnedChars = new java.util.HashSet();
- }
- if (warnedChars.size() < 8 && !warnedChars.contains(ch)) {
- warnedChars.add(ch);
- if (warnedChars.size() == 8) {
- log.warn("Many requested glyphs are not available in font " + getFontName());
- } else {
- log.warn("Glyph " + (int)c + " (0x" + Integer.toHexString(c)
- + ", " + Glyphs.charToGlyphName(c)
- + ") not available in font " + getFontName());
+ if (this.eventListener != null) {
+ this.eventListener.glyphNotAvailable(this, c, getFontName());
+ } else {
+ Character ch = new Character(c);
+ if (warnedChars == null) {
+ warnedChars = new java.util.HashSet();
+ }
+ if (warnedChars.size() < 8 && !warnedChars.contains(ch)) {
+ warnedChars.add(ch);
+ if (warnedChars.size() == 8) {
+ log.warn("Many requested glyphs are not available in font " + getFontName());
+ } else {
+ log.warn("Glyph " + (int)c + " (0x" + Integer.toHexString(c)
+ + ", " + Glyphs.charToGlyphName(c)
+ + ") not available in font " + getFontName());
+ }
}
}
return NOT_FOUND;
diff --git a/src/java/org/apache/fop/fonts/Typeface.java b/src/java/org/apache/fop/fonts/Typeface.java
index 173d2e8a3..b6c78a3b0 100644
--- a/src/java/org/apache/fop/fonts/Typeface.java
+++ b/src/java/org/apache/fop/fonts/Typeface.java
@@ -30,6 +30,9 @@ public abstract class Typeface implements FontMetrics {
*/
private long charMapOps = 0;
+ /** An optional event listener that receives events such as missing glyphs etc. */
+ protected FontEventListener eventListener;
+
/**
* Get the encoding of the font.
* @return the encoding
@@ -80,5 +83,14 @@ public abstract class Typeface implements FontMetrics {
return getAscender(size);
}
+ /**
+ * Sets the font event listener that can be used to receive events about particular events
+ * in this class.
+ * @param listener the font event listener
+ */
+ public void setEventListener(FontEventListener listener) {
+ this.eventListener = listener;
+ }
+
}
diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
index 8207eb140..b223ea7a1 100644
--- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
+++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
@@ -35,6 +35,7 @@ import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.EmbedFontInfo;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontCache;
+import org.apache.fop.fonts.FontEventListener;
import org.apache.fop.fonts.FontLoader;
import org.apache.fop.fonts.FontResolver;
import org.apache.fop.fonts.FontTriplet;
@@ -52,6 +53,17 @@ public class FontInfoFinder {
/** logging instance */
private Log log = LogFactory.getLog(FontInfoFinder.class);
+ private FontEventListener eventListener;
+
+ /**
+ * Sets the font event listener that can be used to receive events about particular events
+ * in this class.
+ * @param listener the font event listener
+ */
+ public void setEventListener(FontEventListener listener) {
+ this.eventListener = listener;
+ }
+
/**
* Attempts to determine FontTriplets from a given CustomFont.
* It seems to be fairly accurate but will probably require some tweaking over time
@@ -180,7 +192,6 @@ public class FontInfoFinder {
// try to determine triplet information from font file
CustomFont customFont = null;
-
if (fontUrl.toExternalForm().endsWith(".ttc")) {
// Get a list of the TTC Font names
List ttcNames = null; //List<String>
@@ -193,7 +204,9 @@ public class FontInfoFinder {
FontFileReader reader = new FontFileReader(in);
ttcNames = ttf.getTTCnames(reader);
} catch (Exception e) {
- log.error(e);
+ if (this.eventListener != null) {
+ this.eventListener.fontLoadingErrorAtAutoDetection(this, fontFileURI, e);
+ }
} finally {
IOUtils.closeQuietly(in);
}
@@ -212,14 +225,16 @@ public class FontInfoFinder {
try {
ttfLoader = new TTFFontLoader(fontFileURI, fontName, resolver);
customFont = ttfLoader.getFont();
+ if (this.eventListener != null) {
+ customFont.setEventListener(this.eventListener);
+ }
} catch (Exception e) {
- //TODO Too verbose (it's an error but we don't care if some fonts can't be loaded)
- //if (log.isErrorEnabled()) {
- log.error("Unable to load font file: " + embedUrl + ". Reason: " + e.getMessage());
- //}
if (fontCache != null) {
fontCache.registerFailedFont(embedUrl, fileLastModified);
}
+ if (this.eventListener != null) {
+ this.eventListener.fontLoadingErrorAtAutoDetection(this, embedUrl, e);
+ }
continue;
}
EmbedFontInfo fi = fontInfoFromCustomFont(fontUrl, customFont, fontCache);
@@ -233,14 +248,16 @@ public class FontInfoFinder {
// The normal case
try {
customFont = FontLoader.loadFont(fontUrl, null, resolver);
+ if (this.eventListener != null) {
+ customFont.setEventListener(this.eventListener);
+ }
} catch (Exception e) {
- //TODO Too verbose (it's an error but we don't care if some fonts can't be loaded)
- //if (log.isErrorEnabled()) {
- log.error("Unable to load font file: " + embedUrl + ". Reason: " + e.getMessage());
- //}
if (fontCache != null) {
fontCache.registerFailedFont(embedUrl, fileLastModified);
}
+ if (this.eventListener != null) {
+ this.eventListener.fontLoadingErrorAtAutoDetection(this, embedUrl, e);
+ }
return null;
}
EmbedFontInfo fi = fontInfoFromCustomFont(fontUrl, customFont, fontCache);
@@ -250,7 +267,6 @@ public class FontInfoFinder {
return null;
}
}
-
}
diff --git a/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java b/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java
index 81b3b4c07..be0995d8c 100644
--- a/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java
+++ b/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java
@@ -27,8 +27,11 @@ import java.util.Map;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.bridge.UserAgent;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.gvt.GraphicsNode;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageException;
@@ -40,7 +43,7 @@ import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
import org.apache.xmlgraphics.util.UnitConv;
-import org.apache.fop.svg.SVGUserAgent;
+import org.apache.fop.svg.SimpleSVGUserAgent;
/**
* This ImageConverter converts SVG images to Java2D.
@@ -52,6 +55,9 @@ import org.apache.fop.svg.SVGUserAgent;
*/
public class ImageConverterSVG2G2D extends AbstractImageConverter {
+ /** logger */
+ private static Log log = LogFactory.getLog(ImageConverterSVG2G2D.class);
+
/** {@inheritDoc} */
public Image convert(Image src, Map hints) throws ImageException {
checkSourceFlavor(src);
@@ -67,9 +73,7 @@ public class ImageConverterSVG2G2D extends AbstractImageConverter {
if (ptm != null) {
pxToMillimeter = (float)UnitConv.mm2in(ptm.doubleValue());
}
- SVGUserAgent ua = new SVGUserAgent(
- pxToMillimeter,
- new AffineTransform());
+ UserAgent ua = createBatikUserAgent(pxToMillimeter);
GVTBuilder builder = new GVTBuilder();
final BridgeContext ctx = new BridgeContext(ua);
@@ -107,6 +111,25 @@ public class ImageConverterSVG2G2D extends AbstractImageConverter {
return g2dImage;
}
+ /**
+ * Creates a user agent for Batik. Override to provide your own user agent.
+ * @param pxToMillimeter the source resolution (in px per millimeter)
+ * @return the newly created user agent
+ */
+ protected SimpleSVGUserAgent createBatikUserAgent(float pxToMillimeter) {
+ return new SimpleSVGUserAgent(
+ pxToMillimeter,
+ new AffineTransform()) {
+
+ /** {@inheritDoc} */
+ public void displayMessage(String message) {
+ //TODO Refine and pipe through to caller
+ log.debug(message);
+ }
+
+ };
+ }
+
/** {@inheritDoc} */
public ImageFlavor getSourceFlavor() {
return ImageFlavor.XML_DOM;
diff --git a/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java b/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java
index e59e06b6b..20557a644 100644
--- a/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java
+++ b/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java
@@ -32,6 +32,7 @@ import org.w3c.dom.svg.SVGDocument;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.UnitProcessor;
+import org.apache.batik.bridge.UserAgent;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.dom.svg.SVGOMDocument;
@@ -46,7 +47,7 @@ import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.util.MimeConstants;
-import org.apache.fop.svg.SVGUserAgent;
+import org.apache.fop.svg.SimpleSVGUserAgent;
import org.apache.fop.util.UnclosableInputStream;
/**
@@ -154,8 +155,15 @@ public class PreloaderSVG extends AbstractImagePreloader {
private ImageInfo createImageInfo(String uri, ImageContext context, SVGDocument doc) {
Element e = doc.getRootElement();
float pxUnitToMillimeter = 25.4f / context.getSourceResolution();
- SVGUserAgent userAg = new SVGUserAgent(pxUnitToMillimeter,
- new AffineTransform());
+ UserAgent userAg = new SimpleSVGUserAgent(pxUnitToMillimeter,
+ new AffineTransform()) {
+
+ /** {@inheritDoc} */
+ public void displayMessage(String message) {
+ log.debug(message);
+ }
+
+ };
BridgeContext ctx = new BridgeContext(userAg);
UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e);
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
index 65d537bcd..4ef0579f2 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
@@ -198,11 +198,11 @@ public abstract class AbstractBreaker {
}
/**
- * Returns a PageBreakingLayoutListener for the PageBreakingAlgorithm to notify about layout
- * problems.
+ * Creates and returns a PageBreakingLayoutListener for the PageBreakingAlgorithm to
+ * notify about layout problems.
* @return the listener instance or null if no notifications are needed
*/
- protected PageBreakingAlgorithm.PageBreakingLayoutListener getLayoutListener() {
+ protected PageBreakingAlgorithm.PageBreakingLayoutListener createLayoutListener() {
return null;
}
@@ -320,7 +320,7 @@ public abstract class AbstractBreaker {
log.debug("PLM> start of algorithm (" + this.getClass().getName()
+ "), flow BPD =" + flowBPD);
PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
- getPageProvider(), getLayoutListener(),
+ getPageProvider(), createLayoutListener(),
alignment, alignmentLast, footnoteSeparatorLength,
isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
int iOptPageCount;
diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
index e8ca88c1c..f01f0e12f 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
@@ -35,7 +35,6 @@ import org.apache.fop.area.CTM;
import org.apache.fop.area.Trait;
import org.apache.fop.datatypes.FODimension;
import org.apache.fop.datatypes.Length;
-import org.apache.fop.fo.FONode;
import org.apache.fop.fo.flow.BlockContainer;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
import org.apache.fop.traits.MinOptMax;
@@ -239,11 +238,11 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
int availableIPD = referenceIPD - getIPIndents();
if (getContentAreaIPD() > availableIPD) {
- log.warn(FONode.decorateWithContextInfo(
- "The extent in inline-progression-direction (width) of a block-container is"
- + " bigger than the available space ("
- + getContentAreaIPD() + "mpt > " + context.getRefIPD() + "mpt)",
- getBlockContainerFO()));
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getBlockContainerFO().getUserAgent().getEventBroadcaster());
+ eventProducer.objectTooWide(this, getBlockContainerFO().getName(),
+ getContentAreaIPD(), context.getRefIPD(),
+ getBlockContainerFO().getLocator());
}
MinOptMax stackLimit = new MinOptMax(relDims.bpd);
@@ -378,10 +377,12 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
*/
if (contentOverflows) {
- log.warn("Contents overflow block-container viewport: clipping");
- if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
- //TODO Throw layout exception
- }
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getBlockContainerFO().getUserAgent().getEventBroadcaster());
+ boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW);
+ eventProducer.viewportOverflow(this, getBlockContainerFO().getName(),
+ breaker.getOverflowAmount(), needClip(), canRecover,
+ getBlockContainerFO().getLocator());
}
}
addKnuthElementsForBorderPaddingAfter(returnList, true);
@@ -526,10 +527,12 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
//TODO Maybe check for page overflow when autoHeight=true
if (!autoHeight & (contentOverflows)) {
- log.warn("Contents overflow block-container viewport: clipping");
- if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
- //TODO Throw layout exception
- }
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getBlockContainerFO().getUserAgent().getEventBroadcaster());
+ boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW);
+ eventProducer.viewportOverflow(this, getBlockContainerFO().getName(),
+ breaker.getOverflowAmount(), needClip(), canRecover,
+ getBlockContainerFO().getLocator());
}
}
@@ -602,7 +605,18 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
}
public boolean isOverflow() {
- return !isEmpty() && (deferredAlg.getPageBreaks().size() > 1);
+ if (isEmpty()) {
+ return false;
+ } else {
+ return (deferredAlg.getPageBreaks().size() > 1)
+ || (deferredAlg.totalWidth - deferredAlg.totalShrink)
+ > deferredAlg.getLineWidth();
+ }
+ }
+
+ public int getOverflowAmount() {
+ return (deferredAlg.totalWidth - deferredAlg.totalShrink)
+ - deferredAlg.getLineWidth();
}
protected LayoutManager getTopLevelLM() {
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java
new file mode 100644
index 000000000..c31a70477
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java
@@ -0,0 +1,168 @@
+/*
+ * 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.layoutmgr;
+
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+
+/**
+ * Event producer interface for block-level layout managers.
+ */
+public interface BlockLevelEventProducer extends EventProducer {
+
+ /**
+ * Provider class for the event producer.
+ */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static BlockLevelEventProducer get(EventBroadcaster broadcaster) {
+ return (BlockLevelEventProducer)broadcaster.getEventProducerFor(
+ BlockLevelEventProducer.class);
+ }
+ }
+
+ /**
+ * The contents of a table-row are too big to fit in the constraints.
+ * @param source the event source
+ * @param row the row number
+ * @param effCellBPD the effective extent in block-progression direction of the cell
+ * @param maxCellBPD the maximum extent in block-progression direction of the cell
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void rowTooTall(Object source, int row, int effCellBPD, int maxCellBPD, Locator loc);
+
+ /**
+ * Auto-table layout is not supported, yet.
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity INFO
+ */
+ void tableFixedAutoWidthNotSupported(Object source, Locator loc);
+
+ /**
+ * An formatting object is too wide.
+ * @param source the event source
+ * @param elementName the formatting object
+ * @param effIPD the effective extent in inline-progression direction of the table contents
+ * @param maxIPD the maximum extent in inline-progression direction available
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void objectTooWide(Object source, String elementName, int effIPD, int maxIPD, Locator loc);
+
+ /**
+ * An overconstrained geometry adjustment rule was triggered (5.3.4, XSL 1.0).
+ * @param source the event source
+ * @param elementName the formatting object
+ * @param amount the amount of the adjustment (in mpt)
+ * @param loc the location of the error or null
+ * @event.severity INFO
+ */
+ void overconstrainedAdjustEndIndent(Object source, String elementName, int amount, Locator loc);
+
+ /**
+ * Contents overflow a viewport.
+ * @param source the event source
+ * @param elementName the formatting object
+ * @param amount the amount by which the contents overflow (in mpt)
+ * @param clip true if the content will be clipped
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @param loc the location of the error or null
+ * @throws LayoutException the layout error provoked by the method call
+ * @event.severity FATAL
+ */
+ void viewportOverflow(Object source, String elementName,
+ int amount, boolean clip, boolean canRecover,
+ Locator loc) throws LayoutException;
+
+ /**
+ * Contents overflow a region viewport.
+ * @param source the event source
+ * @param elementName the formatting object
+ * @param page the page number/name where the overflow happened
+ * @param amount the amount by which the contents overflow (in mpt)
+ * @param clip true if the content will be clipped
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @param loc the location of the error or null
+ * @throws LayoutException the layout error provoked by the method call
+ * @event.severity FATAL
+ */
+ void regionOverflow(Object source, String elementName,
+ String page,
+ int amount, boolean clip, boolean canRecover,
+ Locator loc) throws LayoutException;
+
+ /**
+ * Indicates that FOP doesn't support flows that are not mapped to region-body, yet.
+ * @param source the event source
+ * @param flowName the flow name
+ * @param masterName the page master name
+ * @param loc the location of the error or null
+ * @throws UnsupportedOperationException the layout error provoked by the method call
+ * @event.severity FATAL
+ */
+ void flowNotMappingToRegionBody(Object source, String flowName, String masterName,
+ Locator loc) throws UnsupportedOperationException;
+
+ /**
+ * A page sequence master is exhausted.
+ * @param source the event source
+ * @param pageSequenceMasterName the name of the page sequence master
+ * @param canRecover indicates whether FOP can recover from this problem and continue working
+ * @param loc the location of the error or null
+ * @throws FOPException the error provoked by the method call
+ * @event.severity FATAL
+ */
+ void pageSequenceMasterExhausted(Object source, String pageSequenceMasterName,
+ boolean canRecover, Locator loc) throws FOPException;
+
+ /**
+ * No subsequences in page sequence master.
+ * @param source the event source
+ * @param pageSequenceMasterName the name of the page sequence master
+ * @param loc the location of the error or null
+ * @throws FOPException the error provoked by the method call
+ * @event.severity FATAL
+ */
+ void missingSubsequencesInPageSequenceMaster(Object source, String pageSequenceMasterName,
+ Locator loc) throws FOPException;
+
+ /**
+ * No single-page-master matching in page sequence master.
+ * @param source the event source
+ * @param pageSequenceMasterName the name of the page sequence master
+ * @param pageMasterName the name of the page master not matching
+ * @param loc the location of the error or null
+ * @throws FOPException the error provoked by the method call
+ * @event.severity FATAL
+ */
+ void noMatchingPageMaster(Object source, String pageSequenceMasterName,
+ String pageMasterName, Locator loc) throws FOPException;
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
index 67ed1de9f..cb6db6b01 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
@@ -201,6 +201,10 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
if (ipd < 0) {
//5.3.4, XSL 1.0, Overconstrained Geometry
log.debug("Adjusting end-indent based on overconstrained geometry rules for " + fobj);
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getFObj().getUserAgent().getEventBroadcaster());
+ eventProducer.overconstrainedAdjustEndIndent(this,
+ getFObj().getName(), ipd, getFObj().getLocator());
endIndent += ipd;
ipd = 0;
//TODO Should we skip layout for a block that has ipd=0?
@@ -212,7 +216,7 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
/**
* Sets the content area IPD by directly supplying the value.
* end-indent is adjusted based on overconstrained geometry rules, if necessary.
- *
+ * @param contentIPD the IPD of the content
* @return the resulting content area IPD
*/
protected int updateContentAreaIPDwithOverconstrainedAdjust(int contentIPD) {
@@ -220,6 +224,10 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
if (ipd < 0) {
//5.3.4, XSL 1.0, Overconstrained Geometry
log.debug("Adjusting end-indent based on overconstrained geometry rules for " + fobj);
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getFObj().getUserAgent().getEventBroadcaster());
+ eventProducer.overconstrainedAdjustEndIndent(this,
+ getFObj().getName(), ipd, getFObj().getLocator());
endIndent += ipd;
}
setContentAreaIPD(contentIPD);
diff --git a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
index 21856c781..086d91c31 100644
--- a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
@@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr;
import java.awt.Dimension;
import java.awt.Rectangle;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -46,6 +47,7 @@ import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.Viewport;
import org.apache.fop.datatypes.FODimension;
import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExternalDocument;
import org.apache.fop.layoutmgr.inline.ImageLayout;
@@ -140,17 +142,28 @@ public class ExternalDocumentLayoutManager extends AbstractPageSequenceLayoutMan
pageIndex++;
}
} catch (URISyntaxException e) {
- log.error("Error parsing or constructing URIs based on URI: " + uri);
+ getResourceEventProducer().uriError(this, uri, e,
+ getExternalDocument().getLocator());
return;
}
}
+ } catch (FileNotFoundException fnfe) {
+ getResourceEventProducer().imageNotFound(this, uri, fnfe,
+ getExternalDocument().getLocator());
} catch (IOException ioe) {
- log.error("Image not available: " + uri, ioe);
+ getResourceEventProducer().imageIOError(this, uri, ioe,
+ getExternalDocument().getLocator());
} catch (ImageException ie) {
- log.error("Error while inspecting image: " + uri + " (" + ie.getMessage() + ")");
+ getResourceEventProducer().imageError(this, uri, ie,
+ getExternalDocument().getLocator());
}
}
+ private ResourceEventProducer getResourceEventProducer() {
+ return ResourceEventProducer.Provider.get(
+ getExternalDocument().getUserAgent().getEventBroadcaster());
+ }
+
private void makePageForImage(ImageInfo info, ImageLayout layout) {
this.imageLayout = layout;
curPage = makeNewPage(false, false);
diff --git a/src/java/org/apache/fop/layoutmgr/LayoutException.java b/src/java/org/apache/fop/layoutmgr/LayoutException.java
new file mode 100644
index 000000000..350cc758a
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/LayoutException.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.layoutmgr;
+
+import java.util.Locale;
+
+import org.apache.fop.events.Event;
+import org.apache.fop.events.EventFormatter;
+import org.apache.fop.events.EventExceptionManager.ExceptionFactory;
+
+/**
+ * Exception thrown by FOP if an unrecoverable layout error occurs. An example: An area overflows
+ * a viewport that has overflow="error-if-overflow".
+ *
+ * @todo Discuss if this should become a checked exception.
+ */
+public class LayoutException extends RuntimeException {
+
+ private static final long serialVersionUID = 5157080040923740433L;
+
+ private String localizedMessage;
+ private LayoutManager layoutManager;
+
+ /**
+ * Constructs a new layout exception with the specified detail message.
+ * @param message the detail message.
+ */
+ public LayoutException(String message) {
+ this(message, null);
+ }
+
+ /**
+ * Constructs a new layout exception with the specified detail message.
+ * @param message the detail message
+ * @param lm the layout manager that throws the exception
+ */
+ public LayoutException(String message, LayoutManager lm) {
+ super(message);
+ this.layoutManager = lm;
+ }
+
+ /**
+ * Sets the localized message for this exception.
+ * @param msg the localized message
+ */
+ public void setLocalizedMessage(String msg) {
+ this.localizedMessage = msg;
+ }
+
+ /** {@inheritDoc} */
+ public String getLocalizedMessage() {
+ if (this.localizedMessage != null) {
+ return this.localizedMessage;
+ } else {
+ return super.getLocalizedMessage();
+ }
+ }
+
+ /**
+ * Returns the layout manager that detected the problem.
+ * @return the layout manager (or null)
+ */
+ public LayoutManager getLayoutManager() {
+ return this.layoutManager;
+ }
+
+ /** Exception factory for {@link LayoutException}. */
+ public static class LayoutExceptionFactory implements ExceptionFactory {
+
+ /** {@inheritDoc} */
+ public Throwable createException(Event event) {
+ Object source = event.getSource();
+ LayoutManager lm = (source instanceof LayoutManager) ? (LayoutManager)source : null;
+ String msg = EventFormatter.format(event, Locale.ENGLISH);
+ LayoutException ex = new LayoutException(msg, lm);
+ if (!Locale.ENGLISH.equals(Locale.getDefault())) {
+ ex.setLocalizedMessage(EventFormatter.format(event));
+ }
+ return ex;
+ }
+
+ /** {@inheritDoc} */
+ public Class getExceptionClass() {
+ return LayoutException.class;
+ }
+
+ }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreaker.java b/src/java/org/apache/fop/layoutmgr/PageBreaker.java
index 3e100cd50..d6be75758 100644
--- a/src/java/org/apache/fop/layoutmgr/PageBreaker.java
+++ b/src/java/org/apache/fop/layoutmgr/PageBreaker.java
@@ -27,7 +27,6 @@ import org.apache.fop.area.Block;
import org.apache.fop.area.Footnote;
import org.apache.fop.area.PageViewport;
import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Region;
@@ -79,27 +78,25 @@ public class PageBreaker extends AbstractBreaker {
return pslm.getPageProvider();
}
- /**
- * {@inheritDoc}
- */
- protected PageBreakingLayoutListener getLayoutListener() {
+ /** {@inheritDoc} */
+ protected PageBreakingLayoutListener createLayoutListener() {
return new PageBreakingLayoutListener() {
- public void notifyOverflow(int part, FObj obj) {
+ public void notifyOverflow(int part, int amount, FObj obj) {
Page p = pageProvider.getPage(
false, part, PageProvider.RELTO_CURRENT_ELEMENT_LIST);
RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion(
Region.FO_REGION_BODY);
- String err = FONode.decorateWithContextInfo(
- "Content of the region-body on page "
- + p.getPageViewport().getPageNumberString()
- + " overflows the available area in block-progression dimension.",
- obj);
- if (body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW) {
- throw new RuntimeException(err);
- } else {
- log.warn(err);
- }
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ body.getUserAgent().getEventBroadcaster());
+
+ boolean canRecover = (body.getOverflow() != Constants.EN_ERROR_IF_OVERFLOW);
+ boolean needClip = (body.getOverflow() == Constants.EN_HIDDEN
+ || body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW);
+ eventProducer.regionOverflow(this, body.getName(),
+ p.getPageViewport().getPageNumberString(),
+ amount, needClip, canRecover,
+ body.getLocator());
}
};
@@ -297,7 +294,7 @@ public class PageBreaker extends AbstractBreaker {
//Restart last page
PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm(
getTopLevelLM(),
- getPageProvider(), getLayoutListener(),
+ getPageProvider(), createLayoutListener(),
alg.getAlignment(), alg.getAlignmentLast(),
footnoteSeparatorLength,
isPartOverflowRecoveryActivated(), false, false);
@@ -356,7 +353,7 @@ public class PageBreaker extends AbstractBreaker {
//Restart last page
PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
getTopLevelLM(),
- getPageProvider(), getLayoutListener(),
+ getPageProvider(), createLayoutListener(),
alignment, Constants.EN_START, footnoteSeparatorLength,
isPartOverflowRecoveryActivated(),
pslm.getCurrentPV().getBodyRegion().getColumnCount());
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
index d98d29b5c..5e3d0a887 100644
--- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
@@ -27,7 +27,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
import org.apache.fop.traits.MinOptMax;
@@ -289,7 +288,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
private void resetFootnotes(LinkedList elementLists) {
for (int i = 0; i < elementLists.size(); i++) {
- LinkedList removedList = (LinkedList) footnotesList.remove(footnotesList.size() - 1);
+ /*LinkedList removedList = (LinkedList)*/footnotesList.remove(footnotesList.size() - 1);
lengthList.remove(lengthList.size() - 1);
// update totalFootnotesLength
@@ -332,11 +331,13 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
actualWidth += allFootnotes;
insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
footnoteListIndex = footnotesList.size() - 1;
- footnoteElementIndex = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
- } else if (((canDeferOldFootnotes = checkCanDeferOldFootnotes(pageNode, elementIndex))
+ footnoteElementIndex
+ = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
+ } else if (((canDeferOldFootnotes
+ = checkCanDeferOldFootnotes(pageNode, elementIndex))
|| newFootnotes)
- && (footnoteSplit = getFootnoteSplit(pageNode, getLineWidth() - actualWidth,
- canDeferOldFootnotes)) > 0) {
+ && (footnoteSplit = getFootnoteSplit(pageNode,
+ getLineWidth() - actualWidth, canDeferOldFootnotes)) > 0) {
// it is allowed to break or even defer footnotes if either:
// - there are new footnotes in the last piece of content, and
// there is space to add at least a piece of the first one
@@ -356,7 +357,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
actualWidth += allFootnotes;
insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
footnoteListIndex = footnotesList.size() - 1;
- footnoteElementIndex = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
+ footnoteElementIndex
+ = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
}
} else {
// all footnotes have already been placed on previous pages
@@ -381,7 +383,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
*/
private boolean checkCanDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) {
return (noBreakBetween(node.position, contentElementIndex)
- && deferredFootnotes(node.footnoteListIndex, node.footnoteElementIndex, node.totalFootnotes));
+ && deferredFootnotes(node.footnoteListIndex,
+ node.footnoteElementIndex, node.totalFootnotes));
}
/**
@@ -455,7 +458,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
* @param availableLength available space for footnotes
* @param canDeferOldFootnotes
*/
- private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength, boolean canDeferOldFootnotes) {
+ private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength,
+ boolean canDeferOldFootnotes) {
return getFootnoteSplit(activeNode.footnoteListIndex,
activeNode.footnoteElementIndex,
activeNode.totalFootnotes,
@@ -520,7 +524,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
// try adding a split of the next note
- noteListIterator = ((LinkedList) footnotesList.get(listIndex)).listIterator(elementIndex);
+ noteListIterator = ((LinkedList) footnotesList.get(listIndex))
+ .listIterator(elementIndex);
int prevSplitLength = 0;
int prevIndex = -1;
@@ -754,14 +759,6 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
}
- private int getPartCount() {
- if (pageBreaks == null) {
- return 0;
- } else {
- return pageBreaks.size();
- }
- }
-
public void updateData1(int total, double demerits) {
}
@@ -774,12 +771,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
if (difference + bestActiveNode.availableShrink < 0) {
if (!autoHeight) {
if (layoutListener != null) {
- layoutListener.notifyOverflow(bestActiveNode.line - 1, getFObj());
- } else if (log.isWarnEnabled()) {
- log.warn(FONode.decorateWithContextInfo(
- "Part/page " + (bestActiveNode.line - 1)
- + " overflows the available area in block-progression dimension.",
- getFObj()));
+ layoutListener.notifyOverflow(bestActiveNode.line - 1, -difference, getFObj());
}
}
}
@@ -890,9 +882,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
/**
* Issued when an overflow is detected
* @param part the number of the part (page) this happens on
+ * @param amount the amount by which the area overflows (in mpt)
* @param obj the root FO object where this happens
*/
- void notifyOverflow(int part, FObj obj);
+ void notifyOverflow(int part, int amount, FObj obj);
}
diff --git a/src/java/org/apache/fop/layoutmgr/PageProvider.java b/src/java/org/apache/fop/layoutmgr/PageProvider.java
index e16c3396a..037f02094 100644
--- a/src/java/org/apache/fop/layoutmgr/PageProvider.java
+++ b/src/java/org/apache/fop/layoutmgr/PageProvider.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.fo.Constants;
@@ -74,6 +75,7 @@ public class PageProvider implements Constants {
/**
* Main constructor.
+ * @param ath the area tree handler
* @param ps The page-sequence the provider operates on
*/
public PageProvider(AreaTreeHandler ath, PageSequence ps) {
@@ -258,10 +260,10 @@ public class PageProvider implements Constants {
if (!pageSeq.getMainFlow().getFlowName().equals(body.getRegionName())) {
// this is fine by the XSL Rec (fo:flow's flow-name can be mapped to
// any region), but we don't support it yet.
- throw new FOPException("Flow '" + pageSeq.getMainFlow().getFlowName()
- + "' does not map to the region-body in page-master '"
- + spm.getMasterName() + "'. FOP presently "
- + "does not support this.");
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ pageSeq.getUserAgent().getEventBroadcaster());
+ eventProducer.flowNotMappingToRegionBody(this,
+ pageSeq.getMainFlow().getFlowName(), spm.getMasterName(), spm.getLocator());
}
Page page = new Page(spm, index, pageNumberString, isBlank);
//Set unique key obtained from the AreaTreeHandler
diff --git a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
index b1e414527..763ddf58d 100644
--- a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
@@ -29,10 +29,12 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.RegionReference;
-import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FObj;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
import org.apache.fop.layoutmgr.inline.TextLayoutManager;
import org.apache.fop.traits.MinOptMax;
@@ -240,12 +242,17 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
breaker.doLayout(targetBPD, autoHeight);
if (breaker.isOverflow()) {
if (!autoHeight) {
- //Overflow handling
- if (regionFO.getOverflow() == EN_ERROR_IF_OVERFLOW) {
- //TODO throw layout exception
- }
- log.warn(FONode.decorateWithContextInfo(
- "static-content overflows the available area.", fobj));
+ String page = getPSLM().getCurrentPage().getPageViewport().getPageNumberString();
+
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getStaticContentFO().getUserAgent().getEventBroadcaster());
+ boolean canRecover = (regionFO.getOverflow() != EN_ERROR_IF_OVERFLOW);
+ boolean needClip = (regionFO.getOverflow() == Constants.EN_HIDDEN
+ || regionFO.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW);
+ eventProducer.regionOverflow(this, regionFO.getName(),
+ page,
+ breaker.getOverflowAmount(), needClip, canRecover,
+ getStaticContentFO().getLocator());
}
}
}
@@ -262,7 +269,7 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
private StaticContentLayoutManager lm;
private int displayAlign;
private int ipd;
- private boolean overflow = false;
+ private int overflow = 0;
public StaticContentBreaker(StaticContentLayoutManager lm, int ipd,
int displayAlign) {
@@ -288,9 +295,26 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
}
public boolean isOverflow() {
+ return (this.overflow != 0);
+ }
+
+ public int getOverflowAmount() {
return this.overflow;
}
+ /** {@inheritDoc} */
+ protected PageBreakingLayoutListener createLayoutListener() {
+ return new PageBreakingLayoutListener() {
+
+ public void notifyOverflow(int part, int amount, FObj obj) {
+ if (StaticContentBreaker.this.overflow == 0) {
+ StaticContentBreaker.this.overflow = amount;
+ }
+ }
+
+ };
+ }
+
protected LayoutManager getTopLevelLM() {
return lm;
}
@@ -340,9 +364,6 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
- if (partCount > 1) {
- overflow = true;
- }
//Rendering all parts (not just the first) at once for the case where the parts that
//overflow should be visible.
alg.removeAllPageBreaks();
diff --git a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
index ff7c5b3ce..2af844c5c 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
@@ -60,7 +60,6 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager
*/
private static Log log = LogFactory.getLog(ContentLayoutManager.class);
- private FOUserAgent userAgent;
private Area holder;
private int stackSize;
private LayoutManager parentLM;
diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java
new file mode 100644
index 000000000..51d2720cb
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.layoutmgr.inline;
+
+import org.xml.sax.Locator;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+
+/**
+ * Event producer interface for inline-level layout managers.
+ */
+public interface InlineLevelEventProducer extends EventProducer {
+
+ /**
+ * Provider class for the event producer.
+ */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static InlineLevelEventProducer get(EventBroadcaster broadcaster) {
+ return (InlineLevelEventProducer)broadcaster.getEventProducerFor(
+ InlineLevelEventProducer.class);
+ }
+ }
+
+ /**
+ * fo:leader with "use-content" but without children.
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity ERROR
+ */
+ void leaderWithoutContent(Object source, Locator loc);
+
+ /**
+ * A line overflows.
+ * @param source the event source
+ * @param line the number number
+ * @param overflowLength the length by which the content overflows the available space (in mpt)
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void lineOverflows(Object source, int line, int overflowLength, Locator loc);
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java
index f4bcde96f..1e7c793df 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java
@@ -19,11 +19,15 @@
package org.apache.fop.layoutmgr.inline;
+import java.util.LinkedList;
+import java.util.List;
+
import org.apache.fop.area.Trait;
import org.apache.fop.area.inline.FilledArea;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.Space;
import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.Leader;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
@@ -41,10 +45,6 @@ import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.traits.MinOptMax;
-import java.util.List;
-import java.util.LinkedList;
-import org.apache.fop.fo.FObj;
-
/**
* LayoutManager for the fo:leader formatting object
*/
@@ -159,7 +159,9 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
leaderArea = fa;
} else if (fobj.getLeaderPattern() == EN_USECONTENT) {
if (fobj.getChildNodes() == null) {
- fobj.getLogger().error("Leader use-content with no content");
+ InlineLevelEventProducer eventProducer = InlineLevelEventProducer.Provider.get(
+ getFObj().getUserAgent().getEventBroadcaster());
+ eventProducer.leaderWithoutContent(this, getFObj().getLocator());
return null;
}
@@ -339,9 +341,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
return returnList;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getBaseLength(int lengthBase, FObj fobj) {
return getParent().getBaseLength(lengthBase, getParent().getFObj());
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
index 9a818232c..1258fbe18 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
@@ -34,7 +34,6 @@ import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.FONode;
import org.apache.fop.fo.flow.Block;
import org.apache.fop.fo.properties.CommonHyphenation;
import org.apache.fop.fonts.Font;
@@ -384,11 +383,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager
if (log.isWarnEnabled()) {
int lack = difference + bestActiveNode.availableShrink;
if (lack < 0) {
+ InlineLevelEventProducer eventProducer
+ = InlineLevelEventProducer.Provider.get(
+ getFObj().getUserAgent().getEventBroadcaster());
+ eventProducer.lineOverflows(this, addedPositions + 1,
+ -lack, getFObj().getLocator());
String textDiff = (lack < -50000 ? "more than 50 points" : (-lack) + "mpt");
- log.warn(FONode.decorateWithContextInfo(
- "Line " + (addedPositions + 1)
- + " of a paragraph overflows the available area by "
- + textDiff + ".", getFObj()));
}
}
@@ -1430,7 +1430,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager
auxCount++;
}
}
- log.trace(" Word to hyphenate: " + sbChars.toString());
+ if (log.isTraceEnabled()) {
+ log.trace(" Word to hyphenate: " + sbChars.toString());
+ }
// find hyphenation points
HyphContext hc = getHyphenContext(sbChars);
// ask each LM to hyphenate its word fragment
diff --git a/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java b/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java
index 2ef2be908..bd032e610 100644
--- a/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java
+++ b/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java
@@ -25,9 +25,9 @@ import java.util.ListIterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.datatypes.PercentBaseContext;
-import org.apache.fop.datatypes.Length;
+import org.apache.fop.datatypes.Length;
+import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableColumn;
@@ -77,12 +77,14 @@ public class ColumnSetup {
}
}
//Post-processing the list (looking for gaps)
+ //TODO The following block could possibly be removed
int pos = 1;
ListIterator ppIter = columns.listIterator();
while (ppIter.hasNext()) {
TableColumn col = (TableColumn)ppIter.next();
if (col == null) {
- log.error("Found a gap in the table-columns at position " + pos);
+ assert false; //Gaps are filled earlier by fo.flow.table.Table.finalizeColumns()
+ //log.error("Found a gap in the table-columns at position " + pos);
}
pos++;
}
@@ -100,7 +102,9 @@ public class ColumnSetup {
if (index > size) {
if (index > maxColIndexReferenced) {
maxColIndexReferenced = index;
- if (!(size == 1 && getColumn(1).isImplicitColumn())) {
+ TableColumn col = getColumn(1);
+ if (!(size == 1 && col.isImplicitColumn())) {
+ assert false; //TODO Seems to be removable as this is now done in the FO tree
log.warn(FONode.decorateWithContextInfo(
"There are fewer table-columns than are needed. "
+ "Column " + index + " was accessed, but only "
@@ -193,8 +197,7 @@ public class ColumnSetup {
if (colWidth != null) {
sumCols += colWidth.getValue(tlm);
if (colWidth instanceof TableColLength) {
- factors +=
- ((TableColLength) colWidth).getTableUnits();
+ factors += ((TableColLength) colWidth).getTableUnits();
}
}
}
diff --git a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java
index 9c97ca827..72e78b84f 100644
--- a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java
@@ -24,6 +24,7 @@ import java.util.LinkedList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.table.EffRow;
import org.apache.fop.fo.flow.table.GridUnit;
@@ -32,6 +33,7 @@ import org.apache.fop.fo.flow.table.TableColumn;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.LengthRangeProperty;
+import org.apache.fop.layoutmgr.BlockLevelEventProducer;
import org.apache.fop.layoutmgr.ElementListObserver;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.MinOptMaxUtil;
@@ -193,7 +195,9 @@ class RowGroupLayoutManager {
row.setHeight(rowHeights[rgi]);
row.setExplicitHeight(explicitRowHeight);
// TODO re-enable and improve after clarification
+ //See http://markmail.org/message/h25ycwwu7qglr4k4
// if (maxCellBPD > row.getExplicitHeight().max) {
+//old:
// log.warn(FONode.decorateWithContextInfo(
// "The contents of row " + (row.getIndex() + 1)
// + " are taller than they should be (there is a"
@@ -203,10 +207,12 @@ class RowGroupLayoutManager {
// + " to " + maxCellBPD + " millipoints, but the row shouldn't get"
// + " any taller than " + row.getExplicitHeight() + " millipoints.",
// row.getTableRow()));
+//new (with events):
+// BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Factory.create(
+// tableRow.getUserAgent().getEventBroadcaster());
+// eventProducer.rowTooTall(this, row.getIndex() + 1,
+// maxCellBPD, row.getExplicitHeight().max, tableRow.getLocator());
// }
- if (log.isDebugEnabled()) {
- log.debug(" height=" + rowHeights[rgi] + " explicit=" + explicitRowHeight);
- }
}
}
}
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
index d6bba5cb5..720ca5faa 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
@@ -26,6 +26,7 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.datatypes.LengthBase;
@@ -34,6 +35,7 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableColumn;
+import org.apache.fop.layoutmgr.BlockLevelEventProducer;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.ConditionalElementListener;
@@ -189,9 +191,9 @@ public class TableLayoutManager extends BlockStackingLayoutManager
updateContentAreaIPDwithOverconstrainedAdjust(contentIPD);
} else {
if (!getTable().isAutoLayout()) {
- log.info("table-layout=\"fixed\" and width=\"auto\", "
- + "but auto-layout not supported "
- + "=> assuming width=\"100%\"");
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getTable().getUserAgent().getEventBroadcaster());
+ eventProducer.tableFixedAutoWidthNotSupported(this, getTable().getLocator());
}
updateContentAreaIPDwithOverconstrainedAdjust();
}
@@ -204,11 +206,11 @@ public class TableLayoutManager extends BlockStackingLayoutManager
}
int availableIPD = referenceIPD - getIPIndents();
if (getContentAreaIPD() > availableIPD) {
- log.warn(FONode.decorateWithContextInfo(
- "The extent in inline-progression-direction (width) of a table is"
- + " bigger than the available space ("
- + getContentAreaIPD() + "mpt > " + context.getRefIPD() + "mpt)",
- getTable()));
+ BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+ getTable().getUserAgent().getEventBroadcaster());
+ eventProducer.objectTooWide(this, getTable().getName(),
+ getContentAreaIPD(), context.getRefIPD(),
+ getTable().getLocator());
}
/* initialize unit to determine computed values
diff --git a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java
index f74699fd5..2c47875d9 100644
--- a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java
+++ b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java
@@ -30,12 +30,14 @@ import org.w3c.dom.Document;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.dom.AbstractDocument;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.gvt.GraphicsNode;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
import org.apache.fop.render.RendererContext.RendererContextWrapper;
+import org.apache.fop.svg.SVGEventProducer;
import org.apache.fop.svg.SVGUserAgent;
/**
@@ -46,9 +48,6 @@ import org.apache.fop.svg.SVGUserAgent;
*/
public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererContextConstants {
- /** logging instance */
- private static Log log = LogFactory.getLog(AbstractGenericSVGHandler.class);
-
/** {@inheritDoc} */
public void handleXML(RendererContext context,
Document doc, String ns) throws Exception {
@@ -73,7 +72,7 @@ public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererC
//Prepare
SVGUserAgent ua = new SVGUserAgent(
- context.getUserAgent().getSourcePixelUnitToMillimeter(),
+ context.getUserAgent(),
new AffineTransform());
GVTBuilder builder = new GVTBuilder();
final BridgeContext ctx = new BridgeContext(ua);
@@ -83,7 +82,9 @@ public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererC
try {
root = builder.build(ctx, doc);
} catch (Exception e) {
- log.error("SVG graphic could not be built: " + e.getMessage(), e);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgNotBuilt(this, e, getDocumentURI(doc));
return;
}
@@ -115,6 +116,20 @@ public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererC
}
/**
+ * Gets the document URI from a Document instance if possible.
+ * @param doc the Document
+ * @return the URI or null
+ */
+ protected String getDocumentURI(Document doc) {
+ String docURI = null;
+ if (doc instanceof AbstractDocument) {
+ AbstractDocument level3Doc = (AbstractDocument)doc;
+ docURI = level3Doc.getDocumentURI();
+ }
+ return docURI;
+ }
+
+ /**
* Override this method to update the renderer context if it needs special settings for
* certain conditions.
* @param context the renderer context
diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
index 9e13476f6..b38d973c5 100644
--- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
+++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
@@ -31,6 +31,7 @@ import org.w3c.dom.Document;
import org.apache.batik.parser.AWTTransformProducer;
import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.util.QName;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
@@ -45,7 +46,6 @@ import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fonts.FontMetrics;
import org.apache.fop.traits.BorderProps;
-import org.apache.fop.util.QName;
/**
* Abstract base class for renderers like PDF and PostScript where many painting operations
diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java
index 32c4b33c4..ca3d007d0 100644
--- a/src/java/org/apache/fop/render/AbstractRenderer.java
+++ b/src/java/org/apache/fop/render/AbstractRenderer.java
@@ -67,6 +67,7 @@ import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.Viewport;
import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.Constants;
import org.apache.fop.fonts.FontInfo;
@@ -113,21 +114,15 @@ public abstract class AbstractRenderer
private Set warnedXMLHandlers;
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public abstract void setupFontInfo(FontInfo fontInfo);
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void setUserAgent(FOUserAgent agent) {
userAgent = agent;
}
- /**
- * @return the associated <code>FOUserAgent</code>
- */
+ /** {@inheritDoc} */
public FOUserAgent getUserAgent() {
return userAgent;
}
@@ -797,10 +792,11 @@ public abstract class AbstractRenderer
= new XMLHandlerConfigurator(userAgent);
configurator.configure(ctx, namespace);
handler.handleXML(ctx, doc, namespace);
- } catch (Throwable t) {
+ } catch (Exception e) {
// could not handle document
- log.error("Some XML content will be ignored. "
- + "Could not render XML", t);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ ctx.getUserAgent().getEventBroadcaster());
+ eventProducer.foreignXMLProcessingError(this, doc, namespace, e);
}
} else {
if (warnedXMLHandlers == null) {
@@ -809,8 +805,9 @@ public abstract class AbstractRenderer
if (!warnedXMLHandlers.contains(namespace)) {
// no handler found for document
warnedXMLHandlers.add(namespace);
- log.warn("Some XML content will be ignored. "
- + "No handler defined for XML: " + namespace);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ ctx.getUserAgent().getEventBroadcaster());
+ eventProducer.foreignXMLNoHandler(this, doc, namespace);
}
}
}
diff --git a/src/java/org/apache/fop/render/Renderer.java b/src/java/org/apache/fop/render/Renderer.java
index b40eec0cf..03b4582f7 100644
--- a/src/java/org/apache/fop/render/Renderer.java
+++ b/src/java/org/apache/fop/render/Renderer.java
@@ -89,6 +89,12 @@ public interface Renderer {
void setUserAgent(FOUserAgent agent);
/**
+ * Returns the associated user agent.
+ * @return the user agent
+ */
+ FOUserAgent getUserAgent();
+
+ /**
* Set up the given FontInfo.
*
* @param fontInfo The font information
diff --git a/src/java/org/apache/fop/render/RendererEventProducer.java b/src/java/org/apache/fop/render/RendererEventProducer.java
new file mode 100644
index 000000000..365c8f430
--- /dev/null
+++ b/src/java/org/apache/fop/render/RendererEventProducer.java
@@ -0,0 +1,53 @@
+/*
+ * 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.render;
+
+import java.io.IOException;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+
+/**
+ * Event producer interface for rendering-specific events.
+ */
+public interface RendererEventProducer extends EventProducer {
+
+ /** Provider class for the event producer. */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static RendererEventProducer get(EventBroadcaster broadcaster) {
+ return (RendererEventProducer)broadcaster.getEventProducerFor(
+ RendererEventProducer.class);
+ }
+ }
+
+ /**
+ * I/O error while writing target file.
+ * @param source the event source
+ * @param ioe the original I/O error
+ * @event.severity ERROR
+ */
+ void ioError(Object source, IOException ioe);
+}
diff --git a/src/java/org/apache/fop/render/afp/AFPEventProducer.java b/src/java/org/apache/fop/render/afp/AFPEventProducer.java
new file mode 100644
index 000000000..615c54c32
--- /dev/null
+++ b/src/java/org/apache/fop/render/afp/AFPEventProducer.java
@@ -0,0 +1,63 @@
+/*
+ * 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.render.afp;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.events.model.AbstractEventModelFactory;
+import org.apache.fop.events.model.EventModel;
+
+/**
+ * Event producer interface for AFP-specific events.
+ */
+public interface AFPEventProducer extends EventProducer {
+
+ /** Provider class for the event producer. */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static AFPEventProducer get(EventBroadcaster broadcaster) {
+ return (AFPEventProducer)broadcaster.getEventProducerFor(
+ AFPEventProducer.class);
+ }
+ }
+
+ /** Event model factory for AFP. */
+ public static class EventModelFactory extends AbstractEventModelFactory {
+
+ /** {@inheritDoc} */
+ public EventModel createEventModel() {
+ return loadModel(getClass(), "event-model.xml");
+ }
+
+ }
+
+ /**
+ * Warn about using default font setup.
+ * @param source the event source
+ * @event.severity WARN
+ */
+ void warnDefaultFontSetup(Object source);
+
+}
diff --git a/src/java/org/apache/fop/render/afp/AFPEventProducer.xml b/src/java/org/apache/fop/render/afp/AFPEventProducer.xml
new file mode 100644
index 000000000..8eec9b656
--- /dev/null
+++ b/src/java/org/apache/fop/render/afp/AFPEventProducer.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="org.apache.fop.render.afp.AFPEventProducer.warnDefaultFontSetup">No AFP fonts configured. Using default setup.</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java
index 205873493..83b8048f9 100644
--- a/src/java/org/apache/fop/render/afp/AFPRenderer.java
+++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java
@@ -70,6 +70,7 @@ import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fonts.FontInfo;
@@ -291,7 +292,9 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
}
}
} else {
- log.warn("No AFP fonts configured - using default setup");
+ AFPEventProducer eventProducer = AFPEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.warnDefaultFontSetup(this);
}
if (this.fontInfo.fontLookup("sans-serif", "normal", 400) == null) {
CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZH200 ",
@@ -909,7 +912,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
RendererContext context;
context = super.createRendererContext(x, y, width, height, foreignAttributes);
context.setProperty(AFPRendererContextConstants.AFP_GRAYSCALE,
- new Boolean(!this.colorImages));
+ Boolean.valueOf(!this.colorImages));
return context;
}
@@ -1014,13 +1017,17 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
}
} catch (ImageException ie) {
- log.error("Error while processing image: "
- + (info != null ? info.toString() : uri), ie);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
} catch (FileNotFoundException fe) {
- log.error(fe.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
} catch (IOException ioe) {
- log.error("I/O error while processing image: "
- + (info != null ? info.toString() : uri), ioe);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
}
/*
@@ -1195,7 +1202,9 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
image.getWidth(), image.getHeight(), this.bitsPerPixel);
}
} catch (IOException ioe) {
- log.error("Error while serializing bitmap: " + ioe.getMessage(), ioe);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageWritingError(this, ioe);
}
}
@@ -1305,8 +1314,9 @@ public class AFPRenderer extends AbstractPathOrientedRenderer {
} catch (Throwable ex) {
encoding = AFPConstants.EBCIDIC_ENCODING;
log.warn(
- "renderText():: Error getting encoding for font "
- + " - using default encoding "
+ "renderText():: Error getting encoding for font '"
+ + tf.getFullName()
+ + "' - using default encoding "
+ encoding);
}
diff --git a/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.java b/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.java
new file mode 100644
index 000000000..7b26d0771
--- /dev/null
+++ b/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.java
@@ -0,0 +1,80 @@
+/*
+ * 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.render.bitmap;
+
+import java.io.IOException;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.events.model.AbstractEventModelFactory;
+import org.apache.fop.events.model.EventModel;
+
+/**
+ * Event producer interface for events generated by the bitmap renderers.
+ */
+public interface BitmapRendererEventProducer extends EventProducer {
+
+ /** Provider class for the event producer. */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static BitmapRendererEventProducer get(EventBroadcaster broadcaster) {
+ return (BitmapRendererEventProducer)broadcaster.getEventProducerFor(
+ BitmapRendererEventProducer.class);
+ }
+ }
+
+ /** Event model factory for this event producer. */
+ public static class EventModelFactory extends AbstractEventModelFactory {
+
+ /** {@inheritDoc} */
+ public EventModel createEventModel() {
+ return loadModel(getClass(), "event-model.xml");
+ }
+
+ }
+
+ /**
+ * No filename information available. Stopping early after the first page.
+ * @param source the event source
+ * @event.severity WARN
+ */
+ void stoppingAfterFirstPageNoFilename(Object source);
+
+ /**
+ * Image writer does not support multiple images. Only the first page has been produced.
+ * @param source the event source
+ * @event.severity WARN
+ */
+ void stoppingAfterFirstPageNoMultiWriter(Object source);
+
+ /**
+ * No ImageWriter found.
+ * @param source the event source
+ * @param mime the target MIME type
+ * @throws IOException the I/O error provoked by the method call
+ * @event.severity FATAL
+ */
+ void noImageWriterFound(Object source, String mime) throws IOException;
+}
diff --git a/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml b/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml
new file mode 100644
index 000000000..a05af3e21
--- /dev/null
+++ b/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="org.apache.fop.render.bitmap.BitmapRendererEventProducer.stoppingAfterFirstPageNoFilename">No filename information available. Stopping early after the first page.</message>
+ <message key="org.apache.fop.render.bitmap.BitmapRendererEventProducer.stoppingAfterFirstPageNoMultiWriter">Image writer does not support multiple images. Only the first page has been produced.</message>
+ <message key="org.apache.fop.render.bitmap.BitmapRendererEventProducer.noImageWriterFound">Could not get an ImageWriter to produce "{mime}". The most likely explanation for this is a class loading problem.</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/render/bitmap/PNGRenderer.java b/src/java/org/apache/fop/render/bitmap/PNGRenderer.java
index bedd2c499..8613ef7b8 100644
--- a/src/java/org/apache/fop/render/bitmap/PNGRenderer.java
+++ b/src/java/org/apache/fop/render/bitmap/PNGRenderer.java
@@ -23,12 +23,12 @@ import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.OutputStream;
+import org.apache.commons.io.IOUtils;
+
import org.apache.xmlgraphics.image.writer.ImageWriter;
import org.apache.xmlgraphics.image.writer.ImageWriterParams;
import org.apache.xmlgraphics.image.writer.ImageWriterRegistry;
-import org.apache.commons.io.IOUtils;
-
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.area.PageViewport;
import org.apache.fop.render.java2d.Java2DRenderer;
@@ -74,8 +74,10 @@ public class PNGRenderer extends Java2DRenderer {
OutputStream os = getCurrentOutputStream(i);
if (os == null) {
- log.warn("No filename information available."
- + " Stopping early after the first page.");
+ BitmapRendererEventProducer eventProducer
+ = BitmapRendererEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.stoppingAfterFirstPageNoFilename(this);
break;
}
try {
@@ -104,9 +106,10 @@ public class PNGRenderer extends Java2DRenderer {
// Encode PNG image
ImageWriter writer = ImageWriterRegistry.getInstance().getWriterFor(getMimeType());
if (writer == null) {
- throw new IOException("Could not get an ImageWriter to produce "
- + getMimeType() + ". The most likely explanation for this is a class"
- + " loading problem.");
+ BitmapRendererEventProducer eventProducer
+ = BitmapRendererEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.noImageWriterFound(this, getMimeType());
}
if (log.isDebugEnabled()) {
log.debug("Writing image using " + writer.getClass().getName());
diff --git a/src/java/org/apache/fop/render/bitmap/TIFFRenderer.java b/src/java/org/apache/fop/render/bitmap/TIFFRenderer.java
index 4f64e45e1..9291427d2 100644
--- a/src/java/org/apache/fop/render/bitmap/TIFFRenderer.java
+++ b/src/java/org/apache/fop/render/bitmap/TIFFRenderer.java
@@ -129,7 +129,10 @@ public class TIFFRenderer extends Java2DRenderer {
// Creates writer
ImageWriter writer = ImageWriterRegistry.getInstance().getWriterFor(getMimeType());
if (writer == null) {
- throw new NullPointerException("No ImageWriter for " + getMimeType() + " available!");
+ BitmapRendererEventProducer eventProducer
+ = BitmapRendererEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.noImageWriterFound(this, getMimeType());
}
if (writer.supportsMultiImageWriter()) {
MultiImageWriter multiWriter = writer.createMultiImageWriter(outputStream);
@@ -145,8 +148,10 @@ public class TIFFRenderer extends Java2DRenderer {
} else {
writer.writeImage((RenderedImage) pageImagesItr.next(), outputStream, writerParams);
if (pageImagesItr.hasNext()) {
- log.error("Image encoder does not support multiple images. Only the first page"
- + " has been produced.");
+ BitmapRendererEventProducer eventProducer
+ = BitmapRendererEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.stoppingAfterFirstPageNoFilename(this);
}
}
diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
index 583d2ad2f..0ffe3307a 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
@@ -66,6 +66,7 @@ import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.Constants;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
@@ -930,13 +931,17 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
pos, foreignAttributes);
}
} catch (ImageException ie) {
- log.error("Error while processing image: "
- + (info != null ? info.toString() : uri), ie);
- } catch (FileNotFoundException fnfe) {
- log.error(fnfe.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
+ } catch (FileNotFoundException fe) {
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
} catch (IOException ioe) {
- log.error("I/O error while processing image: "
- + (info != null ? info.toString() : uri), ioe);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
}
}
diff --git a/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java b/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java
index 51b458cfe..64ac823fc 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java
@@ -23,26 +23,25 @@ import java.awt.geom.AffineTransform;
import org.w3c.dom.Document;
-import org.apache.fop.render.Renderer;
-import org.apache.fop.render.XMLHandler;
-import org.apache.fop.render.RendererContext;
-import org.apache.fop.svg.SVGUserAgent;
-
-// Commons-Logging
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.gvt.GraphicsNode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.batik.bridge.GVTBuilder;
-import org.apache.batik.bridge.BridgeContext;
-import org.apache.batik.dom.svg.SVGDOMImplementation;
-import org.apache.batik.gvt.GraphicsNode;
+import org.apache.fop.render.AbstractGenericSVGHandler;
+import org.apache.fop.render.Renderer;
+import org.apache.fop.render.RendererContext;
+import org.apache.fop.svg.SVGEventProducer;
+import org.apache.fop.svg.SVGUserAgent;
/**
* Java2D XML handler for SVG (uses Apache Batik).
* This handler handles XML for foreign objects when rendering to Java2D.
* The properties from the Java2D renderer are subject to change.
*/
-public class Java2DSVGHandler implements XMLHandler, Java2DRendererContextConstants {
+public class Java2DSVGHandler extends AbstractGenericSVGHandler
+ implements Java2DRendererContextConstants {
/** logging instance */
private static Log log = LogFactory.getLog(Java2DSVGHandler.class);
@@ -54,16 +53,6 @@ public class Java2DSVGHandler implements XMLHandler, Java2DRendererContextConsta
//nop
}
- /** {@inheritDoc} */
- public void handleXML(RendererContext context,
- Document doc, String ns) throws Exception {
- Java2DInfo pdfi = getJava2DInfo(context);
-
- if (SVGDOMImplementation.SVG_NAMESPACE_URI.equals(ns)) {
- renderSVGDocument(context, doc, pdfi);
- }
- }
-
/**
* Get the pdf information from the render context.
*
@@ -106,23 +95,18 @@ public class Java2DSVGHandler implements XMLHandler, Java2DRendererContextConsta
}
}
- /**
- * Render the svg document.
- * @param context the renderer context
- * @param doc the svg document
- * @param info the pdf information of the current context
- */
+ /** {@inheritDoc} */
protected void renderSVGDocument(RendererContext context,
- Document doc,
- Java2DInfo info) {
+ Document doc) {
+ Java2DInfo info = getJava2DInfo(context);
+ if (log.isDebugEnabled()) {
+ log.debug("renderSVGDocument(" + context + ", " + doc + ", " + info + ")");
+ }
- log.debug("renderSVGDocument(" + context + ", " + doc + ", " + info + ")");
-
int x = info.currentXPosition;
int y = info.currentYPosition;
- float ptom = context.getUserAgent().getSourcePixelUnitToMillimeter();
- SVGUserAgent ua = new SVGUserAgent(ptom, new AffineTransform());
+ SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(), new AffineTransform());
GVTBuilder builder = new GVTBuilder();
BridgeContext ctx = new BridgeContext(ua);
@@ -131,7 +115,9 @@ public class Java2DSVGHandler implements XMLHandler, Java2DRendererContextConsta
try {
root = builder.build(ctx, doc);
} catch (Exception e) {
- log.error("SVG graphic could not be built: " + e.getMessage(), e);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgNotBuilt(this, e, getDocumentURI(doc));
return;
}
@@ -158,7 +144,9 @@ public class Java2DSVGHandler implements XMLHandler, Java2DRendererContextConsta
try {
root.paint(info.state.getGraph());
} catch (Exception e) {
- log.error("Error while painting SVG", e);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgRenderingError(this, e, getDocumentURI(doc));
}
info.state.getGraph().setTransform(origTransform);
@@ -169,9 +157,4 @@ public class Java2DSVGHandler implements XMLHandler, Java2DRendererContextConsta
return (renderer instanceof Java2DRenderer);
}
-
- /** {@inheritDoc} */
- public String getNamespace() {
- return SVGDOMImplementation.SVG_NAMESPACE_URI;
- }
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLEventProducer.java b/src/java/org/apache/fop/render/pcl/PCLEventProducer.java
new file mode 100644
index 000000000..3e72de293
--- /dev/null
+++ b/src/java/org/apache/fop/render/pcl/PCLEventProducer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.render.pcl;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.events.model.AbstractEventModelFactory;
+import org.apache.fop.events.model.EventModel;
+
+/**
+ * Event producer interface for events generated by the PCL renderer.
+ */
+public interface PCLEventProducer extends EventProducer {
+
+ /** Provider class for the event producer. */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static PCLEventProducer get(EventBroadcaster broadcaster) {
+ return (PCLEventProducer)broadcaster.getEventProducerFor(
+ PCLEventProducer.class);
+ }
+ }
+
+ /** Event model factory for this event producer. */
+ public static class EventModelFactory extends AbstractEventModelFactory {
+
+ /** {@inheritDoc} */
+ public EventModel createEventModel() {
+ return loadModel(getClass(), "event-model.xml");
+ }
+
+ }
+
+ /**
+ * Paper type could not be determined. Falling back to another.
+ * @param source the event source
+ * @param pageWidth the page width (in millipoints)
+ * @param pageHeight the page height (in millipoints)
+ * @param fallbackPaper the paper type that will be used instead
+ * @event.severity WARN
+ */
+ void paperTypeUnavailable(Object source, long pageWidth, long pageHeight, String fallbackPaper);
+
+}
diff --git a/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml b/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml
new file mode 100644
index 000000000..a3b36fd60
--- /dev/null
+++ b/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="org.apache.fop.render.pcl.PCLEventProducer.paperTypeUnavailable">Paper type ({pageWidth} x {pageHeight} mpt) could not be determined. Falling back to: {fallbackPaper}</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java
index 3eb8ec425..5ca9a8bf9 100644
--- a/src/java/org/apache/fop/render/pcl/PCLGenerator.java
+++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java
@@ -43,9 +43,11 @@ import java.text.DecimalFormatSymbols;
import java.util.Locale;
import org.apache.commons.io.output.ByteArrayOutputStream;
-import org.apache.fop.util.UnitConv;
+
import org.apache.xmlgraphics.image.GraphicsUtil;
+import org.apache.fop.util.UnitConv;
+
/**
* This class provides methods for generating PCL print files.
*/
@@ -355,16 +357,16 @@ public class PCLGenerator {
if (usePCLShades
|| Color.black.equals(col)
|| Color.white.equals(col)) {
- writeCommand("*c" + formatDouble4(w / 100) + "h"
- + formatDouble4(h / 100) + "V");
+ writeCommand("*c" + formatDouble4(w / 100.0) + "h"
+ + formatDouble4(h / 100.0) + "V");
int lineshade = convertToPCLShade(col);
writeCommand("*c" + lineshade + "G");
writeCommand("*c2P"); //Shaded fill
} else {
defineGrayscalePattern(col, 32, DITHER_MATRIX_4X4);
- writeCommand("*c" + formatDouble4(w / 100) + "h"
- + formatDouble4(h / 100) + "V");
+ writeCommand("*c" + formatDouble4(w / 100.0) + "h"
+ + formatDouble4(h / 100.0) + "V");
writeCommand("*c32G");
writeCommand("*c4P"); //User-defined pattern
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderer.java b/src/java/org/apache/fop/render/pcl/PCLRenderer.java
index 1d606e919..b89fba9c1 100644
--- a/src/java/org/apache/fop/render/pcl/PCLRenderer.java
+++ b/src/java/org/apache/fop/render/pcl/PCLRenderer.java
@@ -58,6 +58,7 @@ import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.java2d.GraphicContext;
import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+import org.apache.xmlgraphics.util.QName;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.MimeConstants;
@@ -77,6 +78,7 @@ import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.Viewport;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
@@ -85,12 +87,12 @@ import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.PrintRenderer;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.RendererContextConstants;
+import org.apache.fop.render.RendererEventProducer;
import org.apache.fop.render.java2d.FontMetricsMapper;
import org.apache.fop.render.java2d.FontSetup;
import org.apache.fop.render.java2d.Java2DRenderer;
import org.apache.fop.render.pcl.extensions.PCLElementMapping;
import org.apache.fop.traits.BorderProps;
-import org.apache.fop.util.QName;
import org.apache.fop.util.UnitConv;
/* Note:
@@ -208,7 +210,9 @@ public class PCLRenderer extends PrintRenderer {
*/
protected void handleIOTrouble(IOException ioe) {
if (!ioTrouble) {
- log.error("Error while writing to target file", ioe);
+ RendererEventProducer eventProducer = RendererEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.ioError(this, ioe);
ioTrouble = true;
}
}
@@ -417,11 +421,15 @@ public class PCLRenderer extends PrintRenderer {
if (this.currentPageDefinition == null) {
this.currentPageDefinition = PCLPageDefinition.getDefaultPageDefinition();
- log.warn("Paper type could not be determined. Falling back to: "
- + this.currentPageDefinition.getName());
+ PCLEventProducer eventProducer = PCLEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.paperTypeUnavailable(this, pagewidth, pageheight,
+ this.currentPageDefinition.getName());
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("page size: " + currentPageDefinition.getPhysicalPageSize());
+ log.debug("logical page: " + currentPageDefinition.getLogicalPageRect());
}
- log.debug("page size: " + currentPageDefinition.getPhysicalPageSize());
- log.debug("logical page: " + currentPageDefinition.getLogicalPageRect());
if (this.currentPageDefinition.isLandscapeFormat()) {
gen.writeCommand("&l1O"); //Orientation
} else {
@@ -1107,12 +1115,17 @@ public class PCLRenderer extends PrintRenderer {
}
} catch (ImageException ie) {
- log.error("Error while processing image: "
- + (info != null ? info.toString() : uri), ie);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
} catch (FileNotFoundException fe) {
- log.error(fe.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
} catch (IOException ioe) {
- handleIOTrouble(ioe);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
}
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererContext.java b/src/java/org/apache/fop/render/pcl/PCLRendererContext.java
index 62d4bcaa4..422b9d51d 100644
--- a/src/java/org/apache/fop/render/pcl/PCLRendererContext.java
+++ b/src/java/org/apache/fop/render/pcl/PCLRendererContext.java
@@ -19,9 +19,10 @@
package org.apache.fop.render.pcl;
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.render.RendererContext;
-import org.apache.fop.util.QName;
/**
* Wrapper on the RendererContext to access the information structure for drawing
diff --git a/src/java/org/apache/fop/render/pdf/PDFEventProducer.java b/src/java/org/apache/fop/render/pdf/PDFEventProducer.java
new file mode 100644
index 000000000..f8b1bbb33
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFEventProducer.java
@@ -0,0 +1,64 @@
+/*
+ * 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.render.pdf;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.events.model.AbstractEventModelFactory;
+import org.apache.fop.events.model.EventModel;
+
+/**
+ * Event producer interface for events generated by the PDF renderer.
+ */
+public interface PDFEventProducer extends EventProducer {
+
+ /** Provider class for the event producer. */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static PDFEventProducer get(EventBroadcaster broadcaster) {
+ return (PDFEventProducer)broadcaster.getEventProducerFor(
+ PDFEventProducer.class);
+ }
+ }
+
+ /** Event model factory for this event producer. */
+ public static class EventModelFactory extends AbstractEventModelFactory {
+
+ /** {@inheritDoc} */
+ public EventModel createEventModel() {
+ return loadModel(getClass(), "event-model.xml");
+ }
+
+ }
+
+ /**
+ * Some link targets haven't been fully resolved.
+ * @param source the event source
+ * @param count the number of unresolved links
+ * @event.severity WARN
+ */
+ void nonFullyResolvedLinkTargets(Object source, int count);
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml b/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml
new file mode 100644
index 000000000..fd57d5099
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="org.apache.fop.render.pdf.PDFEventProducer.nonFullyResolvedLinkTargets">{count} link target{count,equals,1,,s} could not be fully resolved and now point{count,equals,1,,s} to the top of the page or {count,equals,1,is,are} dysfunctional.</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
index 268791ac2..936bf4384 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -73,6 +73,7 @@ import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.extensions.xmp.XMPMetadata;
@@ -490,13 +491,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
PDFGoTo gt = (PDFGoTo) unfinishedGoTos.get(0);
finishIDGoTo(gt, defaultPos);
}
- boolean one = count == 1;
- String pl = one ? "" : "s";
- String ww = one ? "was" : "were";
- String ia = one ? "is" : "are";
- log.warn("" + count + " link target" + pl + " could not be fully resolved and "
- + ww + " now point to the top of the page or "
- + ia + " dysfunctional."); // dysfunctional if pageref is null
+ PDFEventProducer eventProducer = PDFEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.nonFullyResolvedLinkTargets(this, count);
+ // dysfunctional if pageref is null
}
}
@@ -555,16 +553,17 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
private void renderDestination(DestinationData dd) {
String targetID = dd.getIDRef();
- if (targetID != null && targetID.length() > 0) {
- PageViewport pv = dd.getPageViewport();
- if (pv == null) {
- log.warn("Unresolved destination item received: " + dd.getIDRef());
- }
+ if (targetID == null || targetID.length() == 0) {
+ throw new IllegalArgumentException("DestinationData must contain a ID reference");
+ }
+ PageViewport pv = dd.getPageViewport();
+ if (pv != null) {
PDFGoTo gt = getPDFGoToForID(targetID, pv.getKey());
pdfDoc.getFactory().makeDestination(
dd.getIDRef(), gt.makeReference());
} else {
- log.warn("DestinationData item with null or empty IDRef received.");
+ //Warning already issued by AreaTreeHandler (debug level is sufficient)
+ log.debug("Unresolved destination item received: " + dd.getIDRef());
}
}
@@ -584,22 +583,22 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
PDFOutline pdfOutline = null;
String targetID = bookmarkItem.getIDRef();
- if (targetID != null && targetID.length() > 0) {
- PageViewport pv = bookmarkItem.getPageViewport();
- if (pv != null) {
- String pvKey = pv.getKey();
- PDFGoTo gt = getPDFGoToForID(targetID, pvKey);
- // create outline object:
- PDFOutline parent = parentBookmarkItem != null
- ? parentBookmarkItem
- : pdfDoc.getOutlineRoot();
- pdfOutline = pdfDoc.getFactory().makeOutline(parent,
- bookmarkItem.getBookmarkTitle(), gt, bookmarkItem.showChildItems());
- } else {
- log.warn("Bookmark with IDRef \"" + targetID + "\" has a null PageViewport.");
- }
+ if (targetID == null || targetID.length() == 0) {
+ throw new IllegalArgumentException("DestinationData must contain a ID reference");
+ }
+ PageViewport pv = bookmarkItem.getPageViewport();
+ if (pv != null) {
+ String pvKey = pv.getKey();
+ PDFGoTo gt = getPDFGoToForID(targetID, pvKey);
+ // create outline object:
+ PDFOutline parent = parentBookmarkItem != null
+ ? parentBookmarkItem
+ : pdfDoc.getOutlineRoot();
+ pdfOutline = pdfDoc.getFactory().makeOutline(parent,
+ bookmarkItem.getBookmarkTitle(), gt, bookmarkItem.showChildItems());
} else {
- log.warn("Bookmark item with null or empty IDRef received.");
+ //Warning already issued by AreaTreeHandler (debug level is sufficient)
+ log.debug("Bookmark with IDRef \"" + targetID + "\" has a null PageViewport.");
}
for (int i = 0; i < bookmarkItem.getCount(); i++) {
@@ -1368,15 +1367,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
if (annotsAllowed) {
action = getPDFGoToForID(idRef, pvKey);
}
- } else if (pvKeyOK) {
- log.warn("Internal link trait with PageViewport key " + pvKey
- + " contains no ID reference.");
- } else if (idRefOK) {
- log.warn("Internal link trait with ID reference " + idRef
- + " contains no PageViewport key.");
} else {
- log.warn("Internal link trait received with neither PageViewport key"
- + " nor ID reference.");
+ //Warnings already issued by AreaTreeHandler
}
}
@@ -1671,7 +1663,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
x, y, posInt.width, posInt.height, foreignAttributes);
handler.generateImage(context, img, origin, posInt);
} catch (IOException ioe) {
- log.error("I/O error while handling image: " + info, ioe);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageWritingError(this, ioe);
return;
}
} else {
@@ -1680,13 +1674,17 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
+ info + " (" + img.getClass().getName() + ")");
}
} catch (ImageException ie) {
- log.error("Error while processing image: "
- + (info != null ? info.toString() : uri), ie);
- } catch (FileNotFoundException fnfe) {
- log.error(fnfe.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
+ } catch (FileNotFoundException fe) {
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
} catch (IOException ioe) {
- log.error("I/O error while processing image: "
- + (info != null ? info.toString() : uri), ioe);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
}
// output new data
@@ -1810,18 +1808,34 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
return MIME_TYPE;
}
+ /**
+ * Sets the PDF/A mode for the PDF renderer.
+ * @param mode the PDF/A mode
+ */
public void setAMode(PDFAMode mode) {
this.pdfAMode = mode;
}
+ /**
+ * Sets the PDF/X mode for the PDF renderer.
+ * @param mode the PDF/X mode
+ */
public void setXMode(PDFXMode mode) {
this.pdfXMode = mode;
}
+ /**
+ * Sets the output color profile for the PDF renderer.
+ * @param outputProfileURI the URI to the output color profile
+ */
public void setOutputProfileURI(String outputProfileURI) {
this.outputProfileURI = outputProfileURI;
}
+ /**
+ * Sets the filter map to be used by the PDF renderer.
+ * @param filterMap the filter map
+ */
public void setFilterMap(Map filterMap) {
this.filterMap = filterMap;
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
index cbc0a8ec9..cb7c7cf89 100644
--- a/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
@@ -36,6 +36,8 @@ import org.apache.batik.util.SVGConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.util.QName;
+
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fonts.FontInfo;
@@ -51,8 +53,8 @@ import org.apache.fop.render.RendererContextConstants;
import org.apache.fop.svg.PDFAElementBridge;
import org.apache.fop.svg.PDFBridgeContext;
import org.apache.fop.svg.PDFGraphics2D;
+import org.apache.fop.svg.SVGEventProducer;
import org.apache.fop.svg.SVGUserAgent;
-import org.apache.fop.util.QName;
/**
* PDF XML handler for SVG (uses Apache Batik).
@@ -144,8 +146,9 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
try {
super.renderSVGDocument(context, doc);
} catch (IOException ioe) {
- log.error("I/O error while rendering SVG graphic: "
- + ioe.getMessage(), ioe);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgRenderingError(this, ioe, getDocumentURI(doc));
}
return;
}
@@ -153,15 +156,13 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
int yOffset = pdfInfo.currentYPosition;
FOUserAgent userAgent = context.getUserAgent();
- log.debug("Generating SVG at "
- + userAgent.getTargetResolution()
- + "dpi.");
final float deviceResolution = userAgent.getTargetResolution();
- log.debug("Generating SVG at " + deviceResolution + "dpi.");
- log.debug("Generating SVG at " + deviceResolution + "dpi.");
+ if (log.isDebugEnabled()) {
+ log.debug("Generating SVG at " + deviceResolution + "dpi.");
+ }
final float uaResolution = userAgent.getSourceResolution();
- SVGUserAgent ua = new SVGUserAgent(25.4f / uaResolution, new AffineTransform());
+ SVGUserAgent ua = new SVGUserAgent(userAgent, new AffineTransform());
//Scale for higher resolution on-the-fly images from Batik
double s = uaResolution / deviceResolution;
@@ -188,8 +189,9 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
root = builder.build(ctx, doc);
builder = null;
} catch (Exception e) {
- log.error("svg graphic could not be built: "
- + e.getMessage(), e);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgNotBuilt(this, e, getDocumentURI(doc));
return;
}
// get the 'width' and 'height' attributes of the SVG document
@@ -261,8 +263,9 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
root.paint(graphics);
pdfInfo.currentStream.add(graphics.getString());
} catch (Exception e) {
- log.error("svg graphic could not be rendered: "
- + e.getMessage(), e);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgRenderingError(this, e, getDocumentURI(doc));
}
pdfInfo.pdfState.pop();
renderer.restoreGraphicsState();
diff --git a/src/java/org/apache/fop/render/ps/PSEventProducer.java b/src/java/org/apache/fop/render/ps/PSEventProducer.java
new file mode 100644
index 000000000..451ed1cea
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSEventProducer.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.render.ps;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.events.model.AbstractEventModelFactory;
+import org.apache.fop.events.model.EventModel;
+
+/**
+ * Event producer interface for events generated by the PostScript renderer.
+ */
+public interface PSEventProducer extends EventProducer {
+
+ /** Provider class for the event producer. */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static PSEventProducer get(EventBroadcaster broadcaster) {
+ return (PSEventProducer)broadcaster.getEventProducerFor(
+ PSEventProducer.class);
+ }
+ }
+
+ /** Event model factory for this event producer. */
+ public static class EventModelFactory extends AbstractEventModelFactory {
+
+ /** {@inheritDoc} */
+ public EventModel createEventModel() {
+ return loadModel(getClass(), "event-model.xml");
+ }
+
+ }
+
+ /**
+ * A PostScript dictionary could not be parsed.
+ * @param source the event source
+ * @param content the PostScript content
+ * @param e the original exception
+ * @event.severity ERROR
+ */
+ void postscriptDictionaryParseError(Object source, String content, Exception e);
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSEventProducer.xml b/src/java/org/apache/fop/render/ps/PSEventProducer.xml
new file mode 100644
index 000000000..a0078223a
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSEventProducer.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="org.apache.fop.render.ps.PSEventProducer.postscriptDictionaryParseError">Failed to parse dictionary string. Reason: {e}, content = "{content}"</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java
index 7e32977e6..e4d582ba2 100644
--- a/src/java/org/apache/fop/render/ps/PSRenderer.java
+++ b/src/java/org/apache/fop/render/ps/PSRenderer.java
@@ -85,6 +85,7 @@ import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fonts.Font;
@@ -95,6 +96,7 @@ import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.ImageAdapter;
import org.apache.fop.render.RendererContext;
+import org.apache.fop.render.RendererEventProducer;
import org.apache.fop.render.ps.extensions.PSCommentAfter;
import org.apache.fop.render.ps.extensions.PSCommentBefore;
import org.apache.fop.render.ps.extensions.PSExtensionAttachment;
@@ -296,7 +298,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer
*/
protected void handleIOTrouble(IOException ioe) {
if (!ioTrouble) {
- log.error("Error while writing to target file", ioe);
+ RendererEventProducer eventProducer = RendererEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.ioError(this, ioe);
ioTrouble = true;
}
}
@@ -522,12 +526,17 @@ public class PSRenderer extends AbstractPathOrientedRenderer
}
} catch (ImageException ie) {
- log.error("Error while processing image: "
- + (info != null ? info.toString() : uri), ie);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
} catch (FileNotFoundException fe) {
- log.error(fe.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
} catch (IOException ioe) {
- handleIOTrouble(ioe);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
}
}
@@ -1070,8 +1079,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer
try {
this.pageDeviceDictionary.putAll(PSDictionary.valueOf(content));
} catch (PSDictionaryFormatException e) {
- log.error("Failed to parse dictionary string: "
- + e.getMessage() + ", content = '" + content + "'");
+ PSEventProducer eventProducer = PSEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.postscriptDictionaryParseError(this, content, e);
}
}
} else if (attachment instanceof PSCommentBefore) {
@@ -1169,8 +1179,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer
try {
pageDeviceDictionary.putAll(PSDictionary.valueOf(content));
} catch (PSDictionaryFormatException e) {
- log.error("failed to parse dictionary string: "
- + e.getMessage() + ", [" + content + "]");
+ PSEventProducer eventProducer = PSEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.postscriptDictionaryParseError(this, content, e);
}
}
}
diff --git a/src/java/org/apache/fop/render/ps/PSSVGHandler.java b/src/java/org/apache/fop/render/ps/PSSVGHandler.java
index 5cfe617c8..ebe098282 100644
--- a/src/java/org/apache/fop/render/ps/PSSVGHandler.java
+++ b/src/java/org/apache/fop/render/ps/PSSVGHandler.java
@@ -23,31 +23,24 @@ package org.apache.fop.render.ps;
import java.awt.geom.AffineTransform;
import java.io.IOException;
-// DOM
import org.w3c.dom.Document;
-import org.w3c.dom.svg.SVGDocument;
-import org.w3c.dom.svg.SVGSVGElement;
-// Batik
import org.apache.avalon.framework.configuration.Configuration;
-import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.BridgeContext;
-import org.apache.batik.bridge.ViewBox;
-import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.gvt.GraphicsNode;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
+import org.apache.xmlgraphics.ps.PSGenerator;
-// FOP
import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.render.AbstractGenericSVGHandler;
import org.apache.fop.render.Renderer;
-import org.apache.fop.render.XMLHandler;
import org.apache.fop.render.RendererContext;
+import org.apache.fop.svg.SVGEventProducer;
import org.apache.fop.svg.SVGUserAgent;
-import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
-import org.apache.xmlgraphics.ps.PSGenerator;
-
-// Commons-Logging
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
/**
* PostScript XML handler for SVG. Uses Apache Batik for SVG processing.
@@ -57,7 +50,8 @@ import org.apache.commons.logging.LogFactory;
*
* @version $Id$
*/
-public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
+public class PSSVGHandler extends AbstractGenericSVGHandler
+ implements PSRendererContextConstants {
/** logging instance */
private static Log log = LogFactory.getLog(PSSVGHandler.class);
@@ -68,16 +62,6 @@ public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
public PSSVGHandler() {
}
- /** {@inheritDoc} */
- public void handleXML(RendererContext context,
- Document doc, String ns) throws Exception {
- PSInfo psi = getPSInfo(context);
-
- if (SVGDOMImplementation.SVG_NAMESPACE_URI.equals(ns)) {
- renderSVGDocument(context, doc, psi);
- }
- }
-
/**
* Get the pdf information from the render context.
*
@@ -234,10 +218,10 @@ public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
* Render the svg document.
* @param context the renderer context
* @param doc the svg document
- * @param psInfo the pdf information of the current context
*/
protected void renderSVGDocument(RendererContext context,
- Document doc, PSInfo psInfo) {
+ Document doc) {
+ PSInfo psInfo = getPSInfo(context);
int xOffset = psInfo.currentXPosition;
int yOffset = psInfo.currentYPosition;
PSGenerator gen = psInfo.psGenerator;
@@ -250,9 +234,7 @@ public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
}
SVGUserAgent ua
- = new SVGUserAgent(
- context.getUserAgent().getSourcePixelUnitToMillimeter(),
- new AffineTransform());
+ = new SVGUserAgent(context.getUserAgent(), new AffineTransform());
PSGraphics2D graphics = new PSGraphics2D(strokeText, gen);
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
@@ -273,8 +255,9 @@ public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
try {
root = builder.build(ctx, doc);
} catch (Exception e) {
- log.error("SVG graphic could not be built: "
- + e.getMessage(), e);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgNotBuilt(this, e, getDocumentURI(doc));
return;
}
// get the 'width' and 'height' attributes of the SVG document
@@ -305,10 +288,10 @@ public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
// viewBox puts it.
gen.concatMatrix(sx, 0, 0, sy, xOffset / 1000f, yOffset / 1000f);
+ /*
SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
- psInfo.getWidth() / 1000f, psInfo.getHeight() / 1000f);
- /*
+ psInfo.getWidth() / 1000f, psInfo.getHeight() / 1000f, ctx);
if (!at.isIdentity()) {
double[] vals = new double[6];
at.getMatrix(vals);
@@ -322,15 +305,17 @@ public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
try {
root.paint(graphics);
} catch (Exception e) {
- log.error("SVG graphic could not be rendered: "
- + e.getMessage(), e);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgRenderingError(this, e, getDocumentURI(doc));
}
gen.restoreGraphicsState();
gen.commentln("%FOPEndSVG");
} catch (IOException ioe) {
- log.error("SVG graphic could not be rendered: "
- + ioe.getMessage(), ioe);
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgRenderingError(this, ioe, getDocumentURI(doc));
}
}
@@ -339,10 +324,5 @@ public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
return (renderer instanceof PSRenderer);
}
- /** {@inheritDoc} */
- public String getNamespace() {
- return SVGDOMImplementation.SVG_NAMESPACE_URI;
- }
-
}
diff --git a/src/java/org/apache/fop/render/ps/ResourceHandler.java b/src/java/org/apache/fop/render/ps/ResourceHandler.java
index 0dfb8029f..1a363c90e 100644
--- a/src/java/org/apache/fop/render/ps/ResourceHandler.java
+++ b/src/java/org/apache/fop/render/ps/ResourceHandler.java
@@ -20,8 +20,8 @@
package org.apache.fop.render.ps;
import java.awt.geom.Dimension2D;
-import java.awt.image.RenderedImage;
import java.awt.geom.Rectangle2D;
+import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -68,6 +68,7 @@ import org.apache.xmlgraphics.ps.dsc.events.PostScriptComment;
import org.apache.xmlgraphics.ps.dsc.tools.DSCTools;
import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fonts.FontInfo;
/**
@@ -321,7 +322,10 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors {
throw new UnsupportedOperationException("Unsupported image type: " + img);
}
} catch (ImageException ie) {
- throw new IOException("Error while generating form for image: " + ie.getMessage());
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ userAgent.getEventBroadcaster());
+ eventProducer.imageError(resTracker, (info != null ? info.toString() : uri),
+ ie, null);
}
}
}
diff --git a/src/java/org/apache/fop/render/ps/extensions/AbstractPSCommentElement.java b/src/java/org/apache/fop/render/ps/extensions/AbstractPSCommentElement.java
index a6e77fb13..1eb1d9d13 100644
--- a/src/java/org/apache/fop/render/ps/extensions/AbstractPSCommentElement.java
+++ b/src/java/org/apache/fop/render/ps/extensions/AbstractPSCommentElement.java
@@ -22,7 +22,6 @@ package org.apache.fop.render.ps.extensions;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.ValidationException;
/**
* Base postscript commment element class
@@ -46,8 +45,8 @@ public abstract class AbstractPSCommentElement extends AbstractPSExtensionElemen
protected void startOfNode() throws FOPException {
if (parent.getNameId() != Constants.FO_DECLARATIONS
&& parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
- throw new ValidationException(getName()
- + " must be a child of fo:declarations or fo:simple-page-master.");
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(),
+ "rule.childOfSPMorDeclarations");
}
}
diff --git a/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionElement.java b/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionElement.java
index a10bb7518..31e44d2d2 100644
--- a/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionElement.java
+++ b/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionElement.java
@@ -20,14 +20,14 @@
package org.apache.fop.render.ps.extensions;
// FOP
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
-import org.xml.sax.Locator;
-
/**
* Base class for the PostScript-specific extension elements.
*/
@@ -98,7 +98,7 @@ public abstract class AbstractPSExtensionElement extends FONode {
* @see org.apache.fop.fo.FONode#getNormalNamespacePrefix()
*/
public String getNormalNamespacePrefix() {
- return "fox";
+ return "ps";
}
/**
diff --git a/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionObject.java b/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionObject.java
index 6823d75d9..78b2f91eb 100644
--- a/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionObject.java
+++ b/src/java/org/apache/fop/render/ps/extensions/AbstractPSExtensionObject.java
@@ -20,13 +20,14 @@
package org.apache.fop.render.ps.extensions;
// FOP
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
/**
* Base class for the PostScript-specific extension elements.
@@ -36,15 +37,15 @@ public abstract class AbstractPSExtensionObject extends FONode {
private PSSetupCode setupCode = new PSSetupCode();
/**
+ * Main constructor.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
public AbstractPSExtensionObject(FONode parent) {
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
if (FO_URI.equals(nsURI)) {
@@ -71,7 +72,7 @@ public abstract class AbstractPSExtensionObject extends FONode {
/**{@inheritDoc} */
public String getNormalNamespacePrefix() {
- return "fox";
+ return "ps";
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSCommentAfterElement.java b/src/java/org/apache/fop/render/ps/extensions/PSCommentAfterElement.java
index 306cd7bda..1dd0f4be6 100644
--- a/src/java/org/apache/fop/render/ps/extensions/PSCommentAfterElement.java
+++ b/src/java/org/apache/fop/render/ps/extensions/PSCommentAfterElement.java
@@ -27,6 +27,7 @@ import org.apache.fop.fo.extensions.ExtensionAttachment;
*/
public class PSCommentAfterElement extends AbstractPSCommentElement {
+ /** the element name */
protected static final String ELEMENT = "ps-comment-after";
/**
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSCommentBeforeElement.java b/src/java/org/apache/fop/render/ps/extensions/PSCommentBeforeElement.java
index 6058f355b..5d3c863f5 100644
--- a/src/java/org/apache/fop/render/ps/extensions/PSCommentBeforeElement.java
+++ b/src/java/org/apache/fop/render/ps/extensions/PSCommentBeforeElement.java
@@ -27,6 +27,7 @@ import org.apache.fop.fo.extensions.ExtensionAttachment;
*/
public class PSCommentBeforeElement extends AbstractPSCommentElement {
+ /** the element name */
protected static final String ELEMENT = "ps-comment-before";
/**
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java b/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java
index 456d97430..e69500736 100644
--- a/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java
+++ b/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java
@@ -19,13 +19,15 @@
package org.apache.fop.render.ps.extensions;
+import org.xml.sax.Attributes;
+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.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
/**
* ContentHandler (parser) for restoring PSExtension objects from XML.
@@ -91,25 +93,19 @@ public class PSExtensionHandler extends DefaultHandler
content.append(ch, start, length);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void endDocument() throws SAXException {
if (listener != null) {
listener.notifyObjectBuilt(getObject());
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public Object getObject() {
return returnedObject;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void setObjectBuiltListener(ObjectBuiltListener listener) {
this.listener = listener;
}
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSPageSetupCodeElement.java b/src/java/org/apache/fop/render/ps/extensions/PSPageSetupCodeElement.java
index ad46b9364..207c11e76 100644
--- a/src/java/org/apache/fop/render/ps/extensions/PSPageSetupCodeElement.java
+++ b/src/java/org/apache/fop/render/ps/extensions/PSPageSetupCodeElement.java
@@ -22,13 +22,13 @@ package org.apache.fop.render.ps.extensions;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.ValidationException;
/**
* Extension element for fox:ps-page-setup-code.
*/
public class PSPageSetupCodeElement extends AbstractPSExtensionObject {
+ /** The element name */
protected static final String ELEMENT = "ps-page-setup-code";
/**
@@ -43,7 +43,8 @@ public class PSPageSetupCodeElement extends AbstractPSExtensionObject {
protected void startOfNode() throws FOPException {
super.startOfNode();
if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
- throw new ValidationException(getName() + " must be a child of fo:simple-page-master.");
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(),
+ "rule.childOfSPM");
}
}
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSSetPageDeviceElement.java b/src/java/org/apache/fop/render/ps/extensions/PSSetPageDeviceElement.java
index b512c6888..21acc8001 100644
--- a/src/java/org/apache/fop/render/ps/extensions/PSSetPageDeviceElement.java
+++ b/src/java/org/apache/fop/render/ps/extensions/PSSetPageDeviceElement.java
@@ -19,20 +19,21 @@
package org.apache.fop.render.ps.extensions;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
-import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
/**
* Extension element for ps:ps-setpagedevice.
*/
public class PSSetPageDeviceElement extends AbstractPSExtensionElement {
+ /** The element name */
protected static final String ELEMENT = "ps-setpagedevice";
/**
@@ -52,8 +53,8 @@ public class PSSetPageDeviceElement extends AbstractPSExtensionElement {
super.startOfNode();
if ( !((parent.getNameId() == Constants.FO_DECLARATIONS)
|| (parent.getNameId() == Constants.FO_SIMPLE_PAGE_MASTER)) ) {
- throw new ValidationException( getName()
- + " must be a child of fo:declarations or fo:simple-page-master.");
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(),
+ "rule.childOfSPMorDeclarations");
}
}
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSSetupCodeElement.java b/src/java/org/apache/fop/render/ps/extensions/PSSetupCodeElement.java
index ec7216c44..e76dfeb64 100644
--- a/src/java/org/apache/fop/render/ps/extensions/PSSetupCodeElement.java
+++ b/src/java/org/apache/fop/render/ps/extensions/PSSetupCodeElement.java
@@ -22,13 +22,13 @@ package org.apache.fop.render.ps.extensions;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.ValidationException;
/**
* Extension element for fox:ps-setup-code.
*/
public class PSSetupCodeElement extends AbstractPSExtensionObject {
+ /** The element name */
protected static final String ELEMENT = "ps-setup-code";
/**
@@ -43,7 +43,8 @@ public class PSSetupCodeElement extends AbstractPSExtensionObject {
protected void startOfNode() throws FOPException {
super.startOfNode();
if (parent.getNameId() != Constants.FO_DECLARATIONS) {
- throw new ValidationException(getName() + " must be a child of fo:declarations.");
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(),
+ "rule.childOfDeclarations");
}
}
diff --git a/src/java/org/apache/fop/render/rtf/RTFEventProducer.java b/src/java/org/apache/fop/render/rtf/RTFEventProducer.java
new file mode 100644
index 000000000..a2646af46
--- /dev/null
+++ b/src/java/org/apache/fop/render/rtf/RTFEventProducer.java
@@ -0,0 +1,94 @@
+/*
+ * 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.render.rtf;
+
+import org.xml.sax.Locator;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+import org.apache.fop.events.model.AbstractEventModelFactory;
+import org.apache.fop.events.model.EventModel;
+import org.apache.fop.fo.FONode;
+
+/**
+ * Event producer interface for events generated by the RTF renderer.
+ */
+public interface RTFEventProducer extends EventProducer {
+
+ /** Provider class for the event producer. */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static RTFEventProducer get(EventBroadcaster broadcaster) {
+ return (RTFEventProducer)broadcaster.getEventProducerFor(
+ RTFEventProducer.class);
+ }
+ }
+
+ /** Event model factory for this event producer. */
+ public static class EventModelFactory extends AbstractEventModelFactory {
+
+ /** {@inheritDoc} */
+ public EventModel createEventModel() {
+ return loadModel(getClass(), "event-model.xml");
+ }
+
+ }
+
+ /**
+ * The RTF handler only supports simple-page-masters.
+ * @param source the event source
+ * @param masterReference the reference page-master-set
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void onlySPMSupported(Object source, String masterReference, Locator loc);
+
+ /**
+ * No simple-page-master could be determined-
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void noSPMFound(Object source, Locator loc);
+
+ /**
+ * The RTF handler requires explicit table-columns for now.
+ * @param source the event source
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void explicitTableColumnsRequired(Object source, Locator loc);
+
+ /**
+ * The RTF handler ignored some deferred event (i.e. an unsupported element).
+ * @param source the event source
+ * @param node the FO tree node being ignored
+ * @param start true for start, false for end
+ * @param loc the location of the error or null
+ * @event.severity WARN
+ */
+ void ignoredDeferredEvent(Object source, FONode node, boolean start, Locator loc);
+
+}
diff --git a/src/java/org/apache/fop/render/rtf/RTFEventProducer.xml b/src/java/org/apache/fop/render/rtf/RTFEventProducer.xml
new file mode 100644
index 000000000..8f1f21a81
--- /dev/null
+++ b/src/java/org/apache/fop/render/rtf/RTFEventProducer.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="locator">[ (See position {loc})| (See {#gatherContextInfo})| (No context info available)]</message>
+ <message key="org.apache.fop.render.rtf.RTFEventProducer.onlySPMSupported">Only simple-page-masters are supported on page-sequences. Using default simple-page-master from page-sequence-master "{masterReference}".{{locator}}</message>
+ <message key="org.apache.fop.render.rtf.RTFEventProducer.noSPMFound">No simple-page-master could be determined.</message>
+ <message key="org.apache.fop.render.rtf.RTFEventProducer.explicitTableColumnsRequired">No table-columns found on table. RTF output requires that all table-columns for a table are defined. Output will be incorrect.{{locator}}</message>
+ <message key="org.apache.fop.render.rtf.RTFEventProducer.ignoredDeferredEvent">Ignored deferred event for {node} ({start,if,start,end}).{{locator}}</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java
index d2f2c4192..88e34e17a 100644
--- a/src/java/org/apache/fop/render/rtf/RTFHandler.java
+++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java
@@ -21,6 +21,7 @@ package org.apache.fop.render.rtf;
// Java
import java.awt.geom.Point2D;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -51,6 +52,7 @@ import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.datatypes.SimplePercentBaseContext;
+import org.apache.fop.events.ResourceEventProducer;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.fo.FONode;
@@ -88,6 +90,7 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.FixedLength;
import org.apache.fop.fonts.FontSetup;
import org.apache.fop.render.DefaultFontResolver;
+import org.apache.fop.render.RendererEventProducer;
import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfAfterContainer;
import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfBeforeContainer;
import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfListContainer;
@@ -159,6 +162,16 @@ public class RTFHandler extends FOEventHandler {
}
/**
+ * Central exception handler for I/O exceptions.
+ * @param ioe IOException to handle
+ */
+ protected void handleIOTrouble(IOException ioe) {
+ RendererEventProducer eventProducer = RendererEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.ioError(this, ioe);
+ }
+
+ /**
* {@inheritDoc}
*/
public void startDocument() throws SAXException {
@@ -195,9 +208,9 @@ public class RTFHandler extends FOEventHandler {
this.pagemaster
= pageSeq.getRoot().getLayoutMasterSet().getSimplePageMaster(reference);
if (this.pagemaster == null) {
- log.warn("Only simple-page-masters are supported on page-sequences: "
- + reference);
- log.warn("Using default simple-page-master from page-sequence-master...");
+ RTFEventProducer eventProducer = RTFEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.onlySPMSupported(this, reference, pageSeq.getLocator());
PageSequenceMaster master
= pageSeq.getRoot().getLayoutMasterSet().getPageSequenceMaster(reference);
this.pagemaster = master.getNextSimplePageMaster(
@@ -218,7 +231,9 @@ public class RTFHandler extends FOEventHandler {
PageAttributesConverter.convertPageAttributes(
pagemaster));
} else {
- log.warn("No simple-page-master could be determined!");
+ RTFEventProducer eventProducer = RTFEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.noSPMFound(this, pageSeq.getLocator());
}
builderContext.pushContainer(sect);
@@ -226,9 +241,7 @@ public class RTFHandler extends FOEventHandler {
bHeaderSpecified = false;
bFooterSpecified = false;
} catch (IOException ioe) {
- // TODO could we throw Exception in all FOEventHandler events?
- log.error("startPageSequence: " + ioe.getMessage(), ioe);
- //TODO throw new FOPException(ioe);
+ handleIOTrouble(ioe);
} catch (FOPException fope) {
// TODO could we throw Exception in all FOEventHandler events?
log.error("startPageSequence: " + fope.getMessage(), fope);
@@ -338,8 +351,7 @@ public class RTFHandler extends FOEventHandler {
log.warn("A " + fl.getLocalName() + " has been skipped: " + fl.getFlowName());
}
} catch (IOException ioe) {
- log.error("startFlow: " + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startFlow: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -396,9 +408,7 @@ public class RTFHandler extends FOEventHandler {
textrun.pushBlockAttributes(rtfAttr);
textrun.addBookmark(bl.getId());
} catch (IOException ioe) {
- // TODO could we throw Exception in all FOEventHandler events?
- log.error("startBlock: " + ioe.getMessage());
- throw new RuntimeException("IOException: " + ioe);
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startBlock: " + e.getMessage());
throw new RuntimeException("Exception: " + e);
@@ -427,8 +437,7 @@ public class RTFHandler extends FOEventHandler {
textrun.popBlockAttributes();
} catch (IOException ioe) {
- log.error("startBlock:" + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startBlock:" + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -457,9 +466,7 @@ public class RTFHandler extends FOEventHandler {
textrun.addParagraphBreak();
textrun.pushBlockAttributes(rtfAttr);
} catch (IOException ioe) {
- // TODO could we throw Exception in all FOEventHandler events?
- log.error("startBlock: " + ioe.getMessage());
- throw new RuntimeException("IOException: " + ioe);
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startBlock: " + e.getMessage());
throw new RuntimeException("Exception: " + e);
@@ -486,8 +493,7 @@ public class RTFHandler extends FOEventHandler {
textrun.popBlockAttributes();
} catch (IOException ioe) {
- log.error("startBlock:" + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startBlock:" + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -530,6 +536,8 @@ public class RTFHandler extends FOEventHandler {
table.setBorderAttributes(borderAttributes);
builderContext.pushContainer(table);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startTable:" + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -590,7 +598,6 @@ public class RTFHandler extends FOEventHandler {
log.error("startColumn: " + e.getMessage());
throw new RuntimeException(e.getMessage());
}
-
}
/**
@@ -649,8 +656,7 @@ public class RTFHandler extends FOEventHandler {
textrun.pushInlineAttributes(rtfAttr);
textrun.addBookmark(inl.getId());
} catch (IOException ioe) {
- log.error("startInline:" + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (FOPException fe) {
log.error("startInline:" + fe.getMessage());
throw new RuntimeException(fe.getMessage());
@@ -677,8 +683,7 @@ public class RTFHandler extends FOEventHandler {
RtfTextrun textrun = container.getTextrun();
textrun.popInlineAttributes();
} catch (IOException ioe) {
- log.error("startInline:" + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startInline:" + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -698,6 +703,8 @@ public class RTFHandler extends FOEventHandler {
RtfTable tbl = (RtfTable)builderContext.getContainer(RtfTable.class, true, this);
tbl.setHeaderAttribs(atts);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startBody: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -715,6 +722,8 @@ public class RTFHandler extends FOEventHandler {
try {
RtfTable tbl = (RtfTable)builderContext.getContainer(RtfTable.class, true, this);
tbl.setHeaderAttribs(null);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("endBody: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -745,6 +754,8 @@ public class RTFHandler extends FOEventHandler {
// reset column iteration index to correctly access column widths
builderContext.getTableContext().selectFirstColumn();
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startRow: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -778,6 +789,8 @@ public class RTFHandler extends FOEventHandler {
tctx.selectNextColumn();
}
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("endRow: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -870,6 +883,8 @@ public class RTFHandler extends FOEventHandler {
}
builderContext.pushContainer(cell);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startCell: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -906,8 +921,7 @@ public class RTFHandler extends FOEventHandler {
ListAttributesConverter.convertAttributes(lb));
builderContext.pushContainer(newList);
} catch (IOException ioe) {
- log.error("startList: " + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (FOPException fe) {
log.error("startList: " + fe.getMessage());
throw new RuntimeException(fe.getMessage());
@@ -961,8 +975,7 @@ public class RTFHandler extends FOEventHandler {
builderContext.pushContainer(list.newListItem());
} catch (IOException ioe) {
- log.error("startList: " + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startList: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -995,8 +1008,7 @@ public class RTFHandler extends FOEventHandler {
RtfListItemLabel label = item.new RtfListItemLabel(item);
builderContext.pushContainer(label);
} catch (IOException ioe) {
- log.error("startPageNumber:" + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startPageNumber: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -1077,8 +1089,7 @@ public class RTFHandler extends FOEventHandler {
builderContext.pushContainer(link);
} catch (IOException ioe) {
- log.error("startLink:" + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startLink: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -1104,21 +1115,28 @@ public class RTFHandler extends FOEventHandler {
return;
}
+ String uri = eg.getURL();
+ ImageInfo info = null;
try {
- String uri = eg.getURL();
//set image data
FOUserAgent userAgent = eg.getUserAgent();
ImageManager manager = userAgent.getFactory().getImageManager();
- ImageInfo info = manager.getImageInfo(uri, userAgent.getImageSessionContext());
- if (info == null) {
- log.error("Image could not be found: " + uri);
- return;
- }
+ info = manager.getImageInfo(uri, userAgent.getImageSessionContext());
putGraphic(eg, info);
- } catch (Exception e) {
- log.error("Error while handling an external-graphic: " + e.getMessage(), e);
+ } catch (ImageException ie) {
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
+ } catch (FileNotFoundException fe) {
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
+ } catch (IOException ioe) {
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
}
}
@@ -1144,6 +1162,12 @@ public class RTFHandler extends FOEventHandler {
// Set the image size to the size of the svg.
Point2D csize = new Point2D.Float(-1, -1);
Point2D intrinsicDimensions = child.getDimension(csize);
+ if (intrinsicDimensions == null) {
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.ifoNoIntrinsicSize(this, child.getLocator());
+ return;
+ }
size.setSizeInMillipoints(
(int)Math.round(intrinsicDimensions.getX() * 1000),
(int)Math.round(intrinsicDimensions.getY() * 1000));
@@ -1157,8 +1181,14 @@ public class RTFHandler extends FOEventHandler {
Image converted = manager.convertImage(image, FLAVORS);
putGraphic(ifo, converted);
- } catch (Exception e) {
- log.error("Error while handling an instream-foreign-object: " + e.getMessage(), e);
+ } catch (ImageException ie) {
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, null, ie, null);
+ } catch (IOException ioe) {
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageIOError(this, null, ioe, null);
}
}
@@ -1183,7 +1213,9 @@ public class RTFHandler extends FOEventHandler {
putGraphic(abstractGraphic, image);
} catch (ImageException ie) {
- log.error("Error while loading/processing image: " + info.getOriginalURI(), ie);
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageError(this, null, ie, null);
}
}
@@ -1210,8 +1242,9 @@ public class RTFHandler extends FOEventHandler {
}
if (rawData == null) {
- log.warn(FONode.decorateWithContextInfo("Image could not be embedded: "
- + image, abstractGraphic));
+ ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.imageWritingError(this, null);
return;
}
@@ -1322,9 +1355,7 @@ public class RTFHandler extends FOEventHandler {
builderContext.pushContainer(rtfFootnote);
} catch (IOException ioe) {
- // TODO could we throw Exception in all FOEventHandler events?
- log.error("startFootnote: " + ioe.getMessage());
- throw new RuntimeException("IOException: " + ioe);
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startFootnote: " + e.getMessage());
throw new RuntimeException("Exception: " + e);
@@ -1358,9 +1389,7 @@ public class RTFHandler extends FOEventHandler {
rtfFootnote.startBody();
} catch (IOException ioe) {
- // TODO could we throw Exception in all FOEventHandler events?
- log.error("startFootnoteBody: " + ioe.getMessage());
- throw new RuntimeException("IOException: " + ioe);
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startFootnoteBody: " + e.getMessage());
throw new RuntimeException("Exception: " + e);
@@ -1383,9 +1412,7 @@ public class RTFHandler extends FOEventHandler {
rtfFootnote.endBody();
} catch (IOException ioe) {
- // TODO could we throw Exception in all FOEventHandler events?
- log.error("endFootnoteBody: " + ioe.getMessage());
- throw new RuntimeException("IOException: " + ioe);
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("endFootnoteBody: " + e.getMessage());
throw new RuntimeException("Exception: " + e);
@@ -1421,10 +1448,8 @@ public class RTFHandler extends FOEventHandler {
textrun.pushInlineAttributes(rtfAttr);
textrun.addString(new String(data, start, length - start));
textrun.popInlineAttributes();
- } catch (IOException ioe) {
- // FIXME could we throw Exception in all FOEventHandler events?
- log.error("characters: " + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("characters:" + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -1452,8 +1477,7 @@ public class RTFHandler extends FOEventHandler {
RtfTextrun textrun = container.getTextrun();
textrun.addPageNumber(rtfAttr);
} catch (IOException ioe) {
- log.error("startPageNumber:" + ioe.getMessage());
- throw new RuntimeException(ioe.getMessage());
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startPageNumber: " + e.getMessage());
throw new RuntimeException(e.getMessage());
@@ -1611,7 +1635,9 @@ public class RTFHandler extends FOEventHandler {
endCell( (TableCell) foNode);
}
} else {
- log.warn("Ignored deferred event for " + foNode);
+ RTFEventProducer eventProducer = RTFEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.ignoredDeferredEvent(this, foNode, bStart, foNode.getLocator());
}
}
@@ -1655,8 +1681,9 @@ public class RTFHandler extends FOEventHandler {
}
} else {
//TODO Implement implicit column setup handling!
- log.warn("No table-columns found on table. RTF output requires that all"
- + " table-columns for a table are defined. Output will be incorrect.");
+ RTFEventProducer eventProducer = RTFEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.explicitTableColumnsRequired(this, table.getLocator());
}
//recurse table-header
diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java
index 66cce0ae1..eb3c92e1b 100644
--- a/src/java/org/apache/fop/render/xml/XMLRenderer.java
+++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java
@@ -41,6 +41,7 @@ import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
+import org.apache.xmlgraphics.util.QName;
import org.apache.xmlgraphics.util.XMLizable;
import org.apache.fop.apps.FOPException;
@@ -90,7 +91,6 @@ import org.apache.fop.render.Renderer;
import org.apache.fop.render.RendererContext;
import org.apache.fop.render.XMLHandler;
import org.apache.fop.util.ColorUtil;
-import org.apache.fop.util.QName;
/**
* Renderer that renders areas to XML for debugging purposes.
diff --git a/src/java/org/apache/fop/svg/SVGEventProducer.java b/src/java/org/apache/fop/svg/SVGEventProducer.java
new file mode 100644
index 000000000..8894f3f58
--- /dev/null
+++ b/src/java/org/apache/fop/svg/SVGEventProducer.java
@@ -0,0 +1,89 @@
+/*
+ * 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.svg;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+
+/**
+ * Event producer interface for SVG-related events.
+ */
+public interface SVGEventProducer extends EventProducer {
+
+ /**
+ * Provider class for the event producer.
+ */
+ class Provider {
+
+ /**
+ * Returns an event producer.
+ * @param broadcaster the event broadcaster to use
+ * @return the event producer
+ */
+ public static SVGEventProducer get(EventBroadcaster broadcaster) {
+ return (SVGEventProducer)broadcaster.getEventProducerFor(
+ SVGEventProducer.class);
+ }
+ }
+
+ /**
+ * Error during SVG processing. Either message or e must be set.
+ * @param source the event source
+ * @param message the error message (or null)
+ * @param e the exception (or null)
+ * @event.severity ERROR
+ */
+ void error(Object source, String message, Exception e);
+
+ /**
+ * Alert during SVG processing.
+ * @param source the event source
+ * @param message the error message
+ * @event.severity WARN
+ */
+ void alert(Object source, String message);
+
+ /**
+ * Info during SVG processing.
+ * @param source the event source
+ * @param message the error message
+ * @event.severity INFO
+ */
+ void info(Object source, String message);
+
+ /**
+ * SVG graphic could not be built due to an exception.
+ * @param source the event source
+ * @param e the original exception
+ * @param uri the URI of the SVG graphic
+ * @event.severity ERROR
+ */
+ void svgNotBuilt(Object source, Exception e, String uri);
+
+ /**
+ * SVG graphic could not be rendered due to an exception.
+ * @param source the event source
+ * @param e the original exception
+ * @param uri the URI of the SVG graphic
+ * @event.severity ERROR
+ */
+ void svgRenderingError(Object source, Exception e, String uri);
+
+}
diff --git a/src/java/org/apache/fop/svg/SVGUserAgent.java b/src/java/org/apache/fop/svg/SVGUserAgent.java
index 540f490de..8d7754fcb 100644
--- a/src/java/org/apache/fop/svg/SVGUserAgent.java
+++ b/src/java/org/apache/fop/svg/SVGUserAgent.java
@@ -19,49 +19,42 @@
package org.apache.fop.svg;
-import javax.xml.parsers.SAXParserFactory;
-import org.apache.batik.bridge.UserAgentAdapter;
-
-// Java
import java.awt.geom.AffineTransform;
-import java.awt.geom.Dimension2D;
-import java.awt.Dimension;
-// Commons-Logging
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.apache.fop.apps.FOUserAgent;
/**
- * The SVG user agent.
- * This is an implementation of the batik svg user agent
- * for handling errors and getting user agent values.
+ * The SVG user agent. This is an implementation of the Batik SVG user agent.
*/
-public class SVGUserAgent extends UserAgentAdapter {
- private AffineTransform currentTransform = null;
- private float pixelUnitToMillimeter = 0.0f;
+public class SVGUserAgent extends SimpleSVGUserAgent {
+
+ private SVGEventProducer eventProducer;
+ private Exception lastException;
/**
- * logging instance
+ * Creates a new SVGUserAgent.
+ * @param foUserAgent the FO user agent to associate with this SVG user agent
+ * @param at the current transform
*/
- private Log logger = LogFactory.getLog(SVGUserAgent.class);
+ public SVGUserAgent(FOUserAgent foUserAgent, AffineTransform at) {
+ super(foUserAgent.getSourcePixelUnitToMillimeter(), at);
+ this.eventProducer = SVGEventProducer.Provider.get(foUserAgent.getEventBroadcaster());
+ }
/**
* Creates a new SVGUserAgent.
- * @param pixelUnitToMM the pixel to millimeter conversion factor
- * currently in effect
- * @param at the current transform
+ * @param foUserAgent the FO user agent to associate with this SVG user agent
*/
- public SVGUserAgent(float pixelUnitToMM, AffineTransform at) {
- pixelUnitToMillimeter = pixelUnitToMM;
- currentTransform = at;
+ public SVGUserAgent(FOUserAgent foUserAgent) {
+ this(foUserAgent, new AffineTransform());
}
-
+
/**
- * Returns the logger associated with this user agent.
- * @return Logger the logger
+ * Returns the last exception sent to the {@link #displayError(Exception)} method.
+ * @return the last exception or null if no exception occurred
*/
- protected final Log getLogger() {
- return logger;
+ public Exception getLastException() {
+ return this.lastException;
}
/**
@@ -69,7 +62,7 @@ public class SVGUserAgent extends UserAgentAdapter {
* @param message the message to display
*/
public void displayError(String message) {
- logger.error(message);
+ this.eventProducer.error(this, message, null);
}
/**
@@ -77,7 +70,8 @@ public class SVGUserAgent extends UserAgentAdapter {
* @param ex the exception to display
*/
public void displayError(Exception ex) {
- logger.error("SVG Error" + ex.getMessage(), ex);
+ this.lastException = ex;
+ this.eventProducer.error(this, ex.getLocalizedMessage(), ex);
}
/**
@@ -86,7 +80,7 @@ public class SVGUserAgent extends UserAgentAdapter {
* @param message the message to display
*/
public void displayMessage(String message) {
- logger.info(message);
+ this.eventProducer.info(this, message);
}
/**
@@ -94,78 +88,7 @@ public class SVGUserAgent extends UserAgentAdapter {
* @param message the message to display
*/
public void showAlert(String message) {
- logger.warn(message);
- }
-
- /**
- * Returns a customized the pixel to mm factor.
- * @return the pixel unit to millimeter conversion factor
- */
- public float getPixelUnitToMillimeter() {
- return pixelUnitToMillimeter;
+ this.eventProducer.alert(this, message);
}
- /**
- * Returns the language settings.
- * @return the languages supported
- */
- public String getLanguages() {
- return "en"; // userLanguages;
- }
-
- /**
- * Returns the media type for this rendering.
- * @return the media for fo documents is "print"
- */
- public String getMedia() {
- return "print";
- }
-
- /**
- * Returns the user stylesheet uri.
- * @return null if no user style sheet was specified.
- */
- public String getUserStyleSheetURI() {
- return null; // userStyleSheetURI;
- }
-
- /**
- * Returns the class name of the XML parser.
- * @return the XML parser class name
- */
- public String getXMLParserClassName() {
- try {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- return factory.newSAXParser().getXMLReader().getClass().getName();
- } catch (Exception e) {
- return null;
- }
- }
-
- /**
- * Is the XML parser validating.
- * @return true if the xml parser is validating
- */
- public boolean isXMLParserValidating() {
- return false;
- }
-
- /**
- * Get the transform of the svg document.
- * @return the transform
- */
- public AffineTransform getTransform() {
- return currentTransform;
- }
-
- /**
- * Get the default viewport size for an svg document.
- * This returns a default value of 100x100.
- * @return the default viewport size
- */
- public Dimension2D getViewportSize() {
- return new Dimension(100, 100);
- }
-
-}
-
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java b/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java
new file mode 100644
index 000000000..4df1af34e
--- /dev/null
+++ b/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java
@@ -0,0 +1,126 @@
+/*
+ * 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.svg;
+
+import java.awt.Dimension;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.batik.bridge.UserAgentAdapter;
+
+/**
+ * A simple SVG user agent.
+ * This is an implementation of the Batik SVG user agent. It ignores any message output sent
+ * by Batik.
+ */
+public class SimpleSVGUserAgent extends UserAgentAdapter {
+
+ private AffineTransform currentTransform = null;
+ private float pixelUnitToMillimeter = 0.0f;
+
+ /**
+ * Creates a new user agent.
+ * @param pixelUnitToMM the pixel to millimeter conversion factor currently in effect
+ * @param at the current transform
+ */
+ public SimpleSVGUserAgent(float pixelUnitToMM, AffineTransform at) {
+ pixelUnitToMillimeter = pixelUnitToMM;
+ currentTransform = at;
+ }
+
+ /**
+ * Returns a customized the pixel to mm factor.
+ * @return the pixel unit to millimeter conversion factor
+ */
+ public float getPixelUnitToMillimeter() {
+ return pixelUnitToMillimeter;
+ }
+
+ /**
+ * Returns the language settings.
+ * @return the languages supported
+ */
+ public String getLanguages() {
+ return "en"; // userLanguages;
+ }
+
+ /**
+ * Returns the media type for this rendering.
+ * @return the media for FO documents is "print"
+ */
+ public String getMedia() {
+ return "print";
+ }
+
+ /**
+ * Returns the user stylesheet URI.
+ * @return null if no user style sheet was specified.
+ */
+ public String getUserStyleSheetURI() {
+ return null; // userStyleSheetURI;
+ }
+
+ /**
+ * Returns the class name of the XML parser.
+ * @return the XML parser class name
+ */
+ public String getXMLParserClassName() {
+ try {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ return factory.newSAXParser().getXMLReader().getClass().getName();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Is the XML parser validating.
+ * @return true if the XML parser is validating
+ */
+ public boolean isXMLParserValidating() {
+ return false;
+ }
+
+ /**
+ * Get the transform of the SVG document.
+ * @return the transform
+ */
+ public AffineTransform getTransform() {
+ return currentTransform;
+ }
+
+ /** {@inheritDoc} */
+ public void setTransform(AffineTransform at) {
+ this.currentTransform = at;
+ }
+
+ /**
+ * Get the default viewport size for an SVG document.
+ * This returns a default value of 100x100.
+ * @return the default viewport size
+ */
+ public Dimension2D getViewportSize() {
+ return new Dimension(100, 100);
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/util/QName.java b/src/java/org/apache/fop/util/QName.java
index 390e29dd9..132f5b4dc 100644
--- a/src/java/org/apache/fop/util/QName.java
+++ b/src/java/org/apache/fop/util/QName.java
@@ -19,23 +19,17 @@
package org.apache.fop.util;
-import java.io.Serializable;
-
/**
* Represents a qualified name of an XML element or an XML attribute.
* <p>
* Note: This class allows to carry a namespace prefix but it is not used in the equals() and
* hashCode() methods.
+ * @deprecated Use the XML Graphics Commons variant instead!
*/
-public class QName implements Serializable {
+public class QName extends org.apache.xmlgraphics.util.QName {
private static final long serialVersionUID = -5225376740044770690L;
- private String namespaceURI;
- private String localName;
- private String prefix;
- private int hashCode;
-
/**
* Main constructor.
* @param namespaceURI the namespace URI
@@ -43,16 +37,7 @@ public class QName implements Serializable {
* @param localName the local name
*/
public QName(String namespaceURI, String prefix, String localName) {
- if (localName == null) {
- throw new NullPointerException("Parameter localName must not be null");
- }
- if (localName.length() == 0) {
- throw new IllegalArgumentException("Parameter localName must not be empty");
- }
- this.namespaceURI = namespaceURI;
- this.prefix = prefix;
- this.localName = localName;
- this.hashCode = toHashString().hashCode();
+ super(namespaceURI, prefix, localName);
}
/**
@@ -61,78 +46,7 @@ public class QName implements Serializable {
* @param qName the qualified name
*/
public QName(String namespaceURI, String qName) {
- if (qName == null) {
- throw new NullPointerException("Parameter localName must not be null");
- }
- if (qName.length() == 0) {
- throw new IllegalArgumentException("Parameter localName must not be empty");
- }
- this.namespaceURI = namespaceURI;
- int p = qName.indexOf(':');
- if (p > 0) {
- this.prefix = qName.substring(0, p);
- this.localName = qName.substring(p + 1);
- } else {
- this.prefix = null;
- this.localName = qName;
- }
- this.hashCode = toHashString().hashCode();
+ super(namespaceURI, qName);
}
- /** @return the namespace URI */
- public String getNamespaceURI() {
- return this.namespaceURI;
- }
-
- /** @return the namespace prefix */
- public String getPrefix() {
- return this.prefix;
- }
-
- /** @return the local name */
- public String getLocalName() {
- return this.localName;
- }
-
- /** @return the fully qualified name */
- public String getQName() {
- return getPrefix() != null ? getPrefix() + ':' + getLocalName() : getLocalName();
- }
-
- /** {@inheritDoc} */
- public int hashCode() {
- return this.hashCode;
- }
-
- /** {@inheritDoc} */
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- } else if (obj == this) {
- return true;
- } else {
- if (obj instanceof QName) {
- QName other = (QName)obj;
- if ((getNamespaceURI() == null && other.getNamespaceURI() == null)
- || getNamespaceURI().equals(other.getNamespaceURI())) {
- return getLocalName().equals(other.getLocalName());
- }
- }
- }
- return false;
- }
-
- /** {@inheritDoc} */
- public String toString() {
- return prefix != null
- ? (prefix + ":" + localName)
- : toHashString();
- }
-
- private String toHashString() {
- return (namespaceURI != null
- ? ("{" + namespaceURI + "}" + localName)
- : localName);
- }
-
}
diff --git a/src/java/org/apache/fop/util/XMLResourceBundle.java b/src/java/org/apache/fop/util/XMLResourceBundle.java
new file mode 100644
index 000000000..1b320816b
--- /dev/null
+++ b/src/java/org/apache/fop/util/XMLResourceBundle.java
@@ -0,0 +1,398 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Stack;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.xmlgraphics.util.QName;
+
+/**
+ * This class is a ResourceBundle that loads its contents from XML files instead of properties
+ * files (like PropertiesResourceBundle).
+ * <p>
+ * The XML format for this resource bundle implementation is the following
+ * (the same as Apache Cocoon's XMLResourceBundle):
+ * <pre>
+ * &lt;catalogue xml:lang="en"&gt;
+ * &lt;message key="key1"&gt;Message &lt;br/&gt; Value 1&lt;/message&gt;
+ * &lt;message key="key2"&gt;Message &lt;br/&gt; Value 1&lt;/message&gt;
+ * ...
+ * &lt;/catalogue&gt;
+ * </pre>
+ */
+public class XMLResourceBundle extends ResourceBundle {
+
+ //Note: Some code here has been copied and adapted from Apache Harmony!
+
+ private Properties resources = new Properties();
+
+ private Locale locale;
+
+ private static SAXTransformerFactory tFactory
+ = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+ /**
+ * Creates a resource bundle from an InputStream.
+ * @param in the stream to read from
+ * @throws IOException if an I/O error occurs
+ */
+ public XMLResourceBundle(InputStream in) throws IOException {
+ try {
+ Transformer transformer = tFactory.newTransformer();
+ StreamSource src = new StreamSource(in);
+ SAXResult res = new SAXResult(new CatalogueHandler());
+ transformer.transform(src, res);
+ } catch (TransformerException e) {
+ throw new IOException("Error while parsing XML resource bundle: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Gets a resource bundle using the specified base name, default locale, and class loader.
+ * @param baseName the base name of the resource bundle, a fully qualified class name
+ * @param loader the class loader from which to load the resource bundle
+ * @return a resource bundle for the given base name and the default locale
+ * @throws MissingResourceException if no resource bundle for the specified base name can be
+ * found
+ * @see java.util.ResourceBundle#getBundle(String)
+ */
+ public static ResourceBundle getXMLBundle(String baseName, ClassLoader loader)
+ throws MissingResourceException {
+ return getXMLBundle(baseName, Locale.getDefault(), loader);
+ }
+
+ /**
+ * Gets a resource bundle using the specified base name, locale, and class loader.
+ * @param baseName the base name of the resource bundle, a fully qualified class name
+ * @param locale the locale for which a resource bundle is desired
+ * @param loader the class loader from which to load the resource bundle
+ * @return a resource bundle for the given base name and locale
+ * @throws MissingResourceException if no resource bundle for the specified base name can be
+ * found
+ * @see java.util.ResourceBundle#getBundle(String, Locale, ClassLoader)
+ */
+ public static ResourceBundle getXMLBundle(String baseName, Locale locale, ClassLoader loader)
+ throws MissingResourceException {
+ if (loader == null) {
+ throw new NullPointerException("loader must not be null");
+ }
+ if (baseName == null) {
+ throw new NullPointerException("baseName must not be null");
+ }
+
+ ResourceBundle bundle;
+ if (!locale.equals(Locale.getDefault())) {
+ bundle = handleGetXMLBundle(baseName, "_" + locale, false, loader);
+ if (bundle != null) {
+ return bundle;
+ }
+ }
+ bundle = handleGetXMLBundle(baseName, "_" + Locale.getDefault(), true, loader);
+ if (bundle != null) {
+ return bundle;
+ }
+ throw new MissingResourceException(
+ baseName + " (" + locale + ")", baseName + '_' + locale, null);
+ }
+
+ static class MissingBundle extends ResourceBundle {
+ public Enumeration getKeys() {
+ return null;
+ }
+
+ public Object handleGetObject(String name) {
+ return null;
+ }
+ }
+
+ private static final ResourceBundle MISSING = new MissingBundle();
+ private static final ResourceBundle MISSINGBASE = new MissingBundle();
+
+ private static Map cache = new java.util.WeakHashMap();
+ //<Object, Hashtable<String, ResourceBundle>>
+
+ private static ResourceBundle handleGetXMLBundle(String base, String locale,
+ boolean loadBase, final ClassLoader loader) {
+ XMLResourceBundle bundle = null;
+ String bundleName = base + locale;
+ Object cacheKey = loader != null ? (Object) loader : (Object) "null";
+ Hashtable loaderCache; //<String, ResourceBundle>
+ synchronized (cache) {
+ loaderCache = (Hashtable)cache.get(cacheKey);
+ if (loaderCache == null) {
+ loaderCache = new Hashtable();
+ cache.put(cacheKey, loaderCache);
+ }
+ }
+ ResourceBundle result = (ResourceBundle)loaderCache.get(bundleName);
+ if (result != null) {
+ if (result == MISSINGBASE) {
+ return null;
+ }
+ if (result == MISSING) {
+ if (!loadBase) {
+ return null;
+ }
+ String extension = strip(locale);
+ if (extension == null) {
+ return null;
+ }
+ return handleGetXMLBundle(base, extension, loadBase, loader);
+ }
+ return result;
+ }
+
+ final String fileName = bundleName.replace('.', '/') + ".xml";
+ InputStream stream = (InputStream)AccessController
+ .doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return loader == null
+ ? ClassLoader.getSystemResourceAsStream(fileName)
+ : loader.getResourceAsStream(fileName);
+ }
+ });
+ if (stream != null) {
+ try {
+ try {
+ bundle = new XMLResourceBundle(stream);
+ } finally {
+ stream.close();
+ }
+ bundle.setLocale(locale);
+ } catch (IOException e) {
+ throw new MissingResourceException(e.getMessage(), base, null);
+ }
+ }
+
+ String extension = strip(locale);
+ if (bundle != null) {
+ if (extension != null) {
+ ResourceBundle parent = handleGetXMLBundle(base, extension, true,
+ loader);
+ if (parent != null) {
+ bundle.setParent(parent);
+ }
+ }
+ loaderCache.put(bundleName, bundle);
+ return bundle;
+ }
+
+ if (extension != null) {
+ ResourceBundle fallback = handleGetXMLBundle(base, extension, loadBase, loader);
+ if (fallback != null) {
+ loaderCache.put(bundleName, fallback);
+ return fallback;
+ }
+ }
+ loaderCache.put(bundleName, loadBase ? MISSINGBASE : MISSING);
+ return null;
+ }
+
+ private void setLocale(String name) {
+ String language = "", country = "", variant = "";
+ if (name.length() > 1) {
+ int nextIndex = name.indexOf('_', 1);
+ if (nextIndex == -1) {
+ nextIndex = name.length();
+ }
+ language = name.substring(1, nextIndex);
+ if (nextIndex + 1 < name.length()) {
+ int index = nextIndex;
+ nextIndex = name.indexOf('_', nextIndex + 1);
+ if (nextIndex == -1) {
+ nextIndex = name.length();
+ }
+ country = name.substring(index + 1, nextIndex);
+ if (nextIndex + 1 < name.length()) {
+ variant = name.substring(nextIndex + 1, name.length());
+ }
+ }
+ }
+ this.locale = new Locale(language, country, variant);
+ }
+
+ private static String strip(String name) {
+ int index = name.lastIndexOf('_');
+ if (index != -1) {
+ return name.substring(0, index);
+ }
+ return null;
+ }
+
+ private Enumeration getLocalKeys() {
+ return (Enumeration)resources.propertyNames();
+ }
+
+ /** {@inheritDoc} */
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getKeys() {
+ if (parent == null) {
+ return getLocalKeys();
+ }
+ return new Enumeration() {
+ private Enumeration local = getLocalKeys();
+ private Enumeration pEnum = parent.getKeys();
+
+ private Object nextElement;
+
+ private boolean findNext() {
+ if (nextElement != null) {
+ return true;
+ }
+ while (pEnum.hasMoreElements()) {
+ Object next = pEnum.nextElement();
+ if (!resources.containsKey(next)) {
+ nextElement = next;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean hasMoreElements() {
+ if (local.hasMoreElements()) {
+ return true;
+ }
+ return findNext();
+ }
+
+ public Object nextElement() {
+ if (local.hasMoreElements()) {
+ return local.nextElement();
+ }
+ if (findNext()) {
+ Object result = nextElement;
+ nextElement = null;
+ return result;
+ }
+ // Cause an exception
+ return pEnum.nextElement();
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ protected Object handleGetObject(String key) {
+ if (key == null) {
+ throw new NullPointerException("key must not be null");
+ }
+ return resources.get(key);
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "XMLResourceBundle: " + getLocale();
+ }
+
+ private class CatalogueHandler extends DefaultHandler {
+
+ private static final String CATALOGUE = "catalogue";
+ private static final String MESSAGE = "message";
+
+ private StringBuffer valueBuffer = new StringBuffer();
+ private Stack elementStack = new Stack();
+ private String currentKey = null;
+
+ private boolean isOwnNamespace(String uri) {
+ return ("".equals(uri));
+ }
+
+ private QName getParentElementName() {
+ return (QName)elementStack.peek();
+ }
+
+ /** {@inheritDoc} */
+ public void startElement(String uri, String localName, String qName,
+ Attributes atts) throws SAXException {
+ super.startElement(uri, localName, qName, atts);
+ QName elementName = new QName(uri, qName);
+ if (isOwnNamespace(uri)) {
+ if (CATALOGUE.equals(localName)) {
+ //nop
+ } else if (MESSAGE.equals(localName)) {
+ if (!CATALOGUE.equals(getParentElementName().getLocalName())) {
+ throw new SAXException(MESSAGE + " must be a child of " + CATALOGUE);
+ }
+ this.currentKey = atts.getValue("key");
+ } else {
+ throw new SAXException("Invalid element name: " + elementName);
+ }
+ } else {
+ //ignore
+ }
+ this.valueBuffer.setLength(0);
+ elementStack.push(elementName);
+ }
+
+ /** {@inheritDoc} */
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ super.endElement(uri, localName, qName);
+ elementStack.pop();
+ if (isOwnNamespace(uri)) {
+ if (CATALOGUE.equals(localName)) {
+ //nop
+ } else if (MESSAGE.equals(localName)) {
+ if (this.currentKey == null) {
+ throw new SAXException(
+ "current key is null (attribute 'key' might be mistyped)");
+ }
+ resources.put(this.currentKey, this.valueBuffer.toString());
+ this.currentKey = null;
+ }
+ } else {
+ //ignore
+ }
+ this.valueBuffer.setLength(0);
+ }
+
+ /** {@inheritDoc} */
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ super.characters(ch, start, length);
+ valueBuffer.append(ch, start, length);
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/fop/util/text/AdvancedMessageFormat.java b/src/java/org/apache/fop/util/text/AdvancedMessageFormat.java
new file mode 100644
index 000000000..a2169156a
--- /dev/null
+++ b/src/java/org/apache/fop/util/text/AdvancedMessageFormat.java
@@ -0,0 +1,487 @@
+/*
+ * 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.util.text;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.xmlgraphics.util.Service;
+
+
+/**
+ * Formats messages based on a template and with a set of named parameters. This is similar to
+ * {@link java.util.MessageFormat} but uses named parameters and supports conditional sub-groups.
+ * <p>
+ * Example:
+ * </p>
+ * <p><code>Missing field "{fieldName}"[ at location: {location}]!</code></p>
+ * <ul>
+ * <li>Curly brackets ("{}") are used for fields.</li>
+ * <li>Square brackets ("[]") are used to delimit conditional sub-groups. A sub-group is
+ * conditional when all fields inside the sub-group have a null value. In the case, everything
+ * between the brackets is skipped.</li>
+ * </ul>
+ */
+public class AdvancedMessageFormat {
+
+ /** Regex that matches "," but not "\," (escaped comma) */
+ static final Pattern COMMA_SEPARATOR_REGEX = Pattern.compile("(?<!\\\\),");
+
+ private static final Map PART_FACTORIES = new java.util.HashMap();
+ private static final List OBJECT_FORMATTERS = new java.util.ArrayList();
+ private static final Map FUNCTIONS = new java.util.HashMap();
+
+ private CompositePart rootPart;
+
+ static {
+ Iterator iter;
+ iter = Service.providers(PartFactory.class, true);
+ while (iter.hasNext()) {
+ PartFactory factory = (PartFactory)iter.next();
+ PART_FACTORIES.put(factory.getFormat(), factory);
+ }
+ iter = Service.providers(ObjectFormatter.class, true);
+ while (iter.hasNext()) {
+ OBJECT_FORMATTERS.add((ObjectFormatter)iter.next());
+ }
+ iter = Service.providers(Function.class, true);
+ while (iter.hasNext()) {
+ Function function = (Function)iter.next();
+ FUNCTIONS.put(function.getName(), function);
+ }
+ }
+
+ /**
+ * Construct a new message format.
+ * @param pattern the message format pattern.
+ */
+ public AdvancedMessageFormat(CharSequence pattern) {
+ parsePattern(pattern);
+ }
+
+ private void parsePattern(CharSequence pattern) {
+ rootPart = new CompositePart(false);
+ StringBuffer sb = new StringBuffer();
+ parseInnerPattern(pattern, rootPart, sb, 0);
+ }
+
+ private int parseInnerPattern(CharSequence pattern, CompositePart parent,
+ StringBuffer sb, int start) {
+ assert sb.length() == 0;
+ int i = start;
+ int len = pattern.length();
+ loop:
+ while (i < len) {
+ char ch = pattern.charAt(i);
+ switch (ch) {
+ case '{':
+ if (sb.length() > 0) {
+ parent.addChild(new TextPart(sb.toString()));
+ sb.setLength(0);
+ }
+ i++;
+ int nesting = 1;
+ while (i < len) {
+ ch = pattern.charAt(i);
+ if (ch == '{') {
+ nesting++;
+ } else if (ch == '}') {
+ nesting--;
+ if (nesting == 0) {
+ i++;
+ break;
+ }
+ }
+ sb.append(ch);
+ i++;
+ }
+ parent.addChild(parseField(sb.toString()));
+ sb.setLength(0);
+ break;
+ case ']':
+ i++;
+ break loop; //Current composite is finished
+ case '[':
+ if (sb.length() > 0) {
+ parent.addChild(new TextPart(sb.toString()));
+ sb.setLength(0);
+ }
+ i++;
+ CompositePart composite = new CompositePart(true);
+ parent.addChild(composite);
+ i += parseInnerPattern(pattern, composite, sb, i);
+ break;
+ case '|':
+ if (sb.length() > 0) {
+ parent.addChild(new TextPart(sb.toString()));
+ sb.setLength(0);
+ }
+ parent.newSection();
+ i++;
+ break;
+ case '\\':
+ if (i < len - 1) {
+ i++;
+ ch = pattern.charAt(i);
+ }
+ //no break here! Must be right before "default" section
+ default:
+ sb.append(ch);
+ i++;
+ }
+ }
+ if (sb.length() > 0) {
+ parent.addChild(new TextPart(sb.toString()));
+ sb.setLength(0);
+ }
+ return i - start;
+ }
+
+ private Part parseField(String field) {
+ String[] parts = COMMA_SEPARATOR_REGEX.split(field, 3);
+ String fieldName = parts[0];
+ if (parts.length == 1) {
+ if (fieldName.startsWith("#")) {
+ return new FunctionPart(fieldName.substring(1));
+ } else {
+ return new SimpleFieldPart(fieldName);
+ }
+ } else {
+ String format = parts[1];
+ PartFactory factory = (PartFactory)PART_FACTORIES.get(format);
+ if (factory == null) {
+ throw new IllegalArgumentException(
+ "No PartFactory available under the name: " + format);
+ }
+ if (parts.length == 2) {
+ return factory.newPart(fieldName, null);
+ } else {
+ return factory.newPart(fieldName, parts[2]);
+ }
+ }
+ }
+
+ private static Function getFunction(String functionName) {
+ return (Function)FUNCTIONS.get(functionName);
+ }
+
+ /**
+ * Formats a message with the given parameters.
+ * @param params a Map of named parameters (Contents: <String, Object>)
+ * @return the formatted message
+ */
+ public String format(Map params) {
+ StringBuffer sb = new StringBuffer();
+ format(params, sb);
+ return sb.toString();
+ }
+
+ /**
+ * Formats a message with the given parameters.
+ * @param params a Map of named parameters (Contents: <String, Object>)
+ * @param target the target StringBuffer to write the formatted message to
+ */
+ public void format(Map params, StringBuffer target) {
+ rootPart.write(target, params);
+ }
+
+ /**
+ * Represents a message template part. This interface is implemented by various variants of
+ * the single curly braces pattern ({field}, {field,if,yes,no} etc.).
+ */
+ public interface Part {
+
+ /**
+ * Writes the formatted part to a string buffer.
+ * @param sb the target string buffer
+ * @param params the parameters to work with
+ */
+ void write(StringBuffer sb, Map params);
+
+ /**
+ * Indicates whether there is any content that is generated by this message part.
+ * @param params the parameters to work with
+ * @return true if the part has content
+ */
+ boolean isGenerated(Map params);
+ }
+
+ /**
+ * Implementations of this interface parse a field part and return message parts.
+ */
+ public interface PartFactory {
+
+ /**
+ * Creates a new part by parsing the values parameter to configure the part.
+ * @param fieldName the field name
+ * @param values the unparsed parameter values
+ * @return the new message part
+ */
+ Part newPart(String fieldName, String values);
+
+ /**
+ * Returns the name of the message part format.
+ * @return the name of the message part format
+ */
+ String getFormat();
+ }
+
+ /**
+ * Implementations of this interface format certain objects to strings.
+ */
+ public interface ObjectFormatter {
+
+ /**
+ * Formats an object to a string and writes the result to a string buffer.
+ * @param sb the target string buffer
+ * @param obj the object to be formatted
+ */
+ void format(StringBuffer sb, Object obj);
+
+ /**
+ * Indicates whether a given object is supported.
+ * @param obj the object
+ * @return true if the object is supported by the formatter
+ */
+ boolean supportsObject(Object obj);
+ }
+
+ /**
+ * Implementations of this interface do some computation based on the message parameters
+ * given to it. Note: at the moment, this has to be done in a local-independent way since
+ * there is no locale information.
+ */
+ public interface Function {
+
+ /**
+ * Executes the function.
+ * @param params the message parameters
+ * @return the function result
+ */
+ Object evaluate(Map params);
+
+ /**
+ * Returns the name of the function.
+ * @return the name of the function
+ */
+ Object getName();
+ }
+
+ private static class TextPart implements Part {
+
+ private String text;
+
+ public TextPart(String text) {
+ this.text = text;
+ }
+
+ public void write(StringBuffer sb, Map params) {
+ sb.append(text);
+ }
+
+ public boolean isGenerated(Map params) {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return this.text;
+ }
+ }
+
+ private static class SimpleFieldPart implements Part {
+
+ private String fieldName;
+
+ public SimpleFieldPart(String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ public void write(StringBuffer sb, Map params) {
+ if (!params.containsKey(fieldName)) {
+ throw new IllegalArgumentException(
+ "Message pattern contains unsupported field name: " + fieldName);
+ }
+ Object obj = params.get(fieldName);
+ formatObject(obj, sb);
+ }
+
+ public boolean isGenerated(Map params) {
+ Object obj = params.get(fieldName);
+ return obj != null;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "{" + this.fieldName + "}";
+ }
+ }
+
+ /**
+ * Formats an object to a string and writes the result to a string buffer. This method
+ * usually uses the object's <code>toString()</code> method unless there is an
+ * {@link ObjectFormatter} that supports the object. {@link ObjectFormatter}s are registered
+ * through the service provider mechanism defined by the JAR specification.
+ * @param obj the object to be formatted
+ * @param target the target string buffer
+ */
+ public static void formatObject(Object obj, StringBuffer target) {
+ if (obj instanceof String) {
+ target.append(obj);
+ } else {
+ boolean handled = false;
+ Iterator iter = OBJECT_FORMATTERS.iterator();
+ while (iter.hasNext()) {
+ ObjectFormatter formatter = (ObjectFormatter)iter.next();
+ if (formatter.supportsObject(obj)) {
+ formatter.format(target, obj);
+ handled = true;
+ break;
+ }
+ }
+ if (!handled) {
+ target.append(String.valueOf(obj));
+ }
+ }
+ }
+
+ private static class FunctionPart implements Part {
+
+ private Function function;
+
+ public FunctionPart(String functionName) {
+ this.function = getFunction(functionName);
+ if (this.function == null) {
+ throw new IllegalArgumentException("Unknown function: " + functionName);
+ }
+ }
+
+ public void write(StringBuffer sb, Map params) {
+ Object obj = this.function.evaluate(params);
+ formatObject(obj, sb);
+ }
+
+ public boolean isGenerated(Map params) {
+ Object obj = this.function.evaluate(params);
+ return obj != null;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "{#" + this.function.getName() + "}";
+ }
+ }
+
+ private static class CompositePart implements Part {
+
+ protected List parts = new java.util.ArrayList();
+ private boolean conditional;
+ private boolean hasSections = false;
+
+ public CompositePart(boolean conditional) {
+ this.conditional = conditional;
+ }
+
+ private CompositePart(List parts) {
+ this.parts.addAll(parts);
+ this.conditional = true;
+ }
+
+ public void addChild(Part part) {
+ if (part == null) {
+ throw new NullPointerException("part must not be null");
+ }
+ if (hasSections) {
+ CompositePart composite = (CompositePart)this.parts.get(this.parts.size() - 1);
+ composite.addChild(part);
+ } else {
+ this.parts.add(part);
+ }
+ }
+
+ public void newSection() {
+ if (!hasSections) {
+ List p = this.parts;
+ //Dropping into a different mode...
+ this.parts = new java.util.ArrayList();
+ this.parts.add(new CompositePart(p));
+ hasSections = true;
+ }
+ this.parts.add(new CompositePart(true));
+ }
+
+ public void write(StringBuffer sb, Map params) {
+ if (hasSections) {
+ Iterator iter = this.parts.iterator();
+ while (iter.hasNext()) {
+ CompositePart part = (CompositePart)iter.next();
+ if (part.isGenerated(params)) {
+ part.write(sb, params);
+ break;
+ }
+ }
+ } else {
+ if (isGenerated(params)) {
+ Iterator iter = this.parts.iterator();
+ while (iter.hasNext()) {
+ Part part = (Part)iter.next();
+ part.write(sb, params);
+ }
+ }
+ }
+ }
+
+ public boolean isGenerated(Map params) {
+ if (hasSections) {
+ Iterator iter = this.parts.iterator();
+ while (iter.hasNext()) {
+ Part part = (Part)iter.next();
+ if (part.isGenerated(params)) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ if (conditional) {
+ Iterator iter = this.parts.iterator();
+ while (iter.hasNext()) {
+ Part part = (Part)iter.next();
+ if (!part.isGenerated(params)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return this.parts.toString();
+ }
+ }
+
+
+ static String unescapeComma(String string) {
+ return string.replaceAll("\\\\,", ",");
+ }
+}
diff --git a/src/java/org/apache/fop/util/text/ChoiceFieldPart.java b/src/java/org/apache/fop/util/text/ChoiceFieldPart.java
new file mode 100644
index 000000000..df457a02b
--- /dev/null
+++ b/src/java/org/apache/fop/util/text/ChoiceFieldPart.java
@@ -0,0 +1,91 @@
+/*
+ * 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.util.text;
+
+import java.text.ChoiceFormat;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.fop.util.text.AdvancedMessageFormat.Part;
+import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory;
+
+/**
+ * Defines a "choice" field part that works like {@link ChoiceFormat}.
+ */
+public class ChoiceFieldPart implements Part {
+
+ private static final Pattern VARIABLE_REGEX = Pattern.compile("\\{([^\\}]+)\\}");
+
+ private String fieldName;
+ private ChoiceFormat choiceFormat;
+
+ /**
+ * Creates a new choice part.
+ * @param fieldName the field name to work on
+ * @param choicesPattern the choices pattern (as used by {@link ChoiceFormat})
+ */
+ public ChoiceFieldPart(String fieldName, String choicesPattern) {
+ this.fieldName = fieldName;
+ this.choiceFormat = new ChoiceFormat(choicesPattern);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isGenerated(Map params) {
+ Object obj = params.get(fieldName);
+ return obj != null;
+ }
+
+ /** {@inheritDoc} */
+ public void write(StringBuffer sb, Map params) {
+ Object obj = params.get(fieldName);
+ Number num = (Number)obj;
+ String result = this.choiceFormat.format(num.doubleValue());
+ Matcher m = VARIABLE_REGEX.matcher(result);
+ if (m.find()) {
+ //Resolve inner variables
+ AdvancedMessageFormat f = new AdvancedMessageFormat(result);
+ f.format(params, sb);
+ } else {
+ sb.append(result);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "{" + this.fieldName + ",choice, ....}";
+ }
+
+ /** Factory for ChoiceFieldPart. */
+ public static class Factory implements PartFactory {
+
+ /** {@inheritDoc} */
+ public Part newPart(String fieldName, String values) {
+ return new ChoiceFieldPart(fieldName, values);
+ }
+
+ /** {@inheritDoc} */
+ public String getFormat() {
+ return "choice";
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/util/text/EqualsFieldPart.java b/src/java/org/apache/fop/util/text/EqualsFieldPart.java
new file mode 100644
index 000000000..2114b0d00
--- /dev/null
+++ b/src/java/org/apache/fop/util/text/EqualsFieldPart.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.util.text;
+
+import java.util.Map;
+
+import org.apache.fop.util.text.AdvancedMessageFormat.Part;
+import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory;
+
+/**
+ * Defines an "equals" field part that can compare a field's string value against another string.
+ * It returns either of two possible values attached as additional part parameters. Example:
+ * <code>{field,equals,new,This is new!,This is old!}</code>
+ */
+public class EqualsFieldPart extends IfFieldPart {
+
+ private String equalsValue;
+
+ /**
+ * Creates a new "equals" field part.
+ * @param fieldName the field name
+ * @param values the unparsed parameter values
+ */
+ public EqualsFieldPart(String fieldName, String values) {
+ super(fieldName, values);
+ }
+
+ /** {@inheritDoc} */
+ protected void parseValues(String values) {
+ String[] parts = AdvancedMessageFormat.COMMA_SEPARATOR_REGEX.split(values, 3);
+ this.equalsValue = parts[0];
+ if (parts.length == 1) {
+ throw new IllegalArgumentException(
+ "'equals' format must have at least 2 parameters");
+ }
+ if (parts.length == 3) {
+ ifValue = AdvancedMessageFormat.unescapeComma(parts[1]);
+ elseValue = AdvancedMessageFormat.unescapeComma(parts[2]);
+ } else {
+ ifValue = AdvancedMessageFormat.unescapeComma(parts[1]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected boolean isTrue(Map params) {
+ Object obj = params.get(fieldName);
+ if (obj != null) {
+ return String.valueOf(obj).equals(this.equalsValue);
+ } else {
+ return false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "{" + this.fieldName + ", equals " + this.equalsValue + "}";
+ }
+
+ /**
+ * Part factory for "equals".
+ */
+ public static class Factory implements PartFactory {
+
+ /** {@inheritDoc} */
+ public Part newPart(String fieldName, String values) {
+ return new EqualsFieldPart(fieldName, values);
+ }
+
+ /** {@inheritDoc} */
+ public String getFormat() {
+ return "equals";
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/util/text/GlyphNameFieldPart.java b/src/java/org/apache/fop/util/text/GlyphNameFieldPart.java
new file mode 100644
index 000000000..5d78cdfad
--- /dev/null
+++ b/src/java/org/apache/fop/util/text/GlyphNameFieldPart.java
@@ -0,0 +1,89 @@
+/*
+ * 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.util.text;
+
+import java.util.Map;
+
+import org.apache.xmlgraphics.fonts.Glyphs;
+
+import org.apache.fop.util.text.AdvancedMessageFormat.Part;
+import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory;
+
+/**
+ * Function formatting a character to a glyph name.
+ */
+public class GlyphNameFieldPart implements Part {
+
+ private String fieldName;
+
+ /**
+ * Creates a new glyph name field part
+ * @param fieldName the field name
+ */
+ public GlyphNameFieldPart(String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isGenerated(Map params) {
+ Object obj = params.get(fieldName);
+ return obj != null && getGlyphName(obj).length() > 0;
+ }
+
+ private String getGlyphName(Object obj) {
+ if (obj instanceof Character) {
+ return Glyphs.charToGlyphName(((Character)obj).charValue());
+ } else {
+ throw new IllegalArgumentException(
+ "Value for glyph name part must be a Character but was: "
+ + obj.getClass().getName());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void write(StringBuffer sb, Map params) {
+ if (!params.containsKey(fieldName)) {
+ throw new IllegalArgumentException(
+ "Message pattern contains unsupported field name: " + fieldName);
+ }
+ Object obj = params.get(fieldName);
+ sb.append(getGlyphName(obj));
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "{" + this.fieldName + ",glyph-name}";
+ }
+
+ /** Factory for {@link GlyphNameFieldPart}. */
+ public static class Factory implements PartFactory {
+
+ /** {@inheritDoc} */
+ public Part newPart(String fieldName, String values) {
+ return new GlyphNameFieldPart(fieldName);
+ }
+
+ /** {@inheritDoc} */
+ public String getFormat() {
+ return "glyph-name";
+ }
+
+ }
+}
diff --git a/src/java/org/apache/fop/util/text/HexFieldPart.java b/src/java/org/apache/fop/util/text/HexFieldPart.java
new file mode 100644
index 000000000..19f47f3d7
--- /dev/null
+++ b/src/java/org/apache/fop/util/text/HexFieldPart.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.util.text;
+
+import java.util.Map;
+
+import org.apache.fop.util.text.AdvancedMessageFormat.Part;
+import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory;
+
+/**
+ * Function formatting a number or character to a hex value.
+ */
+public class HexFieldPart implements Part {
+
+ private String fieldName;
+
+ /**
+ * Creates a new hex field part
+ * @param fieldName the field name
+ */
+ public HexFieldPart(String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isGenerated(Map params) {
+ Object obj = params.get(fieldName);
+ return obj != null;
+ }
+
+ /** {@inheritDoc} */
+ public void write(StringBuffer sb, Map params) {
+ if (!params.containsKey(fieldName)) {
+ throw new IllegalArgumentException(
+ "Message pattern contains unsupported field name: " + fieldName);
+ }
+ Object obj = params.get(fieldName);
+ if (obj instanceof Character) {
+ sb.append(Integer.toHexString(((Character)obj).charValue()));
+ } else if (obj instanceof Number) {
+ sb.append(Integer.toHexString(((Number)obj).intValue()));
+ } else {
+ throw new IllegalArgumentException("Incompatible value for hex field part: "
+ + obj.getClass().getName());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "{" + this.fieldName + ",hex}";
+ }
+
+ /** Factory for {@link HexFieldPart}. */
+ public static class Factory implements PartFactory {
+
+ /** {@inheritDoc} */
+ public Part newPart(String fieldName, String values) {
+ return new HexFieldPart(fieldName);
+ }
+
+ /** {@inheritDoc} */
+ public String getFormat() {
+ return "hex";
+ }
+
+ }
+}
diff --git a/src/java/org/apache/fop/util/text/IfFieldPart.java b/src/java/org/apache/fop/util/text/IfFieldPart.java
new file mode 100644
index 000000000..31cd8f36c
--- /dev/null
+++ b/src/java/org/apache/fop/util/text/IfFieldPart.java
@@ -0,0 +1,116 @@
+/*
+ * 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.util.text;
+
+import java.util.Map;
+
+import org.apache.fop.util.text.AdvancedMessageFormat.Part;
+import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory;
+
+/**
+ * Defines an "if" field part that checks if field's value is true or false.
+ * It returns either of two possible values attached as additional part parameters. Example:
+ * <code>{field,if,Yes,No}</code>
+ */
+public class IfFieldPart implements Part {
+
+ /** the field name for the part */
+ protected String fieldName;
+ /** the value being returned if the field is true */
+ protected String ifValue;
+ /** the value being returned if the field is false */
+ protected String elseValue;
+
+ /**
+ * Creates a new "if" field part.
+ * @param fieldName the field name
+ * @param values the unparsed parameter values
+ */
+ public IfFieldPart(String fieldName, String values) {
+ this.fieldName = fieldName;
+ parseValues(values);
+ }
+
+ /**
+ * Parses the parameter values
+ * @param values the unparsed parameter values
+ */
+ protected void parseValues(String values) {
+ String[] parts = AdvancedMessageFormat.COMMA_SEPARATOR_REGEX.split(values, 2);
+ if (parts.length == 2) {
+ ifValue = AdvancedMessageFormat.unescapeComma(parts[0]);
+ elseValue = AdvancedMessageFormat.unescapeComma(parts[1]);
+ } else {
+ ifValue = AdvancedMessageFormat.unescapeComma(values);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void write(StringBuffer sb, Map params) {
+ boolean isTrue = isTrue(params);
+ if (isTrue) {
+ sb.append(ifValue);
+ } else if (elseValue != null) {
+ sb.append(elseValue);
+ }
+ }
+
+ /**
+ * Indicates whether the field's value is true. If the field is not a boolen, it is true
+ * if the field is not null.
+ * @param params the message parameters
+ * @return true the field's value as boolean
+ */
+ protected boolean isTrue(Map params) {
+ Object obj = params.get(fieldName);
+ if (obj instanceof Boolean) {
+ return ((Boolean)obj).booleanValue();
+ } else {
+ return (obj != null);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean isGenerated(Map params) {
+ return isTrue(params) || (elseValue != null);
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "{" + this.fieldName + ", if...}";
+ }
+
+ /**
+ * Part factory for "if".
+ */
+ public static class Factory implements PartFactory {
+
+ /** {@inheritDoc} */
+ public Part newPart(String fieldName, String values) {
+ return new IfFieldPart(fieldName, values);
+ }
+
+ /** {@inheritDoc} */
+ public String getFormat() {
+ return "if";
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/util/text/LocatorFormatter.java b/src/java/org/apache/fop/util/text/LocatorFormatter.java
new file mode 100644
index 000000000..d9532c66d
--- /dev/null
+++ b/src/java/org/apache/fop/util/text/LocatorFormatter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util.text;
+
+import org.xml.sax.Locator;
+
+import org.apache.fop.util.text.AdvancedMessageFormat.ObjectFormatter;
+
+/**
+ * Object formatter for the SAX Locator object.
+ */
+public class LocatorFormatter implements ObjectFormatter {
+
+ /** {@inheritDoc} */
+ public void format(StringBuffer sb, Object obj) {
+ Locator loc = (Locator)obj;
+ sb.append(loc.getLineNumber()).append(":").append(loc.getColumnNumber());
+ }
+
+ /** {@inheritDoc} */
+ public boolean supportsObject(Object obj) {
+ return obj instanceof Locator;
+ }
+
+} \ No newline at end of file