| /** |
| * Copyright (C) 2009 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.inject.internal; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.inject.Key; |
| import com.google.inject.MembersInjector; |
| import com.google.inject.TypeLiteral; |
| import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback; |
| import com.google.inject.spi.InjectionListener; |
| import com.google.inject.spi.InjectionPoint; |
| |
| /** |
| * Injects members of instances of a given type. |
| * |
| * @author jessewilson@google.com (Jesse Wilson) |
| */ |
| final class MembersInjectorImpl<T> implements MembersInjector<T> { |
| private final TypeLiteral<T> typeLiteral; |
| private final InjectorImpl injector; |
| private final ImmutableList<SingleMemberInjector> memberInjectors; |
| private final ImmutableSet<MembersInjector<? super T>> userMembersInjectors; |
| private final ImmutableSet<InjectionListener<? super T>> injectionListeners; |
| /*if[AOP]*/ |
| private final ImmutableList<MethodAspect> addedAspects; |
| /*end[AOP]*/ |
| |
| MembersInjectorImpl(InjectorImpl injector, TypeLiteral<T> typeLiteral, |
| EncounterImpl<T> encounter, ImmutableList<SingleMemberInjector> memberInjectors) { |
| this.injector = injector; |
| this.typeLiteral = typeLiteral; |
| this.memberInjectors = memberInjectors; |
| this.userMembersInjectors = encounter.getMembersInjectors(); |
| this.injectionListeners = encounter.getInjectionListeners(); |
| /*if[AOP]*/ |
| this.addedAspects = encounter.getAspects(); |
| /*end[AOP]*/ |
| } |
| |
| public ImmutableList<SingleMemberInjector> getMemberInjectors() { |
| return memberInjectors; |
| } |
| |
| public void injectMembers(T instance) { |
| Errors errors = new Errors(typeLiteral); |
| try { |
| injectAndNotify(instance, errors, null, null, typeLiteral, false); |
| } catch (ErrorsException e) { |
| errors.merge(e.getErrors()); |
| } |
| |
| errors.throwProvisionExceptionIfErrorsExist(); |
| } |
| |
| void injectAndNotify(final T instance, |
| final Errors errors, |
| final Key<T> key, // possibly null! |
| final ProvisionListenerStackCallback<T> provisionCallback, // possibly null! |
| final Object source, |
| final boolean toolableOnly) throws ErrorsException { |
| if (instance == null) { |
| return; |
| } |
| |
| injector.callInContext(new ContextualCallable<Void>() { |
| @Override |
| public Void call(final InternalContext context) throws ErrorsException { |
| context.pushState(key, source); |
| try { |
| if (provisionCallback != null && provisionCallback.hasListeners()) { |
| provisionCallback.provision(errors, context, new ProvisionCallback<T>() { |
| @Override public T call() { |
| injectMembers(instance, errors, context, toolableOnly); |
| return instance; |
| } |
| }); |
| } else { |
| injectMembers(instance, errors, context, toolableOnly); |
| } |
| } finally { |
| context.popState(); |
| } |
| return null; |
| } |
| }); |
| |
| // TODO: We *could* notify listeners too here, |
| // but it's not clear if we want to. There's no way to know |
| // if a MembersInjector from the usersMemberInjector list wants |
| // toolable injections, so do we really want to notify |
| // about injection? (We could take a strategy of only notifying |
| // if atleast one InjectionPoint was toolable, in which case |
| // the above callInContext could return 'true' if it injected |
| // anything.) |
| if(!toolableOnly) { |
| notifyListeners(instance, errors); |
| } |
| } |
| |
| void notifyListeners(T instance, Errors errors) throws ErrorsException { |
| int numErrorsBefore = errors.size(); |
| for (InjectionListener<? super T> injectionListener : injectionListeners) { |
| try { |
| injectionListener.afterInjection(instance); |
| } catch (RuntimeException e) { |
| errors.errorNotifyingInjectionListener(injectionListener, typeLiteral, e); |
| } |
| } |
| errors.throwIfNewErrors(numErrorsBefore); |
| } |
| |
| void injectMembers(T t, Errors errors, InternalContext context, boolean toolableOnly) { |
| // optimization: use manual for/each to save allocating an iterator here |
| for (int i = 0, size = memberInjectors.size(); i < size; i++) { |
| SingleMemberInjector injector = memberInjectors.get(i); |
| if(!toolableOnly || injector.getInjectionPoint().isToolable()) { |
| injector.inject(errors, context, t); |
| } |
| } |
| |
| // TODO: There's no way to know if a user's MembersInjector wants toolable injections. |
| if(!toolableOnly) { |
| for (MembersInjector<? super T> userMembersInjector : userMembersInjectors) { |
| try { |
| userMembersInjector.injectMembers(t); |
| } catch (RuntimeException e) { |
| errors.errorInUserInjector(userMembersInjector, typeLiteral, e); |
| } |
| } |
| } |
| } |
| |
| @Override public String toString() { |
| return "MembersInjector<" + typeLiteral + ">"; |
| } |
| |
| public ImmutableSet<InjectionPoint> getInjectionPoints() { |
| ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder(); |
| for (SingleMemberInjector memberInjector : memberInjectors) { |
| builder.add(memberInjector.getInjectionPoint()); |
| } |
| return builder.build(); |
| } |
| |
| /*if[AOP]*/ |
| public ImmutableList<MethodAspect> getAddedAspects() { |
| return addedAspects; |
| } |
| /*end[AOP]*/ |
| } |