blob: 8a2e29b11f5964eb977df0709b8d484ce208a59f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2013 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 java.lang.reflect.Method;
import javax.inject.Inject;
import org.eclipse.sisu.Priority;
import com.google.inject.Binding;
import com.google.inject.spi.BindingTargetVisitor;
/**
* Simple {@link RankingFunction} that partitions qualified bindings into two main groups.
* <p>
* Default bindings are given zero or positive ranks; the rest are given negative ranks.
*/
public final class DefaultRankingFunction
implements RankingFunction
{
// ----------------------------------------------------------------------
// Static initialization
// ----------------------------------------------------------------------
static
{
ImplementationVisitor servletVisitor;
try
{
servletVisitor = new ServletVisitor();
}
catch ( final LinkageError e )
{
servletVisitor = null;
}
SERVLET_VISITOR = servletVisitor;
Method jsr250PriorityValue;
try
{
final Class<?> clazz = Priority.class.getClassLoader().loadClass( "javax.annotation.Priority" );
jsr250PriorityValue = clazz.getMethod( "value" );
}
catch ( final Exception e )
{
jsr250PriorityValue = null;
}
catch ( final LinkageError e )
{
jsr250PriorityValue = null;
}
JSR250_PRIORITY_VALUE = jsr250PriorityValue;
}
// ----------------------------------------------------------------------
// Constants
// ----------------------------------------------------------------------
private static final BindingTargetVisitor<Object, Class<?>> SERVLET_VISITOR;
private static final BindingTargetVisitor<Object, Class<?>> TARGET_VISITOR =
SERVLET_VISITOR == null ? ImplementationVisitor.THIS : SERVLET_VISITOR;
private static final Method JSR250_PRIORITY_VALUE;
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private final int primaryRank;
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
public DefaultRankingFunction( final int primaryRank )
{
if ( primaryRank < 0 )
{
throw new IllegalArgumentException( "Primary rank must be zero or more" );
}
this.primaryRank = primaryRank;
}
@Inject
public DefaultRankingFunction()
{
this( 0 ); // use this as the default primary rank unless otherwise configured
}
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
public int maxRank()
{
return primaryRank;
}
public <T> int rank( final Binding<T> binding )
{
final Class<?> implementation = binding.acceptTargetVisitor( TARGET_VISITOR );
if ( null != implementation )
{
if ( null != JSR250_PRIORITY_VALUE )
{
final Object value = getJSR250PriorityValue( implementation );
if ( value instanceof Number )
{
return ( (Number) value ).intValue();
}
}
final Priority priority = implementation.getAnnotation( Priority.class );
if ( null != priority )
{
return priority.value();
}
}
if ( QualifyingStrategy.DEFAULT_QUALIFIER.equals( QualifyingStrategy.qualify( binding.getKey() ) ) )
{
return primaryRank;
}
return primaryRank + Integer.MIN_VALUE; // shifts primary range of [0,MAX_VALUE] down to [MIN_VALUE,-1]
}
// ----------------------------------------------------------------------
// Implementation methods
// ----------------------------------------------------------------------
private static Object getJSR250PriorityValue( final Class<?> clazz )
{
@SuppressWarnings( "unchecked" )
final Object priority = clazz.getAnnotation( (Class<Annotation>) JSR250_PRIORITY_VALUE.getDeclaringClass() );
if ( null != priority )
{
try
{
return JSR250_PRIORITY_VALUE.invoke( priority );
}
catch ( final Exception e )
{
// ignore
}
catch ( final LinkageError e )
{
// ignore
}
}
return null;
}
// ----------------------------------------------------------------------
// Implementation types
// ----------------------------------------------------------------------
/**
* {@link ImplementationVisitor} that can also peek behind filter/servlet bindings.
*/
static final class ServletVisitor
extends ImplementationVisitor
implements com.google.inject.servlet.ServletModuleTargetVisitor<Object, Class<?>>
{
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();
}
}
}