View Javadoc

1   /*
2    *  jDTAUS Core Utilities
3    *  Copyright (C) 2012 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.text.util;
22  
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.lang.ref.Reference;
26  import java.lang.ref.SoftReference;
27  import java.net.URL;
28  import java.util.Collections;
29  import java.util.Enumeration;
30  import java.util.HashMap;
31  import java.util.Map;
32  import java.util.Properties;
33  
34  /**
35   * Provides static methods for encoding/decoding HTML.
36   *
37   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
38   * @version $JDTAUS: HtmlEntities.java 8641 2012-09-27 06:45:17Z schulte $
39   * @since 1.11
40   */
41  class HtmlEntities
42  {
43  
44      /** Mapping of HTML entity names to characters. */
45      private static volatile Reference/*<Map<String,Character>>*/ entityMap;
46  
47      /** Mapping of characters to HTML entity names. */
48      private static volatile Reference/*<Map<Character,String>>*/ characterMap;
49  
50      /** Constant for the prefix of keys of HTML entity properties. */
51      private static final String PROPERTY_PREFIX = "HtmlEntities.entityName.";
52  
53      /**
54       * Decodes an entity name to a character.
55       *
56       * @param entityName The name of the entity to decode.
57       *
58       * @return The character represented by {@code entityName} or {@code null}, if no such character is found.
59       *
60       * @throws NullPointerException if {@code entityName} is {@code null}.
61       */
62      static Character toCharacter( final String entityName )
63      {
64          if ( entityName == null )
65          {
66              throw new NullPointerException( "entityName" );
67          }
68  
69          Map map = entityMap != null ? (Map) entityMap.get() : null;
70  
71          if ( map == null )
72          {
73              map = new HashMap/*<String,Character>*/( 512 );
74              InputStream in = null;
75              boolean close = true;
76  
77              final URL resources = HtmlEntities.class.getResource( "HtmlEntities.properties" );
78              final Properties properties = new Properties();
79  
80              try
81              {
82                  in = resources.openStream();
83                  properties.load( in );
84                  in.close();
85                  close = false;
86              }
87              catch ( final IOException e )
88              {
89                  throw new AssertionError( e );
90              }
91              finally
92              {
93                  try
94                  {
95                      if ( close && in != null )
96                      {
97                          in.close();
98                      }
99                  }
100                 catch ( final IOException e )
101                 {
102                     throw new AssertionError( e );
103                 }
104             }
105 
106             for ( final Enumeration e = properties.propertyNames(); e.hasMoreElements(); )
107             {
108                 final String name = (String) e.nextElement();
109                 final String value = properties.getProperty( name );
110 
111                 if ( name.startsWith( PROPERTY_PREFIX ) )
112                 {
113                     map.put( name.substring( PROPERTY_PREFIX.length() ), new Character( value.charAt( 0 ) ) );
114                 }
115             }
116 
117             entityMap = new SoftReference( Collections.synchronizedMap( map ) );
118         }
119 
120         return (Character) map.get( entityName );
121     }
122 
123     /**
124      * Encodes a character to an entity name.
125      *
126      * @param character The character to encode.
127      *
128      * @return The entity name representing {@code character} or {@code null}, if no such entity name is found.
129      *
130      * @throws NullPointerException if {@code character} is {@code null}.
131      */
132     static String toEntity( final Character character )
133     {
134         if ( character == null )
135         {
136             throw new NullPointerException( "character" );
137         }
138 
139         Map map = characterMap != null ? (Map) characterMap.get() : null;
140 
141         if ( map == null )
142         {
143             map = new HashMap/*<Character,String>*/( 512 );
144             InputStream in = null;
145             boolean close = true;
146 
147             final URL resources = HtmlEntities.class.getResource( "HtmlEntities.properties" );
148             final Properties properties = new Properties();
149 
150             try
151             {
152                 in = resources.openStream();
153                 properties.load( in );
154                 in.close();
155                 close = false;
156             }
157             catch ( final IOException e )
158             {
159                 throw new AssertionError( e );
160             }
161             finally
162             {
163                 try
164                 {
165                     if ( close && in != null )
166                     {
167                         in.close();
168                     }
169                 }
170                 catch ( final IOException e )
171                 {
172                     throw new AssertionError( e );
173                 }
174             }
175 
176             for ( final Enumeration e = properties.propertyNames(); e.hasMoreElements(); )
177             {
178                 final String name = (String) e.nextElement();
179                 final String value = properties.getProperty( name );
180 
181                 if ( name.startsWith( PROPERTY_PREFIX ) )
182                 {
183                     map.put( new Character( value.charAt( 0 ) ), name.substring( PROPERTY_PREFIX.length() ) );
184                 }
185             }
186 
187             characterMap = new SoftReference( Collections.synchronizedMap( map ) );
188         }
189 
190         return (String) map.get( character );
191     }
192 
193     /**
194      * Encodes a string to HTML.
195      *
196      * @param str The string to encode or {@code null}.
197      *
198      * @return {@code str} encoded to HTML or {@code null}.
199      */
200     static String escapeHtml( final String str )
201     {
202         String encoded = null;
203 
204         if ( str != null )
205         {
206             final StringBuffer b = new StringBuffer( str.length() );
207 
208             for ( int i = 0, s0 = str.length(); i < s0; i++ )
209             {
210                 final Character c = new Character( str.charAt( i ) );
211                 final String entityName = toEntity( c );
212 
213                 if ( entityName != null )
214                 {
215                     b.append( '&' ).append( entityName ).append( ';' );
216                 }
217                 else
218                 {
219                     b.append( c );
220                 }
221             }
222 
223             encoded = b.toString();
224         }
225 
226         return encoded;
227     }
228 
229     /**
230      * Decodes HTML to a string.
231      *
232      * @param html The HTML to decode or {@code null}.
233      *
234      * @return {@code html} decoded or {@code null}.
235      */
236     static String unescapeHtml( final String html )
237     {
238         String decoded = null;
239 
240         if ( html != null )
241         {
242             final StringBuffer b = new StringBuffer( html.length() );
243             final StringBuffer entityName = new StringBuffer( 8 );
244             boolean parsingEntityName = false;
245 
246             for ( int i = 0, s0 = html.length(); i < s0; i++ )
247             {
248                 final char c = html.charAt( i );
249 
250                 if ( c == '&' )
251                 {
252                     parsingEntityName = true;
253                     entityName.setLength( 0 );
254                 }
255 
256                 if ( parsingEntityName )
257                 {
258                     if ( c == ';' )
259                     {
260                         final Character character = toCharacter( entityName.toString() );
261 
262                         if ( character != null )
263                         {
264                             b.append( character );
265                         }
266                         else
267                         {
268                             b.append( '&' ).append( entityName.toString() ).append( ';' );
269                         }
270 
271                         parsingEntityName = false;
272                     }
273                     else
274                     {
275                         entityName.append( c );
276                     }
277                 }
278                 else
279                 {
280                     b.append( c );
281                 }
282             }
283 
284             decoded = b.toString();
285         }
286 
287         return decoded;
288     }
289 
290 }