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
;
}
}