Coverage Report - org.apache.commons.flatfile.ImmutableEntity
 
Classes in this File Line Coverage Branch Coverage Complexity
ImmutableEntity
81%
9/11
50%
1/2
4.6
ImmutableEntity$1
N/A
N/A
4.6
ImmutableEntity$ImmutableEntityInvocationHandler
83%
30/36
69%
18/26
4.6
 
 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.lang3.ClassUtils;
 29  
 
 30  
 /**
 31  
  * Immutable Entity factory, using Java 1.3 proxies.
 32  
  * @version $Revision: 1301232 $ $Date: 2012-03-15 17:03:25 -0500 (Thu, 15 Mar 2012) $
 33  
  */
 34  
 public class ImmutableEntity {
 35  44
     private static class ImmutableEntityInvocationHandler implements InvocationHandler {
 36  
         // make sure this array stays sorted:
 37  6
         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  6
         private static final byte[] READ_BUFFER = new byte[1024];
 45  6
         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  88
         private ImmutableEntityInvocationHandler(Entity delegate) {
 54  66
             this.delegate = delegate;
 55  66
         }
 56  
 
 57  
         /**
 58  
          * {@inheritDoc}
 59  
          */
 60  
         // TODO could we simplify/future-proof proxy definition by breaking Entity into smaller interfaces?
 61  
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 62  528
             String name = method.getName();
 63  
 
 64  
             // check allowed void methods; most would be intended to trigger a
 65  
             // change on the target
 66  528
             if (method.getReturnType() == Void.TYPE && Arrays.binarySearch(ALLOWED_VOID, name) < 0) {
 67  6
                 return null;
 68  
             }
 69  522
             if (READ_FROM.equals(name)) {
 70  54
                 InputStream is = (InputStream) args[0];
 71  54
                 int remaining = delegate.length();
 72  126
                 while (remaining > 0) {
 73  54
                     int toread = remaining > BUF_LEN ? BUF_LEN : remaining;
 74  54
                     int read = is.read(READ_BUFFER, 0, toread);
 75  54
                     if (read < 0) {
 76  0
                         break;
 77  
                     }
 78  54
                     remaining -= read;
 79  36
                 }
 80  54
                 return null;
 81  
             }
 82  468
             if (CLONE.equals(name)) {
 83  
                 // it should be safe to return the proxy itself as the clone of
 84  
                 // an IMMUTABLE entity
 85  96
                 return proxy;
 86  
             }
 87  372
             if (EQUALS.equals(name)) {
 88  0
                 return Boolean.valueOf(proxy == args[0]);
 89  
             }
 90  372
             if (IS_SIZABLE.equals(name)) {
 91  0
                 return Boolean.FALSE;
 92  
             }
 93  372
             Object result = method.invoke(delegate, args);
 94  372
             if (GET_CHILD.equals(name)) { // return immutable children
 95  0
                 result = of((Entity) result);
 96  
             }
 97  
             // if delegate returns same result 2x, that indicates it's the true
 98  
             // buffer, which we protect by copying:
 99  372
             if (GET_VALUE.equals(name)) {
 100  6
                 byte[] test = (byte[]) method.invoke(delegate, args);
 101  6
                 if (result == test) {
 102  0
                     result = new byte[test.length];
 103  0
                     System.arraycopy(test, 0, (byte[]) result, 0, test.length);
 104  
                 }
 105  
             }
 106  372
             return result;
 107  
         }
 108  
     }
 109  
 
 110  6
     private static final Map<Entity, Entity> PROXIES = new WeakHashMap<Entity, Entity>();
 111  
 
 112  
     /**
 113  
      * Create a new ImmutableEntity instance.
 114  
      */
 115  0
     private ImmutableEntity() {
 116  0
     }
 117  
 
 118  
     /**
 119  
      * Get an immutable instance of <code>e</code>.
 120  
      * @param e Entity delegate
 121  
      * @return immutable Entity
 122  
      */
 123  
     public static Entity of(Entity e) {
 124  66
         Entity result = PROXIES.get(e);
 125  66
         if (result == null) {
 126  88
             result = (Entity) Proxy.newProxyInstance(e.getClass().getClassLoader(),
 127  22
                     getInterfaces(e), new ImmutableEntityInvocationHandler(e));
 128  66
             PROXIES.put(e, result);
 129  
         }
 130  66
         return result;
 131  
     }
 132  
 
 133  
     /**
 134  
      * Get the interfaces implemented by <code>o</code>.
 135  
      * @param o to inspect.
 136  
      * @return interface array
 137  
      */
 138  
     private static Class<?>[] getInterfaces(Object o) {
 139  66
         final List<Class<?>> interfaces = ClassUtils.getAllInterfaces(o.getClass());
 140  66
         return interfaces.toArray(new Class[interfaces.size()]);
 141  
     }
 142  
 }