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 2725 2007-04-01 18:49:29Z taqua $ 027 * ------------ 028 * (C) Copyright 2000-2005, by Object Refinery Limited. 029 * (C) Copyright 2005-2007, by Pentaho Corporation. 030 */ 031 package org.jfree.report.util; 032 033 import java.util.Enumeration; 034 import java.util.Properties; 035 036 /** 037 * The character entity parser replaces all known occurrences of an entity in the format 038 * &entityname;. 039 * 040 * @author Thomas Morgner 041 */ 042 public class CharacterEntityParser 043 { 044 /** 045 * the entities, keyed by entity name. 046 */ 047 private final Properties entities; 048 049 /** 050 * the reverse lookup entities, keyed by character. 051 */ 052 private final Properties reverse; 053 054 /** 055 * Creates a new CharacterEntityParser and initializes the parser with the given set of 056 * entities. 057 * 058 * @param characterEntities the entities used for the parser 059 */ 060 public CharacterEntityParser (final Properties characterEntities) 061 { 062 entities = characterEntities; 063 reverse = new Properties(); 064 final Enumeration keys = entities.keys(); 065 while (keys.hasMoreElements()) 066 { 067 final String key = (String) keys.nextElement(); 068 final String value = entities.getProperty(key); 069 reverse.setProperty(value, key); 070 } 071 } 072 073 /** 074 * create a new Character entity parser and initializes the parser with the entities 075 * defined in the XML standard. 076 * 077 * @return the CharacterEntityParser initialized with XML entities. 078 */ 079 public static CharacterEntityParser createXMLEntityParser () 080 { 081 final Properties entities = new Properties(); 082 entities.setProperty("amp", "&"); 083 entities.setProperty("quot", "\""); 084 entities.setProperty("lt", "<"); 085 entities.setProperty("gt", ">"); 086 entities.setProperty("apos", "\u0027"); 087 return new CharacterEntityParser(entities); 088 } 089 090 /** 091 * returns the entities used in the parser. 092 * 093 * @return the properties for this parser. 094 */ 095 private Properties getEntities () 096 { 097 return entities; 098 } 099 100 /** 101 * returns the reverse-lookup table for the entities. 102 * 103 * @return the reverse-lookup properties for this parsers. 104 */ 105 private Properties getReverse () 106 { 107 return reverse; 108 } 109 110 /** 111 * Looks up the character for the entity name specified in <code>key</code>. 112 * 113 * @param key the entity name 114 * @return the character as string with a length of 1 115 */ 116 private String lookupCharacter (final String key) 117 { 118 return getEntities().getProperty(key); 119 } 120 121 /** 122 * Performs a reverse lookup, to retrieve the entity name for a given character. 123 * 124 * @param character the character that should be translated into the entity 125 * @return the entity name for the character or the untranslated character. 126 */ 127 private String lookupEntity (final String character) 128 { 129 final String val = getReverse().getProperty(character); 130 if (val == null) 131 { 132 return null; 133 } 134 else 135 { 136 return "&" + val + ";"; 137 } 138 } 139 140 /** 141 * Encode the given String, so that all known entites are encoded. All characters 142 * represented by these entites are now removed from the string. 143 * 144 * @param value the original string 145 * @return the encoded string. 146 */ 147 public String encodeEntities (final String value) 148 { 149 final StringBuffer writer = new StringBuffer(); 150 for (int i = 0; i < value.length(); i++) 151 { 152 final String character = String.valueOf(value.charAt(i)); 153 final String lookup = lookupEntity(character); 154 if (lookup == null) 155 { 156 writer.append(character); 157 } 158 else 159 { 160 writer.append(lookup); 161 } 162 } 163 return writer.toString(); 164 } 165 166 /** 167 * Decode the string, all known entities are replaced by their resolved characters. 168 * 169 * @param value the string that should be decoded. 170 * @return the decoded string. 171 */ 172 public String decodeEntities (final String value) 173 { 174 int parserIndex = 0; 175 int subStart = value.indexOf("&", parserIndex); 176 if (subStart == -1) 177 { 178 return value; 179 } 180 int subEnd = value.indexOf(";", subStart); 181 if (subEnd == -1) 182 { 183 return value; 184 } 185 186 final StringBuffer bufValue = new StringBuffer(value.substring(0, subStart)); 187 do 188 { 189 // at this point we know, that there is at least one entity .. 190 if (value.charAt(subStart + 1) == '#') 191 { 192 final int subValue = TextUtilities.parseInt(value.substring(subStart + 2, subEnd), 0); 193 if ((subValue >= 1) && (subValue <= 65536)) 194 { 195 final char[] chr = new char[1]; 196 chr[0] = (char) subValue; 197 bufValue.append(chr); 198 } 199 else 200 { 201 // invalid entity, do not decode .. 202 bufValue.append(value.substring(subStart, subEnd)); 203 } 204 } 205 else 206 { 207 final String entity = value.substring(subStart + 1, subEnd); 208 final String replaceString = lookupCharacter(entity); 209 if (replaceString != null) 210 { 211 bufValue.append(decodeEntities(replaceString)); 212 } 213 else 214 { 215 bufValue.append("&"); 216 bufValue.append(entity); 217 bufValue.append(";"); 218 } 219 } 220 parserIndex = subEnd + 1; 221 subStart = value.indexOf("&", parserIndex); 222 if (subStart == -1) 223 { 224 bufValue.append(value.substring(parserIndex)); 225 subEnd = -1; 226 } 227 else 228 { 229 subEnd = value.indexOf(";", subStart); 230 if (subEnd == -1) 231 { 232 bufValue.append(value.substring(parserIndex)); 233 } 234 else 235 { 236 bufValue.append(value.substring(parserIndex, subStart)); 237 } 238 } 239 } 240 while (subStart != -1 && subEnd != -1); 241 242 return bufValue.toString(); 243 } 244 } 245