/* * 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 java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; 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; public class ConstructorConfiguration { private Class _class; private Constructor _constructor; private ParameterValues _values; private boolean _publicOnly; /** * Constructs the configuration. By default no constructor is selected and * one of {@link #select(Class...)} or * {@link #greedy()} must be called. * @param aClass Class to construct. */ public ConstructorConfiguration(Class aClass) { _class = aClass; _constructor = null; _publicOnly = true; } /** * Sets whether or no non public constructors are also considered. * Reset the choice of a constructor. * @param aNonPublic * @return */ public ConstructorConfiguration setNonPublic(boolean aNonPublic) { _publicOnly = !aNonPublic; _constructor = null; _values = null; return this; } /** * Selects an explicit constructor. * @param aTypes Arguments of the constructor. * @return Return the injector to allow call chaining. */ public ConstructorConfiguration select(Class... aTypes) { try { _constructor = _class.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 = _class.getDeclaredConstructors(); if (declared.length == 0) { throw new SystemAssemblyException("Class '" + _class + " 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[] values = _values.values(aScope); try { return getConstructor().newInstance(values); } catch (Exception e) { throw new SystemAssemblyException("Could not construct object " + getConstructor() + " " + Arrays.asList(values), e); } } public List getRequiredInterfaces() { getConstructor(); // initialize constructor if needed. return _values.getRequiredInterfaces(); } private Constructor getConstructor() { if (_constructor == null ) { greedy(); } return _constructor; } }