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