package br.com.caelum.iogi.reflection;
import br.com.caelum.iogi.DependenciesInjector;
import br.com.caelum.iogi.Instantiator;
import br.com.caelum.iogi.parameters.Parameters;
import br.com.caelum.iogi.util.Ints;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public class Constructors {
public static final Ordering<ClassConstructor> orderConstructorsBySize = new Ordering<ClassConstructor>() {
public int compare(final ClassConstructor first, final ClassConstructor second) {
return Ints.compare(first.size(), second.size());
}
};
private final Collection<ClassConstructor> classConstructors;
private final DependenciesInjector dependenciesInjector;
public Constructors(final Collection<ClassConstructor> constructors, final DependenciesInjector dependenciesInjector) {
this.classConstructors = constructors;
this.dependenciesInjector = dependenciesInjector;
}
public FilledConstructors compatibleWith(final Parameters relevantParameters) {
final LinkedList<ClassConstructor> compatible = new LinkedList<ClassConstructor>();
for (final ClassConstructor classConstructor : classConstructors) {
if (classConstructor.canInstantiateOrInject(relevantParameters, dependenciesInjector))
compatible.add(classConstructor);
}
return new FilledConstructors(compatible, relevantParameters, dependenciesInjector);
}
public int size() {
return classConstructors.size();
}
public static class FilledConstructors {
private final Collection<ClassConstructor> classConstructors;
private final Parameters parameters;
private final DependenciesInjector dependenciesInjector;
public FilledConstructors(final Collection<ClassConstructor> classConstructors, final Parameters parameters, final DependenciesInjector dependenciesInjector) {
this.classConstructors = classConstructors;
this.parameters = parameters;
this.dependenciesInjector = dependenciesInjector;
}
public FilledConstructor largest() {
if (classConstructors.isEmpty()) {
return FilledConstructor.nullFilledConstructor();
}
final ClassConstructor largestConstructor = orderConstructorsBySize.max(classConstructors);
return new FilledConstructor(largestConstructor, parameters, dependenciesInjector);
}
}
public static class FilledConstructor {
private static FilledConstructor nullFilledConstructor() {
return new FilledConstructor(null,null, null) {
@Override
public NewObject instantiate(final Instantiator<Object> argumentInstantiator) {
return NewObject.nullNewObject();
}
};
}
private final ClassConstructor constructor;
private final Parameters parameters;
private final DependenciesInjector dependenciesInjector;
public FilledConstructor(final ClassConstructor constructor, final Parameters parameters, final DependenciesInjector dependenciesInjector) {
this.constructor = constructor;
this.parameters = parameters;
this.dependenciesInjector = dependenciesInjector;
}
public NewObject instantiate(final Instantiator<Object> argumentInstantiator) {
Object newObjectValue = constructor.construct(argumentValues(argumentInstantiator));
return new NewObject(argumentInstantiator, parameters, newObjectValue);
}
private List<Object> argumentValues(Instantiator<Object> argumentInstantiator) {
final List<Object> argumentValues = Lists.newArrayList();
for (final Target<?> target : constructor.argumentTargets()) {
argumentValues.add(argumentValue(argumentInstantiator, target));
}
return argumentValues;
}
private Object argumentValue(Instantiator<Object> argumentInstantiator, Target<?> target) {
if (needsDependency(target))
return dependenciesInjector.provide(target);
else
return argumentInstantiator.instantiate(target, parameters);
}
private boolean needsDependency(Target<?> target) {
return targetsNeedingADependency().contains(target);
}
private Collection<Target<?>> targetsNeedingADependency() {
return constructor.notFulfilledBy(parameters);
}
}
}