/***************************************************************************
* Copyright 2009-2012 by Christian Ihle *
* kontakt@usikkert.net *
* *
* This file is part of KouInject. *
* *
* KouInject is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version. *
* *
* KouInject is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with KouInject. *
* If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
package net.usikkert.kouinject.factory;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import net.usikkert.kouinject.AnnotationBasedScopeHandler;
import net.usikkert.kouinject.annotation.Produces;
import net.usikkert.kouinject.beandata.BeanKey;
import net.usikkert.kouinject.generics.GenericsHelper;
import net.usikkert.kouinject.generics.TypeMap;
import net.usikkert.kouinject.util.BeanHelper;
import net.usikkert.kouinject.util.ReflectionUtils;
import org.apache.commons.lang.Validate;
/**
* Implementation of {@link FactoryPointHandler} that uses annotations and reflection to scan beans
* for meta-data about factory points.
*
* <p>Factory points are detected based on the {@link net.usikkert.kouinject.annotation.Produces} annotation.
* Scope, qualifier, and dependencies are also detected.</p>
*
* @author Christian Ihle
*/
public class AnnotationBasedFactoryPointHandler implements FactoryPointHandler {
private static final Class<Produces> FACTORY_ANNOTATION = Produces.class;
private static final Class<Inject> INJECTION_ANNOTATION = Inject.class;
private final AnnotationBasedScopeHandler scopeHandler = new AnnotationBasedScopeHandler();
private final ReflectionUtils reflectionUtils = new ReflectionUtils();
private final BeanHelper beanHelper = new BeanHelper();
/**
* {@inheritDoc}
*/
@Override
public List<FactoryPoint<?>> getFactoryPoints(final BeanKey factoryBean) {
Validate.notNull(factoryBean, "Factory bean can not be null");
final Class<?> beanClass = factoryBean.getBeanClass();
Validate.notNull(beanClass, "Factory class can not be null");
final TypeMap typeMap = GenericsHelper.mapTypeVariablesToActualTypes(beanClass);
final List<Method> allMethods = reflectionUtils.findAllMethods(beanClass);
final List<Member> allMembers = reflectionUtils.findAllMembers(beanClass);
return findAllFactoryPoints(allMembers, allMethods, factoryBean, typeMap);
}
private List<FactoryPoint<?>> findAllFactoryPoints(final List<Member> allMembers, final List<Method> allMethods,
final BeanKey factoryBean, final TypeMap typeMap) {
final List<FactoryPoint<?>> factoryPoints = new ArrayList<FactoryPoint<?>>();
for (final Member member : allMembers) {
if (member instanceof Method) {
final Method method = (Method) member;
if (methodIsFactoryPoint(method) && !reflectionUtils.isOverridden(method, allMethods)) {
failIfInjectionPoint(method);
final FactoryPointMethod<?> factoryPointMethod = createFactoryPointMethod(method, factoryBean, typeMap);
factoryPoints.add(factoryPointMethod);
}
}
}
return factoryPoints;
}
private void failIfInjectionPoint(final Method method) {
if (methodIsInjectionPoint(method)) {
throw new UnsupportedOperationException("A factory point can't also be an injection point: " + method);
}
}
private boolean methodIsFactoryPoint(final Method method) {
return !reflectionUtils.isStatic(method) && method.isAnnotationPresent(FACTORY_ANNOTATION);
}
private boolean methodIsInjectionPoint(final Method method) {
return method.isAnnotationPresent(INJECTION_ANNOTATION);
}
private <T> FactoryPointMethod<T> createFactoryPointMethod(final Method method, final BeanKey factoryBean,
final TypeMap typeMap) {
final List<BeanKey> parameters = beanHelper.findParameterKeys(method, typeMap);
final BeanKey returnType = beanHelper.findFactoryReturnType(method, typeMap);
final boolean singleton = scopeHandler.isSingleton(method);
return new FactoryPointMethod<T>(method, factoryBean, returnType, parameters, singleton);
}
}