blob: 0b6e564080874767ce7e171b8feccdeeaa26f712 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.xbean.finder;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Arrays.asList;
/**
* @version $Rev$ $Date: 2012/05/03 13:43:15 $
*/
public abstract class MetaAnnotatedObject<T> implements MetaAnnotated<T> {
protected final Map<Class<? extends Annotation>, MetaAnnotation<?>> annotations = new HashMap<Class<? extends Annotation>, MetaAnnotation<?>>();
protected final T target;
MetaAnnotatedObject(T target, Map<Class<? extends Annotation>, MetaAnnotation<?>> annotations) {
this.target = target;
this.annotations.putAll(annotations);
}
public T get() {
return target;
}
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return annotations.containsKey(annotationClass);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
MetaAnnotation<T> annotation = (MetaAnnotation<T>) annotations.get(annotationClass);
return (annotation == null) ? null : annotation.get();
}
public Annotation[] getAnnotations() {
Annotation[] annotations = new Annotation[this.annotations.size()];
int i = 0;
for (MetaAnnotation annotation : this.annotations.values()) {
annotations[i++] = annotation.get();
}
return annotations;
}
public Collection<MetaAnnotation<?>> getMetaAnnotations() {
return Collections.unmodifiableCollection(annotations.values());
}
@Override
public boolean equals(Object obj) {
return get().equals(obj);
}
@Override
public int hashCode() {
return get().hashCode();
}
@Override
public String toString() {
return get().toString();
}
private static void unroll(Class<? extends Annotation> clazz, int depth, Map<Class<? extends Annotation>, MetaAnnotation<?>> found) {
if (!isMetaAnnotation(clazz)) return;
for (Annotation annotation : getDeclaredMetaAnnotations(clazz)) {
Class<? extends Annotation> type = annotation.annotationType();
MetaAnnotation existing = found.get(type);
if (existing != null) {
if (existing.getDepth() > depth) {
// OVERWRITE
found.put(type, new MetaAnnotation(annotation, depth));
unroll(type, depth + 1, found);
} else if (existing.getDepth() < depth) {
// IGNORE
// ignore, what we have already is higher priority
} else {
// CONFLICT
// They are the same depth and therefore conflicting
existing.getConflicts().add(new MetaAnnotation(annotation, depth));
}
} else {
// NEW
found.put(type, new MetaAnnotation(annotation, depth));
unroll(type, depth + 1, found);
}
}
}
private static Collection<Annotation> getDeclaredMetaAnnotations(Class<? extends Annotation> clazz) {
Map<Class, Annotation> map = new HashMap<Class, Annotation>();
// pull in the annotations declared on this annotation
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
map.put(annotation.annotationType(), annotation);
}
List<Annotation[]> groups = new ArrayList<Annotation[]>();
Class<? extends Annotation> metatype = getMetatype(clazz);
if (metatype != null) {
try {
Class<?> def = clazz.getClassLoader().loadClass(clazz.getName() + "$$");
List<AnnotatedElement> elements = new ArrayList<AnnotatedElement>();
elements.addAll(asList(def.getDeclaredFields()));
elements.addAll(asList(def.getDeclaredConstructors()));
elements.addAll(asList(def.getDeclaredMethods()));
for (Method method : def.getDeclaredMethods()) {
for (Annotation[] array : method.getParameterAnnotations()) {
groups.add(array);
}
}
for (Constructor constructor : def.getDeclaredConstructors()) {
for (Annotation[] array : constructor.getParameterAnnotations()) {
groups.add(array);
}
}
for (AnnotatedElement element : elements) {
groups.add(element.getDeclaredAnnotations());
}
for (Annotation[] annotations : groups) {
if (contains(annotations, clazz)) {
for (Annotation annotation : annotations) {
map.put(annotation.annotationType(), annotation);
}
}
}
} catch (ClassNotFoundException e) {
// inner class is optional
}
}
map.remove(Target.class);
map.remove(Retention.class);
map.remove(Documented.class);
map.remove(metatype);
map.remove(clazz);
return map.values();
}
private static boolean contains(Annotation[] annotations, Class<? extends Annotation> clazz) {
for (Annotation annotation : annotations) {
if (clazz.equals(annotation.annotationType())) return true;
}
return false;
}
private static Class<? extends Annotation> getMetatype(Class<? extends Annotation> clazz) {
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
Class<? extends Annotation> type = annotation.annotationType();
if (isMetatypeAnnotation(type)) return type;
}
return null;
}
private static boolean isMetaAnnotation(Class<? extends Annotation> clazz) {
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
if (isMetatypeAnnotation(annotation.annotationType())) return true;
}
return false;
}
private static boolean isMetatypeAnnotation(Class<? extends Annotation> type) {
if (isSelfAnnotated(type, "Metatype")) return true;
for (Annotation annotation : type.getAnnotations()) {
if (isSelfAnnotated(annotation.annotationType(), "Metaroot")) return true;
}
return false;
}
private static boolean isSelfAnnotated(Class<? extends Annotation> type, String name) {
return type.isAnnotationPresent(type) && type.getSimpleName().equals(name) && validTarget(type);
}
private static boolean validTarget(Class<? extends Annotation> type) {
final Target target = type.getAnnotation(Target.class);
if (target == null) return false;
final ElementType[] targets = target.value();
return targets.length == 1 && targets[0] == ElementType.ANNOTATION_TYPE;
}
protected static Map<Class<? extends Annotation>, MetaAnnotation<?>> unroll(AnnotatedElement element) {
return unroll(element.getDeclaredAnnotations());
}
protected static Map<Class<? extends Annotation>, MetaAnnotation<?>> unroll(Annotation[] annotations) {
final Map<Class<? extends Annotation>, MetaAnnotation<?>> map = new HashMap<Class<? extends Annotation>, MetaAnnotation<?>>();
for (Annotation annotation : annotations) {
map.put(annotation.annotationType(), new MetaAnnotation(annotation, 0));
unroll(annotation.annotationType(), 1, map);
}
return map;
}
protected Annotation[][] unrollParameters(Annotation[][] parameterAnnotations) {
final Annotation[][] unrolledParameters = new Annotation[parameterAnnotations.length][];
int i = 0;
for (Annotation[] annotations : parameterAnnotations) {
final Map<Class<? extends Annotation>, MetaAnnotation<?>> map = unroll(annotations);
int j = 0;
final Annotation[] unrolled = new Annotation[map.size()];
for (MetaAnnotation<?> metaAnnotation : map.values()) {
unrolled[j++] = metaAnnotation.get();
}
unrolledParameters[i++] = unrolled;
}
return unrolledParameters;
}
}