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