Home | History | Annotate | Download | only in internal
      1 /**
      2  * Copyright (C) 2006 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.Maps;
     22 import com.google.inject.Key;
     23 import com.google.inject.internal.InjectorImpl.InjectorOptions;
     24 import com.google.inject.spi.Dependency;
     25 import com.google.inject.spi.DependencyAndSource;
     26 
     27 import java.util.Arrays;
     28 import java.util.List;
     29 import java.util.Map;
     30 
     31 /**
     32  * Internal context. Used to coordinate injections and support circular
     33  * dependencies.
     34  *
     35  * @author crazybob (at) google.com (Bob Lee)
     36  */
     37 final class InternalContext {
     38 
     39   private final InjectorOptions options;
     40 
     41   private Map<Object, ConstructionContext<?>> constructionContexts = Maps.newHashMap();
     42 
     43   /** Keeps track of the type that is currently being requested for injection. */
     44   private Dependency<?> dependency;
     45 
     46   /** Keeps track of the hierarchy of types needed during injection. */
     47   private final DependencyStack state = new DependencyStack();
     48 
     49   InternalContext(InjectorOptions options) {
     50     this.options = options;
     51   }
     52 
     53   public InjectorOptions getInjectorOptions() {
     54     return options;
     55   }
     56 
     57   @SuppressWarnings("unchecked")
     58   public <T> ConstructionContext<T> getConstructionContext(Object key) {
     59     ConstructionContext<T> constructionContext
     60         = (ConstructionContext<T>) constructionContexts.get(key);
     61     if (constructionContext == null) {
     62       constructionContext = new ConstructionContext<T>();
     63       constructionContexts.put(key, constructionContext);
     64     }
     65     return constructionContext;
     66   }
     67 
     68   public Dependency<?> getDependency() {
     69     return dependency;
     70   }
     71 
     72   /** Sets the new current dependency & adds it to the state. */
     73   public Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
     74     Dependency<?> previous = this.dependency;
     75     this.dependency = dependency;
     76     state.add(dependency, source);
     77     return previous;
     78   }
     79 
     80   /** Pops the current state & sets the new dependency. */
     81   public void popStateAndSetDependency(Dependency<?> newDependency) {
     82     state.pop();
     83     this.dependency = newDependency;
     84   }
     85 
     86   /** Adds to the state without setting the dependency. */
     87   public void pushState(Key<?> key, Object source) {
     88     state.add(key, source);
     89   }
     90 
     91   /** Pops from the state without setting a dependency. */
     92   public void popState() {
     93     state.pop();
     94   }
     95 
     96   /** Returns the current dependency chain (all the state). */
     97   public List<DependencyAndSource> getDependencyChain() {
     98     ImmutableList.Builder<DependencyAndSource> builder = ImmutableList.builder();
     99     for (int i = 0; i < state.size(); i += 2) {
    100       Object evenEntry = state.get(i);
    101       Dependency<?> dependency;
    102       if (evenEntry instanceof Key) {
    103         dependency = Dependency.get((Key<?>) evenEntry);
    104       } else {
    105         dependency = (Dependency<?>) evenEntry;
    106       }
    107       builder.add(new DependencyAndSource(dependency, state.get(i + 1)));
    108     }
    109     return builder.build();
    110   }
    111 
    112   /**
    113    * Keeps track of the hierarchy of types needed during injection.
    114    *
    115    * <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
    116    * even indices, and sources on odd indices. This structure is to avoid the memory overhead of
    117    * DependencyAndSource objects, which can add to several tens of megabytes in large applications.
    118    */
    119   private static final class DependencyStack {
    120     private Object[] elements = new Object[16];
    121     private int size = 0;
    122 
    123     public void add(Object dependencyOrKey, Object source) {
    124       if (elements.length < size + 2) {
    125         elements = Arrays.copyOf(elements, (elements.length*3)/2 + 2);
    126       }
    127       elements[size++] = dependencyOrKey;
    128       elements[size++] = source;
    129     }
    130 
    131     public void pop() {
    132       elements[--size] = null;
    133       elements[--size] = null;
    134     }
    135 
    136     public Object get(int i) {
    137       return elements[i];
    138     }
    139 
    140     public int size() {
    141       return size;
    142     }
    143   }
    144 }
    145