blob: de345c6c0285fffd63d40edb67e30d7e63566760 [file] [log] [blame]
sberlinee446ca2010-10-24 21:36:39 +00001/**
2 * Copyright (C) 2008 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject.throwingproviders;
18
19import java.lang.annotation.Annotation;
20import java.lang.reflect.InvocationTargetException;
21import java.lang.reflect.Method;
22import java.util.List;
23import java.util.Set;
24
25import com.google.inject.Binder;
26import com.google.inject.Exposed;
27import com.google.inject.Key;
28import com.google.inject.PrivateBinder;
29import com.google.inject.Provider;
30import com.google.inject.TypeLiteral;
31import com.google.inject.binder.ScopedBindingBuilder;
32import com.google.inject.internal.util.ImmutableSet;
33import com.google.inject.internal.util.StackTraceElements;
34import com.google.inject.spi.Dependency;
35import com.google.inject.spi.HasDependencies;
36import com.google.inject.throwingproviders.ThrowingProviderBinder.SecondaryBinder;
37
38/**
39 * A provider that invokes a method and returns its result.
40 *
41 * @author sameb@google.com (Sam Berlin)
42 */
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000043class CheckedProviderMethod<T> implements CheckedProvider<T>, HasDependencies {
sberlinee446ca2010-10-24 21:36:39 +000044 private final Key<T> key;
45 private final Class<? extends Annotation> scopeAnnotation;
46 private final Object instance;
47 private final Method method;
48 private final ImmutableSet<Dependency<?>> dependencies;
49 private final List<Provider<?>> parameterProviders;
50 private final boolean exposed;
sberlin488284d2010-11-17 14:27:41 +000051 private final Class<? extends CheckedProvider> checkedProvider;
sberlinee446ca2010-10-24 21:36:39 +000052 private final List<TypeLiteral<?>> exceptionTypes;
53
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000054 CheckedProviderMethod(
sberlinee446ca2010-10-24 21:36:39 +000055 Key<T> key,
56 Method method,
57 Object instance,
58 ImmutableSet<Dependency<?>> dependencies,
59 List<Provider<?>> parameterProviders,
60 Class<? extends Annotation> scopeAnnotation,
sberlin488284d2010-11-17 14:27:41 +000061 Class<? extends CheckedProvider> checkedProvider,
sberlinee446ca2010-10-24 21:36:39 +000062 List<TypeLiteral<?>> exceptionTypes) {
63 this.key = key;
64 this.scopeAnnotation = scopeAnnotation;
65 this.instance = instance;
66 this.dependencies = dependencies;
67 this.method = method;
68 this.parameterProviders = parameterProviders;
69 this.exposed = method.isAnnotationPresent(Exposed.class);
sberlin488284d2010-11-17 14:27:41 +000070 this.checkedProvider = checkedProvider;
sberlinee446ca2010-10-24 21:36:39 +000071 this.exceptionTypes = exceptionTypes;
72
73 method.setAccessible(true);
74 }
75
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000076 void configure(Binder binder) {
sberlinee446ca2010-10-24 21:36:39 +000077 binder = binder.withSource(method);
78
79 SecondaryBinder<?> sbinder =
80 ThrowingProviderBinder.create(binder)
sberlin488284d2010-11-17 14:27:41 +000081 .bind(checkedProvider, key.getTypeLiteral().getType());
sberlinee446ca2010-10-24 21:36:39 +000082 if(key.getAnnotation() != null) {
83 sbinder = sbinder.annotatedWith(key.getAnnotation());
84 } else if(key.getAnnotationType() != null) {
85 sbinder = sbinder.annotatedWith(key.getAnnotationType());
86 }
87 ScopedBindingBuilder sbbuilder = sbinder.toProviderMethod(this);
88 if(scopeAnnotation != null) {
89 sbbuilder.in(scopeAnnotation);
90 }
91
92 if (exposed) {
93 // the cast is safe 'cause the only binder we have implements PrivateBinder. If there's a
94 // misplaced @Exposed, calling this will add an error to the binder's error queue
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000095 ((PrivateBinder) binder).expose(sbinder.getKey());
sberlinee446ca2010-10-24 21:36:39 +000096 }
97
98 // Validate the exceptions in the method match the exceptions
sberlin488284d2010-11-17 14:27:41 +000099 // in the CheckedProvider.
sberlinee446ca2010-10-24 21:36:39 +0000100 for(TypeLiteral<?> exType : exceptionTypes) {
sberlin488284d2010-11-17 14:27:41 +0000101 Class<?> exActual = exType.getRawType();
102 // Ignore runtime exceptions & errors.
103 if(RuntimeException.class.isAssignableFrom(exActual) || Error.class.isAssignableFrom(exActual)) {
sberlinee446ca2010-10-24 21:36:39 +0000104 continue;
105 }
106
sberlin488284d2010-11-17 14:27:41 +0000107 boolean notAssignable = true;
108 for(Class<? extends Throwable> exExpected : sbinder.getExceptionTypes()) {
109 if (exExpected.isAssignableFrom(exActual)) {
110 notAssignable = false;
111 break;
sberlinee446ca2010-10-24 21:36:39 +0000112 }
113 }
sberlin488284d2010-11-17 14:27:41 +0000114 if(notAssignable) {
115 binder.addError(
116 "%s is not compatible with the exceptions (%s) declared in the CheckedProvider interface (%s)",
117 exActual, sbinder.getExceptionTypes(), checkedProvider);
118 }
sberlinee446ca2010-10-24 21:36:39 +0000119 }
120 }
121
122 public T get() throws Exception {
123 Object[] parameters = new Object[parameterProviders.size()];
124 for (int i = 0; i < parameters.length; i++) {
125 parameters[i] = parameterProviders.get(i).get();
126 }
127
128 try {
129 // We know this cast is safe becase T is the method's return type.
130 @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" })
131 T result = (T) method.invoke(instance, parameters);
132 return result;
133 } catch (IllegalAccessException e) {
134 throw new AssertionError(e);
135 } catch (InvocationTargetException e) {
136 Throwable t = e.getCause();
137 if(t instanceof Exception) {
138 throw (Exception)t;
139 } else if(t instanceof Error) {
140 throw (Error)t;
141 } else {
142 throw new IllegalStateException(t);
143 }
144 }
145 }
146
147 public Set<Dependency<?>> getDependencies() {
148 return dependencies;
149 }
150
151 @Override public String toString() {
sberlin488284d2010-11-17 14:27:41 +0000152 return "@CheckedProvides " + StackTraceElements.forMember(method).toString();
sberlinee446ca2010-10-24 21:36:39 +0000153 }
154}