/***************************************************************************
* 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.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.usikkert.kouinject.beandata.BeanKey;
import org.apache.commons.lang.Validate;
/**
* Map with all the registered factory points and the beans they can create.
*
* @author Christian Ihle
*/
public class FactoryPointMap {
/** Map with the factory point as value, and the bean it creates as key. */
private final Map<BeanKey, FactoryPoint<?>> factoryPointMap;
/**
* Creates a new instance of this factory point map, ready to use.
*/
public FactoryPointMap() {
this.factoryPointMap = new HashMap<BeanKey, FactoryPoint<?>>();
}
/**
* Adds a factory point to the map.
*
* @param factoryPoint The factory point to add.
*/
public synchronized void addFactoryPoint(final FactoryPoint<?> factoryPoint) {
Validate.notNull(factoryPoint, "FactoryPoint can not be null");
final BeanKey returnType = factoryPoint.getReturnType();
if (factoryPointMap.containsKey(returnType)) {
throw new IllegalArgumentException("Cannot add already existing factory point: " + factoryPoint);
}
factoryPointMap.put(returnType, factoryPoint);
}
/**
* Adds all the factory points in the list to the map.
*
* @param factoryPoints The factory points to add.
*/
public synchronized void addFactoryPoints(final List<FactoryPoint<?>> factoryPoints) {
Validate.notNull(factoryPoints, "FactoryPoints can not be null");
for (final FactoryPoint<?> factoryPoint : factoryPoints) {
addFactoryPoint(factoryPoint);
}
}
/**
* Gets a factory point from the map.
*
* @param beanNeeded The type of bean to get a factory point for.
* @return The factory point for that bean, or {@link IllegalArgumentException} if not found.
*/
public synchronized FactoryPoint<?> getFactoryPoint(final BeanKey beanNeeded) {
return getFactoryPoint(beanNeeded, true);
}
private FactoryPoint<?> getFactoryPoint(final BeanKey beanNeeded, final boolean throwEx) {
Validate.notNull(beanNeeded, "Bean needed can not be null");
final List<BeanKey> matches = new ArrayList<BeanKey>();
for (final BeanKey bean : factoryPointMap.keySet()) {
if (beanNeeded.canInjectFromFactory(bean)) {
matches.add(bean);
}
}
if (matches.isEmpty()) {
if (throwEx) {
throw new IllegalArgumentException("No matching factory point found for " + beanNeeded);
}
else {
return null;
}
}
else if (matches.size() > 1) {
throw new IllegalStateException("Too many matching factory points found for " + beanNeeded + " " + matches);
}
return factoryPointMap.get(matches.get(0));
}
/**
* Returns the number of factory points in the map.
*
* @return The size of the factory point map.
*/
public synchronized int size() {
return factoryPointMap.size();
}
/**
* Checks if the factory point exists in the map.
*
* @param beanNeeded The type of bean to check if there is a factory point for.
* @return If the factory point exists in the map.
*/
public synchronized boolean containsFactoryPoint(final BeanKey beanNeeded) {
final FactoryPoint<?> factoryPoint = getFactoryPoint(beanNeeded, false);
return factoryPoint != null;
}
/**
* Searches for all the factory points that can create matching beans, and returns
* the keys. These keys can be used to get the actual factory points.
*
* <p>Does not return factory points with the any qualifier, as they will often report a match without
* actually knowing what to do with the incoming qualifier.</p>
*
* @param beansNeeded The types of beans to get factory point keys for.
* @return A collection of factory point keys.
*/
public synchronized Collection<BeanKey> findFactoryPointKeys(final BeanKey beansNeeded) {
Validate.notNull(beansNeeded, "Beans needed can not be null");
final Collection<BeanKey> matches = new ArrayList<BeanKey>();
for (final BeanKey factoryPointKey : factoryPointMap.keySet()) {
if (beansNeeded.canInject(factoryPointKey) && !factoryPointKey.hasTheAnyQualifier()) {
matches.add(factoryPointKey);
}
}
return matches;
}
}