View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.util;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Enumeration;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.Properties;
25  
26  import org.springframework.util.Assert;
27  
28  /**
29   * Represents a set of character entity references defined by the
30   * HTML 4.0 standard.
31   *
32   * <p>A complete description of the HTML 4.0 character set can be found
33   * at http://www.w3.org/TR/html4/charset.html.
34   *
35   * @author Juergen Hoeller
36   * @author Martin Kersten
37   * @author Craig Andrews
38   * @since 1.2.1
39   */
40  class HtmlCharacterEntityReferences {
41  
42  	private static final String PROPERTIES_FILE = "HtmlCharacterEntityReferences.properties";
43  
44  	static final char REFERENCE_START = '&';
45  
46  	static final String DECIMAL_REFERENCE_START = "&#";
47  
48  	static final String HEX_REFERENCE_START = "&#x";
49  
50  	static final char REFERENCE_END = ';';
51  
52  	static final char CHAR_NULL = (char) -1;
53  
54  
55  	private final String[] characterToEntityReferenceMap = new String[3000];
56  
57  	private final Map<String, Character> entityReferenceToCharacterMap = new HashMap<String, Character>(252);
58  
59  
60  	/**
61  	 * Returns a new set of character entity references reflecting the HTML 4.0 character set.
62  	 */
63  	public HtmlCharacterEntityReferences() {
64  		Properties entityReferences = new Properties();
65  
66  		// Load reference definition file
67  		InputStream is = HtmlCharacterEntityReferences.class.getResourceAsStream(PROPERTIES_FILE);
68  		if (is == null) {
69  			throw new IllegalStateException(
70  					"Cannot find reference definition file [HtmlCharacterEntityReferences.properties] as class path resource");
71  		}
72  		try {
73  			try {
74  				entityReferences.load(is);
75  			}
76  			finally {
77  				is.close();
78  			}
79  		}
80  		catch (IOException ex) {
81  			throw new IllegalStateException(
82  					"Failed to parse reference definition file [HtmlCharacterEntityReferences.properties]: " +  ex.getMessage());
83  		}
84  
85  		// Parse reference definition properties
86  		Enumeration<?> keys = entityReferences.propertyNames();
87  		while (keys.hasMoreElements()) {
88  			String key = (String) keys.nextElement();
89  			int referredChar = Integer.parseInt(key);
90  			Assert.isTrue((referredChar < 1000 || (referredChar >= 8000 && referredChar < 10000)),
91  					"Invalid reference to special HTML entity: " + referredChar);
92  			int index = (referredChar < 1000 ? referredChar : referredChar - 7000);
93  			String reference = entityReferences.getProperty(key);
94  			this.characterToEntityReferenceMap[index] = REFERENCE_START + reference + REFERENCE_END;
95  			this.entityReferenceToCharacterMap.put(reference, (char) referredChar);
96  		}
97  	}
98  
99  
100 	/**
101 	 * Return the number of supported entity references.
102 	 */
103 	public int getSupportedReferenceCount() {
104 		return this.entityReferenceToCharacterMap.size();
105 	}
106 
107 	/**
108 	 * Return true if the given character is mapped to a supported entity reference.
109 	 */
110 	public boolean isMappedToReference(char character) {
111 		return isMappedToReference(character, WebUtils.DEFAULT_CHARACTER_ENCODING);
112 	}
113 
114 	/**
115 	 * Return true if the given character is mapped to a supported entity reference.
116 	 */
117 	public boolean isMappedToReference(char character, String encoding) {
118 		return (convertToReference(character, encoding) != null);
119 	}
120 
121 	/**
122 	 * Return the reference mapped to the given character or {@code null}.
123 	 */
124 	public String convertToReference(char character) {
125 	   return convertToReference(character, WebUtils.DEFAULT_CHARACTER_ENCODING);
126 	}
127 
128 	/**
129 	 * Return the reference mapped to the given character or {@code null}.
130 	 * @since 4.1.2
131 	 */
132 	public String convertToReference(char character, String encoding) {
133 		if (encoding.startsWith("UTF-")){
134 			switch (character){
135 				case '<':
136 					return "&lt;";
137 				case '>':
138 					return "&gt;";
139 				case '"':
140 					return "&quot;";
141 				case '&':
142 					return "&amp;";
143 				case '\'':
144 					return "&#39;";
145 			}
146 		}
147 		else if (character < 1000 || (character >= 8000 && character < 10000)) {
148 			int index = (character < 1000 ? character : character - 7000);
149 			String entityReference = this.characterToEntityReferenceMap[index];
150 			if (entityReference != null) {
151 				return entityReference;
152 			}
153 		}
154 		return null;
155 	}
156 
157 	/**
158 	 * Return the char mapped to the given entityReference or -1.
159 	 */
160 	public char convertToCharacter(String entityReference) {
161 		Character referredCharacter = this.entityReferenceToCharacterMap.get(entityReference);
162 		if (referredCharacter != null) {
163 			return referredCharacter;
164 		}
165 		return CHAR_NULL;
166 	}
167 
168 }