Clover coverage report - Code Coverage for tapestry release 4.0-alpha-3
Coverage timestamp: Mon May 16 2005 09:05:49 EDT
file stats: LOC: 283   Methods: 9
NCLOC: 183   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
SerializableAdaptor.java 91.3% 96.8% 100% 95.3%
coverage coverage
 1   
 // Copyright 2004, 2005 The Apache Software Foundation
 2   
 //
 3   
 // Licensed under the Apache License, Version 2.0 (the "License");
 4   
 // you may not use this file except in compliance with the License.
 5   
 // You may obtain a copy of the License at
 6   
 //
 7   
 //     http://www.apache.org/licenses/LICENSE-2.0
 8   
 //
 9   
 // Unless required by applicable law or agreed to in writing, software
 10   
 // distributed under the License is distributed on an "AS IS" BASIS,
 11   
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12   
 // See the License for the specific language governing permissions and
 13   
 // limitations under the License.
 14   
 
 15   
 package org.apache.tapestry.util.io;
 16   
 
 17   
 import java.io.ByteArrayInputStream;
 18   
 import java.io.ByteArrayOutputStream;
 19   
 import java.io.IOException;
 20   
 import java.io.InputStream;
 21   
 import java.io.ObjectInputStream;
 22   
 import java.io.ObjectOutputStream;
 23   
 import java.io.OutputStream;
 24   
 import java.io.Serializable;
 25   
 import java.util.zip.GZIPInputStream;
 26   
 import java.util.zip.GZIPOutputStream;
 27   
 
 28   
 import org.apache.tapestry.Tapestry;
 29   
 import org.apache.tapestry.services.DataSqueezer;
 30   
 
 31   
 /**
 32   
  *  The most complicated of the adaptors, this one takes an arbitrary serializable
 33   
  *  object, serializes it to binary, and encodes it in a Base64 encoding.
 34   
  *
 35   
  *  <p>Encoding and decoding of Base64 strings uses code adapted from work in the public
 36   
  *  domain originally written by Jonathan Knudsen and published in
 37   
  *  O'reilly's "Java Cryptography". Note that we use a <em>modified</em> form of Base64 encoding,
 38   
  *  with URL-safe characters to encode the 62 and 63 values and the pad character.
 39   
  *
 40   
  *  <p>TBD:  Work out some class loader issues involved in deserializing.
 41   
  *
 42   
  *  @author Howard Lewis Ship
 43   
  * 
 44   
  **/
 45   
 
 46   
 class SerializableAdaptor implements ISqueezeAdaptor
 47   
 {
 48   
     private static final String PREFIX = "O";
 49   
 
 50   
     /**
 51   
      *  The PAD character, appended to the end of the string to make things
 52   
      *  line up.  In normal Base64, this is the character '='.
 53   
      *
 54   
      **/
 55   
 
 56   
     private static final char PAD = '.';
 57   
 
 58   
     /**
 59   
      *  Representation for the 6-bit code 63, normally '+' in Base64.
 60   
      *
 61   
      **/
 62   
 
 63   
     private static final char CH_62 = '-';
 64   
 
 65   
     /**
 66   
      *  Representation for the 6-bit code 64, normally '/' in Base64.
 67   
      *
 68   
      **/
 69   
 
 70   
     private static final char CH_63 = '_';
 71   
 
 72  2
     public String squeeze(DataSqueezer squeezer, Object data) throws IOException
 73   
     {
 74  2
         ByteArrayOutputStream bos = null;
 75  2
         GZIPOutputStream gos = null;
 76  2
         ObjectOutputStream oos = null;
 77  2
         byte[] byteData = null;
 78   
 
 79  2
         try
 80   
         {
 81  2
             bos = new ByteArrayOutputStream();
 82  2
             gos = new GZIPOutputStream(bos);
 83  2
             oos = new ObjectOutputStream(gos);
 84   
 
 85  2
             oos.writeObject(data);
 86  2
             oos.close();
 87   
         }
 88   
         finally
 89   
         {
 90  2
             close(oos);
 91  2
             close(gos);
 92  2
             close(bos);
 93   
         }
 94   
 
 95  2
         byteData = bos.toByteArray();
 96   
 
 97  2
         StringBuffer encoded = new StringBuffer(2 * byteData.length);
 98  2
         char[] base64 = new char[4];
 99   
 
 100  2
         encoded.append(PREFIX);
 101   
 
 102  2
         for (int i = 0; i < byteData.length; i += 3)
 103   
         {
 104  265
             encodeBlock(byteData, i, base64);
 105  265
             encoded.append(base64);
 106   
         }
 107   
 
 108  2
         return encoded.toString();
 109   
     }
 110   
 
 111  6
     private void close(OutputStream stream)
 112   
     {
 113  6
         if (stream != null)
 114   
         {
 115  6
             try
 116   
             {
 117  6
                 stream.close();
 118   
             }
 119   
             catch (IOException ex)
 120   
             {
 121   
                 // Ignore.
 122   
             }
 123   
         }
 124   
     }
 125   
 
 126  6
     private void close(InputStream stream)
 127   
     {
 128  6
         if (stream != null)
 129   
         {
 130  6
             try
 131   
             {
 132  6
                 stream.close();
 133   
             }
 134   
             catch (IOException ex)
 135   
             {
 136   
                 // Ignore.
 137   
             }
 138   
         }
 139   
     }
 140  2
     public Object unsqueeze(DataSqueezer squeezer, String string) throws IOException
 141   
     {
 142  2
         ByteArrayInputStream bis = null;
 143  2
         GZIPInputStream gis = null;
 144  2
         ObjectInputStream ois = null;
 145  2
         byte[] byteData;
 146   
 
 147   
         // Strip off the first character and decode the rest.
 148   
 
 149  2
         byteData = decode(string.substring(1));
 150   
 
 151  2
         try
 152   
         {
 153  2
             bis = new ByteArrayInputStream(byteData);
 154  2
             gis = new GZIPInputStream(bis);
 155  2
             ois = new ResolvingObjectInputStream(squeezer.getResolver(), gis);
 156   
 
 157  2
             return ois.readObject();
 158   
         }
 159   
         catch (ClassNotFoundException ex)
 160   
         {
 161   
             // The message is the name of the class.
 162   
 
 163  0
             throw new IOException(
 164   
                 Tapestry.format("SerializableAdaptor.class-not-found", ex.getMessage()));
 165   
         }
 166   
         finally
 167   
         {
 168  2
             close(ois);
 169  2
             close(gis);
 170  2
             close(bis);
 171   
         }
 172   
     }
 173   
 
 174  34
     public void register(DataSqueezer squeezer)
 175   
     {
 176  34
         squeezer.register(PREFIX, Serializable.class, this);
 177   
     }
 178   
 
 179  265
     private static void encodeBlock(byte[] raw, int offset, char[] base64) throws IOException
 180   
     {
 181  265
         int block = 0;
 182  265
         int slack = raw.length - offset - 1;
 183  265
         int end = (slack >= 2) ? 2 : slack;
 184   
 
 185  265
         for (int i = 0; i <= end; i++)
 186   
         {
 187  791
             byte b = raw[offset + i];
 188  791
             int neuter = (b < 0) ? b + 256 : b;
 189  791
             block += neuter << (8 * (2 - i));
 190   
         }
 191   
 
 192  265
         for (int i = 0; i < 4; i++)
 193   
         {
 194  1060
             int sixbit = (block >>> (6 * (3 - i))) & 0x3f;
 195  1060
             base64[i] = getChar(sixbit);
 196   
         }
 197   
 
 198  265
         if (slack < 1)
 199  2
             base64[2] = PAD;
 200   
 
 201  265
         if (slack < 2)
 202  2
             base64[3] = PAD;
 203   
     }
 204   
 
 205  1060
     protected static char getChar(int sixBit) throws IOException
 206   
     {
 207  1060
         if (sixBit >= 0 && sixBit <= 25)
 208  450
             return (char) ('A' + sixBit);
 209   
 
 210  610
         if (sixBit >= 26 && sixBit <= 51)
 211  424
             return (char) ('a' + (sixBit - 26));
 212   
 
 213  186
         if (sixBit >= 52 && sixBit <= 61)
 214  151
             return (char) ('0' + (sixBit - 52));
 215   
 
 216  35
         if (sixBit == 62)
 217  17
             return CH_62;
 218   
 
 219  18
         if (sixBit == 63)
 220  18
             return CH_63;
 221   
 
 222  0
         throw new IOException(
 223   
             Tapestry.format("SerializableAdaptor.unable-to-convert", Integer.toString(sixBit)));
 224   
     }
 225   
 
 226  2
     public static byte[] decode(String string) throws IOException
 227   
     {
 228  2
         int pad = 0;
 229  2
         char[] base64 = string.toCharArray();
 230   
 
 231  2
         for (int i = base64.length - 1; base64[i] == PAD; i--)
 232  4
             pad++;
 233   
 
 234  2
         int length = base64.length * 6 / 8 - pad;
 235  2
         byte[] raw = new byte[length];
 236  2
         int rawIndex = 0;
 237   
 
 238  2
         for (int i = 0; i < base64.length; i += 4)
 239   
         {
 240  265
             int block =
 241   
                 (getValue(base64[i]) << 18)
 242   
                     + (getValue(base64[i + 1]) << 12)
 243   
                     + (getValue(base64[i + 2]) << 6)
 244   
                     + (getValue(base64[i + 3]));
 245   
 
 246  265
             for (int j = 0; j < 3 && rawIndex + j < raw.length; j++)
 247  791
                 raw[rawIndex + j] = (byte) ((block >> (8 * (2 - j))) & 0xff);
 248   
 
 249  265
             rawIndex += 3;
 250   
         }
 251   
 
 252  2
         return raw;
 253   
     }
 254   
 
 255  1060
     private static int getValue(char c) throws IOException
 256   
     {
 257  1060
         if (c >= 'A' && c <= 'Z')
 258  446
             return c - 'A';
 259   
 
 260  614
         if (c >= 'a' && c <= 'z')
 261  424
             return c - 'a' + 26;
 262   
 
 263  190
         if (c >= '0' && c <= '9')
 264  151
             return c - '0' + 52;
 265   
 
 266  39
         if (c == CH_62)
 267  17
             return 62;
 268   
 
 269  22
         if (c == CH_63)
 270  18
             return 63;
 271   
 
 272   
         // Pad character
 273   
 
 274  4
         if (c == PAD)
 275  4
             return 0;
 276   
 
 277  0
         throw new IOException(
 278   
             Tapestry.format(
 279   
                 "SerializableAdaptor.unable-to-interpret-char",
 280   
                 new String(new char[] { c })));
 281   
     }
 282   
 
 283   
 }