1 /*
2  * Copyright (C) 2007 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.assistedinject;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 
21 import com.google.inject.ConfigurationException;
22 import com.google.inject.Injector;
23 import com.google.inject.Key;
24 import com.google.inject.Provider;
25 import com.google.inject.internal.Annotations;
26 import java.lang.annotation.Annotation;
27 import java.lang.reflect.ParameterizedType;
28 import java.lang.reflect.Type;
29 
30 /**
31  * Models a method or constructor parameter.
32  *
33  * @author jmourits@google.com (Jerome Mourits)
34  * @author jessewilson@google.com (Jesse Wilson)
35  */
36 class Parameter {
37 
38   private final Type type;
39   private final boolean isAssisted;
40   private final Annotation bindingAnnotation;
41   private final boolean isProvider;
42 
43   private volatile Provider<? extends Object> provider;
44 
Parameter(Type type, Annotation[] annotations)45   public Parameter(Type type, Annotation[] annotations) {
46     this.type = type;
47     this.bindingAnnotation = getBindingAnnotation(annotations);
48     this.isAssisted = hasAssistedAnnotation(annotations);
49     this.isProvider = isProvider(type);
50   }
51 
isProvidedByFactory()52   public boolean isProvidedByFactory() {
53     return isAssisted;
54   }
55 
getType()56   public Type getType() {
57     return type;
58   }
59 
60   @Override
toString()61   public String toString() {
62     StringBuilder result = new StringBuilder();
63     if (isAssisted) {
64       result.append("@Assisted ");
65     }
66     if (bindingAnnotation != null) {
67       result.append(bindingAnnotation).append(" ");
68     }
69     return result.append(type).toString();
70   }
71 
hasAssistedAnnotation(Annotation[] annotations)72   private boolean hasAssistedAnnotation(Annotation[] annotations) {
73     for (Annotation annotation : annotations) {
74       if (annotation.annotationType().equals(Assisted.class)) {
75         return true;
76       }
77     }
78     return false;
79   }
80 
81   /** Returns the Guice {@link Key} for this parameter. */
getValue(Injector injector)82   public Object getValue(Injector injector) {
83     if (null == provider) {
84       synchronized (this) {
85         if (null == provider) {
86           provider =
87               isProvider
88                   ? injector.getProvider(getBindingForType(getProvidedType(type)))
89                   : injector.getProvider(getPrimaryBindingKey());
90         }
91       }
92     }
93 
94     return isProvider ? provider : provider.get();
95   }
96 
isBound(Injector injector)97   public boolean isBound(Injector injector) {
98     return isBound(injector, getPrimaryBindingKey())
99         || isBound(injector, fixAnnotations(getPrimaryBindingKey()));
100   }
101 
isBound(Injector injector, Key<?> key)102   private boolean isBound(Injector injector, Key<?> key) {
103     // This method is particularly lame - we really need an API that can test
104     // for any binding, implicit or explicit
105     try {
106       return injector.getBinding(key) != null;
107     } catch (ConfigurationException e) {
108       return false;
109     }
110   }
111 
112   /**
113    * Replace annotation instances with annotation types, this is only appropriate for testing if a
114    * key is bound and not for injecting.
115    *
116    * <p>See Guice bug 125, https://github.com/google/guice/issues/125
117    */
fixAnnotations(Key<?> key)118   public Key<?> fixAnnotations(Key<?> key) {
119     return key.getAnnotation() == null
120         ? key
121         : Key.get(key.getTypeLiteral(), key.getAnnotation().annotationType());
122   }
123 
getPrimaryBindingKey()124   Key<?> getPrimaryBindingKey() {
125     return isProvider ? getBindingForType(getProvidedType(type)) : getBindingForType(type);
126   }
127 
getProvidedType(Type type)128   private Type getProvidedType(Type type) {
129     return ((ParameterizedType) type).getActualTypeArguments()[0];
130   }
131 
isProvider(Type type)132   private boolean isProvider(Type type) {
133     return type instanceof ParameterizedType
134         && ((ParameterizedType) type).getRawType() == Provider.class;
135   }
136 
getBindingForType(Type type)137   private Key<?> getBindingForType(Type type) {
138     return bindingAnnotation != null ? Key.get(type, bindingAnnotation) : Key.get(type);
139   }
140 
141   /**
142    * Returns the unique binding annotation from the specified list, or {@code null} if there are
143    * none.
144    *
145    * @throws IllegalStateException if multiple binding annotations exist.
146    */
getBindingAnnotation(Annotation[] annotations)147   private Annotation getBindingAnnotation(Annotation[] annotations) {
148     Annotation bindingAnnotation = null;
149     for (Annotation annotation : annotations) {
150       if (Annotations.isBindingAnnotation(annotation.annotationType())) {
151         checkArgument(
152             bindingAnnotation == null,
153             "Parameter has multiple binding annotations: %s and %s",
154             bindingAnnotation,
155             annotation);
156         bindingAnnotation = annotation;
157       }
158     }
159     return bindingAnnotation;
160   }
161 }
162