Bindings.java
001 /*
002  * Copyright 2008-2014 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.core.injection;
017 
018 import griffon.core.injection.*;
019 import griffon.util.AnnotationUtils;
020 
021 import javax.annotation.Nonnull;
022 import javax.annotation.Nullable;
023 import javax.inject.Provider;
024 import javax.inject.Qualifier;
025 import java.lang.annotation.Annotation;
026 import java.util.List;
027 
028 import static griffon.util.AnnotationUtils.harvestQualifiers;
029 import static java.util.Objects.requireNonNull;
030 
031 /**
032  @author Andres Almiray
033  @since 2.0.0
034  */
035 public class Bindings {
036     public static <T> AnnotatedBindingBuilder<T> bind(@Nonnull Class<T> clazz) {
037         requireNonNull(clazz, "Argument 'class' must not be null");
038         return new AnnotatedBindingBuilderImpl<>(clazz);
039     }
040 
041     private static abstract class SingletonBindingBuilderImpl<T> implements SingletonBindingBuilder<T> {
042         protected boolean singleton;
043 
044         @Override
045         public void asSingleton() {
046             singleton = true;
047         }
048     }
049 
050     private abstract static class LinkedBindingBuilderImpl<T> extends SingletonBindingBuilderImpl<T> implements LinkedBindingBuilder<T> {
051         protected Class<? extends T> target;
052         protected T instance;
053         protected Provider<T> provider;
054         protected Class<? extends Provider<T>> providerType;
055 
056         @Nonnull
057         @Override
058         public SingletonBindingBuilder<T> to(@Nonnull Class<? extends T> target) {
059             this.target = requireNonNull(target, "Argument 'target' must not be null");
060             return this;
061         }
062 
063         @Override
064         public void toInstance(@Nonnull T instance) {
065             this.instance = requireNonNull(instance, "Argument 'instance' must not be null");
066         }
067 
068         @Nonnull
069         @Override
070         public SingletonBindingBuilder<T> toProvider(@Nonnull Provider<T> provider) {
071             this.provider = requireNonNull(provider, "Argument 'provider' must not be null");
072             return this;
073         }
074 
075         @Nonnull
076         @Override
077         public SingletonBindingBuilder<T> toProvider(@Nonnull Class<? extends Provider<T>> providerType) {
078             this.providerType = requireNonNull(providerType, "Argument 'providerType' must not be null");
079             return this;
080         }
081     }
082 
083     private static class AnnotatedBindingBuilderImpl<T> extends LinkedBindingBuilderImpl<T> implements AnnotatedBindingBuilder<T> {
084         private final Class<T> source;
085         private Annotation classifier;
086         private Class<? extends Annotation> classifierType;
087 
088         private AnnotatedBindingBuilderImpl(@Nonnull Class<T> source) {
089             this.source = requireNonNull(source, "Argument 'source' must not be null");
090         }
091 
092         @Nonnull
093         @Override
094         public Binding<T> getBinding() {
095             if (instance != null) {
096                 return classifier != null new InstanceBindingImpl<>(source, classifier, instancenew InstanceBindingImpl<>(source, classifierType, instance);
097             else if (providerType != null) {
098                 return classifier != null new ProviderTypeBindingImpl<>(source, providerType, classifier, singletonnew ProviderTypeBindingImpl<>(source, providerType, classifierType, singleton);
099             else if (provider != null) {
100                 return classifier != null new ProviderBindingImpl<>(source, provider, classifier, singletonnew ProviderBindingImpl<>(source, provider, classifierType, singleton);
101             else if (target != null) {
102                 return classifier != null new TargetBindingImpl<>(source, target, classifier, singletonnew TargetBindingImpl<>(source, target, classifierType, singleton);
103             }
104 
105             return classifier != null new TargetBindingImpl<>(source, source, classifier, singletonnew TargetBindingImpl<>(source, source, classifierType, singleton);
106         }
107 
108         @Nonnull
109         @Override
110         public LinkedBindingBuilder<T> withClassifier(@Nonnull Class<? extends Annotation> annotationType) {
111             requireNonNull(annotationType, "Argument 'annotationType' must not be null");
112             AnnotationUtils.requireAnnotation(annotationType, Qualifier.class);
113             this.classifierType = annotationType;
114             return this;
115         }
116 
117         @Nonnull
118         @Override
119         public LinkedBindingBuilder<T> withClassifier(@Nonnull Annotation annotation) {
120             requireNonNull(annotation, "Argument 'annotation' must not be null");
121             this.classifier = annotation;
122             withClassifier(annotation.getClass());
123             return this;
124         }
125     }
126 
127     private static abstract class AbstractBindingImpl<T> implements Binding<T> {
128         protected final Class<T> source;
129         protected final boolean singleton;
130         protected Annotation classifier;
131         protected Class<? extends Annotation> classifierType;
132 
133         protected AbstractBindingImpl(@Nonnull Class<T> source, @Nonnull Annotation classifier, boolean singleton) {
134             this.source = source;
135             this.singleton = singleton;
136             this.classifier = classifier;
137             this.classifierType = classifier.getClass();
138         }
139 
140         protected AbstractBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) {
141             this.source = source;
142             this.singleton = singleton;
143             this.classifierType = classifierType;
144         }
145 
146         @Nonnull
147         @Override
148         public Class<T> getSource() {
149             return source;
150         }
151 
152         @Nullable
153         @Override
154         public Class<? extends Annotation> getClassifierType() {
155             return classifierType;
156         }
157 
158         @Nullable
159         @Override
160         public Annotation getClassifier() {
161             return classifier;
162         }
163 
164         @Override
165         public boolean isSingleton() {
166             return singleton;
167         }
168 
169         protected void updateClassifier(Class<?> klass) {
170             if (this.classifier == null) {
171                 List<Annotation> qualifiers = harvestQualifiers(klass);
172                 if (!qualifiers.isEmpty()) {
173                     this.classifier = qualifiers.get(0);
174                 }
175             }
176         }
177 
178         protected void updateClassifierType(Class<?> klass) {
179             if (this.classifierType == null) {
180                 List<Annotation> qualifiers = harvestQualifiers(klass);
181                 if (!qualifiers.isEmpty()) {
182                     this.classifier = qualifiers.get(0);
183                 }
184             }
185         }
186     }
187 
188     private static class TargetBindingImpl<T> extends AbstractBindingImpl<T> implements TargetBinding<T> {
189         private final Class<? extends T> target;
190 
191         private TargetBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends T> target, @Nonnull Annotation classifier, boolean singleton) {
192             super(source, classifier, singleton);
193             this.target = target;
194             updateClassifier(target);
195         }
196 
197         private TargetBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends T> target, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) {
198             super(source, classifierType, singleton);
199             this.target = target;
200             updateClassifierType(target);
201         }
202 
203         @Nonnull
204         @Override
205         public Class<? extends T> getTarget() {
206             return target;
207         }
208 
209         @Override
210         public String toString() {
211             final StringBuilder sb = new StringBuilder("TargetBinding[");
212             sb.append("source=").append(source.getName());
213             if (classifier != null) {
214                 sb.append(", classifier=").append(classifier);
215             else if (classifierType != null) {
216                 sb.append(", classifierType=").append(classifierType.getName());
217             }
218             sb.append(", target=").append(target.getName());
219             sb.append(", singleton=").append(singleton);
220             sb.append(']');
221             return sb.toString();
222         }
223     }
224 
225     private static class InstanceBindingImpl<T> extends AbstractBindingImpl<T> implements InstanceBinding<T> {
226         private final T instance;
227 
228         protected InstanceBindingImpl(@Nonnull Class<T> source, @Nonnull Annotation classifier, @Nonnull T instance) {
229             super(source, classifier, true);
230             this.instance = instance;
231             updateClassifier(instance.getClass());
232         }
233 
234         protected InstanceBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Annotation> classifierType, @Nonnull T instance) {
235             super(source, classifierType, true);
236             this.instance = instance;
237             updateClassifierType(instance.getClass());
238         }
239 
240         @Nonnull
241         @Override
242         public T getInstance() {
243             return instance;
244         }
245 
246         @Override
247         public String toString() {
248             final StringBuilder sb = new StringBuilder("InstanceBinding[");
249             sb.append("source=").append(source.getName());
250             if (classifier != null) {
251                 sb.append(", classifier=").append(classifier);
252             else if (classifierType != null) {
253                 sb.append(", classifierType=").append(classifierType.getName());
254             }
255             sb.append(", instance=").append(instance);
256             sb.append(", singleton=").append(singleton);
257             sb.append(']');
258             return sb.toString();
259         }
260     }
261 
262     private static class ProviderBindingImpl<T> extends AbstractBindingImpl<T> implements ProviderBinding<T> {
263         private final Provider<T> provider;
264 
265         private ProviderBindingImpl(@Nonnull Class<T> source, @Nonnull Provider<T> provider, @Nonnull Annotation classifier, boolean singleton) {
266             super(source, classifier, singleton);
267             this.provider = provider;
268             updateClassifier(provider.getClass());
269         }
270 
271 
272         private ProviderBindingImpl(@Nonnull Class<T> source, @Nonnull Provider<T> provider, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) {
273             super(source, classifierType, singleton);
274             this.provider = provider;
275             updateClassifierType(provider.getClass());
276         }
277 
278         @Nonnull
279         @Override
280         public Provider<T> getProvider() {
281             return provider;
282         }
283 
284         @Override
285         public String toString() {
286             final StringBuilder sb = new StringBuilder("ProviderBinding[");
287             sb.append("source=").append(source.getName());
288             if (classifier != null) {
289                 sb.append(", classifier=").append(classifier);
290             else if (classifierType != null) {
291                 sb.append(", classifierType=").append(classifierType.getName());
292             }
293             sb.append(", provider=").append(provider);
294             sb.append(", singleton=").append(singleton);
295             sb.append(']');
296             return sb.toString();
297         }
298     }
299 
300     private static class ProviderTypeBindingImpl<T> extends AbstractBindingImpl<T> implements ProviderTypeBinding<T> {
301         private final Class<? extends Provider<T>> providerType;
302 
303         private ProviderTypeBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Provider<T>> providerType, @Nonnull Annotation classifier, boolean singleton) {
304             super(source, classifier, singleton);
305             this.providerType = providerType;
306             updateClassifier(providerType);
307         }
308 
309 
310         private ProviderTypeBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Provider<T>> providerType, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) {
311             super(source, classifierType, singleton);
312             this.providerType = providerType;
313             updateClassifierType(providerType);
314         }
315 
316         @Nonnull
317         @Override
318         public Class<? extends Provider<T>> getProviderType() {
319             return providerType;
320         }
321 
322         @Override
323         public String toString() {
324             final StringBuilder sb = new StringBuilder("ProviderTypeBinding[");
325             sb.append("source=").append(source.getName());
326             if (classifier != null) {
327                 sb.append(", classifier=").append(classifier);
328             else if (classifierType != null) {
329                 sb.append(", classifierType=").append(classifierType.getName());
330             }
331             sb.append(", providerType=").append(providerType);
332             sb.append(", singleton=").append(singleton);
333             sb.append(']');
334             return sb.toString();
335         }
336     }
337 }