/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.libraries.xmlns.writer; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; /** * The character entity parser replaces all known occurrences of an entity in the format &entityname;. * * @author Thomas Morgner */ public class CharacterEntityParser { private String[] charMap; /** * the entities, keyed by entity name. */ private final HashMap entities; /** * Creates a new CharacterEntityParser and initializes the parser with the given set of entities. * * @param characterEntities the entities used for the parser */ public CharacterEntityParser( final Properties characterEntities ) { if ( characterEntities == null ) { throw new NullPointerException( "CharacterEntities must not be null" ); } entities = new HashMap( characterEntities ); charMap = new String[ 65536 ]; final Iterator entries = entities.entrySet().iterator(); while ( entries.hasNext() ) { final Map.Entry entry = (Map.Entry) entries.next(); final String value = (String) entry.getValue(); final String entityName = (String) entry.getKey(); if ( value.length() != 1 ) { throw new IllegalStateException(); } charMap[ value.charAt( 0 ) ] = entityName; } } /** * Creates a new CharacterEntityParser and initializes the parser with the given set of entities. * * @param characterEntities the entities used for the parser */ public CharacterEntityParser( final HashMap characterEntities ) { if ( characterEntities == null ) { throw new NullPointerException( "CharacterEntities must not be null" ); } entities = (HashMap) characterEntities.clone(); charMap = new String[ 65536 ]; final Iterator entries = entities.entrySet().iterator(); while ( entries.hasNext() ) { final Map.Entry entry = (Map.Entry) entries.next(); final String value = (String) entry.getValue(); final String entityName = (String) entry.getKey(); if ( value.length() != 1 ) { throw new IllegalStateException(); } charMap[ value.charAt( 0 ) ] = entityName; } } /** * create a new Character entity parser and initializes the parser with the entities defined in the XML standard. * * @return the CharacterEntityParser initialized with XML entities. */ public static CharacterEntityParser createXMLEntityParser() { final HashMap entities = new HashMap(); entities.put( "amp", "&" ); entities.put( "quot", "\"" ); entities.put( "lt", "<" ); entities.put( "gt", ">" ); entities.put( "apos", "\u0027" ); return new CharacterEntityParser( entities ); } /** * returns the entities used in the parser. * * @return the properties for this parser. */ private HashMap getEntities() { return entities; } /** * Looks up the character for the entity name specified in <code>key</code>. * * @param key the entity name * @return the character as string with a length of 1 */ private String lookupCharacter( final String key ) { return (String) getEntities().get( key ); } /** * Encode the given String, so that all known entites are encoded. All characters represented by these entites are now * removed from the string. * * @param value the original string * @return the encoded string. */ public String encodeEntities( final String value ) { if ( value == null ) { throw new NullPointerException(); } final int length = value.length(); final StringBuffer writer = new StringBuffer( length ); for ( int i = 0; i < length; i++ ) { final char character = value.charAt( i ); final String lookup = charMap[ character ]; if ( lookup == null ) { writer.append( character ); } else { writer.append( '&' ); writer.append( lookup ); writer.append( ';' ); } } return writer.toString(); } /** * Decode the string, all known entities are replaced by their resolved characters. * * @param value the string that should be decoded. * @return the decoded string. */ public String decodeEntities( final String value ) { if ( value == null ) { throw new NullPointerException(); } int parserIndex = 0; int subStart = value.indexOf( '&', parserIndex ); if ( subStart == -1 ) { return value; } int subEnd = value.indexOf( ';', subStart ); if ( subEnd == -1 ) { return value; } final StringBuffer bufValue = new StringBuffer( value.substring( 0, subStart ) ); do { // at this point we know, that there is at least one entity .. if ( value.charAt( subStart + 1 ) == '#' ) { final int subValue = parseInt( value.substring( subStart + 2, subEnd ), 0 ); if ( ( subValue >= 1 ) && ( subValue <= 65536 ) ) { final char[] chr = new char[ 1 ]; chr[ 0 ] = (char) subValue; bufValue.append( chr ); } else { // invalid entity, do not decode .. bufValue.append( value.substring( subStart, subEnd ) ); } } else { final String entity = value.substring( subStart + 1, subEnd ); final String replaceString = lookupCharacter( entity ); if ( replaceString != null ) { bufValue.append( decodeEntities( replaceString ) ); } else { bufValue.append( '&' ); bufValue.append( entity ); bufValue.append( ';' ); } } parserIndex = subEnd + 1; subStart = value.indexOf( '&', parserIndex ); if ( subStart == -1 ) { bufValue.append( value.substring( parserIndex ) ); subEnd = -1; } else { subEnd = value.indexOf( ';', subStart ); if ( subEnd == -1 ) { bufValue.append( value.substring( parserIndex ) ); } else { bufValue.append( value.substring( parserIndex, subStart ) ); } } } while ( subStart != -1 && subEnd != -1 ); return bufValue.toString(); } /** * Parses the given string into an int-value. On errors the default value is returned. * * @param s the string * @param defaultVal the default value that should be used in case of errors * @return the parsed int or the default value. */ private int parseInt( final String s, final int defaultVal ) { if ( s == null ) { return defaultVal; } try { return Integer.parseInt( s ); } catch ( Exception e ) { // ignored .. } return defaultVal; } }