/******************************************************************************* * Copyright (c) 2010-present Sonatype, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Stuart McCulloch (Sonatype, Inc.) - initial API and implementation *******************************************************************************/ package org.eclipse.sisu.inject; import java.lang.annotation.Annotation; import javax.inject.Provider; import javax.inject.Qualifier; import com.google.inject.Binding; import com.google.inject.Key; import com.google.inject.name.Named; import com.google.inject.name.Names; import com.google.inject.spi.ConstructorBinding; import com.google.inject.spi.ProviderKeyBinding; /** * Enumerates the different strategies for qualifying {@link Binding}s against requirement {@link Key}s. */ enum QualifyingStrategy { // ---------------------------------------------------------------------- // Enumerated values // ---------------------------------------------------------------------- UNRESTRICTED { @Override final Annotation qualifies( final Key<?> requirement, final Binding<?> binding ) { final Annotation qualifier = qualify( binding.getKey() ); return null != qualifier ? qualifier : BLANK_QUALIFIER; } }, NAMED { @Override final Annotation qualifies( final Key<?> requirement, final Binding<?> binding ) { final Annotation qualifier = qualify( binding.getKey() ); return qualifier instanceof Named ? qualifier : null; } }, NAMED_WITH_ATTRIBUTES { @Override final Annotation qualifies( final Key<?> requirement, final Binding<?> binding ) { final Annotation qualifier = qualify( binding.getKey() ); if ( requirement.getAnnotation().equals( qualifier ) ) { return qualifier; } // special case for untargeted constructor binding: treat @Named on implementation as an alias if ( binding instanceof ConstructorBinding<?> && null == binding.getKey().getAnnotationType() ) { final Class<?> clazz = binding.getKey().getTypeLiteral().getRawType(); final javax.inject.Named alias = clazz.getAnnotation( javax.inject.Named.class ); if ( null != alias && alias.value().equals( ( (Named) requirement.getAnnotation() ).value() ) && clazz.equals( Implementations.find( binding ) ) ) { return requirement.getAnnotation(); } } return null; } }, MARKED { @Override final Annotation qualifies( final Key<?> requirement, final Binding<?> binding ) { final Class<? extends Annotation> markerType = requirement.getAnnotationType(); final Annotation qualifier = qualify( binding.getKey() ); if ( markerType.isInstance( qualifier ) ) { return qualifier; } // binding only has marker type; upgrade to pseudo-instance if ( markerType.equals( binding.getKey().getAnnotationType() ) && markerType.getDeclaredMethods().length == 0 ) { // this stub is all we need for internal processing return new Annotation() { public Class<? extends Annotation> annotationType() { return markerType; } }; } if ( binding instanceof ProviderKeyBinding<?> ) { final Key<?> providerKey = ( (ProviderKeyBinding<?>) binding ).getProviderKey(); return providerKey.getTypeLiteral().getRawType().getAnnotation( markerType ); } final Class<?> implementation = Implementations.find( binding ); return null != implementation ? implementation.getAnnotation( markerType ) : null; } }, MARKED_WITH_ATTRIBUTES { @Override final Annotation qualifies( final Key<?> requirement, final Binding<?> binding ) { final Annotation qualifier = MARKED.qualifies( requirement, binding ); return requirement.getAnnotation().equals( qualifier ) ? qualifier : null; } }; // ---------------------------------------------------------------------- // Constants // ---------------------------------------------------------------------- static final Annotation DEFAULT_QUALIFIER = Names.named( "default" ); static final Annotation BLANK_QUALIFIER = Names.named( "" ); // ---------------------------------------------------------------------- // Local methods // ---------------------------------------------------------------------- /** * Attempts to qualify the given {@link Binding} against the requirement {@link Key}. * * @param requirement The requirement key * @param binding The binding to qualify * @return Qualifier annotation when the binding qualifies; otherwise {@code null} */ abstract Annotation qualifies( final Key<?> requirement, final Binding<?> binding ); /** * Selects the appropriate qualifying strategy for the given requirement {@link Key}. * * @param key The requirement key * @return Qualifying strategy */ static final QualifyingStrategy selectFor( final Key<?> key ) { final Class<?> qualifierType = key.getAnnotationType(); if ( null == qualifierType ) { return QualifyingStrategy.UNRESTRICTED; } if ( Named.class == qualifierType ) { return key.hasAttributes() ? QualifyingStrategy.NAMED_WITH_ATTRIBUTES : QualifyingStrategy.NAMED; } return key.hasAttributes() ? QualifyingStrategy.MARKED_WITH_ATTRIBUTES : QualifyingStrategy.MARKED; } /** * Computes a canonical {@link Qualifier} annotation for the given binding {@link Key}. * * @param key The key to qualify * @return Qualifier for the key */ static final Annotation qualify( final Key<?> key ) { if ( null == key.getAnnotationType() ) { return DEFAULT_QUALIFIER; } final Annotation qualifier = key.getAnnotation(); if ( qualifier instanceof Provider<?> ) { // the qualifier is actually a wrapper around the original final Object original = ( (Provider<?>) qualifier ).get(); return original instanceof Annotation ? (Annotation) original : DEFAULT_QUALIFIER; } return qualifier; } }