blob: 3f815e59edae40d00809f250c84222c131a7f45c [file] [log] [blame]
/*
* 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;
}
}
}