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