package com.maxifier.guice.decorator;
import com.google.inject.*;
import com.google.inject.util.Providers;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.HashSet;
/**
* Project: Maxifier
* Date: 09.11.2009
* Time: 11:44:23
* <p/>
* Copyright (c) 1999-2009 Magenta Corporation Ltd. All Rights Reserved.
* Magenta Technology proprietary and confidential.
* Use is subject to license terms.
*
* @author Aleksey Didik
*/
public class Decorator {
private final Binder binder;
private Injector inj;
private final Collection<Class<?>> members = new HashSet<Class<?>>(4);
public Decorator(Binder binder) {
this.binder = binder;
binder.requestInjection(new InjectorCatcher());
binder.requestInjection(new Cleaner());
}
public <T> DecoratorProviderBuilder<T> bind(Class<T> clazz) {
members.add(clazz);
return new DecoratorProviderBuilder<T>(clazz);
}
public class DecoratorProviderBuilder<T> {
private final Class<T> binded;
private Class<? extends Annotation> annotation;
private boolean eager = false;
private Scope inScope;
public DecoratorProviderBuilder(Class<T> clazz) {
binded = clazz;
}
public DecoratorProviderBuilder<T> annotatedWith(Class<? extends Annotation> annotation) {
this.annotation = annotation;
return this;
}
public DecoratorProviderBuilder<T> in(Scope scope) {
this.inScope = scope;
return this;
}
public DecoratorProviderBuilder<T> asEagerSingleton() {
this.eager = true;
return this;
}
public <S extends T> DecoratorProvider<T> to(Class<S> clazz) {
checkAndAddMember(clazz, binded);
final DecoratorProvider<T> provider = new DecoratorProvider<T>(binded, clazz);
if (annotation == null) {
if (eager) {
binder.bind(binded).toProvider(provider).asEagerSingleton();
} else if (inScope != null) {
binder.bind(binded).toProvider(provider).in(this.inScope);
} else {
binder.bind(binded).toProvider(provider);
}
} else {
if (eager) {
binder.bind(binded).annotatedWith(annotation).toProvider(provider).asEagerSingleton();
} else if (inScope != null) {
binder.bind(binded).annotatedWith(annotation).toProvider(provider).in(this.inScope);
} else {
binder.bind(binded).annotatedWith(annotation).toProvider(provider);
}
}
return provider;
}
@Override
public String toString() {
return "DecoratorProviderBuilder{" +
"binded=" + binded +
", annotation=" + annotation +
'}';
}
}
private void checkAndAddMember(Class<?> clazz, Class<?> binded) {
if (members.contains(clazz)) {
throw new DuplicateMemberException(binded, clazz);
}
members.add(clazz);
}
public class DecoratorProvider<T> implements Provider<T> {
private final Class<T> binded;
private final Class<? extends T> impl;
private Provider<T> next;
private Injector child;
public DecoratorProvider(Class<T> binded, Class<? extends T> impl) {
this.binded = binded;
this.impl = impl;
}
@Override
public T get() {
if (next == null) {
return inj.getInstance(impl);
}
if (child == null) {
Module module = new AbstractModule() {
@Override
protected void configure() {
this.bind(binded).annotatedWith(Decorated.class).toProvider(next);
}
};
child = inj.createChildInjector(module);
}
return child.getInstance(impl);
}
public DecoratorProvider<T> decorate(Class<? extends T> clazz) {
checkAndAddMember(clazz, binded);
final DecoratorProvider<T> nextProvider = new DecoratorProvider<T>(binded, clazz);
next = nextProvider;
return nextProvider;
}
public void decorate(T instance) {
checkAndAddMember(instance.getClass(), binded);
next = Providers.of(instance);
}
@Override
public String toString() {
return "decorate " + impl;
}
}
private class InjectorCatcher {
@Inject
public void catchInjector(Injector catched) {
inj = catched;
}
}
private class Cleaner {
@Inject
public void clear() {
members.clear();
}
}
@Override
public String toString() {
return "Decorator{" +
"members=" + members +
'}';
}
}