Clover coverage report - Code Coverage for tapestry release 4.0-beta-2
Coverage timestamp: Sat Jul 9 2005 22:02:17 EDT
file stats: LOC: 283   Methods: 9
NCLOC: 183   Classes: 1
30 day Evaluation License registered to hlship@comcast.net Your 30 day evaluation period has expired. Please visit http://www.cenqua.com to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
SerializableAdaptor.java 89.1% 95.7% 100% 93.9%
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  1 public String squeeze(DataSqueezer squeezer, Object data) throws IOException
 73    {
 74  1 ByteArrayOutputStream bos = null;
 75  1 GZIPOutputStream gos = null;
 76  1 ObjectOutputStream oos = null;
 77  1 byte[] byteData = null;
 78   
 79  1 try
 80    {
 81  1 bos = new ByteArrayOutputStream();
 82  1 gos = new GZIPOutputStream(bos);
 83  1 oos = new ObjectOutputStream(gos);
 84   
 85  1 oos.writeObject(data);
 86  1 oos.close();
 87    }
 88    finally
 89    {
 90  1 close(oos);
 91  1 close(gos);
 92  1 close(bos);
 93    }
 94   
 95  1 byteData = bos.toByteArray();
 96   
 97  1 StringBuffer encoded = new StringBuffer(2 * byteData.length);
 98  1 char[] base64 = new char[4];
 99   
 100  1 encoded.append(PREFIX);
 101   
 102  1 for (int i = 0; i < byteData.length; i += 3)
 103    {
 104  163 encodeBlock(byteData, i, base64);
 105  163 encoded.append(base64);
 106    }
 107   
 108  1 return encoded.toString();
 109    }
 110   
 111  3 private void close(OutputStream stream)
 112    {
 113  3 if (stream != null)
 114    {
 115  3 try
 116    {
 117  3 stream.close();
 118    }
 119    catch (IOException ex)
 120    {
 121    // Ignore.
 122    }
 123    }
 124    }
 125   
 126  3 private void close(InputStream stream)
 127    {
 128  3 if (stream != null)
 129    {
 130  3 try
 131    {
 132  3 stream.close();
 133    }
 134    catch (IOException ex)
 135    {
 136    // Ignore.
 137    }
 138    }
 139    }
 140  1 public Object unsqueeze(DataSqueezer squeezer, String string) throws IOException
 141    {
 142  1 ByteArrayInputStream bis = null;
 143  1 GZIPInputStream gis = null;
 144  1 ObjectInputStream ois = null;
 145  1 byte[] byteData;
 146   
 147    // Strip off the first character and decode the rest.
 148   
 149  1 byteData = decode(string.substring(1));
 150   
 151  1 try
 152    {
 153  1 bis = new ByteArrayInputStream(byteData);
 154  1 gis = new GZIPInputStream(bis);
 155  1 ois = new ResolvingObjectInputStream(squeezer.getResolver(), gis);
 156   
 157  1 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  1 close(ois);
 169  1 close(gis);
 170  1 close(bis);
 171    }
 172    }
 173   
 174  37 public void register(DataSqueezer squeezer)
 175    {
 176  37 squeezer.register(PREFIX, Serializable.class, this);
 177    }
 178   
 179  163 private static void encodeBlock(byte[] raw, int offset, char[] base64) throws IOException
 180    {
 181  163 int block = 0;
 182  163 int slack = raw.length - offset - 1;
 183  163 int end = (slack >= 2) ? 2 : slack;
 184   
 185  163 for (int i = 0; i <= end; i++)
 186    {
 187  488 byte b = raw[offset + i];
 188  488 int neuter = (b < 0) ? b + 256 : b;
 189  488 block += neuter << (8 * (2 - i));
 190    }
 191   
 192  163 for (int i = 0; i < 4; i++)
 193    {
 194  652 int sixbit = (block >>> (6 * (3 - i))) & 0x3f;
 195  652 base64[i] = getChar(sixbit);
 196    }
 197   
 198  163 if (slack < 1)
 199  0 base64[2] = PAD;
 200   
 201  163 if (slack < 2)
 202  1 base64[3] = PAD;
 203    }
 204   
 205  652 protected static char getChar(int sixBit) throws IOException
 206    {
 207  652 if (sixBit >= 0 && sixBit <= 25)
 208  274 return (char) ('A' + sixBit);
 209   
 210  378 if (sixBit >= 26 && sixBit <= 51)
 211  261 return (char) ('a' + (sixBit - 26));
 212   
 213  117 if (sixBit >= 52 && sixBit <= 61)
 214  94 return (char) ('0' + (sixBit - 52));
 215   
 216  23 if (sixBit == 62)
 217  12 return CH_62;
 218   
 219  11 if (sixBit == 63)
 220  11 return CH_63;
 221   
 222  0 throw new IOException(
 223    Tapestry.format("SerializableAdaptor.unable-to-convert", Integer.toString(sixBit)));
 224    }
 225   
 226  1 public static byte[] decode(String string) throws IOException
 227    {
 228  1 int pad = 0;
 229  1 char[] base64 = string.toCharArray();
 230   
 231  1 for (int i = base64.length - 1; base64[i] == PAD; i--)
 232  1 pad++;
 233   
 234  1 int length = base64.length * 6 / 8 - pad;
 235  1 byte[] raw = new byte[length];
 236  1 int rawIndex = 0;
 237   
 238  1 for (int i = 0; i < base64.length; i += 4)
 239    {
 240  163 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  163 for (int j = 0; j < 3 && rawIndex + j < raw.length; j++)
 247  488 raw[rawIndex + j] = (byte) ((block >> (8 * (2 - j))) & 0xff);
 248   
 249  163 rawIndex += 3;
 250    }
 251   
 252  1 return raw;
 253    }
 254   
 255  652 private static int getValue(char c) throws IOException
 256    {
 257  652 if (c >= 'A' && c <= 'Z')
 258  273 return c - 'A';
 259   
 260  379 if (c >= 'a' && c <= 'z')
 261  261 return c - 'a' + 26;
 262   
 263  118 if (c >= '0' && c <= '9')
 264  94 return c - '0' + 52;
 265   
 266  24 if (c == CH_62)
 267  12 return 62;
 268   
 269  12 if (c == CH_63)
 270  11 return 63;
 271   
 272    // Pad character
 273   
 274  1 if (c == PAD)
 275  1 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    }