</fileset>
</path>
- <path id="libs-run-classpath">
- <fileset dir="${basedir}/lib">
- <include name="*.jar"/>
- </fileset>
- <fileset dir="${optional.lib.dir}">
+ <path id="libs-build-tools-classpath">
+ <path refid="libs-build-classpath"/>
+ <fileset dir="${basedir}/lib/build">
<include name="*.jar"/>
</fileset>
+ </path>
+
+ <path id="libs-run-classpath">
+ <path refid="libs-build-classpath"/>
<fileset dir="${basedir}/build">
<include name="fop.jar"/>
<include name="fop-hyph.jar" />
<!-- =================================================================== -->
<!-- Compiles the source directory -->
<!-- =================================================================== -->
- <target name="compile-java" depends="init, codegen" description="Compiles the source code">
+ <target name="compile-java" depends="init, codegen">
<!-- create directories -->
<mkdir dir="${build.classes.dir}"/>
<javac destdir="${build.classes.dir}" fork="${javac.fork}" debug="${javac.debug}"
<patternset refid="exclude-jai"/>
<classpath refid="libs-build-classpath"/>
</javac>
+
+ <mkdir dir="${build.sandbox-classes.dir}"/>
+ <javac destdir="${build.sandbox-classes.dir}" fork="${javac.fork}" debug="${javac.debug}"
+ deprecation="${javac.deprecation}" optimize="${javac.optimize}"
+ source="${javac.source}" target="${javac.target}">
+ <src path="${src.sandbox.dir}"/>
+ <patternset includes="**/*.java"/>
+ <patternset refid="exclude-jai"/>
+ <classpath>
+ <path refid="libs-build-classpath"/>
+ <pathelement location="${build.classes.dir}"/>
+ </classpath>
+ </javac>
+ </target>
+
+ <target name="resourcegen" depends="compile-java">
+ <mkdir dir="${build.codegen-classes.dir}"/>
+ <javac destdir="${build.codegen-classes.dir}" fork="${javac.fork}" debug="${javac.debug}"
+ deprecation="${javac.deprecation}" optimize="${javac.optimize}"
+ source="${javac.source}" target="${javac.target}">
+ <src path="${src.codegen.dir}/java"/>
+ <patternset includes="**/*.java"/>
+ <classpath>
+ <path refid="libs-build-tools-classpath"/>
+ <pathelement location="${build.classes.dir}"/>
+ </classpath>
+ </javac>
+ <copy todir="${build.codegen-classes.dir}">
+ <fileset dir="${src.codegen.dir}/java">
+ <include name="**/*.xsl"/>
+ </fileset>
+ </copy>
+
+ <taskdef name="eventResourceGenerator"
+ classname="org.apache.fop.tools.EventProducerCollectorTask">
+ <classpath>
+ <path refid="libs-build-tools-classpath"/>
+ <pathelement location="${build.classes.dir}"/>
+ <pathelement location="${build.codegen-classes.dir}"/>
+ </classpath>
+ </taskdef>
+
+ <eventResourceGenerator
+ modelfile="${build.gensrc.dir}/org/apache/fop/events/event-model.xml"
+ translationfile="${src.java.dir}/org/apache/fop/events/EventFormatter.xml">
+ <fileset dir="${src.java.dir}">
+ <include name="**/*.java"/>
+ <exclude name="org/apache/fop/render/*/**/*.java"/>
+ </fileset>
+ </eventResourceGenerator>
+ <fixcrlf file="${src.java.dir}/org/apache/fop/events/EventFormatter.xml" tab="remove" tablength="2"/>
+ <eventResourceGenerator
+ modelfile="${build.gensrc.dir}/org/apache/fop/render/afp/event-model.xml"
+ translationfile="${src.java.dir}/org/apache/fop/render/afp/AFPEventProducer.xml">
+ <fileset dir="${src.java.dir}">
+ <include name="org/apache/fop/render/afp/**/*.java"/>
+ </fileset>
+ </eventResourceGenerator>
+ <fixcrlf file="${src.java.dir}/org/apache/fop/render/afp/AFPEventProducer.xml" tab="remove" tablength="2"/>
+ <eventResourceGenerator
+ modelfile="${build.gensrc.dir}/org/apache/fop/render/bitmap/event-model.xml"
+ translationfile="${src.java.dir}/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml">
+ <fileset dir="${src.java.dir}">
+ <include name="org/apache/fop/render/bitmap/**/*.java"/>
+ </fileset>
+ </eventResourceGenerator>
+ <fixcrlf file="${src.java.dir}/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml" tab="remove" tablength="2"/>
+ <eventResourceGenerator
+ modelfile="${build.gensrc.dir}/org/apache/fop/render/pcl/event-model.xml"
+ translationfile="${src.java.dir}/org/apache/fop/render/pcl/PCLEventProducer.xml">
+ <fileset dir="${src.java.dir}">
+ <include name="org/apache/fop/render/pcl/**/*.java"/>
+ </fileset>
+ </eventResourceGenerator>
+ <fixcrlf file="${src.java.dir}/org/apache/fop/render/pcl/PCLEventProducer.xml" tab="remove" tablength="2"/>
+ <eventResourceGenerator
+ modelfile="${build.gensrc.dir}/org/apache/fop/render/pdf/event-model.xml"
+ translationfile="${src.java.dir}/org/apache/fop/render/pdf/PDFEventProducer.xml">
+ <fileset dir="${src.java.dir}">
+ <include name="org/apache/fop/render/pdf/**/*.java"/>
+ </fileset>
+ </eventResourceGenerator>
+ <fixcrlf file="${src.java.dir}/org/apache/fop/render/pdf/PDFEventProducer.xml" tab="remove" tablength="2"/>
+ <eventResourceGenerator
+ modelfile="${build.gensrc.dir}/org/apache/fop/render/ps/event-model.xml"
+ translationfile="${src.java.dir}/org/apache/fop/render/ps/PSEventProducer.xml">
+ <fileset dir="${src.java.dir}">
+ <include name="org/apache/fop/render/ps/**/*.java"/>
+ </fileset>
+ </eventResourceGenerator>
+ <fixcrlf file="${src.java.dir}/org/apache/fop/render/ps/PSEventProducer.xml" tab="remove" tablength="2"/>
+ <eventResourceGenerator
+ modelfile="${build.gensrc.dir}/org/apache/fop/render/rtf/event-model.xml"
+ translationfile="${src.java.dir}/org/apache/fop/render/rtf/RTFEventProducer.xml">
+ <fileset dir="${src.java.dir}">
+ <include name="org/apache/fop/render/rtf/**/*.java"/>
+ </fileset>
+ </eventResourceGenerator>
+ <fixcrlf file="${src.java.dir}/org/apache/fop/render/rtf/RTFEventProducer.xml" tab="remove" tablength="2"/>
+ </target>
+
+ <target name="compile-copy-resources" depends="resourcegen">
<copy todir="${build.classes.dir}">
<fileset dir="${src.java.dir}">
<include name="META-INF/**"/>
<include name="**/*.icm"/>
+ <include name="**/*.xml"/>
<include name="**/*.LICENSE.txt"/>
</fileset>
+ <fileset dir="${build.gensrc.dir}">
+ <include name="**/*.xml"/>
+ </fileset>
</copy>
<mkdir dir="${build.viewer.resources.dir}"/>
<copy todir="${build.viewer.resources.dir}">
<copy todir="${build.viewer.images.dir}">
<fileset dir="${src.viewer.images.dir}"/>
</copy>
-
- <mkdir dir="${build.sandbox-classes.dir}"/>
- <javac destdir="${build.sandbox-classes.dir}" fork="${javac.fork}" debug="${javac.debug}"
- deprecation="${javac.deprecation}" optimize="${javac.optimize}"
- source="${javac.source}" target="${javac.target}">
- <src path="${src.sandbox.dir}"/>
- <patternset includes="**/*.java"/>
- <patternset refid="exclude-jai"/>
- <classpath>
- <path refid="libs-build-classpath"/>
- <pathelement location="${build.classes.dir}"/>
- </classpath>
- </javac>
+
+ <!-- sandbox -->
<copy todir="${build.sandbox-classes.dir}">
<fileset dir="${src.sandbox.dir}">
<include name="META-INF/**"/>
</fileset>
</copy>
+
</target>
-
+
+ <target name="compile" depends="compile-java, compile-copy-resources" description="Compiles the source code"/>
<!-- =================================================================== -->
<!-- compiles hyphenation patterns -->
<!-- =================================================================== -->
- <target name="compile-hyphenation" depends="compile-java">
+ <target name="compile-hyphenation" depends="compile">
<path id="hyph-classpath">
<path refid="libs-build-classpath"/>
<pathelement location="${build.classes.dir}"/>
<!-- main FOP JARs -->
<!-- =================================================================== -->
- <target name="uptodate-jar-main" depends="compile-java">
+ <target name="uptodate-jar-main" depends="compile">
<uptodate property="jar.main.uptodate" targetfile="${build.dir}/fop.jar">
<srcfiles dir= "${build.classes.dir}"/>
</uptodate>
</target>
- <target name="jar-main" depends="compile-java,uptodate-jar-main" description="Generates the main jar file" unless="jar.main.uptodate">
+ <target name="jar-main" depends="compile,uptodate-jar-main" description="Generates the main jar file" unless="jar.main.uptodate">
<tstamp>
<format property="ts" pattern="yyyyMMdd-HHmmss-z"/>
</tstamp>
</jar>
</target>
- <target name="uptodate-jar-sandbox" depends="compile-java">
+ <target name="uptodate-jar-sandbox" depends="compile">
<uptodate property="jar.sandbox.uptodate" targetfile="${build.dir}/fop-sandbox.jar">
<srcfiles dir= "${build.sandbox-classes.dir}"/>
</uptodate>
</target>
- <target name="jar-sandbox" depends="compile-java,uptodate-jar-sandbox" description="Generates the sandbox jar file" unless="jar.sandbox.uptodate">
+ <target name="jar-sandbox" depends="compile,uptodate-jar-sandbox" description="Generates the sandbox jar file" unless="jar.sandbox.uptodate">
<tstamp>
<format property="ts" pattern="yyyyMMdd-HHmmss-z"/>
</tstamp>
<include name="xmlgraphics-commons*.jar"/>
</fileset>
- <target name="uptodate-transcoder-pkg" depends="compile-java">
+ <target name="uptodate-transcoder-pkg" depends="compile">
<uptodate property="transcoder.pkg.uptodate" targetfile="${build.dir}/fop-transcoder.jar">
<srcfiles refid="transcoder-classes-files"/>
<srcfiles refid="transcoder-lib-files"/>
</uptodate>
</target>
- <target name="transcoder-pkg" depends="uptodate-transcoder-pkg, compile-java" description="Generates the jar for the transcoder package for Batik" unless="transcoder.pkg.uptodate">
+ <target name="transcoder-pkg" depends="uptodate-transcoder-pkg, compile" description="Generates the jar for the transcoder package for Batik" unless="transcoder.pkg.uptodate">
<echo message="Creating the jar file ${build.dir}/fop-transcoder.jar"/>
<property name="fop-transcoder.name" value="FOP Transcoder Package"/>
</target>
<target name="junit-compile" depends="package, transcoder-pkg, junit-with-xmlunit, junit-without-xmlunit" description="Runs FOP's JUnit tests" if="junit.present">
<mkdir dir="${build.dir}/test-classes"/>
+ <mkdir dir="${build.dir}/test-gensrc"/>
<mkdir dir="${junit.reports.dir}"/>
<javac destdir="${build.dir}/test-classes" fork="${javac.fork}"
debug="${javac.debug}" deprecation="${javac.deprecation}"
</fileset>
</classpath>
</javac>
+ <eventResourceGenerator modelfile="${build.dir}/test-gensrc/org/apache/fop/events/test-event-model.xml">
+ <fileset dir="${basedir}/test/java">
+ <include name="**/*.java"/>
+ </fileset>
+ </eventResourceGenerator>
+ <copy todir="${build.dir}/test-classes">
+ <fileset dir="${basedir}/test/java">
+ <include name="META-INF/**"/>
+ <include name="**/*.xml"/>
+ </fileset>
+ <fileset dir="${build.dir}/test-gensrc">
+ <include name="**/*.xml"/>
+ </fileset>
+ </copy>
</target>
<target name="junit-transcoder" depends="junit-compile" description="Runs FOP's JUnit transcoder tests" if="junit.present">
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package embedding.events;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamSource;
+
+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.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.MimeConstants;
+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;
+
+/**
+ * This class demonstrates how to register an event listener with FOP so you can customize
+ * FOP's error behaviour.
+ */
+public class ExampleEvents {
+
+ // configure fopFactory as desired
+ private FopFactory fopFactory = FopFactory.newInstance();
+
+ /**
+ * Converts an FO file to a PDF file using FOP
+ * @param fo the FO file
+ * @param pdf the target PDF file
+ * @throws IOException In case of an I/O problem
+ * @throws FOPException In case of a FOP problem
+ * @throws TransformerException In case of a problem with XSLT
+ */
+ public void convertFO2PDF(URL fo, File pdf)
+ throws IOException, FOPException, TransformerException {
+
+ OutputStream out = null;
+
+ try {
+ //Create the user agent for this processing run
+ FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
+
+ //Adding a simple logging listener that writes to stdout and stderr
+ foUserAgent.getEventBroadcaster().addEventListener(new SysOutEventListener());
+
+ // Add your own event listener
+ foUserAgent.getEventBroadcaster().addEventListener(new MyEventListener());
+
+ // configure foUserAgent further as desired
+
+ // Setup output stream. Note: Using BufferedOutputStream
+ // for performance reasons (helpful with FileOutputStreams).
+ out = new FileOutputStream(pdf);
+ out = new BufferedOutputStream(out);
+
+ // Construct fop with desired output format
+ Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
+
+ // Setup JAXP using identity transformer
+ TransformerFactory factory = TransformerFactory.newInstance();
+ Transformer transformer = factory.newTransformer(); // identity transformer
+
+ // Setup input stream
+ Source src = new StreamSource(fo.toExternalForm());
+
+ // Resulting SAX events (the generated FO) must be piped through to FOP
+ Result res = new SAXResult(fop.getDefaultHandler());
+
+ // Start XSLT transformation and FOP processing
+ transformer.transform(src, res);
+
+ } finally {
+ IOUtils.closeQuietly(out);
+ }
+ }
+
+ private static 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");
+
+ System.out.println("---=== imageNotFound Event for " + event.getParam("uri")
+ + "!!! ===---");
+ //Stop processing when an image could not be found. Otherwise, FOP would just
+ //continue without the image!
+
+ System.out.println("Throwing a RuntimeException...");
+ throw new RuntimeException(EventFormatter.format(event), fnfe);
+ } else {
+ //ignore all other events
+ }
+ }
+
+ }
+
+ /** A simple event listener that writes the events to stdout and sterr. */
+ private static 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;
+ }
+ }
+ }
+
+
+ /**
+ * This method extracts the original exception from some exception. The exception
+ * might be nested multiple levels deep.
+ * @param t the Throwable to inspect
+ * @return the original Throwable or the method parameter t if there are no nested Throwables.
+ */
+ private static Throwable getOriginalThrowable(Throwable t) {
+ if (t instanceof SAXException) {
+ SAXException saxe = (SAXException)t;
+ if (saxe.getException() != null) {
+ return getOriginalThrowable(saxe.getException());
+ } else {
+ return saxe;
+ }
+ } else {
+ if (t.getCause() != null) {
+ return getOriginalThrowable(t.getCause());
+ } else {
+ return t;
+ }
+ }
+ }
+
+ /**
+ * Main method.
+ * @param args command-line arguments
+ */
+ public static void main(String[] args) {
+ try {
+ System.out.println("FOP ExampleEvents\n");
+ System.out.println("Preparing...");
+
+ //Setup directories
+ File baseDir = new File(".");
+ File outDir = new File(baseDir, "out");
+ outDir.mkdirs();
+
+ //Setup input and output files
+ URL fo = ExampleEvents.class.getResource("missing-image.fo");
+ File pdffile = new File(outDir, "out.pdf");
+
+ System.out.println("Input: XSL-FO (" + fo.toExternalForm() + ")");
+ System.out.println("Output: PDF (" + pdffile + ")");
+ System.out.println();
+ System.out.println("Transforming...");
+
+ ExampleEvents app = new ExampleEvents();
+
+ try {
+ app.convertFO2PDF(fo, pdffile);
+ } catch (TransformerException te) {
+ //Note: We don't get the original exception here!
+ //FOP needs to embed the exception in a SAXException and the TraX transformer
+ //again wraps the SAXException in a TransformerException. Even our own
+ //RuntimeException just wraps the original FileNotFoundException.
+ //So we need to unpack to get the original exception (about three layers deep).
+ Throwable originalThrowable = getOriginalThrowable(te);
+ originalThrowable.printStackTrace(System.err);
+ System.out.println("Aborted!");
+ System.exit(-1);
+ }
+
+ System.out.println("Success!");
+ } catch (Exception e) {
+ //Some other error (shouldn't happen in this example)
+ e.printStackTrace(System.err);
+ System.exit(-1);
+ }
+ }
+
+}
--- /dev/null
+<?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$ -->
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ The following image is not available:
+ <fo:external-graphic src="my-missing-image.png"/>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
--- /dev/null
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
\ No newline at end of file
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.tools;
+
+public class EventConventionException extends Exception {
+
+ public EventConventionException(String message) {
+ super(message);
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.tools;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.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);
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.tools;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.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();
+ }
+ }
+}
--- /dev/null
+<?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>
--- /dev/null
+<?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>
<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>
--- /dev/null
+<?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<String, Object></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>
--- /dev/null
+org.apache.fop.events.ValidationExceptionFactory\r
+org.apache.fop.events.PropertyExceptionFactory\r
+org.apache.fop.events.UnsupportedOperationExceptionFactory\r
+org.apache.fop.layoutmgr.LayoutException$LayoutExceptionFactory\r
--- /dev/null
+org.apache.fop.events.FOPEventModelFactory\r
+org.apache.fop.render.afp.AFPEventProducer$EventModelFactory\r
+org.apache.fop.render.bitmap.BitmapRendererEventProducer$EventModelFactory\r
+org.apache.fop.render.pcl.PCLEventProducer$EventModelFactory\r
+org.apache.fop.render.pdf.PDFEventProducer$EventModelFactory\r
+org.apache.fop.render.ps.PSEventProducer$EventModelFactory\r
+org.apache.fop.render.rtf.RTFEventProducer$EventModelFactory\r
--- /dev/null
+org.apache.fop.fo.FONode$GatherContextInfoFunction
--- /dev/null
+org.apache.fop.util.text.LocatorFormatter
--- /dev/null
+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
private String systemId;
private int line;
private int column;
+
+ private String localizedMessage;
/**
* Constructs a new FOP exception with the specified detail message.
}
}
}
+
+ /**
+ * 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();
+ }
+ }
+
+
}
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;
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.)
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);
+ }
+
+ }
+
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
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);
}
}
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.
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);
}
}
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;
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
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.
*/
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()) {
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);
}
}
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;
* 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);
* 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) {
&& 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 {
return renderer.supportsOutOfOrder() || prepared.isEmpty();
}
+ /**
+ * 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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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;
+ }
+ });
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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();
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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";
+ }
+
+ }
+
+}
--- /dev/null
+<?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 => 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<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 => 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 > {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>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+ \r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+ \r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+-->\r
+<!-- $Id$ -->\r
+<catalogue xml:lang="de">\r
+ <message key="locator">[ (Siehe Position {loc})| (Siehe {#gatherContextInfo})| (Keine Kontextinformationen verfügbar)]</message>\r
+ <message key="org.apache.fop.fo.FOValidationEventProducer.tooManyNodes">In "{elementName}" darf nur ein einziges "{offendingNode}" vorkommen!{{locator}}</message>\r
+ <message key="org.apache.fop.fo.FOValidationEventProducer.missingProperty">Dem Element "{elementName}" fehlt ein verlangtes Property "{propertyName}"!{{locator}}</message>\r
+</catalogue>\r
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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 {
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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<EventProducerModel>)
+ */
+ public Iterator getProducers() {
+ return this.producers.values().iterator();
+ }
+
+ /**
+ * Returns the model of an event producer with the given interface name.
+ * @param interfaceName the fully qualified name of the event producer
+ * @return the model instance for the event producer (or null if it wasn't found)
+ */
+ public EventProducerModel getProducer(String interfaceName) {
+ return (EventProducerModel)this.producers.get(interfaceName);
+ }
+
+ /**
+ * Returns the model of an event producer with the given interface.
+ * @param clazz the interface of the event producer
+ * @return the model instance for the event producer (or null if it wasn't found)
+ */
+ public EventProducerModel getProducer(Class clazz) {
+ return getProducer(clazz.getName());
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ String elName = "event-model";
+ handler.startElement(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);
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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();
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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();
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.events.model;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.xmlgraphics.util.XMLizable;
+
+/**
+ * Represents the model of an event producer with multiple event methods.
+ */
+public class EventProducerModel implements Serializable, XMLizable {
+
+ private static final long serialVersionUID = 122267104123721902L;
+
+ private String interfaceName;
+ private Map methods = new java.util.LinkedHashMap();
+
+ /**
+ * Creates a new instance.
+ * @param interfaceName the fully qualified interface name of the event producer
+ */
+ public EventProducerModel(String interfaceName) {
+ this.interfaceName = interfaceName;
+ }
+
+ /**
+ * Returns the fully qualified interface name of the event producer.
+ * @return the fully qualified interface name
+ */
+ public String getInterfaceName() {
+ return this.interfaceName;
+ }
+
+ /**
+ * Sets the fully qualified interface name of the event producer.
+ * @param name the fully qualified interface name
+ */
+ public void setInterfaceName(String name) {
+ this.interfaceName = name;
+ }
+
+ /**
+ * Adds a model instance of an event method.
+ * @param method the event method model
+ */
+ public void addMethod(EventMethodModel method) {
+ this.methods.put(method.getMethodName(), method);
+ }
+
+ /**
+ * Returns the model instance of an event method for the given method name.
+ * @param methodName the method name
+ * @return the model instance (or null if no method with the given name exists)
+ */
+ public EventMethodModel getMethod(String methodName) {
+ return (EventMethodModel)this.methods.get(methodName);
+ }
+
+ /**
+ * Returns an iterator over the contained event producer methods.
+ * @return an iterator (Iterator<EventMethodModel>)
+ */
+ public Iterator getMethods() {
+ return this.methods.values().iterator();
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute(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);
+ }
+
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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;
+ }
+
+}
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
import java.util.Map;
import org.w3c.dom.DOMImplementation;
+
import org.xml.sax.Locator;
import org.apache.commons.logging.Log;
+ "No element mapping definition found for "
+ FONode.getNodeString(namespaceURI, localName), locator);
} else {
- log.warn("Unknown formatting object " + namespaceURI + "^" + localName);
fobjMaker = new UnknownXMLObj.Maker(namespaceURI);
}
}
// 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.
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;
/**
public FOEventHandler(FOUserAgent foUserAgent) {
this.foUserAgent = foUserAgent;
this.fontInfo = new FontInfo();
+ this.fontInfo.setEventListener(new FontEventAdapter(foUserAgent.getEventBroadcaster()));
}
/**
// Java
import java.util.ListIterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
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;
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
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();
}
* @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,
}
/**
- * 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));
}
/**
* @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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
*/
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);
}
/**
/**
* 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();
return sb.toString();
}
+ /**
+ * 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
* of child nodes
* @return the parent node
*/
- public FObj parentNode();
+ FObj parentNode();
/**
* Convenience method with return type of FONode
* <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
* <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,
* @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
* @return the last node in the list
* @throws NoSuchElementException if the list is empty
*/
- public FONode lastNode();
+ FONode lastNode();
}
}
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;
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.
*
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];
}
}
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;
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
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()) {
}
}
- /**
- * 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());
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);
if (currentPropertyList != null
&& currentPropertyList.getFObj() == currentFObj
&& !foEventHandler.inMarker()) {
- currentPropertyList =
- currentPropertyList.getParentPropertyList();
+ currentPropertyList = currentPropertyList.getParentPropertyList();
}
if (currentFObj.getNameId() == Constants.FO_MARKER) {
/** {@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;
+ }
+
}
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
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.
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);
}
}
}
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
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();
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);
}
}
return markers;
}
+ /** {@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) {
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;
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.
* 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);
* 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 {
} 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);
}
}
}
* @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));
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;
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) {
}
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);
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);
}
}
}
}
/**
- * @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);
}
}
* 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);
}
}
}
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);
}
// 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);
}
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.
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.
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");
}
}
*/
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;
}
}
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());
}
}
* 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. */
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);
}
}
* 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;
}
}
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.
* 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;
}
}
* @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;
}
}
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;
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.
* 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);
+ }
}
/**
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;
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();
= FixedLength.getInstance(-baseline);
}
}
- //TODO Report to caller so he can decide to throw an exception
}
/** {@inheritDoc} */
* <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 */
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:float is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
* 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);
}
+ }
}
/**
* 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) {
} else {
invalidChildError(loc, nsURI, localName);
}
+ }
}
/**
* 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} */
* 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);
+ }
}
/**
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) {
* 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;
}
}
* 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;
}
}
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.
//Additional value
private Point2D intrinsicDimensions;
+ private boolean instrisicSizeDetermined;
private Length intrinsicAlignmentAdjust;
* 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));
}
}
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) {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getIntrinsicHeight() {
prepareIntrinsicSize();
if (intrinsicDimensions != null) {
}
}
- /**
- * {@inheritDoc}
- */
- public Length getIntrinsicAlignmentAdjust()
- {
+ /** {@inheritDoc} */
+ public Length getIntrinsicAlignmentAdjust() {
prepareIntrinsicSize();
return intrinsicAlignmentAdjust;
}
* 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);
}
}
*/
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);
}
}
*/
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();
* @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);
+ }
}
}
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-case is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-properties is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
*/
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 {
} else {
invalidChildError(loc, nsURI, localName);
}
+ }
}
/** {@inheritDoc} */
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-property-set is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
* 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} */
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-switch is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
* 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);
+ }
}
}
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:multi-toggle is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ getName(), getLocator());
notImplementedWarningGiven = true;
}
}
* 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);
+ }
}
}
* 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. */
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;
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.
/**
* 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();
* 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);
+ }
}
/**
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;
}
return "retrieve-marker";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_RETRIEVE_MARKER;
}
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.
*/
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);
}
}
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();
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;
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.
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
* 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
} 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());
}
}
}
} else {
invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
"(marker*,table-column*,table-header?,table-footer?"
+ ",table-body+)");
}
+ if (!hasChildren()) {
+ getParent().removeChild(this);
+ return;
+ }
if (!inMarker()) {
rowGroupBuilder.endTable();
/* clean up */
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;
}
}
* 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);
}
}
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.
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();
}
} 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);
}
}
super(parent);
if (!notImplementedWarningGiven) {
- log.warn("fo:table-caption is not yet implemented.");
+ getFOValidationEventProducer().unimplementedFeature(this, getName(),
+ "fo:table-caption", getLocator());
notImplementedWarningGiven = true;
}
}
* 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;
}
}
*/
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);
}
*/
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;
}
}
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.
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);
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
*/
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);
}
protected void validateChildNode(Locator loc,
String nsURI, String localName)
throws ValidationException {
+ if (FO_URI.equals(nsURI)) {
invalidChildError(loc, nsURI, localName);
+ }
}
/**
*
* @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);
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+
+}
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
borderAfterPrecedence = pList.get(PR_BORDER_AFTER_PRECEDENCE).getNumeric();
&& 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());
}
}
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);
}
}
/* 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());
}
}
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;
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.
*/
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);
+ }
}
}
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;
private PageNumberGenerator pageNumberGenerator;
+ /** the first page number generated by the page sequence */
protected int startingPageNumber = 0;
/**
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();
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 {
}
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.
// 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();
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;
return "color-profile";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_COLOR_PROFILE;
}
// 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();
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
getConcreteParent().addConditionalPageMasterReference(this);
}
* 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);
+ }
}
/**
private Map colorProfiles = null;
/**
+ * Creates a new declarations element.
* @param parent FONode that is the parent of this object
*/
public Declarations(FONode parent) {
((Root) parent).setDeclarations(this);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
// No properties defined for fo:declarations
}
/**
* 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) {
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()
}
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;
}
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");
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;)+");
* 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;
}
return "flow";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_FLOW;
}
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)+");
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);
}
}
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(),
// 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));
}
/**
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);
}
return "layout-master-set";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_LAYOUT_MASTER_SET;
}
private int minPadding = 0; // for decimal formats
// preloaded strings of zeros
- private String zeros[] = {
+ private String[] zeros = {
"", "0", "00", "000", "0000", "00000"
};
}
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"
};
super(parent);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
country = pList.get(PR_COUNTRY).getString();
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
super.startOfNode();
flowMap = new java.util.HashMap();
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());
}
}
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) {
} else {
invalidChildError(loc, nsURI, localName);
}
- } else {
- invalidChildError(loc, nsURI, localName);
}
}
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());
}
}
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.
// 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) {
* 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) {
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
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 {
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;
}
return "page-sequence-master";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_PAGE_SEQUENCE_MASTER;
}
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;
// 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();
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);
+ }
}
}
return "page-sequence-wrapper";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_PAGE_SEQUENCE_WRAPPER;
}
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;
/**
private SimplePageMaster layoutMaster;
/**
+ * Creates a new Region.
+ * @param parent the parent node
* @see org.apache.fop.fo.FONode#FONode(FONode)
*/
protected Region(FONode parent) {
layoutMaster = (SimplePageMaster) parent;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps();
// clip = pList.get(PR_CLIP);
// 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());
}
}
* 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);
+ }
}
/**
* @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")
|| name.equals("xsl-footnote-separator"));
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public boolean generatesReferenceAreas() {
return true;
}
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.
return vpRect;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-after";
}
return "region-after";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_AFTER;
}
// 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();
* 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.
return "region-before";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_BEFORE;
}
// 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());
}
}
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
reldims.bpd - before - after);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-body";
}
return "region-body";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_BODY;
}
// 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.
return vpRect;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-end";
}
return "region-end";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_END;
}
// 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);
}
* 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.
return vpRect;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected String getDefaultRegionName() {
return "xsl-region-start";
}
return "region-start";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_REGION_START;
}
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+)");
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);
+ }
}
}
}
}
- /**
- * 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,
}
/** {@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";
}
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);
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
PageSequenceMaster pageSequenceMaster = (PageSequenceMaster) parent;
* 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,
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;
*/
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) {
}
- /** @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
return bookmarkTree;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public Root getRoot() {
return this;
}
return "root";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_ROOT;
}
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);
}
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();
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
LayoutMasterSet layoutMasterSet = (LayoutMasterSet) parent;
regions = new HashMap(5);
}
- /**
- * Make sure content model satisfied.
- */
+ /** {@inheritDoc} */
protected void endOfNode() throws FOPException {
if (!hasRegionBody) {
missingChildElementError(
* 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);
return "simple-page-master";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_SIMPLE_PAGE_MASTER;
}
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) {
this.state = FIRST;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
masterReference = pList.get(PR_MASTER_REFERENCE).getString();
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
protected void startOfNode() throws FOPException {
PageSequenceMaster pageSequenceMaster = (PageSequenceMaster) parent;
pageSequenceMaster.addSubsequenceSpecifier(this);
* 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} */
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);
}
* 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);
+ }
}
}
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.
// End of property values
/**
+ * Creates a new title element.
* @param parent FONode that is the parent of this object
*/
public Title(FONode parent) {
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;
}
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;
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());
}
}
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);
}
+ }
}
/**
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;
}
return bShow;
}
- public ArrayList getChildBookmarks() {
+ /**
+ * Returns a list of child bookmarks.
+ * @return the list of child bookmarks
+ */
+ public List getChildBookmarks() {
return childBookmarks;
}
return "bookmark";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_BOOKMARK;
}
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;
/**
* 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);
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);
+ }
}
/**
return "bookmark-title";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_BOOKMARK_TITLE;
}
// Java
import java.util.ArrayList;
+import java.util.List;
import org.xml.sax.Locator;
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);
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;
}
return "bookmark-tree";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getNameId() {
return FO_BOOKMARK_TREE;
}
* See the License for the specific language governing permissions and
* limitations under the License.
*
-/* $Id:$ */
+/* $Id$ */
package org.apache.fop.fo.properties;
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
/** Cache for Font instances. */
private Map fontInstanceCache = new java.util.HashMap();
+ private FontEventListener eventListener;
+
/**
* Main constructor
*/
this.usedFonts = new java.util.HashMap();
}
+ /**
+ * 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.)
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);
}
}
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 + "'.");
+ }
}
}
throw new RuntimeException(ioex.getMessage());
}
}
+ realFont.setEventListener(this.eventListener);
isMetricsLoaded = true;
}
}
}
//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;
*/
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
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;
+ }
+
}
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;
/** 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
// 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>
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);
}
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);
// 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);
return null;
}
}
-
}
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;
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.
*/
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);
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);
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;
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;
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;
/**
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);
}
/**
- * 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;
}
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;
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;
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);
*/
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);
//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());
}
}
}
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() {
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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;
+
+}
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?
/**
* 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) {
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);
import java.awt.Dimension;
import java.awt.Rectangle;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
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;
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);
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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;
+ }
+
+ }
+}
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;
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());
}
};
//Restart last page
PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm(
getTopLevelLM(),
- getPageProvider(), getLayoutListener(),
+ getPageProvider(), createLayoutListener(),
alg.getAlignment(), alg.getAlignmentLast(),
footnoteSeparatorLength,
isPartOverflowRecoveryActivated(), false, false);
//Restart last page
PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
getTopLevelLM(),
- getPageProvider(), getLayoutListener(),
+ getPageProvider(), createLayoutListener(),
alignment, Constants.EN_START, footnoteSeparatorLength,
isPartOverflowRecoveryActivated(),
pslm.getCurrentPV().getBodyRegion().getColumnCount());
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;
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
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
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
*/
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));
}
/**
* @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,
}
// 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;
}
}
- private int getPartCount() {
- if (pageBreaks == null) {
- return 0;
- } else {
- return pageBreaks.size();
- }
- }
-
public void updateData1(int total, double demerits) {
}
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());
}
}
}
/**
* 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);
}
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;
/**
* Main constructor.
+ * @param ath the area tree handler
* @param ps The page-sequence the provider operates on
*/
public PageProvider(AreaTreeHandler ath, PageSequence ps) {
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
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;
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());
}
}
}
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) {
}
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;
}
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();
*/
private static Log log = LogFactory.getLog(ContentLayoutManager.class);
- private FOUserAgent userAgent;
private Area holder;
private int stackSize;
private LayoutManager parentLM;
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
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;
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
*/
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;
}
return returnList;
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public int getBaseLength(int lengthBase, FObj fobj) {
return getParent().getBaseLength(lengthBase, getParent().getFObj());
}
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;
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()));
}
}
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
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;
}
}
//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++;
}
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 "
if (colWidth != null) {
sumCols += colWidth.getValue(tlm);
if (colWidth instanceof TableColLength) {
- factors +=
- ((TableColLength) colWidth).getTableUnits();
+ factors += ((TableColLength) colWidth).getTableUnits();
}
}
}
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;
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;
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"
// + " 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);
- }
}
}
}
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;
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;
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();
}
}
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
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;
/**
*/
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 {
//Prepare
SVGUserAgent ua = new SVGUserAgent(
- context.getUserAgent().getSourcePixelUnitToMillimeter(),
+ context.getUserAgent(),
new AffineTransform());
GVTBuilder builder = new GVTBuilder();
final BridgeContext ctx = new BridgeContext(ua);
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;
}
x, y, wrappedContext.getWidth(), wrappedContext.getHeight());
}
+ /**
+ * 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.
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;
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
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;
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;
}
= 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) {
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);
}
}
}
*/
void setUserAgent(FOUserAgent agent);
+ /**
+ * Returns the associated user agent.
+ * @return the user agent
+ */
+ FOUserAgent getUserAgent();
+
/**
* Set up the given FontInfo.
*
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
--- /dev/null
+<?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>
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;
}
}
} 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 ",
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;
}
}
} 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);
}
/*
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);
}
}
} 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);
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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;
+}
--- /dev/null
+<?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>
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;
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 {
// 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());
// 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);
} 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);
}
}
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;
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);
}
}
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);
//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.
*
}
}
- /**
- * 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);
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;
}
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);
return (renderer instanceof Java2DRenderer);
}
-
- /** {@inheritDoc} */
- public String getNamespace() {
- return SVGDOMImplementation.SVG_NAMESPACE_URI;
- }
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
--- /dev/null
+<?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>
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.
*/
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
}
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;
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;
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:
*/
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;
}
}
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 {
}
} 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);
}
}
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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
--- /dev/null
+<?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>
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;
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
}
}
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());
}
}
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++) {
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
}
}
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 {
+ 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
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;
}
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;
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).
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;
}
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;
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
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();
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
--- /dev/null
+<?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>
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;
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;
*/
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;
}
}
}
} 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);
}
}
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) {
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);
}
}
}
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.
*
* @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);
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.
*
* 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;
}
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());
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
// 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);
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));
}
}
return (renderer instanceof PSRenderer);
}
- /** {@inheritDoc} */
- public String getNamespace() {
- return SVGDOMImplementation.SVG_NAMESPACE_URI;
- }
-
}
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;
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;
/**
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);
}
}
}
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
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");
}
}
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.
*/
* @see org.apache.fop.fo.FONode#getNormalNamespacePrefix()
*/
public String getNormalNamespacePrefix() {
- return "fox";
+ return "ps";
}
/**
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.
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)) {
/**{@inheritDoc} */
public String getNormalNamespacePrefix() {
- return "fox";
+ return "ps";
}
/** {@inheritDoc} */
*/
public class PSCommentAfterElement extends AbstractPSCommentElement {
+ /** the element name */
protected static final String ELEMENT = "ps-comment-after";
/**
*/
public class PSCommentBeforeElement extends AbstractPSCommentElement {
+ /** the element name */
protected static final String ELEMENT = "ps-comment-before";
/**
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.
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;
}
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";
/**
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");
}
}
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";
/**
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");
}
}
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";
/**
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");
}
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
--- /dev/null
+<?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>
// Java
import java.awt.geom.Point2D;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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;
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;
FontSetup.setup(fontInfo, null, new DefaultFontResolver(userAgent));
}
+ /**
+ * 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}
*/
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(
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);
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);
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());
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);
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());
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);
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());
table.setBorderAttributes(borderAttributes);
builderContext.pushContainer(table);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startTable:" + e.getMessage());
throw new RuntimeException(e.getMessage());
log.error("startColumn: " + e.getMessage());
throw new RuntimeException(e.getMessage());
}
-
}
/**
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());
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());
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());
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());
// 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());
tctx.selectNextColumn();
}
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("endRow: " + e.getMessage());
throw new RuntimeException(e.getMessage());
}
builderContext.pushContainer(cell);
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
} catch (Exception e) {
log.error("startCell: " + e.getMessage());
throw new RuntimeException(e.getMessage());
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());
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());
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());
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());
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);
}
}
// 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));
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);
}
}
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);
}
}
}
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;
}
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);
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);
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);
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());
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());
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());
}
}
}
} 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
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;
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.
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+
+}
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;
}
/**
* @param message the message to display
*/
public void displayError(String message) {
- logger.error(message);
+ this.eventProducer.error(this, message, null);
}
/**
* @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);
}
/**
* @param message the message to display
*/
public void displayMessage(String message) {
- logger.info(message);
+ this.eventProducer.info(this, message);
}
/**
* @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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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);
+ }
+
+}
+
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
* @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);
}
/**
* @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);
- }
-
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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>
+ * <catalogue xml:lang="en">
+ * <message key="key1">Message <br/> Value 1</message>
+ * <message key="key2">Message <br/> Value 1</message>
+ * ...
+ * </catalogue>
+ * </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);
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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("\\\\,", ",");
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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";
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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";
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.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
--- /dev/null
+org.apache.fop.events.FOPTestEventModelFactory
\ No newline at end of file
import junit.framework.TestSuite;
import org.apache.fop.pdf.PDFObjectTestCase;
+import org.apache.fop.events.BasicEventTestCase;
import org.apache.fop.traits.BorderPropsTestCase;
import org.apache.fop.util.DataURIResolverTestCase;
import org.apache.fop.util.ElementListUtilsTestCase;
import org.apache.fop.util.PDFNumberTestCase;
import org.apache.fop.util.ColorUtilTestCase;
import org.apache.fop.util.UnitConvTestCase;
+import org.apache.fop.util.XMLResourceBundleTestCase;
/**
* Test suite for FOP's utility classes.
suite.addTest(new TestSuite(BorderPropsTestCase.class));
suite.addTest(new TestSuite(ElementListUtilsTestCase.class));
suite.addTest(new TestSuite(DataURIResolverTestCase.class));
+ suite.addTest(new TestSuite(BasicEventTestCase.class));
+ suite.addTest(new TestSuite(XMLResourceBundleTestCase.class));
//$JUnit-END$
return suite;
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.events;
+
+import junit.framework.TestCase;
+
+import org.apache.fop.events.model.EventSeverity;
+
+public class BasicEventTestCase extends TestCase {
+
+ public void testBasics() throws Exception {
+
+ MyEventListener listener = new MyEventListener();
+
+ EventBroadcaster broadcaster = new DefaultEventBroadcaster();
+ broadcaster.addEventListener(listener);
+ assertTrue(broadcaster.hasEventListeners());
+
+ Event ev = new Event(this, "123", EventSeverity.INFO,
+ Event.paramsBuilder()
+ .param("reason", "I'm tired")
+ .param("blah", new Integer(23))
+ .build());
+ broadcaster.broadcastEvent(ev);
+
+ ev = listener.event;
+ assertNotNull(ev);
+ assertEquals("123", listener.event.getEventID());
+ assertEquals(EventSeverity.INFO, listener.event.getSeverity());
+ assertEquals("I'm tired", ev.getParam("reason"));
+ assertEquals(new Integer(23), ev.getParam("blah"));
+
+ broadcaster.removeEventListener(listener);
+ assertFalse(broadcaster.hasEventListeners());
+
+ //Just check that there are no NPEs
+ broadcaster.broadcastEvent(ev);
+ }
+
+ public void testEventProducer() throws Exception {
+ MyEventListener listener = new MyEventListener();
+
+ EventBroadcaster broadcaster = new DefaultEventBroadcaster();
+ broadcaster.addEventListener(listener);
+ assertTrue(broadcaster.hasEventListeners());
+
+
+ TestEventProducer producer = TestEventProducer.Provider.get(broadcaster);
+ producer.complain(this, "I'm tired", 23);
+
+ Event ev = listener.event;
+ assertNotNull(ev);
+ assertEquals("org.apache.fop.events.TestEventProducer.complain",
+ listener.event.getEventID());
+ assertEquals(EventSeverity.WARN, listener.event.getSeverity());
+ assertEquals("I'm tired", ev.getParam("reason"));
+ assertEquals(new Integer(23), ev.getParam("blah"));
+
+ broadcaster.removeEventListener(listener);
+ assertFalse(broadcaster.hasEventListeners());
+
+ //Just check that there are no NPEs
+ broadcaster.broadcastEvent(ev);
+ }
+
+ private class MyEventListener implements EventListener {
+
+ private Event event;
+
+ public void processEvent(Event event) {
+ if (this.event != null) {
+ fail("Multiple events received");
+ }
+ this.event = event;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.events;
+
+import org.apache.fop.events.model.AbstractEventModelFactory;
+import org.apache.fop.events.model.EventModel;
+
+/**
+ * Factory for FOP's test event model.
+ */
+public class FOPTestEventModelFactory extends AbstractEventModelFactory {
+
+ private static final String EVENT_MODEL_FILENAME = "test-event-model.xml";
+
+ /** {@inheritDoc} */
+ public EventModel createEventModel() {
+ return loadModel(getClass(), EVENT_MODEL_FILENAME);
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.events;
+
+public interface TestEventProducer extends EventProducer {
+
+ /**
+ * Complain about something.
+ * @param source the event source
+ * @param reason the reason for the complaint
+ * @param blah the complaint
+ * @event.severity WARN
+ */
+ void complain(Object source, String reason, int blah);
+
+ /**
+ * Express joy about something.
+ * @param source the event source
+ * @param what the cause for the joy
+ * @event.severity INFO
+ */
+ void enjoy(Object source, String what);
+
+ public class Provider {
+
+ public static TestEventProducer get(EventBroadcaster broadcaster) {
+ return (TestEventProducer)broadcaster.getEventProducerFor(TestEventProducer.class);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util;
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.xml.sax.helpers.LocatorImpl;
+
+import org.apache.fop.events.model.EventSeverity;
+import org.apache.fop.util.text.AdvancedMessageFormat;
+
+/**
+ * Tests for EventFormatter.
+ */
+public class AdvancedMessageFormatTestCase extends TestCase {
+
+ public void testFormatting() throws Exception {
+ String msg;
+ AdvancedMessageFormat format;
+
+ String pattern
+ = "Element \"{elementName}\" is missing[ required property \"{propertyName}\"]!";
+ format = new AdvancedMessageFormat(pattern);
+
+ Map params = new java.util.HashMap();
+ params.put("node", new Object());
+ params.put("elementName", "fo:external-graphic");
+ params.put("propertyName", "src");
+
+ msg = format.format(params);
+ assertEquals("Element \"fo:external-graphic\" is missing required property \"src\"!", msg);
+
+ params.remove("propertyName");
+ msg = format.format(params);
+ assertEquals("Element \"fo:external-graphic\" is missing!", msg);
+
+ pattern
+ = "Testing \\{escaped \\[characters\\], now a normal field {elementName}!";
+ format = new AdvancedMessageFormat(pattern);
+ msg = format.format(params);
+ assertEquals("Testing {escaped [characters], now a normal field fo:external-graphic!", msg);
+
+ pattern = "Multi-conditional: [case1: {var1}|case2: {var2}|case3: {var3}]";
+ format = new AdvancedMessageFormat(pattern);
+
+ params = new java.util.HashMap();
+ msg = format.format(params);
+ assertEquals("Multi-conditional: ", msg);
+
+ params.put("var3", "value3");
+ msg = format.format(params);
+ assertEquals("Multi-conditional: case3: value3", msg);
+ params.put("var1", "value1");
+ msg = format.format(params);
+ assertEquals("Multi-conditional: case1: value1", msg);
+ }
+
+ public void testObjectFormatting() throws Exception {
+ String msg;
+ AdvancedMessageFormat format;
+
+ String pattern
+ = "Here's a Locator: {locator}";
+ format = new AdvancedMessageFormat(pattern);
+
+ Map params = new java.util.HashMap();
+ LocatorImpl loc = new LocatorImpl();
+ loc.setColumnNumber(7);
+ loc.setLineNumber(12);
+ params.put("locator", loc);
+
+ msg = format.format(params);
+ assertEquals("Here\'s a Locator: 12:7", msg);
+ }
+
+ public void testIfFormatting() throws Exception {
+ String msg;
+ AdvancedMessageFormat format;
+
+ format = new AdvancedMessageFormat("You are{isBad,if, not} nice!");
+
+ Map params = new java.util.HashMap();
+
+ params.put("isBad", Boolean.FALSE);
+ msg = format.format(params);
+ assertEquals("You are nice!", msg);
+
+ params.put("isBad", Boolean.TRUE);
+ msg = format.format(params);
+ assertEquals("You are not nice!", msg);
+
+ format = new AdvancedMessageFormat("You are{isGood,if, very, not so} nice!");
+
+ params = new java.util.HashMap();
+
+ msg = format.format(params); //isGood is missing
+ assertEquals("You are not so nice!", msg);
+
+ params.put("isGood", Boolean.FALSE);
+ msg = format.format(params);
+ assertEquals("You are not so nice!", msg);
+
+ params.put("isGood", Boolean.TRUE);
+ msg = format.format(params);
+ assertEquals("You are very nice!", msg);
+
+ format = new AdvancedMessageFormat("You are{isGood,if, very\\, very} nice!");
+
+ params = new java.util.HashMap();
+
+ msg = format.format(params); //isGood is missing
+ assertEquals("You are nice!", msg);
+
+ params.put("isGood", Boolean.FALSE);
+ msg = format.format(params);
+ assertEquals("You are nice!", msg);
+
+ params.put("isGood", Boolean.TRUE);
+ msg = format.format(params);
+ assertEquals("You are very, very nice!", msg);
+ }
+
+ public void testEqualsFormatting() throws Exception {
+ String msg;
+ AdvancedMessageFormat format;
+
+ format = new AdvancedMessageFormat(
+ "Error{severity,equals,EventSeverity:FATAL,,\nSome explanation!}");
+
+ Map params = new java.util.HashMap();
+
+ params.put("severity", EventSeverity.FATAL);
+ msg = format.format(params);
+ assertEquals("Error", msg);
+
+ params.put("severity", EventSeverity.WARN);
+ msg = format.format(params);
+ assertEquals("Error\nSome explanation!", msg);
+ }
+
+ public void testChoiceFormatting() throws Exception {
+ String msg;
+ AdvancedMessageFormat format;
+
+ format = new AdvancedMessageFormat(
+ "You have {amount,choice,0#nothing|0<{amount} bucks|100<more than enough}.");
+
+ Map params = new java.util.HashMap();
+
+ params.put("amount", new Integer(0));
+ msg = format.format(params);
+ assertEquals("You have nothing.", msg);
+
+ params.put("amount", new Integer(7));
+ msg = format.format(params);
+ assertEquals("You have 7 bucks.", msg);
+
+ params.put("amount", new Integer(140));
+ msg = format.format(params);
+ assertEquals("You have more than enough.", msg);
+
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for XMLResourceBundle.
+ */
+public class XMLResourceBundleTestCase extends TestCase {
+
+ public void testWithValidFile() throws Exception {
+ ResourceBundle bundle = XMLResourceBundle.getXMLBundle(
+ getClass().getName(), Locale.ENGLISH, getClass().getClassLoader());
+ ResourceBundle bundleDE = XMLResourceBundle.getXMLBundle(
+ getClass().getName(), Locale.GERMAN, getClass().getClassLoader());
+
+ assertEquals("", bundle.getLocale().getLanguage());
+ assertEquals("de", bundleDE.getLocale().getLanguage());
+
+ assertEquals("Hello World!", bundle.getString("hello-world"));
+ assertEquals("Hallo Welt!", bundleDE.getString("hello-world"));
+
+ //Check fallback to English
+ assertEquals("Untranslatable", bundle.getString("untranslatable"));
+ assertEquals("Untranslatable", bundleDE.getString("untranslatable"));
+ }
+
+ public void testWithInvalidFile() throws Exception {
+ try {
+ ResourceBundle bundle = XMLResourceBundle.getXMLBundle(
+ "org.apache.fop.util.invalid-translation-file", getClass().getClassLoader());
+ fail("Expected exception");
+ } catch (MissingResourceException e) {
+ //expected
+ }
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="hello-world">Hello World!</message>
+ <message key="untranslatable">Untranslatable</message>
+</catalogue>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="de">
+ <message key="hello-world">Hallo Welt!</message>
+</catalogue>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key1="hello-world">Hello World!</message>
+ <something>blah</something>
+</catalogue>
\ No newline at end of file