src.main.java.com.eva.properties.Factory Maven / Gradle / Ivy
/*
* $Id: Factory.java 34 2007-02-27 11:12:21Z max $
*
* Copyright (c) 2006-2007 Maximilian Antoni. All rights reserved.
*
* This software is licensed as described in the file LICENSE.txt, which you
* should have received as part of this distribution. The terms are also
* available at http://www.maxantoni.de/projects/eva-properties/license.txt.
*/
package com.eva.properties;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
*
* a factory is created by the token "*", followed by a full qualified
* classname. It will creates instances of that class every time the property is
* resolved. Optional arguments for the constructor can be provided. Here are
* some examples:
*
*
* foo: *org.example.Foo()
creates a new instance of
* org.example.Foo
by calling the standard constructor with no
* arguments every time foo
is resolved.
* foo: *org.example.Foo("Hello World")
like
* above, but passes the string "Hello World" as an argument to the
* constructor.
* foo: *org.example.Foo(${something.else})
like
* above, but passes the result of the reference to something.else
* as an argument to the constructor. The reference is resolved every time a new
* instance is created.
*
*
* @author Max Antoni
* @version $Revision: 34 $
*/
public class Factory implements Replaceable {
private Object[] arguments;
private String className;
/**
* creates a new factory with a classname.
*
* @param inClassName the classname.
*/
public Factory(String inClassName) {
this(inClassName, null);
}
/**
* creates a new factory with a classname and a list of arguments to be
* passed on to the constructor.
*
* @param inClassName the classname.
* @param inArguments the constructor arguments.
*/
public Factory(String inClassName, Object[] inArguments) {
super();
if(inClassName == null) {
throw new NullPointerException("Classname cannot be null.");
}
if("".equals(inClassName)) {
throw new IllegalArgumentException("Classname cannot be empty.");
}
className = inClassName;
arguments = inArguments;
}
/*
* @see com.eva.properties.Replaceable#copy(com.eva.properties.Properties)
*/
public Replaceable copy(Properties inParent) {
if(arguments == null) {
return new Factory(className);
}
Object[] newArguments = new Object[arguments.length];
for(int i = 0; i < arguments.length; i++) {
if(arguments[i] instanceof Properties) {
newArguments[i] = ((Properties) arguments[i]).copy(inParent);
}
else if(arguments[i] instanceof Replaceable) {
newArguments[i] = ((Replaceable) arguments[i]).copy(inParent);
}
else {
newArguments[i] = arguments[i];
}
}
return new Factory(className, newArguments);
}
/*
* @see com.eva.properties.Replaceable#replace(com.eva.properties.Context)
*/
public Object replace(Context inContext) throws PropertiesException {
Object[] replacedArguments = replacedArguments(inContext);
Constructor constructor = findConstructor(inContext,
replacedArguments);
if(replacedArguments != null) {
replaceListProperties(replacedArguments, constructor);
}
try {
return constructor.newInstance(replacedArguments);
}
catch(IllegalArgumentException e) {
throw new PropertiesException(e);
}
catch(InstantiationException e) {
throw new PropertiesException(e);
}
catch(IllegalAccessException e) {
throw new PropertiesException(e);
}
catch(InvocationTargetException e) {
throw new PropertiesException(e.getCause());
}
}
/*
* @see java.lang.Object#toString()
*/
public String toString() {
Writer writer = new Writer();
write(writer);
return writer.toString();
}
/*
* @see com.eva.properties.Replaceable#write(com.eva.properties.Writer)
*/
public void write(Writer inoutWriter) {
inoutWriter.append('*');
inoutWriter.append(className);
if(arguments == null) {
inoutWriter.append('\n');
}
else {
inoutWriter.append("(\n");
for(int i = 0; i < arguments.length; i++) {
inoutWriter.increaseIndentation();
inoutWriter.write(arguments[i]);
inoutWriter.decreaseIndentation();
}
inoutWriter.appendIndentation();
inoutWriter.append(")\n");
}
}
private Constructor findConstructor(Context inContext,
Object[] inoutArguments) throws PropertiesException {
String replacedClassName = Replacer.replace(className, inContext);
if(replacedClassName == null) {
replacedClassName = className;
}
Class clazz = inContext.loadClass(replacedClassName);
if(clazz == null) {
throw new PropertiesException("Class not found: "
+ replacedClassName);
}
Constructor[] constructors = clazz.getConstructors();
for(int i = 0; i < constructors.length; i++) {
Class[] parameterTypes = constructors[i].getParameterTypes();
if(matches(parameterTypes, inoutArguments)) {
return constructors[i];
}
}
String message = getNotFoundMessage(replacedClassName, inoutArguments);
throw new PropertiesException(message);
}
private String getNotFoundMessage(String inClassName, Object[] inArguments) {
StringBuffer buffer = new StringBuffer();
buffer.append("No matching constructor found for \"");
buffer.append(inClassName);
buffer.append('(');
if(inArguments != null) {
for(int i = 0; i < inArguments.length; i++) {
if(i != 0) {
buffer.append(", ");
}
buffer.append(inArguments[i].getClass().getName());
}
}
buffer.append(")\"");
return buffer.toString();
}
private boolean matches(Class[] inClasses, Object[] inoutObjects) {
if(inoutObjects == null) {
return inClasses.length == 0;
}
if(inClasses.length != inoutObjects.length) {
return false;
}
for(int i = 0; i < inClasses.length; i++) {
if(inoutObjects[i] != null
&& !inClasses[i].isInstance(inoutObjects[i])
&& !inClasses[i].isArray()) {
if(inClasses[i].isPrimitive()) {
if(inClasses[i] == Boolean.TYPE
&& inoutObjects[i].getClass() == Boolean.class) {
continue;
}
if(inClasses[i] == Character.TYPE
&& inoutObjects[i].getClass() == Character.class) {
continue;
}
if(Number.class.isInstance(inoutObjects[i])) {
Number number = (Number) inoutObjects[i];
if(inClasses[i] == Integer.TYPE) {
inoutObjects[i] = new Integer(number.intValue());
continue;
}
if(inClasses[i] == Long.TYPE) {
inoutObjects[i] = new Long(number.longValue());
continue;
}
if(inClasses[i] == Float.TYPE) {
inoutObjects[i] = new Float(number.floatValue());
continue;
}
if(inClasses[i] == Double.TYPE) {
inoutObjects[i] = new Double(number.doubleValue());
continue;
}
if(inClasses[i] == Byte.TYPE) {
inoutObjects[i] = new Byte(number.byteValue());
continue;
}
if(inClasses[i] == Short.TYPE) {
inoutObjects[i] = new Short(number.shortValue());
continue;
}
}
}
return false;
}
}
return true;
}
private Object[] replacedArguments(Context inContext)
throws PropertiesException {
if(arguments == null) {
return null;
}
Object[] args = new Object[arguments.length];
for(int i = 0; i < arguments.length; i++) {
args[i] = inContext.replace(arguments[i]);
}
return args;
}
private void replaceListProperties(Object[] inoutArguments,
Constructor inConstructor) {
for(int i = 0; i < inoutArguments.length; i++) {
Class parameterType = inConstructor.getParameterTypes()[i];
if(inoutArguments[i] instanceof ListProperties
&& parameterType.isArray()) {
inoutArguments[i] = ((ListProperties) inoutArguments[i])
.toArray(parameterType.getComponentType());
}
}
}
}