MentaContainer

Rev

Rev 91 | Rev 93 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

package org.mentacontainer.impl;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.mentacontainer.ConfigurableFactory;
import org.mentacontainer.Container;
import org.mentacontainer.util.FindMethod;

/**
 * A simple implementation of the Component interface.
 *
 * @author sergio.oliveira.jr@gmail.com
 */

public class ClassFactory implements ConfigurableFactory {
       
                private final Container container;
       
            private final Class<? extends Object> klass;
           
            private Map<String, Object> props = null;
           
            private List<Object> initValues = null;
           
            private List<Class<? extends Object>> initTypes = null;
           
            private Constructor<? extends Object> constructor = null;
           
            private Map<String, Method> cache = null;
           
            public ClassFactory(Class<? extends Object> klass) {
               
                this(null, klass);
            }
           
            public ClassFactory(Container container, Class<? extends Object> klass) {
               
                this.container = container;
               
                this.klass = klass;
            }
           
            @Override
            public ConfigurableFactory addPropertyValue(String name, Object value) {
               
                if (props == null) {
                       
                    props = new HashMap<String, Object>();
                   
                    cache = new HashMap<String, Method>();
                }
               
                props.put(name, value);
               
                return this;
            }
           
            @Override
            public ConfigurableFactory addPropertyDependency(String property, String key) {
               
                if (container == null) throw new IllegalStateException("Cannot set property dependency because factory does not have container!");
               
                return addPropertyValue(property, new DependencyKey(key));
            }
           
            @Override
            public ConfigurableFactory addInitDependency(String key) {
               
                if (container == null) throw new IllegalStateException("Cannot set constructor dependency because factory does not have container!");
               
                return addInitValue(new DependencyKey(key), container.getType(key));
            }
           
            private ConfigurableFactory addInitValue(Object value, Class<? extends Object> type) {
               
                if (initValues == null) {
                       
                    initValues = new LinkedList<Object>();
                   
                    initTypes = new LinkedList<Class<? extends Object>>();
                }
               
                initValues.add(value);
               
                initTypes.add(type);
               
                return this;
            }
           
            @Override
            public ConfigurableFactory addInitValue(Object value) {
               
                return addInitValue(value, value.getClass());
            }
           
            @Override
            public ConfigurableFactory addInitPrimitive(Object value) {
               
                Class<? extends Object> primitive = getPrimitiveFrom(value);
               
                if (primitive == null) throw new IllegalArgumentException("Value is not a primitive: " + value);
               
                return addInitValue(value, primitive);
            }
           
            private List<Class<? extends Object>> convertToPrimitives(List<Class<? extends Object>> list) {
               
                Iterator<Class<? extends Object>> iter = list.iterator();
               
                List<Class<? extends Object>> results = new LinkedList<Class<? extends Object>>();
               
                while(iter.hasNext()) {
                       
                        Class<? extends Object> klass = iter.next();
                       
                        Class<? extends Object> primitive = getPrimitiveFrom(klass);
                       
                        if (primitive != null) {
                               
                                results.add(primitive);
                               
                        } else {
                               
                                results.add(klass);
                        }
                }
               
                return results;
            }
           
            private Class<? extends Object>[] getClasses(List<Class<? extends Object>> values) {
               
                Class<? extends Object>[] types = (Class<? extends Object>[]) new Class[values.size()];
               
                return values.toArray(types);
            }
           
            private Object [] getValues(List<Object> values) throws InstantiationException {
               
                Object [] array = new Object[values.size()];
               
                int index = 0;
               
                Iterator<Object> iter = values.iterator();
               
                while(iter.hasNext()) {
               
                        Object obj = iter.next();
                       
                        if (obj instanceof DependencyKey) {
                               
                                DependencyKey dk = (DependencyKey) obj;
                               
                                array[index++] = container.get(dk.getKey());
                               
                        } else {
                   
                                array[index++] = obj;
                        }
                }
               
                return array;
            }
           
                /*
                 * Use reflection to set a property in the bean
                 */

