Package org.springmodules.xt.model.generator.factory

Source Code of org.springmodules.xt.model.generator.factory.FactoryGeneratorInterceptor$PropertyPair

/*
* Copyright 2006 - 2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springmodules.xt.model.generator.factory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.log4j.Logger;
import org.springframework.aop.support.AopUtils;
import org.springmodules.xt.model.generator.annotation.ConstructorArg;
import org.springmodules.xt.model.generator.annotation.ConstructorArgType;
import org.springmodules.xt.model.generator.annotation.FactoryMethod;
import org.springmodules.xt.model.generator.annotation.Property;
import org.springmodules.xt.model.generator.annotation.Value;
import org.springmodules.xt.model.generator.support.IllegalArgumentPositionException;
import org.springmodules.xt.model.generator.support.ObjectConstructionException;
import org.springmodules.xt.model.generator.support.ReturnTypeMismatchException;
import org.springmodules.xt.model.introductor.support.IllegalReturnTypeException;

/**
* Java dynamic Proxy-based interceptor for implementing factory generation.
*
* @see org.springmodules.xt.model.generator.factory.DynamicFactoryGenerator
*
* @author Sergio Bossa
*/
public class FactoryGeneratorInterceptor implements InvocationHandler {
   
    private static final Logger logger = Logger.getLogger(FactoryGeneratorInterceptor.class);
   
    private Class productClass;
   
    // Needs to be ordered by argument position:
    private TreeMap<Integer, ConstructorArgPair> constructorArgs = new TreeMap<Integer, ConstructorArgPair>();
    // No need to be ordered:
    private HashMap<String, PropertyPair> properties = new HashMap<String, PropertyPair>();
    // General values map, holding both constructor args and properties values:
    private HashMap<String, Object> values = new HashMap<String, Object>();
   
