Coverage Report - org.apache.commons.flatfile.ImmutableEntity
 
Classes in this File Line Coverage Branch Coverage Complexity
ImmutableEntity
80%
8/10
50%
1/2
0
ImmutableEntity$1
N/A
N/A
0
ImmutableEntity$ImmutableEntityInvocationHandler
83%
30/36
69%
18/26
0
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.flatfile;
 18  
 
 19  
 import java.io.InputStream;
 20  
 import java.lang.reflect.InvocationHandler;
 21  
 import java.lang.reflect.Method;
 22  
 import java.lang.reflect.Proxy;
 23  
 import java.util.Arrays;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 import java.util.WeakHashMap;
 27  
 
 28  
 import org.apache.commons.lang.ClassUtils;
 29  
 
 30  
 /**
 31  
  * Immutable Entity factory, using Java 1.3 proxies.
 32  
  * @version $Revision: 758023 $ $Date: 2009-03-24 16:09:19 -0500 (Tue, 24 Mar 2009) $
 33  
  */
 34  
 public class ImmutableEntity {
 35  11
     private static class ImmutableEntityInvocationHandler implements InvocationHandler {
 36  
         // make sure this array stays sorted:
 37  1
         private static final String[] ALLOWED_VOID = { "readFrom", "writeTo" };
 38  
         private static final String CLONE = "clone";
 39  
         private static final String EQUALS = "equals";
 40  
         private static final String READ_FROM = "readFrom";
 41  
         private static final String GET_CHILD = "getChild";
 42  
         private static final String GET_VALUE = "getValue";
 43  
         private static final String IS_SIZABLE = "isSizable";
 44  1
         private static final byte[] READ_BUFFER = new byte[1024];
 45  1
         private static final int BUF_LEN = READ_BUFFER.length;
 46  
 
 47  
         private Entity delegate;
 48  
 
 49  
         /**
 50  
          * Create a new ImmutableEntityInvocationHandler instance.
 51  
          * @param delegate Entity
 52  
          */
 53  11
         private ImmutableEntityInvocationHandler(Entity delegate) {
 54  11
             this.delegate = delegate;
 55  11
         }
 56  
 
 57  
         /**
 58  
          * {@inheritDoc}
 59  
          */
 60  
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 61  88
             String name = method.getName();
 62  
 
 63  
             // check allowed void methods; most would be intended to trigger a
 64  
             // change on the target
 65  88
             if (method.getReturnType() == Void.TYPE && Arrays.binarySearch(ALLOWED_VOID, name) < 0) {
 66  1
                 return null;
 67  
             }
 68  87
             if (READ_FROM.equals(name)) {
 69  9
                 InputStream is = (InputStream) args[0];
 70  9
                 int remaining = delegate.length();
 71  18
                 while (remaining > 0) {
 72  9
                     int toread = remaining > BUF_LEN ? BUF_LEN : remaining;
 73  9
                     int read = is.read(READ_BUFFER, 0, toread);
 74  9
                     if (read < 0) {
 75  0
                         break;
 76  
                     }
 77  9
                     remaining -= read;
 78  9
                 }
 79  9
                 return null;
 80  
             }
 81  78
             if (CLONE.equals(name)) {
 82  
                 // it should be safe to return the proxy itself as the clone of
 83  
                 // an IMMUTABLE entity
 84  16
                 return proxy;
 85  
             }
 86  62
             if (EQUALS.equals(name)) {
 87  0
                 return Boolean.valueOf(proxy == args[0]);
 88  
             }
 89  62
             if (IS_SIZABLE.equals(name)) {
 90  0
                 return Boolean.FALSE;
 91  
             }
 92  62
             Object result = method.invoke(delegate, args);
 93  62
             if (GET_CHILD.equals(name)) { // return immutable children
 94  0
                 result = getInstance((Entity) result);
 95  
             }
 96  
             // if delegate returns same result 2x, that indicates it's the true
 97  
             // buffer, which we protect by copying:
 98  62
             if (GET_VALUE.equals(name)) {
 99  1
                 byte[] test = (byte[]) method.invoke(delegate, args);
 100  1
                 if (result == test) {
 101  0
                     result = new byte[test.length];
 102  0
                     System.arraycopy(test, 0, (byte[]) result, 0, test.length);
 103  
                 }
 104  
             }
 105  62
             return result;
 106  
         }
 107  
     }
 108  
 
 109  1
     private static final Map<Entity, Entity> PROXIES = new WeakHashMap<Entity, Entity>();
 110  
 
 111  
     /**
 112  
      * Create a new ImmutableEntity instance.
 113  
      */
 114  0
     private ImmutableEntity() {
 115  0
     }
 116  
 
 117  
     /**
 118  
      * Get an immutable instance of <code>e</code>.
 119  
      * @param e Entity delegate
 120  
      * @return immutable Entity
 121  
      */
 122  
     public static Entity getInstance(Entity e) {
 123  11
         Entity result = (Entity) PROXIES.get(e);
 124  11
         if (result == null) {
 125  11
             result = (Entity) Proxy.newProxyInstance(e.getClass().getClassLoader(),
 126  
                     getInterfaces(e), new ImmutableEntityInvocationHandler(e));
 127  11
             PROXIES.put(e, result);
 128  
         }
 129  11
         return result;
 130  
     }
 131  
 
 132  
     /**
 133  
      * Get the interfaces implemented by <code>o</code>.
 134  
      * @param o to inspect.
 135  
      * @return interface array
 136  
      */
 137  
     @SuppressWarnings("unchecked")
 138  
     private static Class<?>[] getInterfaces(Object o) {
 139  11
         List<Class<?>> interfaces = ClassUtils.getAllInterfaces(o.getClass());
 140  11
         return interfaces.toArray(new Class[interfaces.size()]);
 141  
     }
 142  
 }