package tc.oc.commons.core.inject;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.multibindings.Multibinder;
import tc.oc.commons.core.reflect.ResolvableType;
import tc.oc.commons.core.reflect.TypeArgument;
import tc.oc.commons.core.reflect.Types;
/**
* Handy wrapper around a {@link Multibinder}
*/
public class SetBinder<T> {
private final Binder binder;
private final TypeLiteral<T> elementType;
private final TypeLiteral<Set<T>> collectionType;
private final Multibinder<T> multibinder;
protected SetBinder(Binder binder, @Nullable TypeLiteral<T> type) {
this(binder, type == null ? null : Key.get(type));
}
protected SetBinder(Binder binder, @Nullable Key<T> key) {
if(key == null) {
key = Key.get(new ResolvableType<T>(){}.in(getClass()));
}
this.binder = binder.skipSources(SetBinder.class);
this.elementType = key.getTypeLiteral();
this.collectionType = new ResolvableType<Set<T>>(){}.with(new TypeArgument<T>(this.elementType){});
this.multibinder = Multibinder.newSetBinder(binder, key);
}
/**
* Resolve the element type using the class of this object. This will only work
* with a subclass of {@link SetBinder} that specifies {@link T}, hence this
* constructor is protected.
*
* @throws IllegalArgumentException if the element type cannot be resolved
*/
protected SetBinder(Binder binder) {
this(binder, (Key) null);
}
/**
* Create a new SetBinder with an explicit element type (which must be fully specified)
*
* @throws IllegalArgumentException if the element type is not fully specified
*/
public static <E> SetBinder<E> ofType(Binder binder, TypeLiteral<E> elementType) {
return new SetBinder<>(binder, Types.assertFullySpecified(elementType));
}
/**
* Create a new SetBinder, resolving the element type {@link E} in the context of
* the given class.
*
* @throws IllegalArgumentException if the element type cannot be resolved
*/
public static <E> SetBinder<E> inContext(Binder binder, Class<?> declaringClass) {
return new SetBinder<>(binder, new ResolvableType<E>(){}.in(declaringClass));
}
public TypeLiteral<T> elementType() {
return elementType;
}
public TypeLiteral<Set<T>> collectionType() {
return collectionType;
}
protected Binder binder() { return binder; }
public LinkedBindingBuilder<T> addBinding() {
return multibinder.addBinding();
}
}