    /**
     * Constructor.
     *
     * @param productClass The factory product class, that is, the class of the object created by the factory.
     */
    public FactoryGeneratorInterceptor(Class productClass) {
        this.productClass = productClass;
    }
   
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (this.isConstructorArg(method)) {
            return this.putConstructorArg(args, method);
        } else if (this.isPropertySetter(method)) {
            return this.putProperty(args, method);
        } else if (this.isValueSetter(method)) {
            return this.putValue(args, method);
        } else if (this.isGetter(method)) {
            return this.readProperty(args, method);
        } else if (this.isFactoryMethod(method)) {
            Class returnType = method.getReturnType();
            if (!returnType.isAssignableFrom(this.productClass)) {
                throw new ReturnTypeMismatchException("Return type mismatch. Expected assignable from: " + this.productClass + ", found: " + returnType);
            } else {
                Object product = this.make();
                // Fill properties:
                for (Map.Entry<String, PropertyPair> entry : this.properties.entrySet()) {
                    String propertyName = entry.getKey();
                    Object propertyValue = entry.getValue().getValue();
                    Property.AccessType propertyAccess = entry.getValue().getAccess();
                    if (propertyAccess.equals(Property.AccessType.FIELD)) {
                        Field field = this.productClass.getDeclaredField(propertyName);
                        field.setAccessible(true);
                        field.set(product, propertyValue);
                    } else {
                        String methodName = new StringBuilder("set").append(StringUtils.capitalize(propertyName)).toString();
                        Method methodObj = this.productClass.getMethod(methodName, propertyValue.getClass());
                        methodObj.invoke(product, propertyValue);
                    }
                }
                // Return product:
                return product;
            }
        } else {
            if (AopUtils.isEqualsMethod(method)) {
                return this.doEquals(proxy, args[0]);
            } else if (AopUtils.isHashCodeMethod(method)) {
                return this.doHashCode(proxy);
            } else if (AopUtils.isToStringMethod(method)) {
                return this.doToString(proxy);
            } else {
                // Fail fast:
                throw new UnsupportedOperationException("Unsupported method called: " + method.getName());
            }
        }
    }
   
    private boolean isConstructorArg(Method method) {
        if (method.getName().startsWith("set") && method.isAnnotationPresent(ConstructorArg.class)) {
            return true;
        } else {
            return false;
        }
    }
   
    private boolean isPropertySetter(Method method) {
        if (method.getName().startsWith("set") && method.isAnnotationPresent(Property.class)) {
            return true;
        } else {
            return false;
        }
    }
   
    private boolean isValueSetter(Method method) {
        if (method.getName().startsWith("set") && method.isAnnotationPresent(Value.class)) {
            return true;
        } else {
            return false;
        }
    }
   
    private boolean isGetter(Method method) {
        if (method.getName().startsWith("get") || method.getName().startsWith("is")) {
            return true;
        } else {
            return false;
        }
    }
   
    private boolean isFactoryMethod(Method method) {
        if (method.isAnnotationPresent(FactoryMethod.class)) {
            return true;
        } else {
            return false;
        }
    }
   
    private Object putProperty(final Object[] args, final Method method) {
        if (args.length != 1) {
            throw new IllegalStateException("The setter method " + method.getName() + " must have only one argument!");
        } else {
            String name = StringUtils.uncapitalize(method.getName().substring(3));
            Property annotation = method.getAnnotation(Property.class);
            Property.AccessType access = annotation.access();
            PropertyPair pair = new PropertyPair();
            pair.setValue(args[0]);
            pair.setAccess(access);
            this.properties.put(name, pair);
            this.values.put(name, args[0]);
            logger.debug(new StringBuilder("Put property with name and value: ").append(name).append(",").append(args[0]));
            return null;
        }
    }
   
    private Object putValue(final Object[] args, final Method method) {
        if (args.length != 1) {
            throw new IllegalStateException("The setter method " + method.getName() + " must have only one argument!");
        } else {
            String name = StringUtils.uncapitalize(method.getName().substring(3));
            this.values.put(name, args[0]);
            logger.debug(new StringBuilder("Put value property with name and value: ").append(name).append(",").append(args[0]));
            return null;
        }
    }
   
    private Object putConstructorArg(final Object[] args, final Method method) {
        if (args.length != 1) {
            throw new IllegalStateException("The setter method " + method.getName() + " must have only one argument!");
        } else {
            String name = StringUtils.uncapitalize(method.getName().substring(3));
            ConstructorArg annotation = method.getAnnotation(ConstructorArg.class);
            int position = annotation.position();
            Class type = null;
            if (method.isAnnotationPresent(ConstructorArgType.class)) {
                type = method.getAnnotation(ConstructorArgType.class).type();
            } else {
                type = args[0].getClass();
            }
            ConstructorArgPair pair = new ConstructorArgPair();
            pair.setValue(args[0]);
            pair.setType(type);
            this.constructorArgs.put(position, pair);
            this.values.put(name, args[0]);
            logger.debug(new StringBuilder("Put constructor arg with position and value: ").append(position).append(",").append(args[0]));
            return null;
        }
    }
   
    private Object readProperty(final Object[] args, final Method method) {
        if (args != null && args.length != 0) {
            throw new IllegalStateException("The getter method " + method.getName() + " must have no arguments!");
        }
        if (method.getReturnType().isPrimitive()) {
            throw new IllegalReturnTypeException("Return types cannot be primitives.");
        } else {
            String name = null;
            if (method.getName().startsWith("get")) {
                name = StringUtils.uncapitalize(method.getName().substring(3));
            } else {
                name = StringUtils.uncapitalize(method.getName().substring(2));
            }
            Object value = values.get(name);
            logger.debug(new StringBuilder("Read property with name and value: ").append(name).append(",").append(value));
            return value;
        }
    }
   
    private Object make() {
        logger.debug("Making object of class: " + this.productClass);
       
        int argsNr = this.constructorArgs.size();
        Object[] argsArray = new Object[argsNr];
        Class[] typesArray = new Class[argsNr];
        for (Map.Entry<Integer, ConstructorArgPair> entry : this.constructorArgs.entrySet()) {
            int position = entry.getKey();
            Object value = entry.getValue().getValue();
            Class type = entry.getValue().getType();
            if (position < 0 || position > argsNr - 1) {
                throw new IllegalArgumentPositionException("Illegal position: " + position);
            } else {
                argsArray[position] = value;
                typesArray[position] = type;
            }
        }
       
        try {
            Constructor constructor = this.productClass.getConstructor(typesArray);
            Object product = constructor.newInstance(argsArray);
            return product;
        } catch (NoSuchMethodException ex) {
            throw new ObjectConstructionException("No constructor found accepting the following array of types: "
                    + ToStringBuilder.reflectionToString(typesArray, ToStringStyle.SIMPLE_STYLE)
                    + " Have you correctly set all constructor arguments?", ex);
        } catch (InvocationTargetException ex) {
            throw new ObjectConstructionException("Exception thrown by the underlying constructor: "
                    + ex.getMessage(), ex);
        } catch (IllegalAccessException ex) {
            throw new ObjectConstructionException("Cannot access a constructor with the following array of types: "
                    + ToStringBuilder.reflectionToString(typesArray, ToStringStyle.SIMPLE_STYLE)
                    + " Have you correctly set all constructor arguments?", ex);
        } catch (InstantiationException ex) {
            throw new ObjectConstructionException("Unable to instantiate the following class: "
                    + this.productClass, ex);
        }
    }

    private boolean doEquals(Object proxy, Object other) {
        if (other != null && Proxy.isProxyClass(other.getClass())) {
            return Proxy.getInvocationHandler(proxy) == Proxy.getInvocationHandler(other);
        } else {
            return false;
        }
    }
   
    private int doHashCode(Object proxy) {
        return Proxy.getInvocationHandler(proxy).hashCode();
    }
   
    private String doToString(Object proxy) {
        return Proxy.getInvocationHandler(proxy).toString();
    }
   
    /*** Inner classes ***/
   
    private class ConstructorArgPair {
       
        private Object value;
        private Class type;
       
        public Object getValue() {
            return this.value;
        }
       
        public void setValue(Object value) {
            this.value = value;
        }
       
        public Class getType() {
            return this.type;
        }
       
        public void setType(Class type) {
            this.type = type;
        }
    }
   
    private class PropertyPair {
       
        private Object value;
        private Property.AccessType access;
       
        public Object getValue() {
            return this.value;
        }
       
        public void setValue(Object value) {
            this.value = value;
        }
       
        public Property.AccessType getAccess() {
            return this.access;
        }
       
        public void setAccess(Property.AccessType access) {
            this.access = access;
        }
    }
}
TOP

Related Classes of org.springmodules.xt.model.generator.factory.FactoryGeneratorInterceptor$PropertyPair

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.