/* * Copyright 2008 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 org.wamblee.collections.CollectionFilter; import org.wamblee.conditions.Condition; import org.wamblee.system.core.RequiredInterface; import org.wamblee.system.core.Scope; import org.wamblee.system.core.SystemAssemblyException; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Class that allows configuration of the constructor to use. In particular, it * provides: * */ public class ConstructorConfiguration { private Class clazz; private Constructor constructor; private ParameterValues values; private boolean publicOnly; /** * Constructs the configuration. By default the public constructor with the * most arguments will be used. * * @param aClass * Class to construct. */ public ConstructorConfiguration(Class aClass) { clazz = aClass; constructor = null; publicOnly = true; } /** * Sets whether or no non public constructors are also considered. Reset the * choice of a constructor to its default. * * @param aNonPublic * * @return */ public ConstructorConfiguration setNonPublic(boolean aNonPublic) { publicOnly = !aNonPublic; constructor = null; values = null; return this; } /** * Selects an explicit constructor. * * @return Return the injector to allow call chaining. * */ public ConstructorConfiguration select(Class... aTypes) { try { constructor = clazz.getDeclaredConstructor(aTypes); } catch (Exception e) { throw new SystemAssemblyException(e.getMessage(), e); } resetValues(); return this; } /** * Selects the greediest constructor. * * @return The injector to allow call chaining. * * @throws SystemAssemblyException * if the greediest constructor cannot be uniquely identified. */ public ConstructorConfiguration greedy() { Constructor[] declared = clazz.getDeclaredConstructors(); if (declared.length == 0) { throw new SystemAssemblyException("Class '" + clazz + " is an interface, primitive type, or array"); } int max = -1; List> checked = new ArrayList>(); CollectionFilter.filter(Arrays.asList(declared), checked, new Condition>() { @Override public boolean matches(Constructor aObject) { if (!publicOnly) { return true; } else { return Modifier.isPublic(aObject.getModifiers()); } } }); for (Constructor ctor : checked) { if (ctor.getParameterTypes().length > max) { max = ctor.getParameterTypes().length; } } final int max2 = max; List> ctors = checked; List> longest = new ArrayList>(); CollectionFilter.filter(ctors, longest, new Condition>() { @Override public boolean matches(Constructor aObject) { return aObject.getParameterTypes().length == max2; } }); if (longest.size() > 1) { throw new SystemAssemblyException( "Greediest constructor cannot be uniquely determined"); } constructor = longest.get(0); resetValues(); return this; } public ParameterValues getParameters() { getConstructor(); // initialize constructor if needed. return values; } /** * Resets the values. */ private void resetValues() { constructor.setAccessible(true); values = new ParameterValues(constructor.getParameterTypes()); } /** * Creates the object in the given scope. * * @param aScope * Scope containing required interfaces for this object. * * @return object. * */ public Object create(Scope aScope) { Object[] valueArray = values.values(aScope); try { return getConstructor().newInstance(valueArray); } catch (Exception e) { throw new SystemAssemblyException("Could not construct object " + getConstructor() + " " + Arrays.asList(valueArray), e); } } public List getRequiredInterfaces() { getConstructor(); // initialize constructor if needed. return values.getRequiredInterfaces(); } private Constructor getConstructor() { if (constructor == null) { greedy(); } return constructor; } }