]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-3160: Rename util to avoid conflicts with core
authorSimon Steiner <ssteiner@apache.org>
Tue, 5 Mar 2024 12:24:48 +0000 (12:24 +0000)
committerSimon Steiner <ssteiner@apache.org>
Tue, 5 Mar 2024 12:24:48 +0000 (12:24 +0000)
27 files changed:
fop-core/src/main/java/org/apache/fop/area/AreaTreeParser.java
fop-core/src/main/java/org/apache/fop/fo/FONode.java
fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java
fop-core/src/main/java/org/apache/fop/util/text/ChoiceFieldPart.java
fop-core/src/main/java/org/apache/fop/util/text/EqualsFieldPart.java
fop-core/src/main/java/org/apache/fop/util/text/GlyphNameFieldPart.java
fop-core/src/main/java/org/apache/fop/util/text/HexFieldPart.java
fop-core/src/main/java/org/apache/fop/util/text/IfFieldPart.java
fop-core/src/main/java/org/apache/fop/util/text/LocatorFormatter.java
fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function [deleted file]
fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter [deleted file]
fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory [deleted file]
fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$Function [new file with mode: 0644]
fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$ObjectFormatter [new file with mode: 0644]
fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$PartFactory [new file with mode: 0644]
fop-core/src/test/java/org/apache/fop/util/AdvancedMessageFormatTestCase.java
fop-core/src/test/java/org/apache/fop/util/XMLResourceBundleTestCase.java
fop-core/src/test/java/org/apache/fop/visual/AbstractPSPDFBitmapProducer.java
fop-core/src/test/java/org/apache/fop/visual/BitmapProducerJava2D.java
fop-events/src/main/java/org/apache/fop/events/EventFormatter.java
fop-events/src/main/java/org/apache/fop/events/model/EventModelParser.java
fop-util/src/main/java/org/apache/fop/util/DefaultErrorListener.java [deleted file]
fop-util/src/main/java/org/apache/fop/util/XMLResourceBundle.java [deleted file]
fop-util/src/main/java/org/apache/fop/util/text/AdvancedMessageFormat.java [deleted file]
fop-util/src/main/java/org/apache/fop/utils/DefaultErrorListener.java [new file with mode: 0644]
fop-util/src/main/java/org/apache/fop/utils/XMLResourceBundle.java [new file with mode: 0644]
fop-util/src/main/java/org/apache/fop/utils/text/AdvancedMessageFormat.java [new file with mode: 0644]

index 587e8e4494d2aba45ddccfbc2cf0fc691ac1195e..ca4e097efcca285dbd4d91bc0da39557523b988b 100644 (file)
@@ -87,10 +87,10 @@ import org.apache.fop.util.ColorUtil;
 import org.apache.fop.util.ContentHandlerFactory;
 import org.apache.fop.util.ContentHandlerFactoryRegistry;
 import org.apache.fop.util.ConversionUtils;
-import org.apache.fop.util.DefaultErrorListener;
 import org.apache.fop.util.LanguageTags;
 import org.apache.fop.util.XMLConstants;
 import org.apache.fop.util.XMLUtil;
+import org.apache.fop.utils.DefaultErrorListener;
 
 import static org.apache.fop.fo.Constants.FO_REGION_AFTER;
 import static org.apache.fop.fo.Constants.FO_REGION_BEFORE;
index f3c582dd56603a53445837949761d0c934bfe11d..c6c8cc3ce5435642a3e5767ee00d77d2912150c2 100644 (file)
@@ -47,7 +47,7 @@ import org.apache.fop.fo.flow.ChangeBar;
 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;
