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: 185   Methods: 4
NCLOC: 105   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
PersistentPropertyDataEncoderImpl.java 100% 100% 100% 100%
coverage
 1   
 // Copyright 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.record;
 16   
 
 17   
 import java.io.BufferedInputStream;
 18   
 import java.io.BufferedOutputStream;
 19   
 import java.io.ByteArrayInputStream;
 20   
 import java.io.ByteArrayOutputStream;
 21   
 import java.io.IOException;
 22   
 import java.io.InputStream;
 23   
 import java.io.ObjectInputStream;
 24   
 import java.io.ObjectOutputStream;
 25   
 import java.util.ArrayList;
 26   
 import java.util.Collections;
 27   
 import java.util.Iterator;
 28   
 import java.util.List;
 29   
 import java.util.zip.GZIPInputStream;
 30   
 import java.util.zip.GZIPOutputStream;
 31   
 
 32   
 import org.apache.commons.codec.binary.Base64;
 33   
 import org.apache.hivemind.ApplicationRuntimeException;
 34   
 import org.apache.hivemind.HiveMind;
 35   
 import org.apache.hivemind.util.Defense;
 36   
 import org.apache.tapestry.util.io.TeeOutputStream;
 37   
 
 38   
 /**
 39   
  * Responsible for converting lists of {@link org.apache.tapestry.record.PropertyChange}s back and
 40   
  * forth to a URL safe encoded string.
 41   
  * <p>
 42   
  * A possible improvement would be to encode the binary data with encryption both on and off, and
 43   
  * select the shortest (prefixing with a character that identifies whether encryption should be used
 44   
  * to decode).
 45   
  * 
 46   
  * @author Howard M. Lewis Ship
 47   
  * @since 4.0
 48   
  */
 49   
 public class PersistentPropertyDataEncoderImpl implements PersistentPropertyDataEncoder
 50   
 {
 51   
     /**
 52   
      * Prefix on the MIME encoding that indicates that the encoded data is not encoded.
 53   
      */
 54   
 
 55   
     public static final String BYTESTREAM_PREFIX = "B";
 56   
 
 57   
     /**
 58   
      * Prefix on the MIME encoding that indicates that the encoded data is encoded with GZIP.
 59   
      */
 60   
 
 61   
     public static final String GZIP_BYTESTREAM_PREFIX = "Z";
 62   
 
 63  4
     public String encodePageChanges(List changes)
 64   
     {
 65  4
         Defense.notNull(changes, "changes");
 66   
 
 67  4
         if (changes.isEmpty())
 68  1
             return "";
 69   
 
 70  3
         try
 71   
         {
 72  3
             ByteArrayOutputStream bosPlain = new ByteArrayOutputStream();
 73  3
             ByteArrayOutputStream bosCompressed = new ByteArrayOutputStream();
 74   
 
 75  3
             GZIPOutputStream gos = new GZIPOutputStream(bosCompressed);
 76   
 
 77  3
             TeeOutputStream tos = new TeeOutputStream(bosPlain, gos);
 78   
 
 79  3
             ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(tos));
 80   
 
 81  3
             writeChangesToStream(changes, oos);
 82   
 
 83  2
             oos.close();
 84   
 
 85  2
             boolean useCompressed = bosCompressed.size() < bosPlain.size();
 86   
 
 87  2
             byte[] data = useCompressed ? bosCompressed.toByteArray() : bosPlain.toByteArray();
 88   
 
 89  2
             byte[] encoded = Base64.encodeBase64(data);
 90   
 
 91  2
             String prefix = useCompressed ? GZIP_BYTESTREAM_PREFIX : BYTESTREAM_PREFIX;
 92   
 
 93  2
             return prefix + new String(encoded);
 94   
         }
 95   
         catch (Exception ex)
 96   
         {
 97  1
             throw new ApplicationRuntimeException(RecordMessages.encodeFailure(ex), ex);
 98   
         }
 99   
     }
 100   
 
 101  5
     public List decodePageChanges(String encoded)
 102   
     {
 103  5
         if (HiveMind.isBlank(encoded))
 104  1
             return Collections.EMPTY_LIST;
 105   
 
 106  4
         String prefix = encoded.substring(0, 1);
 107   
 
 108  4
         if (!(prefix.equals(BYTESTREAM_PREFIX) || prefix.equals(GZIP_BYTESTREAM_PREFIX)))
 109  1
             throw new ApplicationRuntimeException(RecordMessages.unknownPrefix(prefix));
 110   
 
 111  3
         try
 112   
         {
 113   
             // Strip off the prefix, feed that in as a MIME stream.
 114   
 
 115  3
             byte[] decoded = Base64.decodeBase64(encoded.substring(1).getBytes());
 116   
 
 117  3
             InputStream is = new ByteArrayInputStream(decoded);
 118   
 
 119  3
             if (prefix.equals(GZIP_BYTESTREAM_PREFIX))
 120  2
                 is = new GZIPInputStream(is);
 121   
 
 122   
             // I believe this is more efficient; the buffered input stream should ask the
 123   
             // GZIP stream for large blocks of un-gzipped bytes, with should be more efficient.
 124   
             // The object input stream will probably be looking for just a few bytes at
 125   
             // a time.
 126   
 
 127  2
             ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(is));
 128   
 
 129  2
             List result = readChangesFromStream(ois);
 130   
 
 131  2
             ois.close();
 132   
 
 133  2
             return result;
 134   
         }
 135   
         catch (Exception ex)
 136   
         {
 137  1
             throw new ApplicationRuntimeException(RecordMessages.decodeFailure(ex), ex);
 138   
         }
 139   
     }
 140   
 
 141  3
     private void writeChangesToStream(List changes, ObjectOutputStream oos) throws IOException
 142   
     {
 143  3
         oos.writeInt(changes.size());
 144   
 
 145  3
         Iterator i = changes.iterator();
 146  3
         while (i.hasNext())
 147   
         {
 148  22
             PropertyChange pc = (PropertyChange) i.next();
 149   
 
 150  22
             String componentPath = pc.getComponentPath();
 151  22
             String propertyName = pc.getPropertyName();
 152  22
             Object value = pc.getNewValue();
 153   
 
 154  22
             oos.writeBoolean(componentPath != null);
 155   
 
 156  22
             if (componentPath != null)
 157  10
                 oos.writeUTF(componentPath);
 158   
 
 159  22
             oos.writeUTF(propertyName);
 160  22
             oos.writeObject(value);
 161   
         }
 162   
     }
 163   
 
 164  2
     private List readChangesFromStream(ObjectInputStream ois) throws IOException,
 165   
             ClassNotFoundException
 166   
     {
 167  2
         List result = new ArrayList();
 168   
 
 169  2
         int count = ois.readInt();
 170   
 
 171  2
         for (int i = 0; i < count; i++)
 172   
         {
 173  21
             boolean hasPath = ois.readBoolean();
 174  21
             String componentPath = hasPath ? ois.readUTF() : null;
 175  21
             String propertyName = ois.readUTF();
 176  21
             Object value = ois.readObject();
 177   
 
 178  21
             PropertyChangeImpl pc = new PropertyChangeImpl(componentPath, propertyName, value);
 179   
 
 180  21
             result.add(pc);
 181   
         }
 182   
 
 183  2
         return result;
 184   
     }
 185   
 }