All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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