1 /** 2 * Copyright (C) 2009 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.internal; 18 19 import com.google.common.collect.ImmutableList; 20 import com.google.common.collect.Lists; 21 import com.google.common.collect.Sets; 22 import com.google.inject.ConfigurationException; 23 import com.google.inject.TypeLiteral; 24 import com.google.inject.spi.InjectionPoint; 25 import com.google.inject.spi.TypeListener; 26 import com.google.inject.spi.TypeListenerBinding; 27 28 import java.lang.reflect.Field; 29 import java.util.List; 30 import java.util.Set; 31 32 /** 33 * Members injectors by type. 34 * 35 * @author jessewilson (at) google.com (Jesse Wilson) 36 */ 37 final class MembersInjectorStore { 38 private final InjectorImpl injector; 39 private final ImmutableList<TypeListenerBinding> typeListenerBindings; 40 41 private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache 42 = new FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>>() { 43 @Override protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors) 44 throws ErrorsException { 45 return createWithListeners(type, errors); 46 } 47 }; 48 49 MembersInjectorStore(InjectorImpl injector, 50 List<TypeListenerBinding> typeListenerBindings) { 51 this.injector = injector; 52 this.typeListenerBindings = ImmutableList.copyOf(typeListenerBindings); 53 } 54 55 /** 56 * Returns true if any type listeners are installed. Other code may take shortcuts when there 57 * aren't any type listeners. 58 */ 59 public boolean hasTypeListeners() { 60 return !typeListenerBindings.isEmpty(); 61 } 62 63 /** 64 * Returns a new complete members injector with injection listeners registered. 65 */ 66 @SuppressWarnings("unchecked") // the MembersInjector type always agrees with the passed type 67 public <T> MembersInjectorImpl<T> get(TypeLiteral<T> key, Errors errors) throws ErrorsException { 68 return (MembersInjectorImpl<T>) cache.get(key, errors); 69 } 70 71 /** 72 * Purges a type literal from the cache. Use this only if the type is not actually valid for 73 * binding and needs to be purged. (See issue 319 and 74 * ImplicitBindingTest#testCircularJitBindingsLeaveNoResidue and 75 * #testInstancesRequestingProvidersForThemselvesWithChildInjectors for examples of when this is 76 * necessary.) 77 * 78 * Returns true if the type was stored in the cache, false otherwise. 79 */ 80 boolean remove(TypeLiteral<?> type) { 81 return cache.remove(type); 82 } 83 84 /** 85 * Creates a new members injector and attaches both injection listeners and method aspects. 86 */ 87 private <T> MembersInjectorImpl<T> createWithListeners(TypeLiteral<T> type, Errors errors) 88 throws ErrorsException { 89 int numErrorsBefore = errors.size(); 90 91 Set<InjectionPoint> injectionPoints; 92 try { 93 injectionPoints = InjectionPoint.forInstanceMethodsAndFields(type); 94 } catch (ConfigurationException e) { 95 errors.merge(e.getErrorMessages()); 96 injectionPoints = e.getPartialValue(); 97 } 98 ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors); 99 errors.throwIfNewErrors(numErrorsBefore); 100 101 EncounterImpl<T> encounter = new EncounterImpl<T>(errors, injector.lookups); 102 Set<TypeListener> alreadySeenListeners = Sets.newHashSet(); 103 for (TypeListenerBinding binding : typeListenerBindings) { 104 TypeListener typeListener = binding.getListener(); 105 if (!alreadySeenListeners.contains(typeListener) && binding.getTypeMatcher().matches(type)) { 106 alreadySeenListeners.add(typeListener); 107 try { 108 typeListener.hear(type, encounter); 109 } catch (RuntimeException e) { 110 errors.errorNotifyingTypeListener(binding, type, e); 111 } 112 } 113 } 114 encounter.invalidate(); 115 errors.throwIfNewErrors(numErrorsBefore); 116 117 return new MembersInjectorImpl<T>(injector, type, encounter, injectors); 118 } 119 120 /** 121 * Returns the injectors for the specified injection points. 122 */ 123 ImmutableList<SingleMemberInjector> getInjectors( 124 Set<InjectionPoint> injectionPoints, Errors errors) { 125 List<SingleMemberInjector> injectors = Lists.newArrayList(); 126 for (InjectionPoint injectionPoint : injectionPoints) { 127 try { 128 Errors errorsForMember = injectionPoint.isOptional() 129 ? new Errors(injectionPoint) 130 : errors.withSource(injectionPoint); 131 SingleMemberInjector injector = injectionPoint.getMember() instanceof Field 132 ? new SingleFieldInjector(this.injector, injectionPoint, errorsForMember) 133 : new SingleMethodInjector(this.injector, injectionPoint, errorsForMember); 134 injectors.add(injector); 135 } catch (ErrorsException ignoredForNow) { 136 // ignored for now 137 } 138 } 139 return ImmutableList.copyOf(injectors); 140 } 141 } 142