Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
Encoder |
|
| 5.555555555555555;5.556 | ||||
Encoder$Block |
|
| 5.555555555555555;5.556 |
1 | /* | |
2 | * Copyright (C) 1998-2000 Semiotek Inc. All Rights Reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted under the terms of either of the following | |
6 | * Open Source licenses: | |
7 | * | |
8 | * The GNU General Public License, version 2, or any later version, as | |
9 | * published by the Free Software Foundation | |
10 | * (http://www.fsf.org/copyleft/gpl.html); | |
11 | * | |
12 | * or | |
13 | * | |
14 | * The Semiotek Public License (http://webmacro.org/LICENSE.) | |
15 | * | |
16 | * This software is provided "as is", with NO WARRANTY, not even the | |
17 | * implied warranties of fitness to purpose, or merchantability. You | |
18 | * assume all risks and liabilities associated with its use. | |
19 | * | |
20 | * See www.webmacro.org for more information on the WebMacro project. | |
21 | */ | |
22 | ||
23 | package org.webmacro.util; | |
24 | ||
25 | import org.slf4j.Logger; | |
26 | import org.slf4j.LoggerFactory; | |
27 | import org.webmacro.Broker; | |
28 | import org.webmacro.InitException; | |
29 | import org.webmacro.ResourceException; | |
30 | import org.webmacro.resource.CacheElement; | |
31 | import org.webmacro.resource.CacheManager; | |
32 | import org.webmacro.resource.ResourceLoader; | |
33 | import org.webmacro.resource.TrivialCacheManager; | |
34 | ||
35 | import java.io.UnsupportedEncodingException; | |
36 | ||
37 | /** | |
38 | * An encoder is used to encode strings into a particular encoding in | |
39 | * preparation for sending the data as part of a response. An encoder is | |
40 | * constructed with a particular output encoding and is then expected to | |
41 | * properly encode strings with that output encoding for its lifetime. | |
42 | * | |
43 | * <p><code>Encoder</code> instances are obtained via the | |
44 | * <code>EncoderProvider</code> implementation that is configured at | |
45 | * initialization time. The encoder provider is responsible communicating | |
46 | * the encoding scheme to be used by the encoder at construct time. | |
47 | * | |
48 | * <p> The mechanism is factored into the encoder interface to allow for | |
49 | * caching encoders with differing caching schemes based on the server | |
50 | * environment's requirements or no caching scheme at all. | |
51 | * | |
52 | * @see EncoderProvider | |
53 | * @since 0.96 | |
54 | * @author Michael Bayne | |
55 | */ | |
56 | ||
57 | public class Encoder implements ResourceLoader | |
58 | { | |
59 | ||
60 | private String _encoding; | |
61 | private CacheManager _cache; | |
62 | 2 | static Logger _log = LoggerFactory.getLogger(Encoder.class); |
63 | ||
64 | /** | |
65 | * Creates an encoder instance with the supplied encoding. | |
66 | * | |
67 | * @exception UnsupportedEncodingException Thrown when the underlying | |
68 | * Java encoding mechanism does not provide support for the requesting | |
69 | * encoding. | |
70 | */ | |
71 | public Encoder (String encoding) | |
72 | throws UnsupportedEncodingException | |
73 | 12 | { |
74 | // enforce some specific rules related to choice of encodings | |
75 | 12 | if (encoding == null || |
76 | encoding.equalsIgnoreCase("UNICODE") || | |
77 | encoding.equalsIgnoreCase("UNICODEBIG") || | |
78 | encoding.equalsIgnoreCase("UNICODELITTLE") || | |
79 | encoding.equalsIgnoreCase("UTF16")) | |
80 | { | |
81 | 0 | String err = "The encoding you specified is invalid: " + |
82 | encoding + ". Note that the UNICODE and UTF16 encodings " + | |
83 | "are not supported by WebMacro because they prefix the " + | |
84 | "stream with a marker indicating whether the stream is " + | |
85 | "big endian or little endian. Instead choose the byte " + | |
86 | "ordering yourself by using the UTF-16BE or UTF-16LE " + | |
87 | "encodings."; | |
88 | 0 | throw new UnsupportedEncodingException(err); |
89 | } | |
90 | ||
91 | // Check to be sure that this encoding is supported. this will | |
92 | // throw an UnsupportedEncodingException if the JVM doesn't | |
93 | // support the requested encoding | |
94 | 12 | "some test string".getBytes(encoding); |
95 | ||
96 | // keep track of this for later | |
97 | 12 | _encoding = encoding; |
98 | 12 | } |
99 | ||
100 | public void init (Broker b, Settings config) | |
101 | throws InitException | |
102 | { | |
103 | String cacheManager; | |
104 | ||
105 | 12 | cacheManager = b.getSetting("Encoder." + _encoding + ".CacheManager"); |
106 | 12 | if (cacheManager == null) |
107 | 12 | cacheManager = b.getSetting("Encoder.*.CacheManager"); |
108 | 12 | if (cacheManager == null || cacheManager.equals("")) |
109 | { | |
110 | 0 | _log.info("No cache manager specified for encoding " + _encoding |
111 | + ", using TrivialCacheManager"); | |
112 | 0 | _cache = new TrivialCacheManager(); |
113 | } | |
114 | else | |
115 | { | |
116 | try | |
117 | { | |
118 | 12 | Class c = b.classForName(cacheManager); |
119 | 12 | _cache = (CacheManager) c.newInstance(); |
120 | } | |
121 | 0 | catch (Exception e) |
122 | { | |
123 | 0 | _log.warn("Unable to load cache manager " + cacheManager |
124 | + " for encoding type " + _encoding | |
125 | + ", using TrivialCacheManager. Reason:\n" + e); | |
126 | 0 | _cache = new TrivialCacheManager(); |
127 | 12 | } |
128 | } | |
129 | 12 | _cache.init(b, config, _encoding); |
130 | 12 | } |
131 | ||
132 | /** | |
133 | * Load an object from permanent storage (or construct it) on | |
134 | * demand. | |
135 | */ | |
136 | public Object load (Object query, CacheElement ce) | |
137 | throws ResourceException | |
138 | { | |
139 | try | |
140 | { | |
141 | 532 | if (query instanceof Block) |
142 | { | |
143 | 532 | String[] source = ((Block) query).text; |
144 | 532 | byte[][] encoded = new byte[source.length][]; |
145 | 2786 | for (int i = 0; i < source.length; i++) |
146 | { | |
147 | 2254 | encoded[i] = source[i].getBytes(_encoding); |
148 | } | |
149 | 532 | return encoded; |
150 | ||
151 | } | |
152 | 0 | else if (query instanceof String) |
153 | { | |
154 | 0 | return ((String) query).getBytes(_encoding); |
155 | } | |
156 | else | |
157 | { | |
158 | 0 | return query.toString().getBytes(_encoding); |
159 | } | |
160 | ||
161 | } | |
162 | 0 | catch (UnsupportedEncodingException uee) |
163 | { | |
164 | // this should never happen as we check in the constructor to | |
165 | // ensure that the encoding is supported | |
166 | 0 | throw new ResourceException("Unable to encode: " + uee); |
167 | } | |
168 | } | |
169 | ||
170 | public Object load (String query, CacheElement ce) | |
171 | throws ResourceException | |
172 | { | |
173 | try | |
174 | { | |
175 | 0 | return query.getBytes(_encoding); |
176 | } | |
177 | 0 | catch (UnsupportedEncodingException uee) |
178 | { | |
179 | // this should never happen as we check in the constructor to | |
180 | // ensure that the encoding is supported | |
181 | 0 | throw new ResourceException("Unable to encode: " + uee); |
182 | } | |
183 | } | |
184 | ||
185 | /** | |
186 | * Encodes the supplied string using the encoding bound to this encoder | |
187 | * at construct time. | |
188 | * | |
189 | * @return The encoded version of the supplied string. | |
190 | * | |
191 | * @exception UnsupportedEncodingException Thrown when the underlying | |
192 | * Java encoding mechanism does not provide support for the encoding | |
193 | * used by this encoder instance. | |
194 | */ | |
195 | public final byte[] encode (String source) | |
196 | throws UnsupportedEncodingException | |
197 | { | |
198 | try | |
199 | { | |
200 | 0 | return (byte[]) _cache.get(source, this); |
201 | } | |
202 | 0 | catch (ResourceException e) |
203 | { | |
204 | 0 | throw new UnsupportedEncodingException("Encoder: Could not encode; " |
205 | + e); | |
206 | } | |
207 | } | |
208 | ||
209 | /** | |
210 | * Encodes the supplied block of strings using the encoding bound to | |
211 | * this encoder at construct time. | |
212 | * | |
213 | * @return The encoded version of the supplied block of strings. | |
214 | * | |
215 | * @exception UnsupportedEncodingException Thrown when the underlying | |
216 | * Java encoding mechanism does not provide support for the encoding | |
217 | * used by this encoder instance. | |
218 | */ | |
219 | public final byte[][] encode (Block source) | |
220 | throws UnsupportedEncodingException | |
221 | { | |
222 | try | |
223 | { | |
224 | 56066 | return (byte[][]) _cache.get(source, this); |
225 | } | |
226 | 0 | catch (ResourceException e) |
227 | { | |
228 | 0 | throw new UnsupportedEncodingException("Encoder: Could not encode; " |
229 | + e); | |
230 | } | |
231 | } | |
232 | ||
233 | /** | |
234 | * The block class provides a means by which encoder users can encode | |
235 | * entire blocks of text at once (and have those encoded blocks | |
236 | * cached). | |
237 | */ | |
238 | public static class Block | |
239 | { | |
240 | ||
241 | public String[] text; | |
242 | ||
243 | public Block (String[] text) | |
244 | 1484 | { |
245 | 1484 | this.text = text; |
246 | ||
247 | // we compute the combined hash of our string array so that we | |
248 | // can behave as an efficient, stable key while allowing our | |
249 | // strings to maintain happy independent existences | |
250 | 1484 | long strhash = 0; |
251 | 6750 | for (int i = 0; i < text.length; i++) |
252 | { | |
253 | 5266 | strhash = (strhash + (long) text[i].hashCode()) % |
254 | Integer.MAX_VALUE; | |
255 | } | |
256 | 1484 | _hashCode = (int) strhash; |
257 | 1484 | } |
258 | ||
259 | public int hashCode () | |
260 | { | |
261 | 56598 | return _hashCode; |
262 | } | |
263 | ||
264 | public boolean equals (Object other) | |
265 | { | |
266 | // we try to be as efficient as possible about this, but to be | |
267 | // correct, we have to compare every string | |
268 | 69930 | if (!(other instanceof Block)) |
269 | { | |
270 | 0 | return false; |
271 | } | |
272 | ||
273 | 69930 | Block ob = (Block) other; |
274 | ||
275 | // check the obvious things | |
276 | 69930 | if (this == ob || text == ob.text) |
277 | { | |
278 | 10750 | return true; |
279 | } | |
280 | 59180 | if (text == null || ob.text == null) |
281 | { | |
282 | 0 | return false; |
283 | } | |
284 | 59180 | if (ob.text.length != text.length) |
285 | { | |
286 | 14396 | return false; |
287 | } | |
288 | ||
289 | // compare each string individually | |
290 | 170392 | for (int i = 0; i < text.length; i++) |
291 | { | |
292 | 125608 | if (!text[i].equals(ob.text[i])) |
293 | { | |
294 | 0 | return false; |
295 | } | |
296 | } | |
297 | ||
298 | 44784 | return true; |
299 | } | |
300 | ||
301 | protected int _hashCode; | |
302 | } | |
303 | } |