1/**
2 * Copyright (C) 2008 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
17package com.google.inject.internal;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20
21import com.google.inject.Binder;
22import com.google.inject.Key;
23import com.google.inject.Scope;
24import com.google.inject.spi.Element;
25import com.google.inject.spi.InstanceBinding;
26
27import java.lang.annotation.Annotation;
28import java.util.List;
29
30/**
31 * Bind a value or constant.
32 *
33 * @author jessewilson@google.com (Jesse Wilson)
34 */
35public abstract class AbstractBindingBuilder<T> {
36
37  public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
38  public static final String SINGLE_INSTANCE_AND_SCOPE
39      = "Setting the scope is not permitted when binding to a single instance.";
40  public static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
41  public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. "
42      + "Use toProvider(Providers.of(null)) if this is your intended behaviour.";
43  public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
44  public static final String ANNOTATION_ALREADY_SPECIFIED
45      = "More than one annotation is specified for this binding.";
46
47  protected static final Key<?> NULL_KEY = Key.get(Void.class);
48
49  protected List<Element> elements;
50  protected int position;
51  protected final Binder binder;
52  private BindingImpl<T> binding;
53
54  public AbstractBindingBuilder(Binder binder, List<Element> elements, Object source, Key<T> key) {
55    this.binder = binder;
56    this.elements = elements;
57    this.position = elements.size();
58    this.binding = new UntargettedBindingImpl<T>(source, key, Scoping.UNSCOPED);
59    elements.add(position, this.binding);
60  }
61
62  protected BindingImpl<T> getBinding() {
63    return binding;
64  }
65
66  protected BindingImpl<T> setBinding(BindingImpl<T> binding) {
67    this.binding = binding;
68    elements.set(position, binding);
69    return binding;
70  }
71
72  /** Sets the binding to a copy with the specified annotation on the bound key */
73  protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
74    checkNotNull(annotationType, "annotationType");
75    checkNotAnnotated();
76    return setBinding(binding.withKey(
77        Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
78  }
79
80  /** Sets the binding to a copy with the specified annotation on the bound key */
81  protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
82    checkNotNull(annotation, "annotation");
83    checkNotAnnotated();
84    return setBinding(binding.withKey(
85        Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
86  }
87
88  public void in(final Class<? extends Annotation> scopeAnnotation) {
89    checkNotNull(scopeAnnotation, "scopeAnnotation");
90    checkNotScoped();
91    setBinding(getBinding().withScoping(Scoping.forAnnotation(scopeAnnotation)));
92  }
93
94  public void in(final Scope scope) {
95    checkNotNull(scope, "scope");
96    checkNotScoped();
97    setBinding(getBinding().withScoping(Scoping.forInstance(scope)));
98  }
99
100  public void asEagerSingleton() {
101    checkNotScoped();
102    setBinding(getBinding().withScoping(Scoping.EAGER_SINGLETON));
103  }
104
105  protected boolean keyTypeIsSet() {
106    return !Void.class.equals(binding.getKey().getTypeLiteral().getType());
107  }
108
109  protected void checkNotTargetted() {
110    if (!(binding instanceof UntargettedBindingImpl)) {
111      binder.addError(IMPLEMENTATION_ALREADY_SET);
112    }
113  }
114
115  protected void checkNotAnnotated() {
116    if (binding.getKey().getAnnotationType() != null) {
117      binder.addError(ANNOTATION_ALREADY_SPECIFIED);
118    }
119  }
120
121  protected void checkNotScoped() {
122    // Scoping isn't allowed when we have only one instance.
123    if (binding instanceof InstanceBinding) {
124      binder.addError(SINGLE_INSTANCE_AND_SCOPE);
125      return;
126    }
127
128    if (binding.getScoping().isExplicitlyScoped()) {
129      binder.addError(SCOPE_ALREADY_SET);
130    }
131  }
132}