+import org.apache.fop.utils.text.AdvancedMessageFormat.Function;
 
 /**
  * Base class for nodes in the XML tree
index a75e61cf3639a0680068ebff2d85b277507c1766..47529d11a2ea5031f87f1bb5d3e30fcc78526996 100644 (file)
@@ -65,9 +65,9 @@ import org.apache.fop.util.ColorUtil;
 import org.apache.fop.util.ContentHandlerFactory;
 import org.apache.fop.util.ContentHandlerFactoryRegistry;
 import org.apache.fop.util.DOMBuilderContentHandlerFactory;
-import org.apache.fop.util.DefaultErrorListener;
 import org.apache.fop.util.LanguageTags;
 import org.apache.fop.util.XMLUtil;
+import org.apache.fop.utils.DefaultErrorListener;
 
 /**
  * This is a parser for the intermediate format XML which converts the intermediate file into
index c87b9c61f5d8e7678bc5b84d1567fc47aa81e713..12b47c8ac78aa103d491c5da07230a034bf65225 100644 (file)
@@ -24,8 +24,9 @@ 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;
+import org.apache.fop.utils.text.AdvancedMessageFormat;
+import org.apache.fop.utils.text.AdvancedMessageFormat.Part;
+import org.apache.fop.utils.text.AdvancedMessageFormat.PartFactory;
 
 /**
  * Defines a "choice" field part that works like {@link ChoiceFormat}.
index 153322f2762117b7949d4167c7e88675430249d4..bafb9b156f5f7230a6aa5c44fe8a9412af724ec1 100644 (file)
@@ -21,8 +21,9 @@ 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;
+import org.apache.fop.utils.text.AdvancedMessageFormat;
+import org.apache.fop.utils.text.AdvancedMessageFormat.Part;
+import org.apache.fop.utils.text.AdvancedMessageFormat.PartFactory;
 
 /**
  * Defines an "equals" field part that can compare a field's string value against another string.
index 48346c13a9c272e7070c8a68e466afb96516ed87..21683b066a68ff30a6c2fa19abd12fbd10055349 100644 (file)
@@ -23,8 +23,8 @@ 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;
+import org.apache.fop.utils.text.AdvancedMessageFormat.Part;
+import org.apache.fop.utils.text.AdvancedMessageFormat.PartFactory;
 
 /**
  * Function formatting a character to a glyph name.
index 075adb00fc18da1ca24f55a115c047be8820437d..61affb4bad8e2234a2d469d20c675ba1b343fbf8 100644 (file)
@@ -21,8 +21,8 @@ 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;
+import org.apache.fop.utils.text.AdvancedMessageFormat.Part;
+import org.apache.fop.utils.text.AdvancedMessageFormat.PartFactory;
 
 /**
  * Function formatting a number or character to a hex value.
index 52c06bad134d4fbb5edb0e768aa0969ed0ff2827..315b2c0de205608d7d0861c71d1ab9d235a05899 100644 (file)
@@ -21,8 +21,9 @@ 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;
+import org.apache.fop.utils.text.AdvancedMessageFormat;
+import org.apache.fop.utils.text.AdvancedMessageFormat.Part;
+import org.apache.fop.utils.text.AdvancedMessageFormat.PartFactory;
 
 /**
  * Defines an "if" field part that checks if field's value is true or false.
index 07e7f2aaa5e5375231ed3964e0a45856de5b16c7..39842610ec32416df10c4848068d1dee2cec0523 100644 (file)
@@ -21,7 +21,7 @@ package org.apache.fop.util.text;
 
 import org.xml.sax.Locator;
 
-import org.apache.fop.util.text.AdvancedMessageFormat.ObjectFormatter;
+import org.apache.fop.utils.text.AdvancedMessageFormat.ObjectFormatter;
 
 /**
  * Object formatter for the SAX Locator object.
diff --git a/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function b/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function
deleted file mode 100644 (file)
index 375130f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.fop.fo.FONode$GatherContextInfoFunction
diff --git a/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter b/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter
deleted file mode 100644 (file)
index 9e3860b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.fop.util.text.LocatorFormatter
diff --git a/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory b/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory
deleted file mode 100644 (file)
index 1647fb8..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-org.apache.fop.util.text.IfFieldPart$Factory
-org.apache.fop.util.text.EqualsFieldPart$Factory
-org.apache.fop.util.text.ChoiceFieldPart$Factory
-org.apache.fop.util.text.HexFieldPart$Factory
-org.apache.fop.util.text.GlyphNameFieldPart$Factory
-org.apache.fop.events.EventFormatter$LookupFieldPartFactory
diff --git a/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$Function b/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$Function
new file mode 100644 (file)
index 0000000..375130f
--- /dev/null
@@ -0,0 +1 @@
+org.apache.fop.fo.FONode$GatherContextInfoFunction
diff --git a/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$ObjectFormatter b/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$ObjectFormatter
new file mode 100644 (file)
index 0000000..9e3860b
--- /dev/null
@@ -0,0 +1 @@
+org.apache.fop.util.text.LocatorFormatter
diff --git a/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$PartFactory b/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$PartFactory
new file mode 100644 (file)
index 0000000..1647fb8
--- /dev/null
@@ -0,0 +1,6 @@
+org.apache.fop.util.text.IfFieldPart$Factory
+org.apache.fop.util.text.EqualsFieldPart$Factory
+org.apache.fop.util.text.ChoiceFieldPart$Factory
+org.apache.fop.util.text.HexFieldPart$Factory
+org.apache.fop.util.text.GlyphNameFieldPart$Factory
+org.apache.fop.events.EventFormatter$LookupFieldPartFactory
index f0d965cd9c2da1ffe19bfab0b1120cf7c997c7f2..3a8712ceef7ee40d2c2aaff17294db71ac015fd3 100644 (file)
@@ -27,7 +27,7 @@ import org.xml.sax.helpers.LocatorImpl;
 import static org.junit.Assert.assertEquals;
 
 import org.apache.fop.events.model.EventSeverity;
-import org.apache.fop.util.text.AdvancedMessageFormat;
+import org.apache.fop.utils.text.AdvancedMessageFormat;
 
 /**
  * Tests for EventFormatter.
index 60aa16d83c44f51d22262eab25e41712c4f72371..d67c8da5664d9c7f26abee27f31d441780cd3878 100644 (file)
@@ -28,6 +28,8 @@ import org.junit.Test;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
+import org.apache.fop.utils.XMLResourceBundle;
+
 /**
  * Tests for XMLResourceBundle.
  */
