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