/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.everrest.core;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* @author andrew00x
*/
public abstract class BaseDependencySupplier implements DependencySupplier {
protected final Class<? extends Annotation> injectAnnotationClass;
protected final DependencyNameDetector nameDetector;
public BaseDependencySupplier(Class<? extends Annotation> injectAnnotationClass, DependencyNameDetector nameDetector) {
if (injectAnnotationClass == null) {
throw new IllegalArgumentException("Inject annotation class may not be null. ");
}
this.injectAnnotationClass = injectAnnotationClass;
this.nameDetector = nameDetector;
}
public BaseDependencySupplier(Class<? extends Annotation> injectAnnotationClass) {
if (injectAnnotationClass == null) {
throw new IllegalArgumentException("Inject annotation class may not be null. ");
}
this.injectAnnotationClass = injectAnnotationClass;
nameDetector = null;
}
public BaseDependencySupplier() {
this(javax.inject.Inject.class, new DependencyNameDetector() {
@Override
public String getName(Parameter parameter) {
for (Annotation a : parameter.getAnnotations()) {
if (javax.inject.Named.class.isInstance(a)) {
String name = ((javax.inject.Named)a).value();
if (!name.isEmpty()) {
return name;
}
}
}
return null;
}
});
}
@Override
public final Object getInstance(Parameter parameter) {
boolean injectable = false;
if (parameter instanceof FieldInjector) {
for (Annotation a : parameter.getAnnotations()) {
if (injectAnnotationClass.isInstance(a)) {
injectable = true;
break;
}
}
} else {
// Annotation required for fields only.
injectable = true;
}
if (injectable) {
String name = nameDetector != null ? nameDetector.getName(parameter) : null;
if (name != null) {
return getInstanceByName(name);
}
Class<?> parameterClass = parameter.getParameterClass();
if (isProvider(parameterClass)) {
return getProvider(parameter.getGenericType());
}
return getInstance(parameterClass);
}
return null;
}
/**
* Get instance of dependency by name. This is optional capability so by default this method returns {@code null}. Override it if
* back-end (e.g. IoC container) supports getting components by key (name).
*
* @param name
* of dependency
* @return object of required type or null if instance described by {@code name} may not be produced
* @throws RuntimeException
* if any error occurs while creating instance of {@code name}
*/
public Object getInstanceByName(String name) {
return null;
}
/**
* Check is {@code clazz} is javax.inject.Provider (not subclass of it).
*
* @param clazz
* class to be checked
* @return {@code true} if {@code clazz} is javax.inject.Provider and {@code false} otherwise
*/
protected final boolean isProvider(Class<?> clazz) {
return javax.inject.Provider.class == clazz;
}
/**
* Get Provider of type {@code providerType}.
*
* @param providerType
* parameterized javax.inject.Provider type
* @return Provider the instance of javax.inject.Provider for specified {@code providerType}
*/
public javax.inject.Provider<?> getProvider(Type providerType) {
if (!(providerType instanceof ParameterizedType)) {
throw new RuntimeException("Cannot inject provider without type parameter. ");
}
if (((ParameterizedType)providerType).getRawType() != javax.inject.Provider.class) {
throw new RuntimeException(String.format("Type %s is not javax.inject.Provider. ", providerType));
}
final Type actualType = ((ParameterizedType)providerType).getActualTypeArguments()[0];
final Class<?> componentType;
if (actualType instanceof Class) {
componentType = (Class<?>)actualType;
} else if (actualType instanceof ParameterizedType) {
componentType = (Class<?>)((ParameterizedType)actualType).getRawType();
} else {
throw new RuntimeException(String.format("Unsupported type %s. ", actualType));
}
// javax.inject.Provider#get() may return null. Such behavior may be unexpected by caller.
// May be overridden if back-end (e.g. IoC container) provides better solution.
return new javax.inject.Provider<Object>() {
@Override
public Object get() {
return getInstance(componentType);
}
};
}
/**
* Implementation of this interface determine name of dependency required for constructors or fields of Resource or
* Provider. Typically the name of the dependency may be set by annotation with value as component's qualifier, e.g.
* @javax.inject.Named.
*/
public interface DependencyNameDetector {
/**
* Get name of parameter.
*
* @param parameter
* the Parameter
* @return name of {@code parameter} or {@code null} if name can't be detected
*/
String getName(Parameter parameter);
}
}