blob: 438cea5289142440241e4ef58305bf23be0aed18 [file] [log] [blame]
/**
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.inject.spi;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.Keys;
import com.google.inject.internal.MoreTypes;
import com.google.inject.internal.Nullability;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* A constructor, method or field that can receive injections.
*
* @author crazybob@google.com (Bob Lee)
*/
public class InjectionPoint implements Serializable {
private final boolean optional;
private final Member member;
private final ImmutableList<Dependency<?>> dependencies;
private InjectionPoint(Member member,
ImmutableList<Dependency<?>> dependencies, boolean optional) {
this.member = member;
this.dependencies = dependencies;
this.optional = optional;
}
private InjectionPoint(Method method) throws ErrorsException {
this.member = method;
Inject inject = method.getAnnotation(Inject.class);
this.optional = inject.optional();
this.dependencies = forMember(method, method.getGenericParameterTypes(),
method.getParameterAnnotations(), new Errors());
}
private InjectionPoint(Constructor<?> constructor, Errors errors) throws ErrorsException {
this.member = constructor;
this.optional = false;
// TODO(jessewilson): make sure that if @Inject it exists, its not optional
this.dependencies = forMember(constructor, constructor.getGenericParameterTypes(),
constructor.getParameterAnnotations(), errors);
}
private InjectionPoint(Field field) throws ErrorsException {
this.member = field;
Inject inject = field.getAnnotation(Inject.class);
this.optional = inject.optional();
Annotation[] annotations = field.getAnnotations();
Key<?> key = Keys.get(field.getGenericType(), field, annotations, new Errors());
this.dependencies = ImmutableList.<Dependency<?>>of(
newDependency(key, Nullability.allowsNull(annotations), -1));
}
private ImmutableList<Dependency<?>> forMember(Member member, Type[] genericParameterTypes,
Annotation[][] annotations, Errors errors) throws ErrorsException {
Iterator<Annotation[]> annotationsIterator = Arrays.asList(annotations).iterator();
List<Dependency<?>> dependencies = Lists.newArrayList();
int index = 0;
for (Type parameterType : genericParameterTypes) {
try {
Annotation[] parameterAnnotations = annotationsIterator.next();
Key<?> key = Keys.get(parameterType, member, parameterAnnotations, errors);
dependencies.add(newDependency(key, Nullability.allowsNull(parameterAnnotations), index));
index++;
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
}
errors.throwIfNecessary();
return ImmutableList.copyOf(dependencies);
}
private <T> Dependency<T> newDependency(Key<T> key, boolean allowsNull, int parameterIndex) {
return new Dependency<T>(this, key, allowsNull, parameterIndex);
}
public Member getMember() {
return member;
}
/**
* Returns the dependencies for this injection point. If the injection point is for a method or
* constructor, the dependencies will correspond to that member's parameters. Field injection
* points always have a single dependency for the field itself.
*/
public List<Dependency<?>> getDependencies() {
return dependencies;
}
public boolean isOptional() {
return optional;
}
@Override public boolean equals(Object o) {
return o instanceof InjectionPoint
&& member == ((InjectionPoint) o).member;
}
@Override public int hashCode() {
return member.hashCode();
}
@Override public String toString() {
return MoreTypes.toString(member);
}
/**
* Returns a new injection point for {@code constructor}.
*
* @param constructor a no arguments constructor, or a constructor with any number of arguments
* and the {@literal @}{@link Inject} annotation.
*/
public static InjectionPoint get(Constructor constructor, Errors errors)
throws ErrorsException {
return new InjectionPoint(constructor, errors);
}
/**
* Returns a new injection point for {@code method}.
*
* @param method a method with the {@literal @}{@link Inject} annotation.
*/
public static InjectionPoint get(Method method) throws ErrorsException {
return new InjectionPoint(method);
}
/**
* Returns a new injection point for {@code field}.
*
* @param field a field with the {@literal @}{@link Inject} annotation.
*/
public static InjectionPoint get(Field field) throws ErrorsException {
return new InjectionPoint(field);
}
private Object writeReplace() throws ObjectStreamException {
Member serializableMember = member != null ? MoreTypes.serializableCopy(member) : null;
return new InjectionPoint(serializableMember, dependencies, optional);
}
}