diff options
Diffstat (limited to 'src/java/org/apache/fop/util')
-rw-r--r-- | src/java/org/apache/fop/util/BreakUtil.java | 65 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/CharUtilities.java | 5 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/ColorUtil.java | 294 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/LogUtil.java | 2 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/QName.java | 190 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/UnitConv.java | 2 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/XMLResourceBundle.java | 398 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/XMLizable.java | 13 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/text/AdvancedMessageFormat.java | 487 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/text/ChoiceFieldPart.java | 91 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/text/EqualsFieldPart.java | 92 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/text/GlyphNameFieldPart.java | 89 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/text/HexFieldPart.java | 84 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/text/IfFieldPart.java | 116 | ||||
-rw-r--r-- | src/java/org/apache/fop/util/text/LocatorFormatter.java | 42 |
15 files changed, 1664 insertions, 306 deletions
diff --git a/src/java/org/apache/fop/util/BreakUtil.java b/src/java/org/apache/fop/util/BreakUtil.java new file mode 100644 index 000000000..c0528464d --- /dev/null +++ b/src/java/org/apache/fop/util/BreakUtil.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.util; + +import org.apache.fop.fo.Constants; + +/** + * A utility class for manipulating break classes (the break-before and break-after properties). + */ +public final class BreakUtil { + + private BreakUtil() { } + + // TODO replace that with a proper 1.5 enumeration ASAP + private static int getBreakClassPriority(int breakClass) { + switch (breakClass) { + case Constants.EN_AUTO: return 0; + case Constants.EN_COLUMN: return 1; + case Constants.EN_PAGE: return 2; + case Constants.EN_EVEN_PAGE: return 3; + case Constants.EN_ODD_PAGE: return 3; + default: throw new IllegalArgumentException(); + } + } + + /** + * Compares the given break classes and return the one that wins. even-page and + * odd-page win over page, which wins over column, which wins over auto. If even-page + * and odd-page are compared to each other, which one will be returned is undefined. + * + * @param break1 a break class, one of {@link Constants#EN_AUTO}, + * {@link Constants#EN_COLUMN}, {@link Constants#EN_PAGE}, + * {@link Constants#EN_EVEN_PAGE}, {@link Constants#EN_ODD_PAGE} + * @param break2 another break class + * @return the break class that wins the comparison + */ + public static int compareBreakClasses(int break1, int break2) { + // TODO implement some warning mechanism if even-page and odd-page are being compared + int p1 = getBreakClassPriority(break1); + int p2 = getBreakClassPriority(break2); + if (p1 < p2) { + return break2; + } else { + return break1; + } + } + +} diff --git a/src/java/org/apache/fop/util/CharUtilities.java b/src/java/org/apache/fop/util/CharUtilities.java index bfcc90a64..4910a371c 100644 --- a/src/java/org/apache/fop/util/CharUtilities.java +++ b/src/java/org/apache/fop/util/CharUtilities.java @@ -68,7 +68,10 @@ public class CharUtilities { public static final char ZERO_WIDTH_NOBREAK_SPACE = '\uFEFF'; /** soft hyphen */ public static final char SOFT_HYPHEN = '\u00AD'; - + /** missing ideograph */ + public static final char MISSING_IDEOGRAPH = '\u25A1'; + /** Unicode value indicating the the character is "not a character". */ + public static final char NOT_A_CHARACTER = '\uFFFF'; /** * Utility class: Constructor prevents instantiating when subclassed. diff --git a/src/java/org/apache/fop/util/ColorUtil.java b/src/java/org/apache/fop/util/ColorUtil.java index 37762b1e8..fbfc68c36 100644 --- a/src/java/org/apache/fop/util/ColorUtil.java +++ b/src/java/org/apache/fop/util/ColorUtil.java @@ -22,13 +22,11 @@ package org.apache.fop.util; import java.awt.Color; import java.awt.color.ColorSpace; import java.util.Collections; -import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.expr.PropertyException; @@ -76,8 +74,8 @@ public final class ColorUtil { * <li>system-color(colorname)</li> * <li>transparent</li> * <li>colorname</li> - * <li>fop-rgb-icc</li> - * <li>cmyk</li> + * <li>fop-rgb-icc(r,g,b,cs,cs-src,[num]+) (r/g/b: 0..1, num: 0..1)</li> + * <li>cmyk(c,m,y,k) (0..1)</li> * </ul> * * @param foUserAgent FOUserAgent object @@ -167,30 +165,28 @@ public final class ColorUtil { try { if (poss != -1 && pose != -1) { value = value.substring(poss + 1, pose); - StringTokenizer st = new StringTokenizer(value, ","); - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - red = Float.parseFloat(str.substring(2)) / 255f; - } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - green = Float.parseFloat(str.substring(2)) / 255f; - } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - blue = Float.parseFloat(str.substring(2)) / 255f; - } else { - throw new NumberFormatException(); + String[] args = value.split(","); + if (args.length != 3) { + throw new PropertyException( + "Invalid number of arguments for a java.awt.Color: " + value); } - if ((red < 0.0 || red > 1.0) || (green < 0.0 || green > 1.0) + + red = Float.parseFloat(args[0].trim().substring(2)) / 255f; + green = Float.parseFloat(args[1].trim().substring(2)) / 255f; + blue = Float.parseFloat(args[2].trim().substring(2)) / 255f; + if ((red < 0.0 || red > 1.0) + || (green < 0.0 || green > 1.0) || (blue < 0.0 || blue > 1.0)) { throw new PropertyException("Color values out of range"); } } else { - throw new NullPointerException(); + throw new IllegalArgumentException( + "Invalid format for a java.awt.Color: " + value); } + } catch (PropertyException pe) { + throw pe; } catch (Exception e) { - throw new PropertyException("Unknown color format: " + value); + throw new PropertyException(e); } return new Color(red, green, blue); } @@ -210,44 +206,46 @@ public final class ColorUtil { int pose = value.indexOf(")"); if (poss != -1 && pose != -1) { value = value.substring(poss + 1, pose); - StringTokenizer st = new StringTokenizer(value, ","); try { + String[] args = value.split(","); + if (args.length != 3) { + throw new PropertyException( + "Invalid number of arguments: rgb(" + value + ")"); + } float red = 0.0f, green = 0.0f, blue = 0.0f; - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - if (str.endsWith("%")) { - red = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - red = Float.parseFloat(str) / 255f; - } + String str = args[0].trim(); + if (str.endsWith("%")) { + red = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100f; + } else { + red = Float.parseFloat(str) / 255f; } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - if (str.endsWith("%")) { - green = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - green = Float.parseFloat(str) / 255f; - } + str = args[1].trim(); + if (str.endsWith("%")) { + green = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100f; + } else { + green = Float.parseFloat(str) / 255f; } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - if (str.endsWith("%")) { - blue = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - blue = Float.parseFloat(str) / 255f; - } + str = args[2].trim(); + if (str.endsWith("%")) { + blue = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100f; + } else { + blue = Float.parseFloat(str) / 255f; } - if ((red < 0.0 || red > 1.0) || (green < 0.0 || green > 1.0) + if ((red < 0.0 || red > 1.0) + || (green < 0.0 || green > 1.0) || (blue < 0.0 || blue > 1.0)) { throw new PropertyException("Color values out of range"); } parsedColor = new Color(red, green, blue); + } catch (PropertyException pe) { + //simply re-throw + throw pe; } catch (Exception e) { - throw new PropertyException( - "Arguments to rgb() must be [0..255] or [0%..100%]"); + //wrap in a PropertyException + throw new PropertyException(e); } } else { throw new PropertyException("Unknown color format: " + value @@ -269,29 +267,28 @@ public final class ColorUtil { Color parsedColor = null; try { int len = value.length(); - if ((len >= 4) && (len <= 5)) { - // note: divide by 15 so F = FF = 1 and so on - float red = Integer.parseInt(value.substring(1, 2), 16) / 15f; - float green = Integer.parseInt(value.substring(2, 3), 16) / 15f; - float blue = Integer.parseInt(value.substring(3, 4), 16) / 15f; - float alpha = 1.0f; - if (len == 5) { - alpha = Integer.parseInt(value.substring(4), 16) / 15f; - } - parsedColor = new Color(red, green, blue, alpha); + int alpha; + if (len == 5 || len == 9) { + alpha = Integer.parseInt( + value.substring((len == 5) ? 3 : 7), 16); + } else { + alpha = 0xFF; + } + int red = 0, green = 0, blue = 0; + if ((len == 4) || (len == 5)) { + //multiply by 0x11 = 17 = 255/15 + red = Integer.parseInt(value.substring(1, 2), 16) * 0x11; + green = Integer.parseInt(value.substring(2, 3), 16) * 0x11; + blue = Integer.parseInt(value.substring(3, 4), 16) * 0X11; } else if ((len == 7) || (len == 9)) { - int red = Integer.parseInt(value.substring(1, 3), 16); - int green = Integer.parseInt(value.substring(3, 5), 16); - int blue = Integer.parseInt(value.substring(5, 7), 16); - int alpha = 255; - if (len == 9) { - alpha = Integer.parseInt(value.substring(7), 16); - } - parsedColor = new Color(red, green, blue, alpha); + red = Integer.parseInt(value.substring(1, 3), 16); + green = Integer.parseInt(value.substring(3, 5), 16); + blue = Integer.parseInt(value.substring(5, 7), 16); } else { throw new NumberFormatException(); } - } catch (NumberFormatException e) { + parsedColor = new Color(red, green, blue, alpha); + } catch (Exception e) { throw new PropertyException("Unknown color format: " + value + ". Must be #RGB. #RGBA, #RRGGBB, or #RRGGBBAA"); } @@ -311,61 +308,50 @@ public final class ColorUtil { int poss = value.indexOf("("); int pose = value.indexOf(")"); if (poss != -1 && pose != -1) { - value = value.substring(poss + 1, pose); - StringTokenizer st = new StringTokenizer(value, ","); + String[] args = value.substring(poss + 1, pose).split(","); + try { - float red = 0.0f, green = 0.0f, blue = 0.0f; - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - red = Float.parseFloat(str); - } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - green = Float.parseFloat(str); + if (args.length < 5) { + throw new PropertyException("Too few arguments for rgb-icc() function"); } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - blue = Float.parseFloat(str); - } - /* Verify rgb replacement arguments */ - if ((red < 0.0 || red > 1.0) - || (green < 0.0 || green > 1.0) - || (blue < 0.0 || blue > 1.0)) { - throw new PropertyException("Color values out of range"); - } /* Get and verify ICC profile name */ - String iccProfileName = null; - if (st.hasMoreTokens()) { - iccProfileName = st.nextToken().trim(); - } - if (iccProfileName == null || iccProfileName.length() == 0) { + String iccProfileName = args[3].trim(); + if (iccProfileName == null || "".equals(iccProfileName)) { throw new PropertyException("ICC profile name missing"); } /* Get and verify ICC profile source */ - String iccProfileSrc = null; - if (st.hasMoreTokens()) { - iccProfileSrc = st.nextToken().trim(); - // Strip quotes - iccProfileSrc = iccProfileSrc.substring(1, iccProfileSrc.length() - 1); - } - if (iccProfileSrc == null || iccProfileSrc.length() == 0) { + String iccProfileSrc = args[4].trim(); + if (iccProfileSrc == null || "".equals(iccProfileSrc)) { throw new PropertyException("ICC profile source missing"); } - /* ICC profile arguments */ - List iccArgList = new LinkedList(); - while (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - iccArgList.add(new Float(str)); + if (iccProfileSrc.startsWith("\"") || iccProfileSrc.startsWith("'")) { + iccProfileSrc = iccProfileSrc.substring(1); + } + if (iccProfileSrc.endsWith("\"") || iccProfileSrc.endsWith("'")) { + iccProfileSrc = iccProfileSrc.substring(0, iccProfileSrc.length() - 1); } - /* Copy ICC profile arguments from list to array */ - float[] iccComponents = new float[iccArgList.size()]; - for (int ix = 0; ix < iccArgList.size(); ix++) { - iccComponents[ix] = ((Float)iccArgList.get(ix)).floatValue(); + /* ICC profile arguments */ + float[] iccComponents = new float[args.length - 5]; + for (int ix = 4; ++ix < args.length;) { + iccComponents[ix - 5] = Float.parseFloat(args[ix].trim()); } /* Ask FOP factory to get ColorSpace for the specified ICC profile source */ ColorSpace colorSpace = (foUserAgent != null ? foUserAgent.getFactory().getColorSpace( foUserAgent.getBaseURL(), iccProfileSrc) : null); + + float red = 0, green = 0, blue = 0; + red = Float.parseFloat(args[0].trim()); + green = Float.parseFloat(args[1].trim()); + blue = Float.parseFloat(args[2].trim()); + /* Verify rgb replacement arguments */ + if ((red < 0 || red > 1) + || (green < 0 || green > 1) + || (blue < 0 || blue > 1)) { + throw new PropertyException("Color values out of range. " + + "Fallback RGB arguments to fop-rgb-icc() must be [0..1]"); + } + if (colorSpace != null) { // ColorSpace available - create ColorExt (keeps track of replacement rgb // values for possible later colorTOsRGBString call @@ -375,15 +361,19 @@ public final class ColorUtil { // ICC profile could not be loaded - use rgb replacement values */ log.warn("Color profile '" + iccProfileSrc + "' not found. Using rgb replacement values."); - parsedColor = new Color(red, green, blue); + parsedColor = new Color(Math.round(red * 255), + Math.round(green * 255), Math.round(blue * 255)); } + } catch (PropertyException pe) { + //simply re-throw + throw pe; } catch (Exception e) { - throw new PropertyException( - "Arguments to rgb-icc() must be [0..255] or [0%..100%]"); + //wrap in a PropertyException + throw new PropertyException(e); } } else { throw new PropertyException("Unknown color format: " + value - + ". Must be fop-rgb-icc(r,g,b,NCNAME,\"src\",....)"); + + ". Must be fop-rgb-icc(r,g,b,NCNAME,src,....)"); } return parsedColor; } @@ -403,61 +393,58 @@ public final class ColorUtil { int pose = value.indexOf(")"); if (poss != -1 && pose != -1) { value = value.substring(poss + 1, pose); - StringTokenizer st = new StringTokenizer(value, ","); + String[] args = value.split(","); try { + if (args.length != 4) { + throw new PropertyException( + "Invalid number of arguments: cmyk(" + value + ")"); + } float cyan = 0.0f, magenta = 0.0f, yellow = 0.0f, black = 0.0f; - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - if (str.endsWith("%")) { - cyan = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - cyan = Float.parseFloat(str); - } + String str = args[0].trim(); + if (str.endsWith("%")) { + cyan = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100.0f; + } else { + cyan = Float.parseFloat(str); + } + str = args[1].trim(); + if (str.endsWith("%")) { + magenta = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100.0f; + } else { + magenta = Float.parseFloat(str); } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - if (str.endsWith("%")) { - magenta = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - magenta = Float.parseFloat(str); - } + str = args[2].trim(); + if (str.endsWith("%")) { + yellow = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100.0f; + } else { + yellow = Float.parseFloat(str); } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - if (str.endsWith("%")) { - yellow = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - yellow = Float.parseFloat(str); - } + str = args[3].trim(); + if (str.endsWith("%")) { + black = Float.parseFloat(str.substring(0, + str.length() - 1)) / 100.0f; + } else { + black = Float.parseFloat(str); } - if (st.hasMoreTokens()) { - String str = st.nextToken().trim(); - if (str.endsWith("%")) { - black = Float.parseFloat(str.substring(0, - str.length() - 1)) / 100.0f; - } else { - black = Float.parseFloat(str); - } - } + if ((cyan < 0.0 || cyan > 1.0) || (magenta < 0.0 || magenta > 1.0) || (yellow < 0.0 || yellow > 1.0) || (black < 0.0 || black > 1.0)) { - throw new PropertyException("Color values out of range"); + throw new PropertyException("Color values out of range" + + "Arguments to cmyk() must be in the range [0%-100%] or [0.0-1.0]"); } float[] cmyk = new float[] {cyan, magenta, yellow, black}; CMYKColorSpace cmykCs = CMYKColorSpace.getInstance(); float[] rgb = cmykCs.toRGB(cmyk); parsedColor = ColorExt.createFromFoRgbIcc(rgb[0], rgb[1], rgb[2], null, "#CMYK", cmykCs, cmyk); - - + } catch (PropertyException pe) { + throw pe; } catch (Exception e) { - throw new PropertyException( - "Arguments to cmyk() must be in the range [0%-100%] or [0.0-1.0]"); + throw new PropertyException(e); } } else { throw new PropertyException("Unknown color format: " + value @@ -668,7 +655,6 @@ public final class ColorUtil { colorMap.put("whitesmoke", new Color(245, 245, 245)); colorMap.put("yellow", new Color(255, 255, 0)); colorMap.put("yellowgreen", new Color(154, 205, 50)); - colorMap.put("transparent", new Color(0, 0, 0, 0)); } diff --git a/src/java/org/apache/fop/util/LogUtil.java b/src/java/org/apache/fop/util/LogUtil.java index 60eafb51a..e33397fcb 100644 --- a/src/java/org/apache/fop/util/LogUtil.java +++ b/src/java/org/apache/fop/util/LogUtil.java @@ -15,7 +15,7 @@ * limitations under the License. */ -/* $Id: $ */ +/* $Id$ */ package org.apache.fop.util; diff --git a/src/java/org/apache/fop/util/QName.java b/src/java/org/apache/fop/util/QName.java index 0794d3088..132f5b4dc 100644 --- a/src/java/org/apache/fop/util/QName.java +++ b/src/java/org/apache/fop/util/QName.java @@ -1,138 +1,52 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* $Id$ */
-
-package org.apache.fop.util;
-
-import java.io.Serializable;
-
-/**
- * Represents a qualified name of an XML element or an XML attribute.
- * <p>
- * Note: This class allows to carry a namespace prefix but it is not used in the equals() and
- * hashCode() methods.
- */
-public class QName implements Serializable {
-
- private static final long serialVersionUID = -5225376740044770690L;
-
- private String namespaceURI;
- private String localName;
- private String prefix;
- private int hashCode;
-
- /**
- * Main constructor.
- * @param namespaceURI the namespace URI
- * @param prefix the namespace prefix, may be null
- * @param localName the local name
- */
- public QName(String namespaceURI, String prefix, String localName) {
- if (localName == null) {
- throw new NullPointerException("Parameter localName must not be null");
- }
- if (localName.length() == 0) {
- throw new IllegalArgumentException("Parameter localName must not be empty");
- }
- this.namespaceURI = namespaceURI;
- this.prefix = prefix;
- this.localName = localName;
- this.hashCode = toHashString().hashCode();
- }
-
- /**
- * Main constructor.
- * @param namespaceURI the namespace URI
- * @param qName the qualified name
- */
- public QName(String namespaceURI, String qName) {
- if (qName == null) {
- throw new NullPointerException("Parameter localName must not be null");
- }
- if (qName.length() == 0) {
- throw new IllegalArgumentException("Parameter localName must not be empty");
- }
- this.namespaceURI = namespaceURI;
- int p = qName.indexOf(':');
- if (p > 0) {
- this.prefix = qName.substring(0, p);
- this.localName = qName.substring(p + 1);
- } else {
- this.prefix = null;
- this.localName = qName;
- }
- this.hashCode = toHashString().hashCode();
- }
-
- /** @return the namespace URI */
- public String getNamespaceURI() {
- return this.namespaceURI;
- }
-
- /** @return the namespace prefix */
- public String getPrefix() {
- return this.prefix;
- }
-
- /** @return the local name */
- public String getLocalName() {
- return this.localName;
- }
-
- /** @return the fully qualified name */
- public String getQName() {
- return getPrefix() != null ? getPrefix() + ':' + getLocalName() : getLocalName();
- }
-
- /** {@inheritDoc} */
- public int hashCode() {
- return this.hashCode;
- }
-
- /** {@inheritDoc} */
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- } else if (obj == this) {
- return true;
- } else {
- if (obj instanceof QName) {
- QName other = (QName)obj;
- if ((getNamespaceURI() == null && other.getNamespaceURI() == null)
- || getNamespaceURI().equals(other.getNamespaceURI())) {
- return getLocalName().equals(other.getLocalName());
- }
- }
- }
- return false;
- }
-
- /** {@inheritDoc} */
- public String toString() {
- return prefix != null
- ? (prefix + ":" + localName)
- : toHashString();
- }
-
- private String toHashString() {
- return (namespaceURI != null
- ? ("{" + namespaceURI + "}" + localName)
- : localName);
- }
-
-}
+/* + * 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; + +/** + * Represents a qualified name of an XML element or an XML attribute. + * <p> + * Note: This class allows to carry a namespace prefix but it is not used in the equals() and + * hashCode() methods. + * @deprecated Use the XML Graphics Commons variant instead! + */ +public class QName extends org.apache.xmlgraphics.util.QName { + + private static final long serialVersionUID = -5225376740044770690L; + + /** + * Main constructor. + * @param namespaceURI the namespace URI + * @param prefix the namespace prefix, may be null + * @param localName the local name + */ + public QName(String namespaceURI, String prefix, String localName) { + super(namespaceURI, prefix, localName); + } + + /** + * Main constructor. + * @param namespaceURI the namespace URI + * @param qName the qualified name + */ + public QName(String namespaceURI, String qName) { + super(namespaceURI, qName); + } + +} diff --git a/src/java/org/apache/fop/util/UnitConv.java b/src/java/org/apache/fop/util/UnitConv.java index 626321ff9..cd3276b9f 100644 --- a/src/java/org/apache/fop/util/UnitConv.java +++ b/src/java/org/apache/fop/util/UnitConv.java @@ -15,7 +15,7 @@ * limitations under the License. */ -/* $Id: FixedLength.java 279656 2005-09-08 22:06:48Z pietsch $ */ +/* $Id$ */ package org.apache.fop.util; diff --git a/src/java/org/apache/fop/util/XMLResourceBundle.java b/src/java/org/apache/fop/util/XMLResourceBundle.java new file mode 100644 index 000000000..1b320816b --- /dev/null +++ b/src/java/org/apache/fop/util/XMLResourceBundle.java @@ -0,0 +1,398 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util; + +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.Stack; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.stream.StreamSource; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import org.apache.xmlgraphics.util.QName; + +/** + * This class is a ResourceBundle that loads its contents from XML files instead of properties + * files (like PropertiesResourceBundle). + * <p> + * The XML format for this resource bundle implementation is the following + * (the same as Apache Cocoon's XMLResourceBundle): + * <pre> + * <catalogue xml:lang="en"> + * <message key="key1">Message <br/> Value 1</message> + * <message key="key2">Message <br/> Value 1</message> + * ... + * </catalogue> + * </pre> + */ +public class XMLResourceBundle extends ResourceBundle { + + //Note: Some code here has been copied and adapted from Apache Harmony! + + private Properties resources = new Properties(); + + private Locale locale; + + private static SAXTransformerFactory tFactory + = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + + /** + * Creates a resource bundle from an InputStream. + * @param in the stream to read from + * @throws IOException if an I/O error occurs + */ + public XMLResourceBundle(InputStream in) throws IOException { + try { + Transformer transformer = tFactory.newTransformer(); + StreamSource src = new StreamSource(in); + SAXResult res = new SAXResult(new CatalogueHandler()); + transformer.transform(src, res); + } catch (TransformerException e) { + throw new IOException("Error while parsing XML resource bundle: " + e.getMessage()); + } + } + + /** + * Gets a resource bundle using the specified base name, default locale, and class loader. + * @param baseName the base name of the resource bundle, a fully qualified class name + * @param loader the class loader from which to load the resource bundle + * @return a resource bundle for the given base name and the default locale + * @throws MissingResourceException if no resource bundle for the specified base name can be + * found + * @see java.util.ResourceBundle#getBundle(String) + */ + public static ResourceBundle getXMLBundle(String baseName, ClassLoader loader) + throws MissingResourceException { + return getXMLBundle(baseName, Locale.getDefault(), loader); + } + + /** + * Gets a resource bundle using the specified base name, locale, and class loader. + * @param baseName the base name of the resource bundle, a fully qualified class name + * @param locale the locale for which a resource bundle is desired + * @param loader the class loader from which to load the resource bundle + * @return a resource bundle for the given base name and locale + * @throws MissingResourceException if no resource bundle for the specified base name can be + * found + * @see java.util.ResourceBundle#getBundle(String, Locale, ClassLoader) + */ + public static ResourceBundle getXMLBundle(String baseName, Locale locale, ClassLoader loader) + throws MissingResourceException { + if (loader == null) { + throw new NullPointerException("loader must not be null"); + } + if (baseName == null) { + throw new NullPointerException("baseName must not be null"); + } + + ResourceBundle bundle; + if (!locale.equals(Locale.getDefault())) { + bundle = handleGetXMLBundle(baseName, "_" + locale, false, loader); + if (bundle != null) { + return bundle; + } + } + bundle = handleGetXMLBundle(baseName, "_" + Locale.getDefault(), true, loader); + if (bundle != null) { + return bundle; + } + throw new MissingResourceException( + baseName + " (" + locale + ")", baseName + '_' + locale, null); + } + + static class MissingBundle extends ResourceBundle { + public Enumeration getKeys() { + return null; + } + + public Object handleGetObject(String name) { + return null; + } + } + + private static final ResourceBundle MISSING = new MissingBundle(); + private static final ResourceBundle MISSINGBASE = new MissingBundle(); + + private static Map cache = new java.util.WeakHashMap(); + //<Object, Hashtable<String, ResourceBundle>> + + private static ResourceBundle handleGetXMLBundle(String base, String locale, + boolean loadBase, final ClassLoader loader) { + XMLResourceBundle bundle = null; + String bundleName = base + locale; + Object cacheKey = loader != null ? (Object) loader : (Object) "null"; + Hashtable loaderCache; //<String, ResourceBundle> + synchronized (cache) { + loaderCache = (Hashtable)cache.get(cacheKey); + if (loaderCache == null) { + loaderCache = new Hashtable(); + cache.put(cacheKey, loaderCache); + } + } + ResourceBundle result = (ResourceBundle)loaderCache.get(bundleName); + if (result != null) { + if (result == MISSINGBASE) { + return null; + } + if (result == MISSING) { + if (!loadBase) { + return null; + } + String extension = strip(locale); + if (extension == null) { + return null; + } + return handleGetXMLBundle(base, extension, loadBase, loader); + } + return result; + } + + final String fileName = bundleName.replace('.', '/') + ".xml"; + InputStream stream = (InputStream)AccessController + .doPrivileged(new PrivilegedAction() { + public Object run() { + return loader == null + ? ClassLoader.getSystemResourceAsStream(fileName) + : loader.getResourceAsStream(fileName); + } + }); + if (stream != null) { + try { + try { + bundle = new XMLResourceBundle(stream); + } finally { + stream.close(); + } + bundle.setLocale(locale); + } catch (IOException e) { + throw new MissingResourceException(e.getMessage(), base, null); + } + } + + String extension = strip(locale); + if (bundle != null) { + if (extension != null) { + ResourceBundle parent = handleGetXMLBundle(base, extension, true, + loader); + if (parent != null) { + bundle.setParent(parent); + } + } + loaderCache.put(bundleName, bundle); + return bundle; + } + + if (extension != null) { + ResourceBundle fallback = handleGetXMLBundle(base, extension, loadBase, loader); + if (fallback != null) { + loaderCache.put(bundleName, fallback); + return fallback; + } + } + loaderCache.put(bundleName, loadBase ? MISSINGBASE : MISSING); + return null; + } + + private void setLocale(String name) { + String language = "", country = "", variant = ""; + if (name.length() > 1) { + int nextIndex = name.indexOf('_', 1); + if (nextIndex == -1) { + nextIndex = name.length(); + } + language = name.substring(1, nextIndex); + if (nextIndex + 1 < name.length()) { + int index = nextIndex; + nextIndex = name.indexOf('_', nextIndex + 1); + if (nextIndex == -1) { + nextIndex = name.length(); + } + country = name.substring(index + 1, nextIndex); + if (nextIndex + 1 < name.length()) { + variant = name.substring(nextIndex + 1, name.length()); + } + } + } + this.locale = new Locale(language, country, variant); + } + + private static String strip(String name) { + int index = name.lastIndexOf('_'); + if (index != -1) { + return name.substring(0, index); + } + return null; + } + + private Enumeration getLocalKeys() { + return (Enumeration)resources.propertyNames(); + } + + /** {@inheritDoc} */ + public Locale getLocale() { + return this.locale; + } + + /** {@inheritDoc} */ + public Enumeration getKeys() { + if (parent == null) { + return getLocalKeys(); + } + return new Enumeration() { + private Enumeration local = getLocalKeys(); + private Enumeration pEnum = parent.getKeys(); + + private Object nextElement; + + private boolean findNext() { + if (nextElement != null) { + return true; + } + while (pEnum.hasMoreElements()) { + Object next = pEnum.nextElement(); + if (!resources.containsKey(next)) { + nextElement = next; + return true; + } + } + return false; + } + + public boolean hasMoreElements() { + if (local.hasMoreElements()) { + return true; + } + return findNext(); + } + + public Object nextElement() { + if (local.hasMoreElements()) { + return local.nextElement(); + } + if (findNext()) { + Object result = nextElement; + nextElement = null; + return result; + } + // Cause an exception + return pEnum.nextElement(); + } + }; + } + + /** {@inheritDoc} */ + protected Object handleGetObject(String key) { + if (key == null) { + throw new NullPointerException("key must not be null"); + } + return resources.get(key); + } + + /** {@inheritDoc} */ + public String toString() { + return "XMLResourceBundle: " + getLocale(); + } + + private class CatalogueHandler extends DefaultHandler { + + private static final String CATALOGUE = "catalogue"; + private static final String MESSAGE = "message"; + + private StringBuffer valueBuffer = new StringBuffer(); + private Stack elementStack = new Stack(); + private String currentKey = null; + + private boolean isOwnNamespace(String uri) { + return ("".equals(uri)); + } + + private QName getParentElementName() { + return (QName)elementStack.peek(); + } + + /** {@inheritDoc} */ + public void startElement(String uri, String localName, String qName, + Attributes atts) throws SAXException { + super.startElement(uri, localName, qName, atts); + QName elementName = new QName(uri, qName); + if (isOwnNamespace(uri)) { + if (CATALOGUE.equals(localName)) { + //nop + } else if (MESSAGE.equals(localName)) { + if (!CATALOGUE.equals(getParentElementName().getLocalName())) { + throw new SAXException(MESSAGE + " must be a child of " + CATALOGUE); + } + this.currentKey = atts.getValue("key"); + } else { + throw new SAXException("Invalid element name: " + elementName); + } + } else { + //ignore + } + this.valueBuffer.setLength(0); + elementStack.push(elementName); + } + + /** {@inheritDoc} */ + public void endElement(String uri, String localName, String qName) throws SAXException { + super.endElement(uri, localName, qName); + elementStack.pop(); + if (isOwnNamespace(uri)) { + if (CATALOGUE.equals(localName)) { + //nop + } else if (MESSAGE.equals(localName)) { + if (this.currentKey == null) { + throw new SAXException( + "current key is null (attribute 'key' might be mistyped)"); + } + resources.put(this.currentKey, this.valueBuffer.toString()); + this.currentKey = null; + } + } else { + //ignore + } + this.valueBuffer.setLength(0); + } + + /** {@inheritDoc} */ + public void characters(char[] ch, int start, int length) throws SAXException { + super.characters(ch, start, length); + valueBuffer.append(ch, start, length); + } + + } + +} diff --git a/src/java/org/apache/fop/util/XMLizable.java b/src/java/org/apache/fop/util/XMLizable.java index c1213638b..a16131989 100644 --- a/src/java/org/apache/fop/util/XMLizable.java +++ b/src/java/org/apache/fop/util/XMLizable.java @@ -25,20 +25,11 @@ package org.apache.fop.util; * src/java/org/apache/excalibur/xml/sax/XMLizable.java */ -import org.xml.sax.ContentHandler; -import org.xml.sax.SAXException; - /** * This interface can be implemented by classes willing to provide an XML representation * of their current state as SAX events. + * @deprecated Use the interface in Apache XML Graphics Commons instead. */ -public interface XMLizable { - - /** - * Generates SAX events representing the object's state. - * @param handler ContentHandler instance to send the SAX events to - * @throws SAXException if there's a problem generating the SAX events - */ - void toSAX(ContentHandler handler) throws SAXException; +public interface XMLizable extends org.apache.xmlgraphics.util.XMLizable { } diff --git a/src/java/org/apache/fop/util/text/AdvancedMessageFormat.java b/src/java/org/apache/fop/util/text/AdvancedMessageFormat.java new file mode 100644 index 000000000..a2169156a --- /dev/null +++ b/src/java/org/apache/fop/util/text/AdvancedMessageFormat.java @@ -0,0 +1,487 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util.text; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.xmlgraphics.util.Service; + + +/** + * Formats messages based on a template and with a set of named parameters. This is similar to + * {@link java.util.MessageFormat} but uses named parameters and supports conditional sub-groups. + * <p> + * Example: + * </p> + * <p><code>Missing field "{fieldName}"[ at location: {location}]!</code></p> + * <ul> + * <li>Curly brackets ("{}") are used for fields.</li> + * <li>Square brackets ("[]") are used to delimit conditional sub-groups. A sub-group is + * conditional when all fields inside the sub-group have a null value. In the case, everything + * between the brackets is skipped.</li> + * </ul> + */ +public class AdvancedMessageFormat { + + /** Regex that matches "," but not "\," (escaped comma) */ + static final Pattern COMMA_SEPARATOR_REGEX = Pattern.compile("(?<!\\\\),"); + + private static final Map PART_FACTORIES = new java.util.HashMap(); + private static final List OBJECT_FORMATTERS = new java.util.ArrayList(); + private static final Map FUNCTIONS = new java.util.HashMap(); + + private CompositePart rootPart; + + static { + Iterator iter; + iter = Service.providers(PartFactory.class, true); + while (iter.hasNext()) { + PartFactory factory = (PartFactory)iter.next(); + PART_FACTORIES.put(factory.getFormat(), factory); + } + iter = Service.providers(ObjectFormatter.class, true); + while (iter.hasNext()) { + OBJECT_FORMATTERS.add((ObjectFormatter)iter.next()); + } + iter = Service.providers(Function.class, true); + while (iter.hasNext()) { + Function function = (Function)iter.next(); + FUNCTIONS.put(function.getName(), function); + } + } + + /** + * Construct a new message format. + * @param pattern the message format pattern. + */ + public AdvancedMessageFormat(CharSequence pattern) { + parsePattern(pattern); + } + + private void parsePattern(CharSequence pattern) { + rootPart = new CompositePart(false); + StringBuffer sb = new StringBuffer(); + parseInnerPattern(pattern, rootPart, sb, 0); + } + + private int parseInnerPattern(CharSequence pattern, CompositePart parent, + StringBuffer sb, int start) { + assert sb.length() == 0; + int i = start; + int len = pattern.length(); + loop: + while (i < len) { + char ch = pattern.charAt(i); + switch (ch) { + case '{': + if (sb.length() > 0) { + parent.addChild(new TextPart(sb.toString())); + sb.setLength(0); + } + i++; + int nesting = 1; + while (i < len) { + ch = pattern.charAt(i); + if (ch == '{') { + nesting++; + } else if (ch == '}') { + nesting--; + if (nesting == 0) { + i++; + break; + } + } + sb.append(ch); + i++; + } + parent.addChild(parseField(sb.toString())); + sb.setLength(0); + break; + case ']': + i++; + break loop; //Current composite is finished + case '[': + if (sb.length() > 0) { + parent.addChild(new TextPart(sb.toString())); + sb.setLength(0); + } + i++; + CompositePart composite = new CompositePart(true); + parent.addChild(composite); + i += parseInnerPattern(pattern, composite, sb, i); + break; + case '|': + if (sb.length() > 0) { + parent.addChild(new TextPart(sb.toString())); + sb.setLength(0); + } + parent.newSection(); + i++; + break; + case '\\': + if (i < len - 1) { + i++; + ch = pattern.charAt(i); + } + //no break here! Must be right before "default" section + default: + sb.append(ch); + i++; + } + } + if (sb.length() > 0) { + parent.addChild(new TextPart(sb.toString())); + sb.setLength(0); + } + return i - start; + } + + private Part parseField(String field) { + String[] parts = COMMA_SEPARATOR_REGEX.split(field, 3); + String fieldName = parts[0]; + if (parts.length == 1) { + if (fieldName.startsWith("#")) { + return new FunctionPart(fieldName.substring(1)); + } else { + return new SimpleFieldPart(fieldName); + } + } else { + String format = parts[1]; + PartFactory factory = (PartFactory)PART_FACTORIES.get(format); + if (factory == null) { + throw new IllegalArgumentException( + "No PartFactory available under the name: " + format); + } + if (parts.length == 2) { + return factory.newPart(fieldName, null); + } else { + return factory.newPart(fieldName, parts[2]); + } + } + } + + private static Function getFunction(String functionName) { + return (Function)FUNCTIONS.get(functionName); + } + + /** + * Formats a message with the given parameters. + * @param params a Map of named parameters (Contents: <String, Object>) + * @return the formatted message + */ + public String format(Map params) { + StringBuffer sb = new StringBuffer(); + format(params, sb); + return sb.toString(); + } + + /** + * Formats a message with the given parameters. + * @param params a Map of named parameters (Contents: <String, Object>) + * @param target the target StringBuffer to write the formatted message to + */ + public void format(Map params, StringBuffer target) { + rootPart.write(target, params); + } + + /** + * Represents a message template part. This interface is implemented by various variants of + * the single curly braces pattern ({field}, {field,if,yes,no} etc.). + */ + public interface Part { + + /** + * Writes the formatted part to a string buffer. + * @param sb the target string buffer + * @param params the parameters to work with + */ + void write(StringBuffer sb, Map params); + + /** + * Indicates whether there is any content that is generated by this message part. + * @param params the parameters to work with + * @return true if the part has content + */ + boolean isGenerated(Map params); + } + + /** + * Implementations of this interface parse a field part and return message parts. + */ + public interface PartFactory { + + /** + * Creates a new part by parsing the values parameter to configure the part. + * @param fieldName the field name + * @param values the unparsed parameter values + * @return the new message part + */ + Part newPart(String fieldName, String values); + + /** + * Returns the name of the message part format. + * @return the name of the message part format + */ + String getFormat(); + } + + /** + * Implementations of this interface format certain objects to strings. + */ + public interface ObjectFormatter { + + /** + * Formats an object to a string and writes the result to a string buffer. + * @param sb the target string buffer + * @param obj the object to be formatted + */ + void format(StringBuffer sb, Object obj); + + /** + * Indicates whether a given object is supported. + * @param obj the object + * @return true if the object is supported by the formatter + */ + boolean supportsObject(Object obj); + } + + /** + * Implementations of this interface do some computation based on the message parameters + * given to it. Note: at the moment, this has to be done in a local-independent way since + * there is no locale information. + */ + public interface Function { + + /** + * Executes the function. + * @param params the message parameters + * @return the function result + */ + Object evaluate(Map params); + + /** + * Returns the name of the function. + * @return the name of the function + */ + Object getName(); + } + + private static class TextPart implements Part { + + private String text; + + public TextPart(String text) { + this.text = text; + } + + public void write(StringBuffer sb, Map params) { + sb.append(text); + } + + public boolean isGenerated(Map params) { + return true; + } + + /** {@inheritDoc} */ + public String toString() { + return this.text; + } + } + + private static class SimpleFieldPart implements Part { + + private String fieldName; + + public SimpleFieldPart(String fieldName) { + this.fieldName = fieldName; + } + + public void write(StringBuffer sb, Map params) { + if (!params.containsKey(fieldName)) { + throw new IllegalArgumentException( + "Message pattern contains unsupported field name: " + fieldName); + } + Object obj = params.get(fieldName); + formatObject(obj, sb); + } + + public boolean isGenerated(Map params) { + Object obj = params.get(fieldName); + return obj != null; + } + + /** {@inheritDoc} */ + public String toString() { + return "{" + this.fieldName + "}"; + } + } + + /** + * Formats an object to a string and writes the result to a string buffer. This method + * usually uses the object's <code>toString()</code> method unless there is an + * {@link ObjectFormatter} that supports the object. {@link ObjectFormatter}s are registered + * through the service provider mechanism defined by the JAR specification. + * @param obj the object to be formatted + * @param target the target string buffer + */ + public static void formatObject(Object obj, StringBuffer target) { + if (obj instanceof String) { + target.append(obj); + } else { + boolean handled = false; + Iterator iter = OBJECT_FORMATTERS.iterator(); + while (iter.hasNext()) { + ObjectFormatter formatter = (ObjectFormatter)iter.next(); + if (formatter.supportsObject(obj)) { + formatter.format(target, obj); + handled = true; + break; + } + } + if (!handled) { + target.append(String.valueOf(obj)); + } + } + } + + private static class FunctionPart implements Part { + + private Function function; + + public FunctionPart(String functionName) { + this.function = getFunction(functionName); + if (this.function == null) { + throw new IllegalArgumentException("Unknown function: " + functionName); + } + } + + public void write(StringBuffer sb, Map params) { + Object obj = this.function.evaluate(params); + formatObject(obj, sb); + } + + public boolean isGenerated(Map params) { + Object obj = this.function.evaluate(params); + return obj != null; + } + + /** {@inheritDoc} */ + public String toString() { + return "{#" + this.function.getName() + "}"; + } + } + + private static class CompositePart implements Part { + + protected List parts = new java.util.ArrayList(); + private boolean conditional; + private boolean hasSections = false; + + public CompositePart(boolean conditional) { + this.conditional = conditional; + } + + private CompositePart(List parts) { + this.parts.addAll(parts); + this.conditional = true; + } + + public void addChild(Part part) { + if (part == null) { + throw new NullPointerException("part must not be null"); + } + if (hasSections) { + CompositePart composite = (CompositePart)this.parts.get(this.parts.size() - 1); + composite.addChild(part); + } else { + this.parts.add(part); + } + } + + public void newSection() { + if (!hasSections) { + List p = this.parts; + //Dropping into a different mode... + this.parts = new java.util.ArrayList(); + this.parts.add(new CompositePart(p)); + hasSections = true; + } + this.parts.add(new CompositePart(true)); + } + + public void write(StringBuffer sb, Map params) { + if (hasSections) { + Iterator iter = this.parts.iterator(); + while (iter.hasNext()) { + CompositePart part = (CompositePart)iter.next(); + if (part.isGenerated(params)) { + part.write(sb, params); + break; + } + } + } else { + if (isGenerated(params)) { + Iterator iter = this.parts.iterator(); + while (iter.hasNext()) { + Part part = (Part)iter.next(); + part.write(sb, params); + } + } + } + } + + public boolean isGenerated(Map params) { + if (hasSections) { + Iterator iter = this.parts.iterator(); + while (iter.hasNext()) { + Part part = (Part)iter.next(); + if (part.isGenerated(params)) { + return true; + } + } + return false; + } else { + if (conditional) { + Iterator iter = this.parts.iterator(); + while (iter.hasNext()) { + Part part = (Part)iter.next(); + if (!part.isGenerated(params)) { + return false; + } + } + } + return true; + } + } + + /** {@inheritDoc} */ + public String toString() { + return this.parts.toString(); + } + } + + + static String unescapeComma(String string) { + return string.replaceAll("\\\\,", ","); + } +} diff --git a/src/java/org/apache/fop/util/text/ChoiceFieldPart.java b/src/java/org/apache/fop/util/text/ChoiceFieldPart.java new file mode 100644 index 000000000..df457a02b --- /dev/null +++ b/src/java/org/apache/fop/util/text/ChoiceFieldPart.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util.text; + +import java.text.ChoiceFormat; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.fop.util.text.AdvancedMessageFormat.Part; +import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory; + +/** + * Defines a "choice" field part that works like {@link ChoiceFormat}. + */ +public class ChoiceFieldPart implements Part { + + private static final Pattern VARIABLE_REGEX = Pattern.compile("\\{([^\\}]+)\\}"); + + private String fieldName; + private ChoiceFormat choiceFormat; + + /** + * Creates a new choice part. + * @param fieldName the field name to work on + * @param choicesPattern the choices pattern (as used by {@link ChoiceFormat}) + */ + public ChoiceFieldPart(String fieldName, String choicesPattern) { + this.fieldName = fieldName; + this.choiceFormat = new ChoiceFormat(choicesPattern); + } + + /** {@inheritDoc} */ + public boolean isGenerated(Map params) { + Object obj = params.get(fieldName); + return obj != null; + } + + /** {@inheritDoc} */ + public void write(StringBuffer sb, Map params) { + Object obj = params.get(fieldName); + Number num = (Number)obj; + String result = this.choiceFormat.format(num.doubleValue()); + Matcher m = VARIABLE_REGEX.matcher(result); + if (m.find()) { + //Resolve inner variables + AdvancedMessageFormat f = new AdvancedMessageFormat(result); + f.format(params, sb); + } else { + sb.append(result); + } + } + + /** {@inheritDoc} */ + public String toString() { + return "{" + this.fieldName + ",choice, ....}"; + } + + /** Factory for ChoiceFieldPart. */ + public static class Factory implements PartFactory { + + /** {@inheritDoc} */ + public Part newPart(String fieldName, String values) { + return new ChoiceFieldPart(fieldName, values); + } + + /** {@inheritDoc} */ + public String getFormat() { + return "choice"; + } + + } + +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/util/text/EqualsFieldPart.java b/src/java/org/apache/fop/util/text/EqualsFieldPart.java new file mode 100644 index 000000000..2114b0d00 --- /dev/null +++ b/src/java/org/apache/fop/util/text/EqualsFieldPart.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util.text; + +import java.util.Map; + +import org.apache.fop.util.text.AdvancedMessageFormat.Part; +import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory; + +/** + * Defines an "equals" field part that can compare a field's string value against another string. + * It returns either of two possible values attached as additional part parameters. Example: + * <code>{field,equals,new,This is new!,This is old!}</code> + */ +public class EqualsFieldPart extends IfFieldPart { + + private String equalsValue; + + /** + * Creates a new "equals" field part. + * @param fieldName the field name + * @param values the unparsed parameter values + */ + public EqualsFieldPart(String fieldName, String values) { + super(fieldName, values); + } + + /** {@inheritDoc} */ + protected void parseValues(String values) { + String[] parts = AdvancedMessageFormat.COMMA_SEPARATOR_REGEX.split(values, 3); + this.equalsValue = parts[0]; + if (parts.length == 1) { + throw new IllegalArgumentException( + "'equals' format must have at least 2 parameters"); + } + if (parts.length == 3) { + ifValue = AdvancedMessageFormat.unescapeComma(parts[1]); + elseValue = AdvancedMessageFormat.unescapeComma(parts[2]); + } else { + ifValue = AdvancedMessageFormat.unescapeComma(parts[1]); + } + } + + /** {@inheritDoc} */ + protected boolean isTrue(Map params) { + Object obj = params.get(fieldName); + if (obj != null) { + return String.valueOf(obj).equals(this.equalsValue); + } else { + return false; + } + } + + /** {@inheritDoc} */ + public String toString() { + return "{" + this.fieldName + ", equals " + this.equalsValue + "}"; + } + + /** + * Part factory for "equals". + */ + public static class Factory implements PartFactory { + + /** {@inheritDoc} */ + public Part newPart(String fieldName, String values) { + return new EqualsFieldPart(fieldName, values); + } + + /** {@inheritDoc} */ + public String getFormat() { + return "equals"; + } + + } +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/util/text/GlyphNameFieldPart.java b/src/java/org/apache/fop/util/text/GlyphNameFieldPart.java new file mode 100644 index 000000000..5d78cdfad --- /dev/null +++ b/src/java/org/apache/fop/util/text/GlyphNameFieldPart.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util.text; + +import java.util.Map; + +import org.apache.xmlgraphics.fonts.Glyphs; + +import org.apache.fop.util.text.AdvancedMessageFormat.Part; +import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory; + +/** + * Function formatting a character to a glyph name. + */ +public class GlyphNameFieldPart implements Part { + + private String fieldName; + + /** + * Creates a new glyph name field part + * @param fieldName the field name + */ + public GlyphNameFieldPart(String fieldName) { + this.fieldName = fieldName; + } + + /** {@inheritDoc} */ + public boolean isGenerated(Map params) { + Object obj = params.get(fieldName); + return obj != null && getGlyphName(obj).length() > 0; + } + + private String getGlyphName(Object obj) { + if (obj instanceof Character) { + return Glyphs.charToGlyphName(((Character)obj).charValue()); + } else { + throw new IllegalArgumentException( + "Value for glyph name part must be a Character but was: " + + obj.getClass().getName()); + } + } + + /** {@inheritDoc} */ + public void write(StringBuffer sb, Map params) { + if (!params.containsKey(fieldName)) { + throw new IllegalArgumentException( + "Message pattern contains unsupported field name: " + fieldName); + } + Object obj = params.get(fieldName); + sb.append(getGlyphName(obj)); + } + + /** {@inheritDoc} */ + public String toString() { + return "{" + this.fieldName + ",glyph-name}"; + } + + /** Factory for {@link GlyphNameFieldPart}. */ + public static class Factory implements PartFactory { + + /** {@inheritDoc} */ + public Part newPart(String fieldName, String values) { + return new GlyphNameFieldPart(fieldName); + } + + /** {@inheritDoc} */ + public String getFormat() { + return "glyph-name"; + } + + } +} diff --git a/src/java/org/apache/fop/util/text/HexFieldPart.java b/src/java/org/apache/fop/util/text/HexFieldPart.java new file mode 100644 index 000000000..19f47f3d7 --- /dev/null +++ b/src/java/org/apache/fop/util/text/HexFieldPart.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util.text; + +import java.util.Map; + +import org.apache.fop.util.text.AdvancedMessageFormat.Part; +import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory; + +/** + * Function formatting a number or character to a hex value. + */ +public class HexFieldPart implements Part { + + private String fieldName; + + /** + * Creates a new hex field part + * @param fieldName the field name + */ + public HexFieldPart(String fieldName) { + this.fieldName = fieldName; + } + + /** {@inheritDoc} */ + public boolean isGenerated(Map params) { + Object obj = params.get(fieldName); + return obj != null; + } + + /** {@inheritDoc} */ + public void write(StringBuffer sb, Map params) { + if (!params.containsKey(fieldName)) { + throw new IllegalArgumentException( + "Message pattern contains unsupported field name: " + fieldName); + } + Object obj = params.get(fieldName); + if (obj instanceof Character) { + sb.append(Integer.toHexString(((Character)obj).charValue())); + } else if (obj instanceof Number) { + sb.append(Integer.toHexString(((Number)obj).intValue())); + } else { + throw new IllegalArgumentException("Incompatible value for hex field part: " + + obj.getClass().getName()); + } + } + + /** {@inheritDoc} */ + public String toString() { + return "{" + this.fieldName + ",hex}"; + } + + /** Factory for {@link HexFieldPart}. */ + public static class Factory implements PartFactory { + + /** {@inheritDoc} */ + public Part newPart(String fieldName, String values) { + return new HexFieldPart(fieldName); + } + + /** {@inheritDoc} */ + public String getFormat() { + return "hex"; + } + + } +} diff --git a/src/java/org/apache/fop/util/text/IfFieldPart.java b/src/java/org/apache/fop/util/text/IfFieldPart.java new file mode 100644 index 000000000..31cd8f36c --- /dev/null +++ b/src/java/org/apache/fop/util/text/IfFieldPart.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util.text; + +import java.util.Map; + +import org.apache.fop.util.text.AdvancedMessageFormat.Part; +import org.apache.fop.util.text.AdvancedMessageFormat.PartFactory; + +/** + * Defines an "if" field part that checks if field's value is true or false. + * It returns either of two possible values attached as additional part parameters. Example: + * <code>{field,if,Yes,No}</code> + */ +public class IfFieldPart implements Part { + + /** the field name for the part */ + protected String fieldName; + /** the value being returned if the field is true */ + protected String ifValue; + /** the value being returned if the field is false */ + protected String elseValue; + + /** + * Creates a new "if" field part. + * @param fieldName the field name + * @param values the unparsed parameter values + */ + public IfFieldPart(String fieldName, String values) { + this.fieldName = fieldName; + parseValues(values); + } + + /** + * Parses the parameter values + * @param values the unparsed parameter values + */ + protected void parseValues(String values) { + String[] parts = AdvancedMessageFormat.COMMA_SEPARATOR_REGEX.split(values, 2); + if (parts.length == 2) { + ifValue = AdvancedMessageFormat.unescapeComma(parts[0]); + elseValue = AdvancedMessageFormat.unescapeComma(parts[1]); + } else { + ifValue = AdvancedMessageFormat.unescapeComma(values); + } + } + + /** {@inheritDoc} */ + public void write(StringBuffer sb, Map params) { + boolean isTrue = isTrue(params); + if (isTrue) { + sb.append(ifValue); + } else if (elseValue != null) { + sb.append(elseValue); + } + } + + /** + * Indicates whether the field's value is true. If the field is not a boolen, it is true + * if the field is not null. + * @param params the message parameters + * @return true the field's value as boolean + */ + protected boolean isTrue(Map params) { + Object obj = params.get(fieldName); + if (obj instanceof Boolean) { + return ((Boolean)obj).booleanValue(); + } else { + return (obj != null); + } + } + + /** {@inheritDoc} */ + public boolean isGenerated(Map params) { + return isTrue(params) || (elseValue != null); + } + + /** {@inheritDoc} */ + public String toString() { + return "{" + this.fieldName + ", if...}"; + } + + /** + * Part factory for "if". + */ + public static class Factory implements PartFactory { + + /** {@inheritDoc} */ + public Part newPart(String fieldName, String values) { + return new IfFieldPart(fieldName, values); + } + + /** {@inheritDoc} */ + public String getFormat() { + return "if"; + } + + } +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/util/text/LocatorFormatter.java b/src/java/org/apache/fop/util/text/LocatorFormatter.java new file mode 100644 index 000000000..d9532c66d --- /dev/null +++ b/src/java/org/apache/fop/util/text/LocatorFormatter.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.util.text; + +import org.xml.sax.Locator; + +import org.apache.fop.util.text.AdvancedMessageFormat.ObjectFormatter; + +/** + * Object formatter for the SAX Locator object. + */ +public class LocatorFormatter implements ObjectFormatter { + + /** {@inheritDoc} */ + public void format(StringBuffer sb, Object obj) { + Locator loc = (Locator)obj; + sb.append(loc.getLineNumber()).append(":").append(loc.getColumnNumber()); + } + + /** {@inheritDoc} */ + public boolean supportsObject(Object obj) { + return obj instanceof Locator; + } + +}
\ No newline at end of file |