index 5edae8074d1fe57d92bcf8ee022a5a6f36a5ce0a..b6a9147f3bf74c5aa522912002a09e5f37ae94a9 100644 (file)
@@ -40,7 +40,7 @@ import org.apache.fop.apps.FopFactory;
 import org.apache.fop.configuration.Configurable;
 import org.apache.fop.configuration.Configuration;
 import org.apache.fop.configuration.ConfigurationException;
-import org.apache.fop.util.DefaultErrorListener;
+import org.apache.fop.utils.DefaultErrorListener;
 
 /**
  * BitmapProducer implementation that uses the PS or PDF renderer and an external converter
index ae3b097cb41b31edfee4ec9720176162dc3bd3c2..90b810dad6a125d12b3f17e21bf7d462037d5785 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.fop.apps.MimeConstants;
 import org.apache.fop.configuration.Configurable;
 import org.apache.fop.configuration.Configuration;
 import org.apache.fop.configuration.ConfigurationException;
-import org.apache.fop.util.DefaultErrorListener;
+import org.apache.fop.utils.DefaultErrorListener;
 
 /**
  * BitmapProducer implementation that uses the Java2DRenderer to create bitmaps.
index 7f56c83963a51d866a80ddc1f9bde8c1d11a87c9..1c1698c4ea3810ae0d978569f5da42e953bb181f 100644 (file)
@@ -29,10 +29,10 @@ 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;
+import org.apache.fop.utils.XMLResourceBundle;
+import org.apache.fop.utils.text.AdvancedMessageFormat;
+import org.apache.fop.utils.text.AdvancedMessageFormat.Part;
+import org.apache.fop.utils.text.AdvancedMessageFormat.PartFactory;
 
 /**
  * Converts events into human-readable, localized messages.
index 5441d1c4fe4351b327f1c611359a090d692ca858..7d3b7a03c4eed8d78b0f7f889321c6972b6980a9 100644 (file)
@@ -35,7 +35,7 @@ import org.xml.sax.helpers.DefaultHandler;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import org.apache.fop.util.DefaultErrorListener;
+import org.apache.fop.utils.DefaultErrorListener;
 
 /**
  * This is a parser for the event model XML.
diff --git a/fop-util/src/main/java/org/apache/fop/util/DefaultErrorListener.java b/fop-util/src/main/java/org/apache/fop/util/DefaultErrorListener.java
deleted file mode 100644 (file)
index 3c554af..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 javax.xml.transform.ErrorListener;
-import javax.xml.transform.TransformerException;
-
-import org.apache.commons.logging.Log;
-
-/**
- * Standard ErrorListener implementation for in-FOP use. Some Xalan-J versions don't properly
- * re-throw exceptions.
- */
-public class DefaultErrorListener implements ErrorListener {
-
-    private Log log;
-
-    /**
-     * Main constructor
-     * @param log the log instance to send log events to
-     */
-    public DefaultErrorListener(Log log) {
-        this.log = log;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void warning(TransformerException exc) {
-        log.warn(exc.toString());
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void error(TransformerException exc) throws TransformerException {
-        throw exc;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void fatalError(TransformerException exc)
-            throws TransformerException {
-        throw exc;
-    }
-
-}
diff --git a/fop-util/src/main/java/org/apache/fop/util/XMLResourceBundle.java b/fop-util/src/main/java/org/apache/fop/util/XMLResourceBundle.java
deleted file mode 100644 (file)
index 80aec2d..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* $Id$ */
-
-package org.apache.fop.util;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Locale;
-import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.Properties;
-import java.util.ResourceBundle;
-import java.util.Stack;
-
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.sax.SAXResult;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.stream.StreamSource;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import org.apache.xmlgraphics.util.QName;
-
-/**
- * This class is a ResourceBundle that loads its contents from XML files instead of properties
- * files (like PropertiesResourceBundle).
- * <p>
- * The XML format for this resource bundle implementation is the following
- * (the same as Apache Cocoon's XMLResourceBundle):
- * <pre>
- * &lt;catalogue xml:lang="en"&gt;
- *   &lt;message key="key1"&gt;Message &lt;br/&gt; Value 1&lt;/message&gt;
- *   &lt;message key="key2"&gt;Message &lt;br/&gt; Value 1&lt;/message&gt;
- *   ...
- * &lt;/catalogue&gt;
- * </pre>
- */
-public class XMLResourceBundle extends ResourceBundle {
-
-    //Note: Some code here has been copied and adapted from Apache Harmony!
-
-    private Properties resources = new Properties();
-
-    private Locale locale;
-
-    private static SAXTransformerFactory tFactory
-        = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
-
-    /**
-     * Creates a resource bundle from an InputStream.
-     * @param in the stream to read from
-     * @throws IOException if an I/O error occurs
-     */
-    public XMLResourceBundle(InputStream in) throws IOException {
-        try {
-            Transformer transformer = tFactory.newTransformer();
-            StreamSource src = new StreamSource(in);
-            SAXResult res = new SAXResult(new CatalogueHandler());
-            transformer.transform(src, res);
-        } catch (TransformerException e) {
-            throw new IOException("Error while parsing XML resource bundle: " + e.getMessage());
-        }
-    }
-
-    /**
-     * Gets a resource bundle using the specified base name, default locale, and class loader.
-     * @param baseName the base name of the resource bundle, a fully qualified class name
-     * @param loader the class loader from which to load the resource bundle
-     * @return a resource bundle for the given base name and the default locale
-     * @throws MissingResourceException if no resource bundle for the specified base name can be
-     *                                          found
-     * @see java.util.ResourceBundle#getBundle(String)
-     */
-    public static ResourceBundle getXMLBundle(String baseName, ClassLoader loader)
-                throws MissingResourceException {
-        return getXMLBundle(baseName, Locale.getDefault(), loader);
-    }
-
-    /**
-     * Gets a resource bundle using the specified base name, locale, and class loader.
-     * @param baseName the base name of the resource bundle, a fully qualified class name
-     * @param locale the locale for which a resource bundle is desired
-     * @param loader the class loader from which to load the resource bundle
-     * @return a resource bundle for the given base name and locale
-     * @throws MissingResourceException if no resource bundle for the specified base name can be
-     *                                          found
-     * @see java.util.ResourceBundle#getBundle(String, Locale, ClassLoader)
-     */
-    public static ResourceBundle getXMLBundle(String baseName, Locale locale, ClassLoader loader)
-                throws MissingResourceException {
-        if (loader == null) {
-            throw new NullPointerException("loader must not be null");
-        }
-        if (baseName == null) {
-            throw new NullPointerException("baseName must not be null");
-        }
-        assert locale != 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 = "";
-        String country = "";
-        String 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;
-
-        private boolean isOwnNamespace(String uri) {
-            return ("".equals(uri));
-        }
-
-        private QName getParentElementName() {
-            return (QName)elementStack.peek();
-        }
-
-        /** {@inheritDoc} */
-        public void startElement(String uri, String localName, String qName,
-                Attributes atts) throws SAXException {
-            super.startElement(uri, localName, qName, atts);
-            QName elementName = new QName(uri, qName);
-            if (isOwnNamespace(uri)) {
-                if (CATALOGUE.equals(localName)) {
-                    //nop
-                } else if (MESSAGE.equals(localName)) {
-                    if (!CATALOGUE.equals(getParentElementName().getLocalName())) {
-                        throw new SAXException(MESSAGE + " must be a child of " + CATALOGUE);
-                    }
-                    this.currentKey = atts.getValue("key");
-                } else {
-                    throw new SAXException("Invalid element name: " + elementName);
-                }
-            } else {
-                //ignore
-            }
-            this.valueBuffer.setLength(0);
-            elementStack.push(elementName);
-        }
-
-        /** {@inheritDoc} */
-        public void endElement(String uri, String localName, String qName) throws SAXException {
-            super.endElement(uri, localName, qName);
-            elementStack.pop();
-            if (isOwnNamespace(uri)) {
-                if (CATALOGUE.equals(localName)) {
-                    //nop
-                } else if (MESSAGE.equals(localName)) {
-                    if (this.currentKey == null) {
-                        throw new SAXException(
-                                "current key is null (attribute 'key' might be mistyped)");
-                    }
-                    resources.put(this.currentKey, this.valueBuffer.toString());
-                    this.currentKey = null;
-                }
-            } else {
-                //ignore
-            }
-            this.valueBuffer.setLength(0);
-        }
-
-        /** {@inheritDoc} */
-        public void characters(char[] ch, int start, int length) throws SAXException {
-            super.characters(ch, start, length);
-            valueBuffer.append(ch, start, length);
-        }
-
-    }
-
-}
diff --git a/fop-util/src/main/java/org/apache/fop/util/text/AdvancedMessageFormat.java b/fop-util/src/main/java/org/apache/fop/util/text/AdvancedMessageFormat.java
deleted file mode 100644 (file)
index dafc374..0000000
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * 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.text.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<String, PartFactory> PART_FACTORIES
-        = new java.util.HashMap<String, PartFactory>();
-    private static final List<ObjectFormatter> OBJECT_FORMATTERS
-        = new java.util.ArrayList<ObjectFormatter>();
-    private static final Map<Object, Function> FUNCTIONS
-        = new java.util.HashMap<Object, Function>();
-
-    private CompositePart rootPart;
-
-    static {
-        Iterator<Object> iter;
-        iter = Service.providers(PartFactory.class);
-        while (iter.hasNext()) {
-            PartFactory factory = (PartFactory)iter.next();
-            PART_FACTORIES.put(factory.getFormat(), factory);
-        }
-        iter = Service.providers(ObjectFormatter.class);
-        while (iter.hasNext()) {
-            OBJECT_FORMATTERS.add((ObjectFormatter)iter.next());
-        }
-        iter = Service.providers(Function.class);
-        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);
-                }
-                sb.append(ch);
-                i++;
-                break;
-            default:
-                sb.append(ch);
-                i++;
-                break;
-            }
-        }
-        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 = 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 FUNCTIONS.get(functionName);
-    }
-
-    /**
-     * Formats a message with the given parameters.
-     * @param params a Map of named parameters (Contents: &lt;String, Object&gt;)
-     * @return the formatted message
-     */
-    public String format(Map<String, Object> 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: &lt;String, Object&gt;)
-     * @param target the target StringBuffer to write the formatted message to
-     */
-    public void format(Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> params) {
-            sb.append(text);
-        }
-
-        public boolean isGenerated(Map<String, Object> 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<String, Object> 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<String, Object> 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;
-            for (ObjectFormatter formatter : OBJECT_FORMATTERS) {
-                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<String, Object> params) {
-            Object obj = this.function.evaluate(params);
-            formatObject(obj, sb);
-        }
-
-        public boolean isGenerated(Map<String, Object> 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<Part> parts = new java.util.ArrayList<Part>();
-        private boolean conditional;
-        private boolean hasSections;
-
-        public CompositePart(boolean conditional) {
-            this.conditional = conditional;
-        }
-
-        private CompositePart(List<Part> 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<Part> p = this.parts;
-                //Dropping into a different mode...
-                this.parts = new java.util.ArrayList<Part>();
-                this.parts.add(new CompositePart(p));
-                hasSections = true;
-            }
-            this.parts.add(new CompositePart(true));
-        }
-
-        public void write(StringBuffer sb, Map<String, Object> params) {
-            if (hasSections) {
-                for (Part part : this.parts) {
-                    if (part.isGenerated(params)) {
-                        part.write(sb, params);
-                        break;
-                    }
-                }
-            } else {
-                if (isGenerated(params)) {
-                    for (Part part : this.parts) {
-                        part.write(sb, params);
-                    }
-                }
-            }
-        }
-
-        public boolean isGenerated(Map<String, Object> params) {
-            if (hasSections) {
-                for (Part part : this.parts) {
-                    if (part.isGenerated(params)) {
-                        return true;
-                    }
-                }
-                return false;
-            } else {
-                if (conditional) {
-                    for (Part part : this.parts) {
-                        if (!part.isGenerated(params)) {
-                            return false;
-                        }
-                    }
-                }
-                return true;
-            }
-        }
-
-        /** {@inheritDoc} */
-        public String toString() {
-            return this.parts.toString();
-        }
-    }
-
-
-    static String unescapeComma(String string) {
-        return string.replaceAll("\\\\,", ",");
-    }
-}
diff --git a/fop-util/src/main/java/org/apache/fop/utils/DefaultErrorListener.java b/fop-util/src/main/java/org/apache/fop/utils/DefaultErrorListener.java
new file mode 100644 (file)
index 0000000..64ca98d
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.utils;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Standard ErrorListener implementation for in-FOP use. Some Xalan-J versions don't properly
+ * re-throw exceptions.
+ */
+public class DefaultErrorListener implements ErrorListener {
+
+    private Log log;
+
+    /**
+     * Main constructor
+     * @param log the log instance to send log events to
+     */
+    public DefaultErrorListener(Log log) {
+        this.log = log;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void warning(TransformerException exc) {
+        log.warn(exc.toString());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void error(TransformerException exc) throws TransformerException {
+        throw exc;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void fatalError(TransformerException exc)
+            throws TransformerException {
+        throw exc;
+    }
+
+}
diff --git a/fop-util/src/main/java/org/apache/fop/utils/XMLResourceBundle.java b/fop-util/src/main/java/org/apache/fop/utils/XMLResourceBundle.java
new file mode 100644 (file)
index 0000000..747284e
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * 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.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Stack;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.xmlgraphics.util.QName;
+
+/**
+ * This class is a ResourceBundle that loads its contents from XML files instead of properties
+ * files (like PropertiesResourceBundle).
+ * <p>
+ * The XML format for this resource bundle implementation is the following
+ * (the same as Apache Cocoon's XMLResourceBundle):
+ * <pre>
+ * &lt;catalogue xml:lang="en"&gt;
+ *   &lt;message key="key1"&gt;Message &lt;br/&gt; Value 1&lt;/message&gt;
+ *   &lt;message key="key2"&gt;Message &lt;br/&gt; Value 1&lt;/message&gt;
+ *   ...
+ * &lt;/catalogue&gt;
+ * </pre>
+ */
+public class XMLResourceBundle extends ResourceBundle {
+
+    //Note: Some code here has been copied and adapted from Apache Harmony!
+
+    private Properties resources = new Properties();
+
+    private Locale locale;
+
+    private static SAXTransformerFactory tFactory
+        = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+    /**
+     * Creates a resource bundle from an InputStream.
+     * @param in the stream to read from
+     * @throws IOException if an I/O error occurs
+     */
+    public XMLResourceBundle(InputStream in) throws IOException {
+        try {
+            Transformer transformer = tFactory.newTransformer();
+            StreamSource src = new StreamSource(in);
+            SAXResult res = new SAXResult(new CatalogueHandler());
+            transformer.transform(src, res);
+        } catch (TransformerException e) {
+            throw new IOException("Error while parsing XML resource bundle: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Gets a resource bundle using the specified base name, default locale, and class loader.
+     * @param baseName the base name of the resource bundle, a fully qualified class name
+     * @param loader the class loader from which to load the resource bundle
+     * @return a resource bundle for the given base name and the default locale
+     * @throws MissingResourceException if no resource bundle for the specified base name can be
+     *                                          found
+     * @see java.util.ResourceBundle#getBundle(String)
+     */
+    public static ResourceBundle getXMLBundle(String baseName, ClassLoader loader)
+                throws MissingResourceException {
+        return getXMLBundle(baseName, Locale.getDefault(), loader);
+    }
+
+    /**
+     * Gets a resource bundle using the specified base name, locale, and class loader.
+     * @param baseName the base name of the resource bundle, a fully qualified class name
+     * @param locale the locale for which a resource bundle is desired
+     * @param loader the class loader from which to load the resource bundle
+     * @return a resource bundle for the given base name and locale
+     * @throws MissingResourceException if no resource bundle for the specified base name can be
+     *                                          found
+     * @see java.util.ResourceBundle#getBundle(String, Locale, ClassLoader)
+     */
+    public static ResourceBundle getXMLBundle(String baseName, Locale locale, ClassLoader loader)
+                throws MissingResourceException {
+        if (loader == null) {
+            throw new NullPointerException("loader must not be null");
+        }
+        if (baseName == null) {
+            throw new NullPointerException("baseName must not be null");
+        }
+        assert locale != 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 = "";
+        String country = "";
+        String 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;
+
+        private boolean isOwnNamespace(String uri) {
+            return ("".equals(uri));
+        }
+
+        private QName getParentElementName() {
+            return (QName)elementStack.peek();
+        }
+
+        /** {@inheritDoc} */
+        public void startElement(String uri, String localName, String qName,
+                Attributes atts) throws SAXException {
+            super.startElement(uri, localName, qName, atts);
+            QName elementName = new QName(uri, qName);
+            if (isOwnNamespace(uri)) {
+                if (CATALOGUE.equals(localName)) {
+                    //nop
+                } else if (MESSAGE.equals(localName)) {
+                    if (!CATALOGUE.equals(getParentElementName().getLocalName())) {
+                        throw new SAXException(MESSAGE + " must be a child of " + CATALOGUE);
+                    }
+                    this.currentKey = atts.getValue("key");
+                } else {
+                    throw new SAXException("Invalid element name: " + elementName);
+                }
+            } else {
+                //ignore
+            }
+            this.valueBuffer.setLength(0);
+            elementStack.push(elementName);
+        }
+
+        /** {@inheritDoc} */
+        public void endElement(String uri, String localName, String qName) throws SAXException {
+            super.endElement(uri, localName, qName);
+            elementStack.pop();
+            if (isOwnNamespace(uri)) {
+                if (CATALOGUE.equals(localName)) {
+                    //nop
+                } else if (MESSAGE.equals(localName)) {
+                    if (this.currentKey == null) {
+                        throw new SAXException(
+                                "current key is null (attribute 'key' might be mistyped)");
+                    }
+                    resources.put(this.currentKey, this.valueBuffer.toString());
+                    this.currentKey = null;
+                }
+            } else {
+                //ignore
+            }
+            this.valueBuffer.setLength(0);
+        }
+
+        /** {@inheritDoc} */
+        public void characters(char[] ch, int start, int length) throws SAXException {
+            super.characters(ch, start, length);
+            valueBuffer.append(ch, start, length);
+        }
+
+    }
+
+}
diff --git a/fop-util/src/main/java/org/apache/fop/utils/text/AdvancedMessageFormat.java b/fop-util/src/main/java/org/apache/fop/utils/text/AdvancedMessageFormat.java
new file mode 100644 (file)
index 0000000..5ebf33f
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * 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.utils.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.text.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) */
+    public static final Pattern COMMA_SEPARATOR_REGEX = Pattern.compile("(?<!\\\\),");
+
+    private static final Map<String, PartFactory> PART_FACTORIES
+        = new java.util.HashMap<String, PartFactory>();
+    private static final List<ObjectFormatter> OBJECT_FORMATTERS
+        = new java.util.ArrayList<ObjectFormatter>();
+    private static final Map<Object, Function> FUNCTIONS
+        = new java.util.HashMap<Object, Function>();
+
+    private CompositePart rootPart;
+
+    static {
+        Iterator<Object> iter;
+        iter = Service.providers(PartFactory.class);
+        while (iter.hasNext()) {
+            PartFactory factory = (PartFactory)iter.next();
+            PART_FACTORIES.put(factory.getFormat(), factory);
+        }
+        iter = Service.providers(ObjectFormatter.class);
+        while (iter.hasNext()) {
+            OBJECT_FORMATTERS.add((ObjectFormatter)iter.next());
+        }
+        iter = Service.providers(Function.class);
+        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);
+                }
+                sb.append(ch);
+                i++;
+                break;
+            default:
+                sb.append(ch);
+                i++;
+                break;
+            }
+        }
+        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 = 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 FUNCTIONS.get(functionName);
+    }
+
+    /**
+     * Formats a message with the given parameters.
+     * @param params a Map of named parameters (Contents: &lt;String, Object&gt;)
+     * @return the formatted message
+     */
+    public String format(Map<String, Object> 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: &lt;String, Object&gt;)
+     * @param target the target StringBuffer to write the formatted message to
+     */
+    public void format(Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> params) {
+            sb.append(text);
+        }
+
+        public boolean isGenerated(Map<String, Object> 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<String, Object> 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<String, Object> 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;
+            for (ObjectFormatter formatter : OBJECT_FORMATTERS) {
+                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<String, Object> params) {
+            Object obj = this.function.evaluate(params);
+            formatObject(obj, sb);
+        }
+
+        public boolean isGenerated(Map<String, Object> 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<Part> parts = new java.util.ArrayList<Part>();
+        private boolean conditional;
+        private boolean hasSections;
+
+        public CompositePart(boolean conditional) {
+            this.conditional = conditional;
+        }
+
+        private CompositePart(List<Part> 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<Part> p = this.parts;
+                //Dropping into a different mode...
+                this.parts = new java.util.ArrayList<Part>();
+                this.parts.add(new CompositePart(p));
+                hasSections = true;
+            }
+            this.parts.add(new CompositePart(true));
+        }
+
+        public void write(StringBuffer sb, Map<String, Object> params) {
+            if (hasSections) {
+                for (Part part : this.parts) {
+                    if (part.isGenerated(params)) {
+                        part.write(sb, params);
+                        break;
+                    }
+                }
+            } else {
+                if (isGenerated(params)) {
+                    for (Part part : this.parts) {
+                        part.write(sb, params);
+                    }
+                }
+            }
+        }
+
+        public boolean isGenerated(Map<String, Object> params) {
+            if (hasSections) {
+                for (Part part : this.parts) {
+                    if (part.isGenerated(params)) {
+                        return true;
+                    }
+                }
+                return false;
+            } else {
+                if (conditional) {
+                    for (Part part : this.parts) {
+                        if (!part.isGenerated(params)) {
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            }
+        }
+
+        /** {@inheritDoc} */
+        public String toString() {
+            return this.parts.toString();
+        }
+    }
+
+
+    public static String unescapeComma(String string) {
+        return string.replaceAll("\\\\,", ",");
+    }
+}