package sk.stuba.fiit.perconik.utilities.reflect.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import com.google.common.base.CaseFormat;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import sk.stuba.fiit.perconik.utilities.MoreLists;
import sk.stuba.fiit.perconik.utilities.reflect.accessor.Accessor;
import sk.stuba.fiit.perconik.utilities.reflect.accessor.Accessors;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Maps.newLinkedHashMap;
import static com.google.common.collect.Sets.newTreeSet;
public final class Annotations {
private Annotations() {}
public static List<Annotation> ofClass(final Class<?> type) {
return ofElement(type);
}
public static List<Annotation> ofClasses(final Iterable<? extends Class<?>> types) {
return ofElements(types);
}
public static List<Annotation> ofElement(final AnnotatedElement element) {
return ImmutableList.copyOf(element.getAnnotations());
}
public static List<Annotation> ofElements(final Iterable<? extends AnnotatedElement> elements) {
ImmutableList.Builder<Annotation> annotations = ImmutableList.builder();
for (AnnotatedElement element: elements) {
annotations.addAll(ofElement(element));
}
return annotations.build();
}
public static Annotable asAnnotable(final Collection<Annotation> annotations) {
return new EnumeratedAnnotable(annotations.iterator());
}
public static Annotable asAnnotable(final Iterable<Annotation> annotations) {
return new EnumeratedAnnotable(annotations.iterator());
}
public static Annotable asAnnotable(final Iterator<Annotation> annotations) {
return new EnumeratedAnnotable(annotations);
}
public static Map<String, String> toData(final Annotation annotation) {
Map<String, String> data = newLinkedHashMap();
for (Entry<String, Object> entry: toElements(annotation).entrySet()) {
String key = keyToString(entry.getKey());
String value = valueToString(entry.getValue());
data.put(key, value == null || value.isEmpty() ? null : value);
}
return data;
}
public static Map<String, Object> toElements(final Annotation annotation) {
Map<String, Object> elements = newLinkedHashMap();
for (Method method: annotation.annotationType().getDeclaredMethods()) {
String name = method.getName();
Accessor<Object> accessor = Accessors.ofInstanceMethod(annotation, Object.class, name).get();
elements.put(name, accessor.get());
}
return elements;
}
public static String toString(final Annotation annotation) {
Class<? extends Annotation> type = annotation.annotationType();
Map<String, String> data = Maps.filterValues(toData(annotation), notNull());
StringBuilder builder = new StringBuilder(keyToString(type.getSimpleName()));
if (data.size() == 1 && data.containsKey("value")) {
builder.append(" (").append(data.get("value")).append(")");
} else if (data.size() != 0) {
builder.append(" (");
Joiner.on(", ").withKeyValueSeparator(": ").appendTo(builder, data);
builder.append(")");
}
return builder.toString();
}
public static String toString(final Iterable<Annotation> annotations) {
Set<String> values = newTreeSet();
for (Annotation annotation: annotations) {
values.add(toString(annotation));
}
return Joiner.on(", ").join(values);
}
private static String keyToString(final Object object) {
return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, object.toString());
}
private static String valueToString(final Object object) {
Set<String> elements = newTreeSet();
for (Object element: MoreLists.wrap(object)) {
if (element instanceof Class) {
elements.add(((Class<?>) element).getCanonicalName());
continue;
}
String value = Objects.toString(element, "");
if (value.isEmpty()) {
continue;
}
elements.add(value);
}
return Joiner.on(", ").join(elements);
}
}