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