001: /**
002: * ========================================
003: * JFreeReport : a free Java report library
004: * ========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * $Id: CharacterEntityParser.java 3048 2007-07-28 18:02:42Z tmorgner $
027: * ------------
028: * (C) Copyright 2000-2005, by Object Refinery Limited.
029: * (C) Copyright 2005-2007, by Pentaho Corporation.
030: */package org.jfree.report.util;
031:
032: import java.util.Enumeration;
033: import java.util.Properties;
034:
035: /**
036: * The character entity parser replaces all known occurrences of an entity in the format
037: * &entityname;.
038: *
039: * @author Thomas Morgner
040: */
041: public class CharacterEntityParser {
042: /**
043: * the entities, keyed by entity name.
044: */
045: private final Properties entities;
046:
047: /**
048: * the reverse lookup entities, keyed by character.
049: */
050: private final Properties reverse;
051:
052: /**
053: * Creates a new CharacterEntityParser and initializes the parser with the given set of
054: * entities.
055: *
056: * @param characterEntities the entities used for the parser
057: */
058: public CharacterEntityParser(final Properties characterEntities) {
059: entities = characterEntities;
060: reverse = new Properties();
061: final Enumeration keys = entities.keys();
062: while (keys.hasMoreElements()) {
063: final String key = (String) keys.nextElement();
064: final String value = entities.getProperty(key);
065: reverse.setProperty(value, key);
066: }
067: }
068:
069: /**
070: * create a new Character entity parser and initializes the parser with the entities
071: * defined in the XML standard.
072: *
073: * @return the CharacterEntityParser initialized with XML entities.
074: */
075: public static CharacterEntityParser createXMLEntityParser() {
076: final Properties entities = new Properties();
077: entities.setProperty("amp", "&");
078: entities.setProperty("quot", "\"");
079: entities.setProperty("lt", "<");
080: entities.setProperty("gt", ">");
081: entities.setProperty("apos", "\u0027");
082: return new CharacterEntityParser(entities);
083: }
084:
085: /**
086: * returns the entities used in the parser.
087: *
088: * @return the properties for this parser.
089: */
090: private Properties getEntities() {
091: return entities;
092: }
093:
094: /**
095: * returns the reverse-lookup table for the entities.
096: *
097: * @return the reverse-lookup properties for this parsers.
098: */
099: private Properties getReverse() {
100: return reverse;
101: }
102:
103: /**
104: * Looks up the character for the entity name specified in <code>key</code>.
105: *
106: * @param key the entity name
107: * @return the character as string with a length of 1
108: */
109: private String lookupCharacter(final String key) {
110: return getEntities().getProperty(key);
111: }
112:
113: /**
114: * Performs a reverse lookup, to retrieve the entity name for a given character.
115: *
116: * @param character the character that should be translated into the entity
117: * @return the entity name for the character or the untranslated character.
118: */
119: private String lookupEntity(final String character) {
120: final String val = getReverse().getProperty(character);
121: if (val == null) {
122: return null;
123: } else {
124: return "&" + val + ";";
125: }
126: }
127:
128: /**
129: * Encode the given String, so that all known entites are encoded. All characters
130: * represented by these entites are now removed from the string.
131: *
132: * @param value the original string
133: * @return the encoded string.
134: */
135: public String encodeEntities(final String value) {
136: final StringBuffer writer = new StringBuffer();
137: for (int i = 0; i < value.length(); i++) {
138: final String character = String.valueOf(value.charAt(i));
139: final String lookup = lookupEntity(character);
140: if (lookup == null) {
141: writer.append(character);
142: } else {
143: writer.append(lookup);
144: }
145: }
146: return writer.toString();
147: }
148:
149: /**
150: * Decode the string, all known entities are replaced by their resolved characters.
151: *
152: * @param value the string that should be decoded.
153: * @return the decoded string.
154: */
155: public String decodeEntities(final String value) {
156: int parserIndex = 0;
157: int subStart = value.indexOf("&", parserIndex);
158: if (subStart == -1) {
159: return value;
160: }
161: int subEnd = value.indexOf(";", subStart);
162: if (subEnd == -1) {
163: return value;
164: }
165:
166: final StringBuffer bufValue = new StringBuffer(value.substring(
167: 0, subStart));
168: do {
169: // at this point we know, that there is at least one entity ..
170: if (value.charAt(subStart + 1) == '#') {
171: final int subValue = TextUtilities.parseInt(value
172: .substring(subStart + 2, subEnd), 0);
173: if ((subValue >= 1) && (subValue <= 65536)) {
174: final char[] chr = new char[1];
175: chr[0] = (char) subValue;
176: bufValue.append(chr);
177: } else {
178: // invalid entity, do not decode ..
179: bufValue.append(value.substring(subStart, subEnd));
180: }
181: } else {
182: final String entity = value.substring(subStart + 1,
183: subEnd);
184: final String replaceString = lookupCharacter(entity);
185: if (replaceString != null) {
186: bufValue.append(decodeEntities(replaceString));
187: } else {
188: bufValue.append("&");
189: bufValue.append(entity);
190: bufValue.append(";");
191: }
192: }
193: parserIndex = subEnd + 1;
194: subStart = value.indexOf("&", parserIndex);
195: if (subStart == -1) {
196: bufValue.append(value.substring(parserIndex));
197: subEnd = -1;
198: } else {
199: subEnd = value.indexOf(";", subStart);
200: if (subEnd == -1) {
201: bufValue.append(value.substring(parserIndex));
202: } else {
203: bufValue.append(value.substring(parserIndex,
204: subStart));
205: }
206: }
207: } while (subStart != -1 && subEnd != -1);
208:
209: return bufValue.toString();
210: }
211: }
|