/* * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.griffon.runtime.core.injection; import griffon.core.injection.Binding; import griffon.core.injection.InstanceBinding; import griffon.core.injection.ProviderBinding; import griffon.core.injection.ProviderTypeBinding; import griffon.core.injection.TargetBinding; import griffon.util.AnnotationUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Provider; import javax.inject.Qualifier; import java.lang.annotation.Annotation; import java.util.List; import static griffon.util.AnnotationUtils.harvestQualifiers; import static java.util.Objects.requireNonNull; /** * @author Andres Almiray * @since 2.0.0 */ public class Bindings { public static <T> AnnotatedBindingBuilder<T> bind(@Nonnull Class<T> clazz) { requireNonNull(clazz, "Argument 'class' must not be null"); return new AnnotatedBindingBuilderImpl<>(clazz); } private static abstract class SingletonBindingBuilderImpl<T> implements SingletonBindingBuilder<T> { protected boolean singleton; @Override public void asSingleton() { singleton = true; } } private abstract static class LinkedBindingBuilderImpl<T> extends SingletonBindingBuilderImpl<T> implements LinkedBindingBuilder<T> { protected Class<? extends T> target; protected T instance; protected Provider<T> provider; protected Class<? extends Provider<T>> providerType; @Nonnull @Override public SingletonBindingBuilder<T> to(@Nonnull Class<? extends T> target) { this.target = requireNonNull(target, "Argument 'target' must not be null"); return this; } @Override public void toInstance(@Nonnull T instance) { this.instance = requireNonNull(instance, "Argument 'instance' must not be null"); } @Nonnull @Override public SingletonBindingBuilder<T> toProvider(@Nonnull Provider<T> provider) { this.provider = requireNonNull(provider, "Argument 'provider' must not be null"); return this; } @Nonnull @Override public SingletonBindingBuilder<T> toProvider(@Nonnull Class<? extends Provider<T>> providerType) { this.providerType = requireNonNull(providerType, "Argument 'providerType' must not be null"); return this; } } private static class AnnotatedBindingBuilderImpl<T> extends LinkedBindingBuilderImpl<T> implements AnnotatedBindingBuilder<T> { private final Class<T> source; private Annotation classifier; private Class<? extends Annotation> classifierType; private AnnotatedBindingBuilderImpl(@Nonnull Class<T> source) { this.source = requireNonNull(source, "Argument 'source' must not be null"); } @Nonnull @Override public Binding<T> getBinding() { if (instance != null) { return classifier != null ? new InstanceBindingImpl<>(source, classifier, instance) : new InstanceBindingImpl<>(source, classifierType, instance); } else if (providerType != null) { return classifier != null ? new ProviderTypeBindingImpl<>(source, providerType, classifier, singleton) : new ProviderTypeBindingImpl<>(source, providerType, classifierType, singleton); } else if (provider != null) { return classifier != null ? new ProviderBindingImpl<>(source, provider, classifier, singleton) : new ProviderBindingImpl<>(source, provider, classifierType, singleton); } else if (target != null) { return classifier != null ? new TargetBindingImpl<>(source, target, classifier, singleton) : new TargetBindingImpl<>(source, target, classifierType, singleton); } return classifier != null ? new TargetBindingImpl<>(source, source, classifier, singleton) : new TargetBindingImpl<>(source, source, classifierType, singleton); } @Nonnull @Override public LinkedBindingBuilder<T> withClassifier(@Nonnull Class<? extends Annotation> annotationType) { requireNonNull(annotationType, "Argument 'annotationType' must not be null"); AnnotationUtils.requireAnnotation(annotationType, Qualifier.class); this.classifierType = annotationType; return this; } @Nonnull @Override public LinkedBindingBuilder<T> withClassifier(@Nonnull Annotation annotation) { requireNonNull(annotation, "Argument 'annotation' must not be null"); this.classifier = annotation; withClassifier(annotation.getClass()); return this; } } private static abstract class AbstractBindingImpl<T> implements Binding<T> { protected final Class<T> source; protected final boolean singleton; protected Annotation classifier; protected Class<? extends Annotation> classifierType; protected AbstractBindingImpl(@Nonnull Class<T> source, @Nonnull Annotation classifier, boolean singleton) { this.source = source; this.singleton = singleton; this.classifier = classifier; this.classifierType = classifier.getClass(); } protected AbstractBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) { this.source = source; this.singleton = singleton; this.classifierType = classifierType; } @Nonnull @Override public Class<T> getSource() { return source; } @Nullable @Override public Class<? extends Annotation> getClassifierType() { return classifierType; } @Nullable @Override public Annotation getClassifier() { return classifier; } @Override public boolean isSingleton() { return singleton; } protected void updateClassifier(Class<?> klass) { if (this.classifier == null) { List<Annotation> qualifiers = harvestQualifiers(klass); if (!qualifiers.isEmpty()) { this.classifier = qualifiers.get(0); } } } protected void updateClassifierType(Class<?> klass) { if (this.classifierType == null) { List<Annotation> qualifiers = harvestQualifiers(klass); if (!qualifiers.isEmpty()) { this.classifier = qualifiers.get(0); } } } } private static class TargetBindingImpl<T> extends AbstractBindingImpl<T> implements TargetBinding<T> { private final Class<? extends T> target; private TargetBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends T> target, @Nonnull Annotation classifier, boolean singleton) { super(source, classifier, singleton); this.target = target; updateClassifier(target); } private TargetBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends T> target, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) { super(source, classifierType, singleton); this.target = target; updateClassifierType(target); } @Nonnull @Override public Class<? extends T> getTarget() { return target; } @Override public String toString() { final StringBuilder sb = new StringBuilder("TargetBinding["); sb.append("source=").append(source.getName()); if (classifier != null) { sb.append(", classifier=").append(classifier); } else if (classifierType != null) { sb.append(", classifierType=").append(classifierType.getName()); } sb.append(", target=").append(target.getName()); sb.append(", singleton=").append(singleton); sb.append(']'); return sb.toString(); } } private static class InstanceBindingImpl<T> extends AbstractBindingImpl<T> implements InstanceBinding<T> { private final T instance; protected InstanceBindingImpl(@Nonnull Class<T> source, @Nonnull Annotation classifier, @Nonnull T instance) { super(source, classifier, true); this.instance = instance; updateClassifier(instance.getClass()); } protected InstanceBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Annotation> classifierType, @Nonnull T instance) { super(source, classifierType, true); this.instance = instance; updateClassifierType(instance.getClass()); } @Nonnull @Override public T getInstance() { return instance; } @Override public String toString() { final StringBuilder sb = new StringBuilder("InstanceBinding["); sb.append("source=").append(source.getName()); if (classifier != null) { sb.append(", classifier=").append(classifier); } else if (classifierType != null) { sb.append(", classifierType=").append(classifierType.getName()); } sb.append(", instance=").append(instance); sb.append(", singleton=").append(singleton); sb.append(']'); return sb.toString(); } } private static class ProviderBindingImpl<T> extends AbstractBindingImpl<T> implements ProviderBinding<T> { private final Provider<T> provider; private ProviderBindingImpl(@Nonnull Class<T> source, @Nonnull Provider<T> provider, @Nonnull Annotation classifier, boolean singleton) { super(source, classifier, singleton); this.provider = provider; updateClassifier(provider.getClass()); } private ProviderBindingImpl(@Nonnull Class<T> source, @Nonnull Provider<T> provider, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) { super(source, classifierType, singleton); this.provider = provider; updateClassifierType(provider.getClass()); } @Nonnull @Override public Provider<T> getProvider() { return provider; } @Override public String toString() { final StringBuilder sb = new StringBuilder("ProviderBinding["); sb.append("source=").append(source.getName()); if (classifier != null) { sb.append(", classifier=").append(classifier); } else if (classifierType != null) { sb.append(", classifierType=").append(classifierType.getName()); } sb.append(", provider=").append(provider); sb.append(", singleton=").append(singleton); sb.append(']'); return sb.toString(); } } private static class ProviderTypeBindingImpl<T> extends AbstractBindingImpl<T> implements ProviderTypeBinding<T> { private final Class<? extends Provider<T>> providerType; private ProviderTypeBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Provider<T>> providerType, @Nonnull Annotation classifier, boolean singleton) { super(source, classifier, singleton); this.providerType = providerType; updateClassifier(providerType); } private ProviderTypeBindingImpl(@Nonnull Class<T> source, @Nonnull Class<? extends Provider<T>> providerType, @Nonnull Class<? extends Annotation> classifierType, boolean singleton) { super(source, classifierType, singleton); this.providerType = providerType; updateClassifierType(providerType); } @Nonnull @Override public Class<? extends Provider<T>> getProviderType() { return providerType; } @Override public String toString() { final StringBuilder sb = new StringBuilder("ProviderTypeBinding["); sb.append("source=").append(source.getName()); if (classifier != null) { sb.append(", classifier=").append(classifier); } else if (classifierType != null) { sb.append(", classifierType=").append(classifierType.getName()); } sb.append(", providerType=").append(providerType.getName()); sb.append(", singleton=").append(singleton); sb.append(']'); return sb.toString(); } } }