/* * Copyright 2005-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wamblee.system.adapters; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.wamblee.collections.CollectionFilter; import org.wamblee.conditions.Condition; import org.wamblee.reflection.ReflectionUtils; import org.wamblee.system.core.RequiredInterface; import org.wamblee.system.core.Scope; import org.wamblee.system.core.SystemAssemblyException; /** * Represents the configuration for exposing the setters of a class as required * interfaces. * * @author Erik Brakkee */ public class SetterConfiguration { private Class clazz; private boolean publicOnly; private Map setters; /** * Constructs the setter configuration. By default no setters are added. * * @param aClass * Class which is being configured. */ public SetterConfiguration(Class aClass) { clazz = aClass; publicOnly = true; setters = new HashMap(); } /** * Makes sure that all available setters are used. * */ public SetterConfiguration initAllSetters() { setters.clear(); for (Method method : getAllSetters(clazz, publicOnly)) { setters.put(method, createParameterValues(method)); } return this; } /** * Called to set whether non-public setters are also used. By default only * public setters are used. The currently selected setters remain chosen. * * @param aIsNonPublic * Non public flag. * */ public SetterConfiguration setNonPublic(boolean aIsNonPublic) { publicOnly = !aIsNonPublic; return this; } /** * Removes all setters. * * @return Reference to the current object to allow call chaining. */ public SetterConfiguration clear() { setters.clear(); return this; } /** * Removes a setter from the set of methods. * * @param aName * Name of the setter to remove. * * @return Reference to the current object to allow call chaining. * */ public SetterConfiguration remove(String aName) { for (Method method : setters.keySet()) { if (method.getName().equals(aName)) { setters.remove(method); return this; } } throw new IllegalArgumentException( "No method configured by the name of '" + aName + "'"); } /** * Removes the method from the set of methods. * * @param aMethod * Method to remove. * * @return * */ public SetterConfiguration remove(Method aMethod) { if (!aMethod.getDeclaringClass().isAssignableFrom(clazz)) { throw new RuntimeException("Method " + aMethod + " not found in class " + clazz + " or its superclasses"); } for (Method method : setters.keySet()) { if (method.equals(aMethod)) { setters.remove(method); return this; } } throw new IllegalArgumentException("Method '" + aMethod + "' was not configured. "); } /** * Adds a given setter name to the setters. * * @param aName * Name of a setter method. * * @return Reference to the current object to allow call chaining. * */ public SetterConfiguration add(final String aName) { List methods = new ArrayList(); CollectionFilter.filter(getAllSetters(clazz, publicOnly), methods, new Condition() { @Override public boolean matches(Method aObject) { return aObject.getName().equals(aName); } }); if (methods.size() == 0) { throw new IllegalArgumentException("Method '" + aName + "' not found in " + clazz.getName()); } // TODO is it possible to get more than one setter here in case the // subclass overrides // the baseclass method? setters.put(methods.get(0), createParameterValues(methods.get(0))); return this; } /** * Adds a given setter identified by the type it accepts to the list of * setters.N * * @param aType * Type to look for. Note that this must be the exact type as * autoboxing and autounboxing is not used. * * @return Reference to the current object to allow call chaining. * * @throws IllegalArgumentException * In case no setter is found or multiple setters are found. */ public SetterConfiguration addSetter(final Class aType) { List result = new ArrayList(); CollectionFilter.filter(getAllSetters(clazz, publicOnly), result, new Condition() { @Override public boolean matches(Method aObject) { Class type = aObject.getParameterTypes()[0]; return type.equals(aType); } }); if (result.size() == 0) { throw new IllegalArgumentException("No setter found in class '" + clazz.getName() + "' that has a setter with argument type '" + aType.getName() + "'"); } if (result.size() > 1) { StringBuffer settersString = new StringBuffer(); for (Method method : result) { settersString.append((method.getName() + " ")); } throw new IllegalArgumentException( "Multiple setters found in class '" + clazz.getName() + " that accept type '" + aType.getName() + "': " + settersString); } Method method = result.get(0); setters.put(method, createParameterValues(method)); return this; } /** * Gets all setters for the current class. * * * @return List of all setters. */ public static List getAllSetters(Class aClass, boolean aPublicOnly) { List result = new ArrayList(); for (Method method : getAllMethods(aClass)) { if (!aPublicOnly || Modifier.isPublic(method.getModifiers())) { if (method.getName().startsWith("set") && (method.getParameterTypes().length == 1)) { method.setAccessible(true); result.add(method); } } } return result; } private static ParameterValues createParameterValues(Method aMethod) { Class[] paramTypes = aMethod.getParameterTypes(); String[] paramNames = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { paramNames[i] = aMethod.getName() + "." + i; } return new ParameterValues(paramNames, paramTypes); } private static final List getAllMethods(Class aClass) { return ReflectionUtils.getAllMethods(aClass); } /** * Gets the required interfaces based on the configured setteres. * * @return List of required interfaces. */ public List getRequiredInterfaces() { List result = new ArrayList(); for (Method method : setters.keySet()) { result.addAll(setters.get(method).getRequiredInterfaces()); } return result; } /** * Invokes all configured setters with the appropriate values. * * @param aScope * Scope within which invocation takes place. * @param aObject * Object on which the invocation takes place. * */ public void inject(Scope aScope, Object aObject) { if (!clazz.isInstance(aObject)) { throw new IllegalArgumentException("Object '" + aObject + "' is not an instance of " + clazz.getName()); } for (Method method : setters.keySet()) { ParameterValues values = setters.get(method); try { method.invoke(aObject, values.values(aScope)); } catch (IllegalAccessException e) { throw new SystemAssemblyException("Problem invoking " + method + " with " + values, e); } catch (InvocationTargetException e) { throw new SystemAssemblyException("Problem invoking " + method + " with " + values, e); } } } /** * Returns the parameter values for allowing detailed configuration of how * parameter values are set. * * @param aMethod * Setter name without the "set" prefix with the first character * converted to lower case. * * @return Parameter values. * */ public ParameterValues values(String aMethod) { for (Method method : setters.keySet()) { if (method.getName().equals(aMethod)) { return setters.get(method); } } throw new IllegalArgumentException("No setter method '" + aMethod + "' found"); } public List getSetters() { return new ArrayList(setters.keySet()); } }