Coverage Report - de.smartics.properties.spi.config.proxy.PropertiesProxyInvocationHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertiesProxyInvocationHandler
0%
0/51
0%
0/30
3,091
 
 1  
 /*
 2  
  * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package de.smartics.properties.spi.config.proxy;
 17  
 
 18  
 import java.io.Serializable;
 19  
 import java.lang.reflect.Method;
 20  
 
 21  
 import de.smartics.properties.api.config.domain.SerializableConfigurationProperties;
 22  
 import de.smartics.properties.api.config.domain.UnknownPropertyException;
 23  
 import de.smartics.properties.api.core.annotations.PropertyMetaDataMethod;
 24  
 import de.smartics.properties.api.core.domain.PropertiesContext;
 25  
 import de.smartics.properties.api.core.domain.PropertyDescriptor;
 26  
 import de.smartics.properties.api.core.domain.PropertyKey;
 27  
 import de.smartics.properties.api.core.domain.PropertyValidationException;
 28  
 import de.smartics.properties.spi.core.metadata.PropertyMetaDataParser;
 29  
 import de.smartics.util.lang.Arguments;
 30  
 
 31  
 /**
 32  
  * The invocation handler receives every method invocation on the proxy that is
 33  
  * wrapped over the properties interface. It analyzes the intent of this method
 34  
  * call and provides (calculates) the return value. The main task is to detect
 35  
  * whether a value, a property key or a property description has to be returned.
 36  
  * After this decision making the task to calculate the return value is
 37  
  * delegated to a specialized class.
 38  
  */
 39  
 public final class PropertiesProxyInvocationHandler implements
 40  
     java.lang.reflect.InvocationHandler, Serializable
 41  
 {
 42  
   // ********************************* Fields *********************************
 43  
 
 44  
   // --- constants ------------------------------------------------------------
 45  
 
 46  
   /**
 47  
    * The class version identifier.
 48  
    */
 49  
   private static final long serialVersionUID = 1L;
 50  
 
 51  
   // --- members --------------------------------------------------------------
 52  
 
 53  
   /**
 54  
    * To access the properties.
 55  
    *
 56  
    * @serial
 57  
    */
 58  
   private final SerializableConfigurationProperties configurationProperties;
 59  
 
 60  
   // ****************************** Initializer *******************************
 61  
 
 62  
   // ****************************** Constructors ******************************
 63  
 
 64  
   /**
 65  
    * Default constructor.
 66  
    *
 67  
    * @param configurationProperties the configuration properties on that the
 68  
    *          method calls are delegated.
 69  
    * @throws NullPointerException if {@code configurationProperties} is
 70  
    *           <code>null</code>.
 71  
    */
 72  
   public PropertiesProxyInvocationHandler(
 73  
       final SerializableConfigurationProperties configurationProperties)
 74  
     throws NullPointerException
 75  0
   {
 76  0
     Arguments.checkNotNull("configurationProperties", configurationProperties);
 77  
 
 78  0
     this.configurationProperties = configurationProperties;
 79  0
   }
 80  
 
 81  
   // ****************************** Inner Classes *****************************
 82  
 
 83  
   // ********************************* Methods ********************************
 84  
 
 85  
   // --- init -----------------------------------------------------------------
 86  
 
 87  
   // --- get&set --------------------------------------------------------------
 88  
 
 89  
   // --- business -------------------------------------------------------------
 90  
 
 91  
   /**
 92  
    * {@inheritDoc}
 93  
    *
 94  
    * @throws InvalidArgumentsException when arguments are passed. Only no arg
 95  
    *           methods are supported.
 96  
    * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
 97  
    *      java.lang.reflect.Method, java.lang.Object[])
 98  
    */
 99  
   public Object invoke(final Object proxy, final Method method,
 100  
       final Object[] args) throws InvalidArgumentsException
 101  
   {
 102  0
     if (proxy == null)
 103  
     {
 104  0
       return null;
 105  
     }
 106  
 
 107  0
     final String methodName = method.getName();
 108  0
     if ("equals".equals(methodName))
 109  
     {
 110  0
       return handleMethodEquals(proxy, args);
 111  
     }
 112  
 
 113  0
     checkNoArgs(args);
 114  
 
 115  0
     if ("toString".equals(methodName))
 116  
     {
 117  0
       return handleMethodToString(proxy);
 118  
     }
 119  0
     else if ("hashCode".equals(methodName))
 120  
     {
 121  0
       return handleMethodHashCode(proxy);
 122  
     }
 123  
 
 124  0
     return handlePropertyMethod(method);
 125  
   }
 126  
 
 127  
   private Object handlePropertyMethod(final Method method)
 128  
     throws UnknownPropertyException, PropertyValidationException,
 129  
     InvalidArgumentsException
 130  
   {
 131  0
     final Class<?> declaringClass = method.getDeclaringClass();
 132  0
     final PropertiesContext context =
 133  
         configurationProperties.getContext(declaringClass);
 134  0
     final PropertyMetaDataParser parser =
 135  
         PropertyMetaDataParser.create(context);
 136  
 
 137  0
     if (isPropertyKeyMethod(method))
 138  
     {
 139  0
       final Method propertyMethod =
 140  
           PropertyMethodNameUtilities.fetchPropertyMethod(method);
 141  0
       return parser.readKey(propertyMethod);
 142  
     }
 143  0
     else if (isPropertyDescriptorMethod(method))
 144  
     {
 145  0
       final Method propertyMethod =
 146  
           PropertyMethodNameUtilities.fetchPropertyMethod(method);
 147  0
       return parser.readDescriptor(propertyMethod);
 148  
     }
 149  0
     else if (isPropertyMethod(method))
 150  
     {
 151  0
       final PropertyKey key = parser.readKey(method);
 152  0
       return configurationProperties.getPropertyValue(key);
 153  
     }
 154  
     else
 155  
     {
 156  0
       throw new InvalidArgumentsException(
 157  
           "The method '" + method.getName() + "' of interface '"
 158  
               + method.getDeclaringClass().getCanonicalName()
 159  
               + "' is not a property method, not a property key method and "
 160  
               + "not a property descriptor method.");
 161  
     }
 162  
   }
 163  
 
 164  
   Object handleMethodEquals(final Object proxy, final Object[] args)
 165  
   {
 166  0
     checkOneArg(args);
 167  0
     return proxy == args[0];
 168  
   }
 169  
 
 170  
   private Object handleMethodHashCode(final Object proxy)
 171  
     throws SecurityException
 172  
   {
 173  0
     final Method[] methods = proxy.getClass().getMethods();
 174  0
     int hash = proxy.getClass().getCanonicalName().hashCode();
 175  0
     for (int i = 0; i < methods.length; i++)
 176  
     {
 177  0
       hash = hash * 37 + methods[i].getName().hashCode();
 178  
     }
 179  0
     return hash;
 180  
   }
 181  
 
 182  
   private Object handleMethodToString(final Object proxy)
 183  
     throws SecurityException
 184  
   {
 185  0
     final StringBuilder toStringBuilder =
 186  
         new StringBuilder("Proxy for: ").append(proxy.getClass().getName());
 187  0
     final Method[] methods = proxy.getClass().getMethods();
 188  0
     for (int i = 0; i < methods.length; i++)
 189  
     {
 190  0
       toStringBuilder.append("::").append(methods[i].getName());
 191  
     }
 192  0
     return toStringBuilder.toString();
 193  
   }
 194  
 
 195  
   private boolean isPropertyKeyMethod(final Method m)
 196  
   {
 197  0
     final Class<?> returnType = m.getReturnType();
 198  0
     return PropertyKey.class.equals(returnType);
 199  
   }
 200  
 
 201  
   private boolean isPropertyDescriptorMethod(final Method m)
 202  
   {
 203  0
     final Class<?> returnType = m.getReturnType();
 204  0
     return PropertyDescriptor.class.equals(returnType);
 205  
   }
 206  
 
 207  
   private boolean isPropertyMethod(final Method m)
 208  
   {
 209  0
     return !PropertyMethodNameUtilities.isMethodAnnotatedWithAnnotation(
 210  
         PropertyMetaDataMethod.class, m);
 211  
   }
 212  
 
 213  
   /**
 214  
    * Check that the method called has exactly one arg.
 215  
    *
 216  
    * @param args the method args.
 217  
    */
 218  
   private void checkOneArg(final Object[] args)
 219  
   {
 220  0
     if (args == null || args.length != 1)
 221  
     {
 222  0
       throw new InvalidArgumentsException("Equals need one parameter.");
 223  
     }
 224  0
   }
 225  
 
 226  
   /**
 227  
    * Check that the method called has no args, as only noarg methods are
 228  
    * supported for property methods right now.
 229  
    *
 230  
    * @param args the method args.
 231  
    */
 232  
   private void checkNoArgs(final Object[] args)
 233  
   {
 234  0
     if (args != null && args.length > 0)
 235  
     {
 236  0
       throw new InvalidArgumentsException(
 237  
           "Only methods without arguments are supported for property methods.");
 238  
     }
 239  0
   }
 240  
 
 241  
   // --- object basics --------------------------------------------------------
 242  
 
 243  
 }