View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.internal.injection;
6   
7   import static mockit.internal.injection.InjectionPoint.INJECT_CLASS;
8   import static mockit.internal.util.Utilities.getClassType;
9   
10  import edu.umd.cs.findbugs.annotations.NonNull;
11  import edu.umd.cs.findbugs.annotations.Nullable;
12  
13  import java.lang.reflect.ParameterizedType;
14  import java.lang.reflect.Type;
15  import java.util.ArrayList;
16  import java.util.Collections;
17  import java.util.List;
18  
19  import javax.inject.Provider;
20  
21  import mockit.internal.injection.InjectionPoint.KindOfInjectionPoint;
22  import mockit.internal.reflection.GenericTypeReflection;
23  
24  public final class InjectionProviders {
25      @NonNull
26      private List<InjectionProvider> injectables;
27      @NonNull
28      private List<InjectionProvider> consumedInjectionProviders;
29      private Type typeOfInjectionPoint;
30      private KindOfInjectionPoint kindOfInjectionPoint;
31  
32      InjectionProviders(@NonNull LifecycleMethods lifecycleMethods) {
33          injectables = Collections.emptyList();
34          consumedInjectionProviders = new ArrayList<>();
35      }
36  
37      boolean setInjectables(
38              @SuppressWarnings("ParameterHidesMemberVariable") @NonNull List<? extends InjectionProvider> injectables) {
39          if (injectables.isEmpty()) {
40              this.injectables = Collections.emptyList();
41              return false;
42          }
43  
44          this.injectables = new ArrayList<>(injectables);
45          return true;
46      }
47  
48      @NonNull
49      List<InjectionProvider> addInjectables(@NonNull List<? extends InjectionProvider> injectablesToAdd) {
50          if (!injectablesToAdd.isEmpty()) {
51              if (injectables.isEmpty()) {
52                  injectables = new ArrayList<>(injectablesToAdd);
53              } else {
54                  injectables.addAll(injectablesToAdd);
55              }
56          }
57  
58          return injectables;
59      }
60  
61      public void setTypeOfInjectionPoint(@NonNull Type typeOfInjectionPoint,
62              @NonNull KindOfInjectionPoint kindOfInjectionPoint) {
63          this.typeOfInjectionPoint = typeOfInjectionPoint;
64          this.kindOfInjectionPoint = kindOfInjectionPoint;
65      }
66  
67      @Nullable
68      public InjectionProvider getProviderByTypeAndOptionallyName(@NonNull String nameOfInjectionPoint,
69              @NonNull TestedClass testedClass) {
70          if (kindOfInjectionPoint == KindOfInjectionPoint.Required) {
71              Type elementTypeOfIterable = getElementTypeIfIterable(typeOfInjectionPoint);
72  
73              if (elementTypeOfIterable != null) {
74                  return findInjectablesByTypeOnly(elementTypeOfIterable, testedClass);
75              }
76          }
77  
78          return findInjectableByTypeAndOptionallyName(nameOfInjectionPoint, testedClass);
79      }
80  
81      @Nullable
82      private static Type getElementTypeIfIterable(@NonNull Type injectableType) {
83          if (injectableType instanceof ParameterizedType) {
84              ParameterizedType parameterizedType = (ParameterizedType) injectableType;
85              Class<?> classOfInjectionPoint = (Class<?>) parameterizedType.getRawType();
86  
87              if (Iterable.class.isAssignableFrom(classOfInjectionPoint)) {
88                  return parameterizedType.getActualTypeArguments()[0];
89              }
90          }
91  
92          return null;
93      }
94  
95      @Nullable
96      public InjectionProvider findNextInjectableForInjectionPoint(@NonNull TestedClass testedClass) {
97          for (InjectionProvider injectable : injectables) {
98              if (hasTypeAssignableToInjectionPoint(injectable, testedClass)
99                      && !consumedInjectionProviders.contains(injectable)) {
100                 return injectable;
101             }
102         }
103 
104         return null;
105     }
106 
107     private boolean hasTypeAssignableToInjectionPoint(@NonNull InjectionProvider injectable,
108             @NonNull TestedClass testedClass) {
109         Type declaredType = injectable.getDeclaredType();
110         return isAssignableToInjectionPoint(declaredType, testedClass);
111     }
112 
113     boolean isAssignableToInjectionPoint(@NonNull Type injectableType, @NonNull TestedClass testedClass) {
114         if (testedClass.reflection.areMatchingTypes(typeOfInjectionPoint, injectableType)) {
115             return true;
116         }
117 
118         if (typeOfInjectionPoint instanceof ParameterizedType) {
119             ParameterizedType parameterizedType = (ParameterizedType) typeOfInjectionPoint;
120             Class<?> classOfInjectionPoint = (Class<?>) parameterizedType.getRawType();
121 
122             if (kindOfInjectionPoint == KindOfInjectionPoint.Required
123                     && Iterable.class.isAssignableFrom(classOfInjectionPoint)
124                     || INJECT_CLASS != null && Provider.class.isAssignableFrom(classOfInjectionPoint)) {
125                 Type providedType = parameterizedType.getActualTypeArguments()[0];
126 
127                 if (providedType.equals(injectableType)) {
128                     return true;
129                 }
130 
131                 Class<?> injectableClass = getClassType(injectableType);
132                 Class<?> providedClass = getClassType(providedType);
133 
134                 return providedClass.isAssignableFrom(injectableClass);
135             }
136         }
137 
138         return false;
139     }
140 
141     @Nullable
142     private InjectionProvider findInjectablesByTypeOnly(@NonNull Type elementType, @NonNull TestedClass testedClass) {
143         GenericTypeReflection typeReflection = testedClass.reflection;
144         MultiValuedProvider found = null;
145 
146         for (InjectionProvider injectable : injectables) {
147             Type injectableType = injectable.getDeclaredType();
148             Type elementTypeOfIterable = getElementTypeIfIterable(injectableType);
149 
150             if (elementTypeOfIterable != null && typeReflection.areMatchingTypes(elementType, elementTypeOfIterable)) {
151                 return injectable;
152             }
153 
154             if (isAssignableToInjectionPoint(injectableType, testedClass)) {
155                 if (found == null) {
156                     found = new MultiValuedProvider(elementType);
157                 }
158 
159                 found.addInjectable(injectable);
160             }
161         }
162 
163         return found;
164     }
165 
166     @Nullable
167     private InjectionProvider findInjectableByTypeAndOptionallyName(@NonNull String nameOfInjectionPoint,
168             @NonNull TestedClass testedClass) {
169         InjectionProvider foundInjectable = null;
170 
171         for (InjectionProvider injectable : injectables) {
172             if (hasTypeAssignableToInjectionPoint(injectable, testedClass)) {
173                 if (nameOfInjectionPoint.equals(injectable.getName())) {
174                     return injectable;
175                 }
176 
177                 if (foundInjectable == null) {
178                     foundInjectable = injectable;
179                 }
180             }
181         }
182 
183         return foundInjectable;
184     }
185 
186     @Nullable
187     InjectionProvider findInjectableByTypeAndName(@NonNull String nameOfInjectionPoint,
188             @NonNull TestedClass testedClass) {
189         for (InjectionProvider injectable : injectables) {
190             if (hasTypeAssignableToInjectionPoint(injectable, testedClass)
191                     && nameOfInjectionPoint.equals(injectable.getName())) {
192                 return injectable;
193             }
194         }
195 
196         return null;
197     }
198 
199     @Nullable
200     Object getValueToInject(@NonNull InjectionProvider injectionProvider, @Nullable Object currentTestClassInstance) {
201         if (consumedInjectionProviders.contains(injectionProvider)) {
202             return null;
203         }
204 
205         Object value = injectionProvider.getValue(currentTestClassInstance);
206 
207         if (value != null) {
208             consumedInjectionProviders.add(injectionProvider);
209         }
210 
211         return value;
212     }
213 
214     void resetConsumedInjectionProviders() {
215         consumedInjectionProviders.clear();
216     }
217 
218     @NonNull
219     public List<InjectionProvider> saveConsumedInjectionProviders() {
220         List<InjectionProvider> previouslyConsumed = consumedInjectionProviders;
221         consumedInjectionProviders = new ArrayList<>();
222         return previouslyConsumed;
223     }
224 
225     public void restoreConsumedInjectionProviders(@NonNull List<InjectionProvider> previouslyConsumed) {
226         consumedInjectionProviders = previouslyConsumed;
227     }
228 }