From d3ef77c7d66c49c84df92d5863dae49beaeca708 Mon Sep 17 00:00:00 2001 From: Simon Steiner Date: Tue, 5 Mar 2024 12:24:48 +0000 Subject: FOP-3160: Rename util to avoid conflicts with core --- .../java/org/apache/fop/area/AreaTreeParser.java | 2 +- .../src/main/java/org/apache/fop/fo/FONode.java | 2 +- .../apache/fop/render/intermediate/IFParser.java | 2 +- .../org/apache/fop/util/text/ChoiceFieldPart.java | 5 +- .../org/apache/fop/util/text/EqualsFieldPart.java | 5 +- .../apache/fop/util/text/GlyphNameFieldPart.java | 4 +- .../org/apache/fop/util/text/HexFieldPart.java | 4 +- .../java/org/apache/fop/util/text/IfFieldPart.java | 5 +- .../org/apache/fop/util/text/LocatorFormatter.java | 2 +- ...he.fop.util.text.AdvancedMessageFormat$Function | 1 - ...util.text.AdvancedMessageFormat$ObjectFormatter | 1 - ...fop.util.text.AdvancedMessageFormat$PartFactory | 6 - ...e.fop.utils.text.AdvancedMessageFormat$Function | 1 + ...tils.text.AdvancedMessageFormat$ObjectFormatter | 1 + ...op.utils.text.AdvancedMessageFormat$PartFactory | 6 + .../fop/util/AdvancedMessageFormatTestCase.java | 2 +- .../apache/fop/util/XMLResourceBundleTestCase.java | 2 + .../fop/visual/AbstractPSPDFBitmapProducer.java | 2 +- .../apache/fop/visual/BitmapProducerJava2D.java | 2 +- .../java/org/apache/fop/events/EventFormatter.java | 8 +- .../apache/fop/events/model/EventModelParser.java | 2 +- .../org/apache/fop/util/DefaultErrorListener.java | 65 --- .../org/apache/fop/util/XMLResourceBundle.java | 400 ----------------- .../fop/util/text/AdvancedMessageFormat.java | 483 --------------------- .../org/apache/fop/utils/DefaultErrorListener.java | 65 +++ .../org/apache/fop/utils/XMLResourceBundle.java | 400 +++++++++++++++++ .../fop/utils/text/AdvancedMessageFormat.java | 483 +++++++++++++++++++++ 27 files changed, 983 insertions(+), 978 deletions(-) delete mode 100644 fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function delete mode 100644 fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter delete mode 100644 fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory create mode 100644 fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$Function create mode 100644 fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$ObjectFormatter create mode 100644 fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$PartFactory delete mode 100644 fop-util/src/main/java/org/apache/fop/util/DefaultErrorListener.java delete mode 100644 fop-util/src/main/java/org/apache/fop/util/XMLResourceBundle.java delete mode 100644 fop-util/src/main/java/org/apache/fop/util/text/AdvancedMessageFormat.java create mode 100644 fop-util/src/main/java/org/apache/fop/utils/DefaultErrorListener.java create mode 100644 fop-util/src/main/java/org/apache/fop/utils/XMLResourceBundle.java create mode 100644 fop-util/src/main/java/org/apache/fop/utils/text/AdvancedMessageFormat.java diff --git a/fop-core/src/main/java/org/apache/fop/area/AreaTreeParser.java b/fop-core/src/main/java/org/apache/fop/area/AreaTreeParser.java index 587e8e449..ca4e097ef 100644 --- a/fop-core/src/main/java/org/apache/fop/area/AreaTreeParser.java +++ b/fop-core/src/main/java/org/apache/fop/area/AreaTreeParser.java @@ -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; diff --git a/fop-core/src/main/java/org/apache/fop/fo/FONode.java b/fop-core/src/main/java/org/apache/fop/fo/FONode.java index f3c582dd5..c6c8cc3ce 100644 --- a/fop-core/src/main/java/org/apache/fop/fo/FONode.java +++ b/fop-core/src/main/java/org/apache/fop/fo/FONode.java @@ -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 diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java index a75e61cf3..47529d11a 100644 --- a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java +++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java @@ -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 diff --git a/fop-core/src/main/java/org/apache/fop/util/text/ChoiceFieldPart.java b/fop-core/src/main/java/org/apache/fop/util/text/ChoiceFieldPart.java index c87b9c61f..12b47c8ac 100644 --- a/fop-core/src/main/java/org/apache/fop/util/text/ChoiceFieldPart.java +++ b/fop-core/src/main/java/org/apache/fop/util/text/ChoiceFieldPart.java @@ -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}. diff --git a/fop-core/src/main/java/org/apache/fop/util/text/EqualsFieldPart.java b/fop-core/src/main/java/org/apache/fop/util/text/EqualsFieldPart.java index 153322f27..bafb9b156 100644 --- a/fop-core/src/main/java/org/apache/fop/util/text/EqualsFieldPart.java +++ b/fop-core/src/main/java/org/apache/fop/util/text/EqualsFieldPart.java @@ -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. diff --git a/fop-core/src/main/java/org/apache/fop/util/text/GlyphNameFieldPart.java b/fop-core/src/main/java/org/apache/fop/util/text/GlyphNameFieldPart.java index 48346c13a..21683b066 100644 --- a/fop-core/src/main/java/org/apache/fop/util/text/GlyphNameFieldPart.java +++ b/fop-core/src/main/java/org/apache/fop/util/text/GlyphNameFieldPart.java @@ -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. diff --git a/fop-core/src/main/java/org/apache/fop/util/text/HexFieldPart.java b/fop-core/src/main/java/org/apache/fop/util/text/HexFieldPart.java index 075adb00f..61affb4ba 100644 --- a/fop-core/src/main/java/org/apache/fop/util/text/HexFieldPart.java +++ b/fop-core/src/main/java/org/apache/fop/util/text/HexFieldPart.java @@ -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. diff --git a/fop-core/src/main/java/org/apache/fop/util/text/IfFieldPart.java b/fop-core/src/main/java/org/apache/fop/util/text/IfFieldPart.java index 52c06bad1..315b2c0de 100644 --- a/fop-core/src/main/java/org/apache/fop/util/text/IfFieldPart.java +++ b/fop-core/src/main/java/org/apache/fop/util/text/IfFieldPart.java @@ -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. diff --git a/fop-core/src/main/java/org/apache/fop/util/text/LocatorFormatter.java b/fop-core/src/main/java/org/apache/fop/util/text/LocatorFormatter.java index 07e7f2aaa..39842610e 100644 --- a/fop-core/src/main/java/org/apache/fop/util/text/LocatorFormatter.java +++ b/fop-core/src/main/java/org/apache/fop/util/text/LocatorFormatter.java @@ -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 index 375130f2a..000000000 --- a/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$Function +++ /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 index 9e3860b31..000000000 --- a/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$ObjectFormatter +++ /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 index 1647fb8d5..000000000 --- a/fop-core/src/main/resources/META-INF/services/org.apache.fop.util.text.AdvancedMessageFormat$PartFactory +++ /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 index 000000000..375130f2a --- /dev/null +++ b/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$Function @@ -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 index 000000000..9e3860b31 --- /dev/null +++ b/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$ObjectFormatter @@ -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 index 000000000..1647fb8d5 --- /dev/null +++ b/fop-core/src/main/resources/META-INF/services/org.apache.fop.utils.text.AdvancedMessageFormat$PartFactory @@ -0,0 +1,6 @@ +org.apache.fop.util.text.IfFieldPart$Factory +org.apache.fop.util.text.EqualsFieldPart$Factory +org.apache.fop.util.text.ChoiceFieldPart$Factory +org.apache.fop.util.text.HexFieldPart$Factory +org.apache.fop.util.text.GlyphNameFieldPart$Factory +org.apache.fop.events.EventFormatter$LookupFieldPartFactory diff --git a/fop-core/src/test/java/org/apache/fop/util/AdvancedMessageFormatTestCase.java b/fop-core/src/test/java/org/apache/fop/util/AdvancedMessageFormatTestCase.java index f0d965cd9..3a8712cee 100644 --- a/fop-core/src/test/java/org/apache/fop/util/AdvancedMessageFormatTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/util/AdvancedMessageFormatTestCase.java @@ -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. diff --git a/fop-core/src/test/java/org/apache/fop/util/XMLResourceBundleTestCase.java b/fop-core/src/test/java/org/apache/fop/util/XMLResourceBundleTestCase.java index 60aa16d83..d67c8da56 100644 --- a/fop-core/src/test/java/org/apache/fop/util/XMLResourceBundleTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/util/XMLResourceBundleTestCase.java @@ -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. */ diff --git a/fop-core/src/test/java/org/apache/fop/visual/AbstractPSPDFBitmapProducer.java b/fop-core/src/test/java/org/apache/fop/visual/AbstractPSPDFBitmapProducer.java index 5edae8074..b6a9147f3 100644 --- a/fop-core/src/test/java/org/apache/fop/visual/AbstractPSPDFBitmapProducer.java +++ b/fop-core/src/test/java/org/apache/fop/visual/AbstractPSPDFBitmapProducer.java @@ -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 diff --git a/fop-core/src/test/java/org/apache/fop/visual/BitmapProducerJava2D.java b/fop-core/src/test/java/org/apache/fop/visual/BitmapProducerJava2D.java index ae3b097cb..90b810dad 100644 --- a/fop-core/src/test/java/org/apache/fop/visual/BitmapProducerJava2D.java +++ b/fop-core/src/test/java/org/apache/fop/visual/BitmapProducerJava2D.java @@ -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. diff --git a/fop-events/src/main/java/org/apache/fop/events/EventFormatter.java b/fop-events/src/main/java/org/apache/fop/events/EventFormatter.java index 7f56c8396..1c1698c4e 100644 --- a/fop-events/src/main/java/org/apache/fop/events/EventFormatter.java +++ b/fop-events/src/main/java/org/apache/fop/events/EventFormatter.java @@ -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. diff --git a/fop-events/src/main/java/org/apache/fop/events/model/EventModelParser.java b/fop-events/src/main/java/org/apache/fop/events/model/EventModelParser.java index 5441d1c4f..7d3b7a03c 100644 --- a/fop-events/src/main/java/org/apache/fop/events/model/EventModelParser.java +++ b/fop-events/src/main/java/org/apache/fop/events/model/EventModelParser.java @@ -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 index 3c554afc7..000000000 --- a/fop-util/src/main/java/org/apache/fop/util/DefaultErrorListener.java +++ /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 index 80aec2d2f..000000000 --- a/fop-util/src/main/java/org/apache/fop/util/XMLResourceBundle.java +++ /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). - *

