| /* |
| * 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.awt.Rectangle; |
| import java.awt.geom.Rectangle2D; |
| |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.AttributesImpl; |
| |
| /** |
| * A collection of utility method for XML handling. |
| */ |
| public final class XMLUtil implements XMLConstants { |
| |
| private XMLUtil() { |
| } |
| |
| /** |
| * Returns an attribute value as a boolean value. |
| * @param attributes the Attributes object |
| * @param name the name of the attribute |
| * @param defaultValue the default value if the attribute is not specified |
| * @return the attribute value as a boolean |
| */ |
| public static boolean getAttributeAsBoolean(Attributes attributes, String name, |
| boolean defaultValue) { |
| String s = attributes.getValue(name); |
| if (s == null) { |
| return defaultValue; |
| } else { |
| return Boolean.valueOf(s).booleanValue(); |
| } |
| } |
| |
| /** |
| * Returns an attribute value as a int value. |
| * @param attributes the Attributes object |
| * @param name the name of the attribute |
| * @param defaultValue the default value if the attribute is not specified |
| * @return the attribute value as an int |
| */ |
| public static int getAttributeAsInt(Attributes attributes, String name, |
| int defaultValue) { |
| String s = attributes.getValue(name); |
| if (s == null) { |
| return defaultValue; |
| } else { |
| return Integer.parseInt(s); |
| } |
| } |
| |
| /** |
| * Returns an attribute value as a int value. |
| * @param attributes the Attributes object |
| * @param name the name of the attribute |
| * @return the attribute value as an int |
| * @throws SAXException if the attribute is missing |
| */ |
| public static int getAttributeAsInt(Attributes attributes, String name) throws SAXException { |
| String s = attributes.getValue(name); |
| if (s == null) { |
| throw new SAXException("Attribute '" + name + "' is missing"); |
| } else { |
| return Integer.parseInt(s); |
| } |
| } |
| |
| /** |
| * Returns an attribute value as a Integer value. |
| * @param attributes the Attributes object |
| * @param name the name of the attribute |
| * @return the attribute value as an Integer or null if the attribute is missing |
| */ |
| public static Integer getAttributeAsInteger(Attributes attributes, String name) { |
| String s = attributes.getValue(name); |
| if (s == null) { |
| return null; |
| } else { |
| return new Integer(s); |
| } |
| } |
| |
| /** |
| * Returns an attribute value as a Rectangle2D value. The string value is expected as 4 |
| * double-precision numbers separated by whitespace. |
| * @param attributes the Attributes object |
| * @param name the name of the attribute |
| * @return the attribute value as an Rectangle2D |
| */ |
| public static Rectangle2D getAttributeAsRectangle2D(Attributes attributes, String name) { |
| String s = attributes.getValue(name).trim(); |
| double[] values = ConversionUtils.toDoubleArray(s, "\\s"); |
| if (values.length != 4) { |
| throw new IllegalArgumentException("Rectangle must consist of 4 double values!"); |
| } |
| return new Rectangle2D.Double(values[0], values[1], values[2], values[3]); |
| } |
| |
| /** |
| * Returns an attribute value as a Rectangle value. The string value is expected as 4 |
| * integer numbers separated by whitespace. |
| * @param attributes the Attributes object |
| * @param name the name of the attribute |
| * @return the attribute value as an Rectangle |
| */ |
| public static Rectangle getAttributeAsRectangle(Attributes attributes, String name) { |
| String s = attributes.getValue(name); |
| if (s == null) { |
| return null; |
| } |
| int[] values = ConversionUtils.toIntArray(s.trim(), "\\s"); |
| if (values.length != 4) { |
| throw new IllegalArgumentException("Rectangle must consist of 4 int values!"); |
| } |
| return new Rectangle(values[0], values[1], values[2], values[3]); |
| } |
| |
| /** |
| * Returns an attribute value as a integer array. The string value is expected as 4 |
| * integer numbers separated by whitespace. |
| * @param attributes the Attributes object |
| * @param name the name of the attribute |
| * @return the attribute value as an int array |
| */ |
| public static int[] getAttributeAsIntArray(Attributes attributes, String name) { |
| String s = attributes.getValue(name); |
| if (s == null) { |
| return null; |
| } else { |
| return ConversionUtils.toIntArray(s.trim(), "\\s"); |
| } |
| } |
| |
| /** |
| * Adds an attribute to a given {@link AttributesImpl} instance. |
| * @param atts the attributes collection |
| * @param attribute the attribute to add |
| * @param value the attribute's CDATA value |
| */ |
| public static void addAttribute(AttributesImpl atts, |
| org.apache.xmlgraphics.util.QName attribute, String value) { |
| atts.addAttribute(attribute.getNamespaceURI(), |
| attribute.getLocalName(), attribute.getQName(), XMLUtil.CDATA, value); |
| } |
| |
| /** |
| * Adds an attribute to a given {@link AttributesImpl} instance. The attribute will be |
| * added in the default namespace. |
| * @param atts the attributes collection |
| * @param localName the local name of the attribute |
| * @param value the attribute's CDATA value |
| */ |
| public static void addAttribute(AttributesImpl atts, String localName, String value) { |
| atts.addAttribute("", localName, localName, XMLUtil.CDATA, value); |
| } |
| |
| /** |
| * Encode a glyph position adjustments array as a string, where the string value |
| * adheres to the following syntax: |
| * |
| * count ( 'Z' repeat | number ) |
| * |
| * where each token is separated by whitespace, except that 'Z' followed by repeat |
| * are considered to be a single token with no intervening whitespace, and where |
| * 'Z' repeat encodes repeated zeroes. |
| * @param dp the adjustments array |
| * @param paCount the number of entries to encode from adjustments array |
| * @return the encoded value |
| */ |
| public static String encodePositionAdjustments(int[][] dp, int paCount) { |
| assert dp != null; |
| StringBuffer sb = new StringBuffer(); |
| int na = paCount; |
| int nz = 0; |
| sb.append(na); |
| for (int i = 0; i < na; i++) { |
| int[] pa = dp [ i ]; |
| if (pa != null) { |
| for (int k = 0; k < 4; k++) { |
| int a = pa [ k ]; |
| if (a != 0) { |
| encodeNextAdjustment(sb, nz, a); |
| nz = 0; |
| } else { |
| nz++; |
| } |
| } |
| } else { |
| nz += 4; |
| } |
| } |
| encodeNextAdjustment(sb, nz, 0); |
| return sb.toString(); |
| } |
| |
| /** |
| * Encode a glyph position adjustments array as a string, where the string value |
| * adheres to the following syntax: |
| * |
| * count ( 'Z' repeat | number ) |
| * |
| * where each token is separated by whitespace, except that 'Z' followed by repeat |
| * are considered to be a single token with no intervening whitespace. |
| * @param dp the adjustments array |
| * @return the encoded value |
| */ |
| public static String encodePositionAdjustments(int[][] dp) { |
| assert dp != null; |
| return encodePositionAdjustments(dp, dp.length); |
| } |
| |
| private static void encodeNextAdjustment(StringBuffer sb, int nz, int a) { |
| encodeZeroes(sb, nz); |
| encodeAdjustment(sb, a); |
| } |
| |
| private static void encodeZeroes(StringBuffer sb, int nz) { |
| if (nz > 0) { |
| sb.append(' '); |
| if (nz == 1) { |
| sb.append('0'); |
| } else { |
| sb.append('Z'); |
| sb.append(nz); |
| } |
| } |
| } |
| |
| private static void encodeAdjustment(StringBuffer sb, int a) { |
| if (a != 0) { |
| sb.append(' '); |
| sb.append(a); |
| } |
| } |
| |
| /** |
| * Decode a string as a glyph position adjustments array, where the string |
| * shall adhere to the syntax specified by {@link #encodePositionAdjustments}. |
| * @param value the encoded value |
| * @return the position adjustments array |
| */ |
| public static int[][] decodePositionAdjustments(String value) { |
| int[][] dp = null; |
| if (value != null) { |
| String[] sa = value.split("\\s"); |
| if (sa != null) { |
| if (sa.length > 0) { |
| int na = Integer.parseInt(sa[0]); |
| dp = new int [ na ] [ 4 ]; |
| for (int i = 1, n = sa.length, k = 0; i < n; i++) { |
| String s = sa [ i ]; |
| if (s.charAt(0) == 'Z') { |
| int nz = Integer.parseInt(s.substring(1)); |
| k += nz; |
| } else { |
| dp [ k / 4 ] [ k % 4 ] = Integer.parseInt(s); |
| k += 1; |
| } |
| } |
| } |
| } |
| } |
| return dp; |
| } |
| |
| /** |
| * Returns an attribute value as a glyph position adjustments array. The string value |
| * is expected to be a non-empty sequence of either Z<repeat> or <number>, where the |
| * former encodes a repeat count (of zeroes) and the latter encodes a integer number, |
| * and where each item is separated by whitespace. |
| * @param attributes the Attributes object |
| * @param name the name of the attribute |
| * @return the position adjustments array |
| */ |
| public static int[][] getAttributeAsPositionAdjustments(Attributes attributes, String name) { |
| String s = attributes.getValue(name); |
| if (s == null) { |
| return null; |
| } else { |
| return decodePositionAdjustments(s.trim()); |
| } |
| } |
| |
| /** |
| * Escape '<', '>' and '&' using NCRs. |
| * @param unescaped string |
| * @return escaped string |
| */ |
| public static String escape(String unescaped) { |
| int needsEscape = 0; |
| for (int i = 0, n = unescaped.length(); i < n; ++i) { |
| char c = unescaped.charAt(i); |
| if ((c == '<') || (c == '>') || (c == '&')) { |
| ++needsEscape; |
| } |
| } |
| if (needsEscape > 0) { |
| StringBuffer sb = new StringBuffer(unescaped.length() + 6 * needsEscape); |
| for (int i = 0, n = unescaped.length(); i < n; ++i) { |
| char c = unescaped.charAt(i); |
| if ((c == '<') || (c == '>') || (c == '&')) { |
| sb.append("&#x"); |
| sb.append(Integer.toString(c, 16)); |
| sb.append(';'); |
| } else { |
| sb.append(c); |
| } |
| } |
| return sb.toString(); |
| } else { |
| return unescaped; |
| } |
| } |
| |
| } |