1 /** 2 * Copyright (C) 2011 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.cache.CacheBuilder; 20 import com.google.common.cache.CacheLoader; 21 import com.google.common.cache.LoadingCache; 22 import com.google.common.collect.ImmutableList; 23 import com.google.common.collect.ImmutableSet; 24 import com.google.common.collect.Lists; 25 import com.google.inject.Binding; 26 import com.google.inject.Injector; 27 import com.google.inject.Key; 28 import com.google.inject.Stage; 29 import com.google.inject.spi.ProvisionListener; 30 import com.google.inject.spi.ProvisionListenerBinding; 31 32 import java.util.List; 33 import java.util.Set; 34 import java.util.logging.Logger; 35 36 /** 37 * {@link ProvisionListenerStackCallback} for each key. 38 * 39 * @author sameb (at) google.com (Sam Berlin) 40 */ 41 final class ProvisionListenerCallbackStore { 42 43 // TODO(sameb): Consider exposing this in the API somehow? Maybe? 44 // Lots of code often want to skip over the internal stuffs. 45 private static final Set<Key<?>> INTERNAL_BINDINGS = 46 ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(Logger.class)); 47 48 private final ImmutableList<ProvisionListenerBinding> listenerBindings; 49 50 private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache 51 = CacheBuilder.newBuilder().build( 52 new CacheLoader<KeyBinding, ProvisionListenerStackCallback<?>>() { 53 public ProvisionListenerStackCallback<?> load(KeyBinding key) { 54 return create(key.binding); 55 } 56 }); 57 58 ProvisionListenerCallbackStore(List<ProvisionListenerBinding> listenerBindings) { 59 this.listenerBindings = ImmutableList.copyOf(listenerBindings); 60 } 61 62 /** Returns a new {@link ProvisionListenerStackCallback} for the key. 63 */ 64 @SuppressWarnings("unchecked") // the ProvisionListenerStackCallback type always agrees with the passed type 65 public <T> ProvisionListenerStackCallback<T> get(Binding<T> binding) { 66 // Never notify any listeners for internal bindings. 67 if (!INTERNAL_BINDINGS.contains(binding.getKey())) { 68 return (ProvisionListenerStackCallback<T>) cache.getUnchecked( 69 new KeyBinding(binding.getKey(), binding)); 70 } 71 return ProvisionListenerStackCallback.emptyListener(); 72 } 73 74 /** 75 * Purges a key from the cache. Use this only if the type is not actually valid for 76 * binding and needs to be purged. (See issue 319 and 77 * ImplicitBindingTest#testCircularJitBindingsLeaveNoResidue and 78 * #testInstancesRequestingProvidersForThemselvesWithChildInjectors for examples of when this is 79 * necessary.) 80 * 81 * Returns true if the type was stored in the cache, false otherwise. 82 */ 83 boolean remove(Binding<?> type) { 84 return cache.asMap().remove(type) != null; 85 } 86 87 /** 88 * Creates a new {@link ProvisionListenerStackCallback} with the correct listeners 89 * for the key. 90 */ 91 private <T> ProvisionListenerStackCallback<T> create(Binding<T> binding) { 92 List<ProvisionListener> listeners = null; 93 for (ProvisionListenerBinding provisionBinding : listenerBindings) { 94 if (provisionBinding.getBindingMatcher().matches(binding)) { 95 if (listeners == null) { 96 listeners = Lists.newArrayList(); 97 } 98 listeners.addAll(provisionBinding.getListeners()); 99 } 100 } 101 if (listeners == null || listeners.isEmpty()) { 102 // Optimization: don't bother constructing the callback if there are 103 // no listeners. 104 return ProvisionListenerStackCallback.emptyListener(); 105 } 106 return new ProvisionListenerStackCallback<T>(binding, listeners); 107 } 108 109 /** A struct that holds key & binding but uses just key for equality/hashcode. */ 110 private static class KeyBinding { 111 final Key<?> key; 112 final Binding<?> binding; 113 114 KeyBinding(Key<?> key, Binding<?> binding) { 115 this.key = key; 116 this.binding = binding; 117 } 118 119 @Override 120 public boolean equals(Object obj) { 121 return obj instanceof KeyBinding && key.equals(((KeyBinding)obj).key); 122 } 123 @Override 124 public int hashCode() { 125 return key.hashCode(); 126 } 127 } 128 } 129