##### Coverage Report - org.webmacro.util.Encoder

Classes in this File | Line Coverage | Branch Coverage | Complexity
Encoder |
52% | 22/42
50% | 11/22
| 5.555555555555555;5.556
Encoder$Block |
85% | 18/21
72% | 13/18
| 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 |  |
     }