package tc.oc.commons.core.inject;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.AnnotatedElement;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
import javax.inject.Qualifier;
import com.google.common.collect.Iterators;
/**
* Associates a class with another class, for whatever purpose.
*
* When used as a binding annotation for @Inject, it qualifies the resolved dependency
* as the one belonging to the given class. This could be established through a chain
* of @BelongsTo annotations, or through some other means.
*/
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BelongsTo {
Class<?> value();
class Impl implements BelongsTo {
private final Class<?> v;
public Impl(Class<?> v) {
this.v = v;
}
@Override
public Class<?> value() {
return v;
}
@Override
public Class<? extends Annotation> annotationType() {
return BelongsTo.class;
}
@Override
public boolean equals(Object obj) {
return obj instanceof BelongsTo &&
value().equals(((BelongsTo) obj).value());
}
@Override
public int hashCode() {
return value().hashCode();
}
@Override
public String toString() {
return "@" + BelongsTo.class.getSimpleName() + "(" + value().getSimpleName() + ")";
}
public static Iterator<Class<?>> ownerIterator(AnnotatedElement element) {
final BelongsTo belongsTo = element.getAnnotation(BelongsTo.class);
return new Iterator<Class<?>>() {
final Set<Class<?>> seen = new HashSet<>();
Class<?> cls = belongsTo == null ? null : belongsTo.value();
@Override
public boolean hasNext() {
return cls != null;
}
@Override
public Class<?> next() {
if(cls == null) throw new NoSuchElementException();
final Class<?> next = cls;
final BelongsTo belongsTo = cls.getAnnotation(BelongsTo.class);
cls = belongsTo != null && seen.add(belongsTo.value()) ? belongsTo.value() : null;
return next;
}
};
}
public static Stream<Class<?>> owners(AnnotatedElement element) {
return StreamSupport.stream(Spliterators.spliterator(ownerIterator(element), 0, 0), false);
}
public static @Nullable Class<?> owner(AnnotatedElement element) {
return Iterators.getLast(ownerIterator(element), null);
}
}
}