                private void setValue(Object bean, String name, Object value) {
               
                        try {
                               
                                StringBuffer sb = new StringBuffer(30);
                                sb.append("set");
                                sb.append(name.substring(0,1).toUpperCase());
                               
                                if (name.length() > 1) sb.append(name.substring(1));
                   
                    String methodName = sb.toString();
                   
                    if (!cache.containsKey(name)) {
                       
                        Method m = null;
                       
                        try {
                               
                            m = FindMethod.getMethod(klass, methodName, new Class[] { value.getClass() });
                           
                        } catch(Exception e) {
                               
                            // try primitive...
                               
                            Class<? extends Object> primitive = getPrimitiveFrom(value);
                           
                            if (primitive != null) {
                               
                                try {
                                       
                                    m = klass.getMethod(methodName, new Class[] { primitive });
                                   
                                } catch(Exception ex) {
                                        // not found!
                                }
                            }
                           
                            if (m == null) {
                               
                                throw new InstantiationException("Cannot find method for property: " + name);
                            }
                        }
                       
                        if (m != null) {
                               
                            cache.put(name, m);
                           
                            m.setAccessible(true);
                        }
                    }    

                    Method m = cache.get(name);
                   
                    if (m != null) {
                       
                        m.invoke(bean, new Object[] { value });
                    }                
                   
                        } catch(Exception e) {
                               
                                throw new RuntimeException("Error trying to set a property with reflection: " + name, e);
                        }
                }
           
            private static Class<? extends Object> getPrimitiveFrom(Object w) {
                if (w instanceof Boolean) { return Boolean.TYPE; }
                else if (w instanceof Byte) { return Byte.TYPE; }
                else if (w instanceof Short) { return Short.TYPE; }
                else if (w instanceof Character) { return Character.TYPE; }
                else if (w instanceof Integer) { return Integer.TYPE; }
                else if (w instanceof Long) { return Long.TYPE; }
                else if (w instanceof Float) { return Float.TYPE; }
                else if (w instanceof Double) { return Double.TYPE; }
                return null;
            }
           
            private static Class<? extends Object> getPrimitiveFrom(Class<? extends Object> klass) {
                if (klass.equals(Boolean.class)) { return Boolean.TYPE; }
                else if (klass.equals(Byte.class)) { return Byte.TYPE; }
                else if (klass.equals(Short.class)) { return Short.TYPE; }
                else if (klass.equals(Character.class)) { return Character.TYPE; }
                else if (klass.equals(Integer.class)) { return Integer.TYPE; }
                else if (klass.equals(Long.class)) { return Long.TYPE; }
                else if (klass.equals(Float.class)) { return Float.TYPE; }
                else if (klass.equals(Double.class)) { return Double.TYPE; }
                return null;
            }
           
            @Override
            public <T> T getInstance()  {
               
                Object obj = null;
               
                if (initValues != null && initValues.size() > 0) {
                       
                    if (constructor == null) {
                       
                        try {
                               
                            constructor = klass.getConstructor(getClasses(initTypes));
                           
                        } catch(Exception e) {
                               
                                // try primitives...
                               
                                try {
                               
                                        constructor = klass.getConstructor(getClasses(convertToPrimitives(initTypes)));
                                       
                                } catch(Exception ee) {

                                        throw new RuntimeException("Cannot find a constructor for class: " + klass);
                                }
                        }
                    }
                   
                    try {
                       
                        obj = constructor.newInstance(getValues(initValues));
                       
                    } catch(Exception e) {
                       
                        throw new RuntimeException("Cannot create instance from constructor: " + constructor, e);
                    }
                   
                } else {
                       
                    try {
                       
                        obj = klass.newInstance();
                       
                    } catch(Exception e) {
                       
                        throw new RuntimeException("Cannot create instance from class: " + klass, e);
                    }
                }
               
                if (props != null && props.size() > 0) {
                       
                    Iterator<String> iter = props.keySet().iterator();
                   
                    while(iter.hasNext()) {
                       
                        String name = iter.next();
                       
                        Object value = props.get(name);
                       
                        if (value instanceof DependencyKey) {
                               
                                DependencyKey dk = (DependencyKey) value;
                               
                                value = container.get(dk.getKey());
                        }
                       
                        setValue(obj, name, value);
                    }
                }
               
                return (T) obj;
            }
           
            private static class DependencyKey {
               
                private String key;
               
                public DependencyKey(String key) { this.key = key; }
               
                private String getKey() { return key; }
            }
           
            @Override
            public Class<? extends Object> getType() {
                return klass;
            }
        }