Home | History | Annotate | Download | only in internal
      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