- * The XML format for this resource bundle implementation is the following - * (the same as Apache Cocoon's XMLResourceBundle): - *

- * <catalogue xml:lang="en">
- *   <message key="key1">Message <br/> Value 1</message>
- *   <message key="key2">Message <br/> Value 1</message>
- *   ...
- * </catalogue>
- * 
- */ -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(); - //> - - 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; // - 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 index dafc3741b..000000000 --- a/fop-util/src/main/java/org/apache/fop/util/text/AdvancedMessageFormat.java +++ /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. - *

- * Example: - *

- *

Missing field "{fieldName}"[ at location: {location}]!

- *
    - *
  • Curly brackets ("{}") are used for fields.
  • - *
  • 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.
  • - *
- */ -public class AdvancedMessageFormat { - - /** Regex that matches "," but not "\," (escaped comma) */ - static final Pattern COMMA_SEPARATOR_REGEX = Pattern.compile("(? 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); - 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: <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 toString() 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 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; - - 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) { - 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 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 index 000000000..64ca98d1b --- /dev/null +++ b/fop-util/src/main/java/org/apache/fop/utils/DefaultErrorListener.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.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 index 000000000..747284ec4 --- /dev/null +++ b/fop-util/src/main/java/org/apache/fop/utils/XMLResourceBundle.java @@ -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). + *

+ * The XML format for this resource bundle implementation is the following + * (the same as Apache Cocoon's XMLResourceBundle): + *

+ * <catalogue xml:lang="en">
+ *   <message key="key1">Message <br/> Value 1</message>
+ *   <message key="key2">Message <br/> Value 1</message>
+ *   ...
+ * </catalogue>
+ * 
+ */ +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(); + //> + + 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; // + 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 index 000000000..5ebf33fcb --- /dev/null +++ b/fop-util/src/main/java/org/apache/fop/utils/text/AdvancedMessageFormat.java @@ -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. + *

+ * Example: + *

+ *

Missing field "{fieldName}"[ at location: {location}]!

+ *
    + *
  • Curly brackets ("{}") are used for fields.
  • + *
  • 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.
  • + *
+ */ +public class AdvancedMessageFormat { + + /** Regex that matches "," but not "\," (escaped comma) */ + public static final Pattern COMMA_SEPARATOR_REGEX = Pattern.compile("(? 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); + 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: <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 toString() 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 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; + + 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) { + 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 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("\\\\,", ","); + } +} -- cgit v1.2.3