1 /** 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 17 package com.google.inject.throwingproviders; 18 19 import com.google.common.collect.ImmutableSet; 20 import com.google.inject.Binder; 21 import com.google.inject.Exposed; 22 import com.google.inject.Key; 23 import com.google.inject.PrivateBinder; 24 import com.google.inject.Provider; 25 import com.google.inject.TypeLiteral; 26 import com.google.inject.binder.ScopedBindingBuilder; 27 import com.google.inject.internal.util.StackTraceElements; 28 import com.google.inject.spi.Dependency; 29 import com.google.inject.spi.HasDependencies; 30 import com.google.inject.throwingproviders.ThrowingProviderBinder.SecondaryBinder; 31 32 import java.lang.annotation.Annotation; 33 import java.lang.reflect.InvocationTargetException; 34 import java.lang.reflect.Method; 35 import java.util.List; 36 import java.util.Set; 37 38 /** 39 * A provider that invokes a method and returns its result. 40 * 41 * @author sameb (at) google.com (Sam Berlin) 42 */ 43 class CheckedProviderMethod<T> implements CheckedProvider<T>, HasDependencies { 44 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; 51 private final Class<? extends CheckedProvider> checkedProvider; 52 private final List<TypeLiteral<?>> exceptionTypes; 53 private final boolean scopeExceptions; 54 55 CheckedProviderMethod( 56 Key<T> key, 57 Method method, 58 Object instance, 59 ImmutableSet<Dependency<?>> dependencies, 60 List<Provider<?>> parameterProviders, 61 Class<? extends Annotation> scopeAnnotation, 62 Class<? extends CheckedProvider> checkedProvider, 63 List<TypeLiteral<?>> exceptionTypes, 64 boolean scopeExceptions) { 65 this.key = key; 66 this.scopeAnnotation = scopeAnnotation; 67 this.instance = instance; 68 this.dependencies = dependencies; 69 this.method = method; 70 this.parameterProviders = parameterProviders; 71 this.exposed = method.isAnnotationPresent(Exposed.class); 72 this.checkedProvider = checkedProvider; 73 this.exceptionTypes = exceptionTypes; 74 this.scopeExceptions = scopeExceptions; 75 76 method.setAccessible(true); 77 } 78 79 void configure(Binder binder) { 80 binder = binder.withSource(method); 81 82 SecondaryBinder<?, ?> sbinder = 83 ThrowingProviderBinder.create(binder) 84 .bind(checkedProvider, key.getTypeLiteral()); 85 if(key.getAnnotation() != null) { 86 sbinder = sbinder.annotatedWith(key.getAnnotation()); 87 } else if(key.getAnnotationType() != null) { 88 sbinder = sbinder.annotatedWith(key.getAnnotationType()); 89 } 90 sbinder.scopeExceptions(scopeExceptions); 91 ScopedBindingBuilder sbbuilder = sbinder.toProviderMethod(this); 92 if(scopeAnnotation != null) { 93 sbbuilder.in(scopeAnnotation); 94 } 95 96 if (exposed) { 97 // the cast is safe 'cause the only binder we have implements PrivateBinder. If there's a 98 // misplaced @Exposed, calling this will add an error to the binder's error queue 99 ((PrivateBinder) binder).expose(sbinder.getKey()); 100 } 101 102 CheckedProvideUtils.validateExceptions( 103 binder, exceptionTypes, sbinder.getExceptionTypes(), checkedProvider); 104 } 105 106 public T get() throws Exception { 107 Object[] parameters = new Object[parameterProviders.size()]; 108 for (int i = 0; i < parameters.length; i++) { 109 parameters[i] = parameterProviders.get(i).get(); 110 } 111 112 try { 113 // We know this cast is safe becase T is the method's return type. 114 @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" }) 115 T result = (T) method.invoke(instance, parameters); 116 return result; 117 } catch (IllegalAccessException e) { 118 throw new AssertionError(e); 119 } catch (InvocationTargetException e) { 120 Throwable t = e.getCause(); 121 if(t instanceof Exception) { 122 throw (Exception)t; 123 } else if(t instanceof Error) { 124 throw (Error)t; 125 } else { 126 throw new IllegalStateException(t); 127 } 128 } 129 } 130 131 public Set<Dependency<?>> getDependencies() { 132 return dependencies; 133 } 134 135 @Override public String toString() { 136 return "@CheckedProvides " + StackTraceElements.forMember(method); 137 } 138 } 139