src.main.java.com.eva.properties.Factory Maven / Gradle / Ivy
/*
* $Id: Factory.java 109 2007-03-24 14:55:03Z 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.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
/**
*
* 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: 109 $
*/
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");
}
}
/**
* helper method that finds the best matching constructor for the given
* arguments.
*
* @param inContext the context.
* @param inoutArguments the arguments for the constructor.
* @return the constructor.
* @throws PropertiesException if no matching constructor was found.
*/
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);
}
/**
* returns a message describing the construtor that was searched by this
* factory.
*
* @param inClassName the actually used class name.
* @param inArguments the arguments used to find the constructor.
* @return the 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();
}
/**
* tests if the provided objects can be converted to the provided classes.
* If the arrays don't have the same length or one of the objects cannot be
* converted, the match fails.
*
* @param inClasses the classes.
* @param inoutObjects the objects.
* @return true
if the provided objects ca be converted to
* the provided classes, false
otherwise.
*/
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;
}
/**
* uses the given context to replace the arguments for this factory, if
* possible.
*
* @param inContext the context.
* @return the replaced arguments.
* @throws PropertiesException if one of the arguments cannot be replaced.
*/
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;
}
/**
*
* checks wether one of the provided arguments is a list and the
* corresponding constructor argument is an array. If this is the case, the
* list is converted to an array using the type of the constructor argument.
*
*
* A ListProperties
object is converted using the method
* {@link ListProperties#toArray(Class)}.
*
*
* @param inoutArguments the arguments.
* @param inConstructor the constructor.
*/
private void replaceListProperties(Object[] inoutArguments,
Constructor inConstructor) {
for(int i = 0; i < inoutArguments.length; i++) {
Class parameterType = inConstructor.getParameterTypes()[i];
if(inoutArguments[i] instanceof List && parameterType.isArray()) {
List list = (List) inoutArguments[i];
Class type = parameterType.getComponentType();
if(list instanceof ListProperties) {
inoutArguments[i] = ((ListProperties) list).toArray(type);
}
else {
inoutArguments[i] = list.toArray((Object[]) Array
.newInstance(type, list.size()));
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy