/*************************************************************************** * 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.beandata; import java.lang.reflect.Type; import net.usikkert.kouinject.generics.GenericsHelper; import net.usikkert.kouinject.generics.TypeLiteral; import org.apache.commons.lang.Validate; /** * Represents a bean key for acquiring a real bean instance or the bean data required * to create an instance. * * @author Christian Ihle */ public class BeanKey { /** Value of qualifier used to mark that an injection point accepts beans with any or no qualifier. */ private static final String ANY_QUALIFIER = "any"; private final Class<?> beanClass; private final Type beanType; private final String qualifier; /** * Creates a new bean key for the specified bean class, with no qualifier. * * @param beanClass The actual bean class for this key. */ public BeanKey(final Class<?> beanClass) { this(beanClass, null); } /** * Creates a new bean key for the specified bean class, with the specified qualifier. * * @param beanClass The actual bean class for this key. * @param qualifier The qualifier for this key. */ public BeanKey(final Class<?> beanClass, final String qualifier) { Validate.notNull(beanClass, "Bean class can not be null"); this.beanClass = beanClass; this.beanType = beanClass; this.qualifier = qualifier; } /** * Creates a new bean key for the specified bean type, with no qualifier. * * @param beanType The type literal with the actual type and class for this key. */ public BeanKey(final TypeLiteral<?> beanType) { this(beanType, null); } /** * Creates a new bean key for the specified bean type, with the specified qualifier. * * @param beanType The type literal with the actual type and class for this key. * @param qualifier The qualifier for this key. */ public BeanKey(final TypeLiteral<?> beanType, final String qualifier) { Validate.notNull(beanType, "Bean type can not be null"); this.beanClass = beanType.getGenericClass(); this.beanType = beanType.getGenericType(); this.qualifier = qualifier; } /** * Gets the actual bean class for this key. If this is e.g. a {@link javax.inject.Provider}, * then this bean class is the generic type argument of the provider. * * @return The bean class for this key. */ public Class<?> getBeanClass() { return beanClass; } /** * Gets the actual generic type for this key. If this is e.g. a {@link javax.inject.Provider}, * then this generic type is the generic type argument of the provider. * * @return The generic type for this key. */ public Type getBeanType() { return beanType; } /** * If this bean key is for a {@link javax.inject.Provider}. * * @return If this is for a provider. False for the default implementation. */ public boolean isProvider() { return false; } /** * If this bean key is for a {@link java.util.Collection} of beans. * * @return If this is for a collection of beans. False for the default implementation. */ public boolean isCollection() { return false; } /** * If this bean key is for a {@link net.usikkert.kouinject.CollectionProvider} of beans. * * @return If this is for a collection provider of beans. False for the default implementation. */ public boolean isCollectionProvider() { return false; } /** * If this bean key can be used to create a correct instance of the bean it represents. * Providers and collections aren't beans. * * @return If this key is for a bean that can be instantiated. */ public boolean isBeanForCreation() { return !isCollection() && !isProvider() && !isCollectionProvider(); } /** * Gets the qualifier for this bean key. * * <p>A qualifier combined with the type helps identify the bean to inject. * See {@link #canInject(BeanKey)} for details regarding qualifier rules.</p> * * @return The qualifier. */ public String getQualifier() { return qualifier; } /** * Returns the actual type as requested originally. * * @return The actual bean type as a new bean key. * @throws UnsupportedOperationException Not supported for this key. */ public BeanKey getActualBeanKey() { throw new UnsupportedOperationException("Not supported for this key"); } /** * If this key represents a field or parameter marked for injection, * could the bean be injected? * * <p>Rules:</p> * <ul> * <li>The bean must be of the same type or a supertype.</li> * <li>The qualifier must be identical, even if it's <code>null</code>.</li> * <li>Except if the type is an exact match and this qualifier is <code>null</code>.</li> * <li>Or this qualifier is <code>any</code>.</li> * </ul> * * @param bean The bean to check. * @return If the bean can be injected into this key. */ public boolean canInject(final BeanKey bean) { if (bean == null) { return false; } if (bean == this) { return true; } if (beanType.equals(bean.getBeanType()) && qualifier == null) { return true; } if (GenericsHelper.isAssignableFrom(beanType, bean.getBeanType())) { if (isTheSameQualifier(qualifier, bean.getQualifier())) { return true; } else if (hasTheAnyQualifier()) { return true; } } return false; } /** * If this key represents a field or parameter marked for injection, * could the bean, if created by a factory, be injected? * * <p>The same rules as {@link #canInject(BeanKey)} apply, with one exception. Factories may also * specify the <code>any</code> qualifier to create beans for any injection point of a matching type.</p> * * @param factoryCreatedBean The factory-created bean to check. * @return If the factory-created bean can be injected into this key. */ public boolean canInjectFromFactory(final BeanKey factoryCreatedBean) { if (factoryCreatedBean == null) { return false; } if (canInject(factoryCreatedBean)) { return true; } return GenericsHelper.isAssignableFrom(beanType, factoryCreatedBean.getBeanType()) && factoryCreatedBean.hasTheAnyQualifier(); } /** * Checks if this bean key has the <code>any</code> qualifier. * * @return If this bean key has the <code>any</code> qualifier. */ public boolean hasTheAnyQualifier() { return isTheSameQualifier(qualifier, ANY_QUALIFIER); } private boolean isTheSameQualifier(final String actualQualifier, final String expectedQualifier) { if (actualQualifier == null && expectedQualifier == null) { return true; } return actualQualifier != null && actualQualifier.equalsIgnoreCase(expectedQualifier); } /** * Checks equality based on {@link #getBeanType()} and {@link #getQualifier()}. * * @param obj The key to compare with this. * @return If the key is equal to this. */ @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof BeanKey)) { return false; } final BeanKey beanKey = (BeanKey) obj; if (beanType.equals(beanKey.getBeanType())) { if (qualifier == null && beanKey.getQualifier() == null) { return true; } else if (qualifier != null && qualifier.equalsIgnoreCase(beanKey.getQualifier())) { return true; } } return false; } @Override public int hashCode() { if (qualifier != null) { return beanType.hashCode() + qualifier.toLowerCase().hashCode(); } else { return beanType.hashCode(); } } @Override public String toString() { final StringBuilder toStringBuilder = new StringBuilder(); if (qualifier != null) { toStringBuilder.append("[q=").append(qualifier).append("] "); } toStringBuilder.append(beanType.toString()); return toStringBuilder.toString(); } }