/******************************************************************************* * 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 org.eclipse.sisu.Description; import org.eclipse.sisu.Priority; import com.google.inject.Binding; import com.google.inject.spi.BindingTargetVisitor; import com.google.inject.spi.ConstructorBinding; import com.google.inject.spi.DefaultBindingTargetVisitor; import com.google.inject.spi.ExposedBinding; import com.google.inject.spi.InstanceBinding; import com.google.inject.spi.LinkedKeyBinding; import com.google.inject.spi.ProviderInstanceBinding; import com.google.inject.spi.UntargettedBinding; /** * Utility methods for discovering the implementations behind Guice bindings. */ final class Implementations { // ---------------------------------------------------------------------- // Static initialization // ---------------------------------------------------------------------- static { boolean hasGuiceServlet; try { hasGuiceServlet = BindingTargetVisitor.class.isInstance( ServletFinder.THIS ); } catch ( final LinkageError e ) { hasGuiceServlet = false; } HAS_GUICE_SERVLET = hasGuiceServlet; boolean hasJsr250Priority; try { hasJsr250Priority = javax.annotation.Priority.class.isAnnotation(); } catch ( final LinkageError e ) { hasJsr250Priority = false; } HAS_JSR250_PRIORITY = hasJsr250Priority; } // ---------------------------------------------------------------------- // Constants // ---------------------------------------------------------------------- private static final boolean HAS_GUICE_SERVLET; private static final boolean HAS_JSR250_PRIORITY; // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- private Implementations() { // static utility class, not allowed to create instances } // ---------------------------------------------------------------------- // Utility methods // ---------------------------------------------------------------------- /** * Attempts to find the implementation behind the given {@link Binding}. * * @param binding The binding * @return Implementation class behind the binding; {@code null} if it couldn't be found */ public static Class<?> find( final Binding<?> binding ) { return binding.acceptTargetVisitor( ClassFinder.THIS ); } /** * Attempts to find an annotation on the implementation behind the given {@link Binding}. * * @param binding The binding * @param annotationType The annotation type * @return Annotation on the bound implementation; {@code null} if it couldn't be found */ public static <T extends Annotation> T getAnnotation( final Binding<?> binding, final Class<T> annotationType ) { final boolean isPriority = Priority.class.equals( annotationType ); // peek behind servlet/filter extension bindings when checking priority, so we can order them by rank final Class<?> implementation = binding.acceptTargetVisitor( HAS_GUICE_SERVLET && isPriority ? ServletFinder.THIS : ClassFinder.THIS ); T annotation = null; if ( null != implementation ) { annotation = implementation.getAnnotation( annotationType ); if ( null == annotation ) { if ( HAS_JSR250_PRIORITY && isPriority ) { annotation = adaptJsr250( binding, implementation ); } else if ( Description.class.equals( annotationType ) ) { annotation = adaptLegacy( binding, implementation ); } } } return annotation; } // ---------------------------------------------------------------------- // Implementation methods // ---------------------------------------------------------------------- @SuppressWarnings( "unchecked" ) private static <T extends Annotation> T adaptJsr250( final Binding<?> binding, final Class<?> clazz ) { final javax.annotation.Priority jsr250 = clazz.getAnnotation( javax.annotation.Priority.class ); return null != jsr250 ? (T) new PrioritySource( binding.getSource(), jsr250.value() ) : null; } @SuppressWarnings( { "unchecked", "deprecation" } ) private static <T extends Annotation> T adaptLegacy( final Binding<?> binding, final Class<?> clazz ) { final org.sonatype.inject.Description legacy = clazz.getAnnotation( org.sonatype.inject.Description.class ); return null != legacy ? (T) new DescriptionSource( binding.getSource(), legacy.value() ) : null; } // ---------------------------------------------------------------------- // Implementation types // ---------------------------------------------------------------------- /** * {@link BindingTargetVisitor} that attempts to find the implementations behind bindings. */ static class ClassFinder extends DefaultBindingTargetVisitor<Object, Class<?>> { // ---------------------------------------------------------------------- // Constants // ---------------------------------------------------------------------- static final BindingTargetVisitor<Object, Class<?>> THIS = new ClassFinder(); // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- @Override public Class<?> visit( final UntargettedBinding<?> binding ) { return binding.getKey().getTypeLiteral().getRawType(); } @Override public Class<?> visit( final LinkedKeyBinding<?> binding ) { // this assumes only one level of indirection: api-->impl return binding.getLinkedKey().getTypeLiteral().getRawType(); } @Override public Class<?> visit( final ConstructorBinding<?> binding ) { return binding.getConstructor().getDeclaringType().getRawType(); } @Override public Class<?> visit( final InstanceBinding<?> binding ) { return binding.getInstance().getClass(); } @Override public Class<?> visit( final ProviderInstanceBinding<?> binding ) { final Provider<?> provider = Guice4.getProviderInstance( binding ); if ( provider instanceof DeferredProvider<?> ) { try { // deferred providers let us peek at the underlying implementation type return ( (DeferredProvider<?>) provider ).getImplementationClass().load(); } catch ( final TypeNotPresentException e ) // NOPMD { // fall-through } } return null; } @Override public Class<?> visit( final ExposedBinding<?> binding ) { return binding.getPrivateElements().getInjector().getBinding( binding.getKey() ).acceptTargetVisitor( this ); } } /** * {@link ClassFinder} that can also peek behind servlet/filter bindings. */ static final class ServletFinder extends ClassFinder implements com.google.inject.servlet.ServletModuleTargetVisitor<Object, Class<?>> { // ---------------------------------------------------------------------- // Constants // ---------------------------------------------------------------------- @SuppressWarnings( "hiding" ) static final BindingTargetVisitor<Object, Class<?>> THIS = new ServletFinder(); // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- public Class<?> visit( final com.google.inject.servlet.InstanceFilterBinding binding ) { return binding.getFilterInstance().getClass(); } public Class<?> visit( final com.google.inject.servlet.InstanceServletBinding binding ) { return binding.getServletInstance().getClass(); } public Class<?> visit( final com.google.inject.servlet.LinkedFilterBinding binding ) { // this assumes only one level of indirection: api-->impl return binding.getLinkedKey().getTypeLiteral().getRawType(); } public Class<?> visit( final com.google.inject.servlet.LinkedServletBinding binding ) { // this assumes only one level of indirection: api-->impl return binding.getLinkedKey().getTypeLiteral().getRawType(); } } }