blob: bf0de8b6763c81f7ec9b2fbae198f36a925f4bc3 [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.johnzon.mapper.access;
import static java.util.Arrays.asList;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
public final class Meta {
private Meta() {
// no-op
}
public static <T extends Annotation> T getAnnotation(final AnnotatedElement holder, final Class<T> api) {
return getDirectAnnotation(holder, api);
}
public static <T extends Annotation> T getClassOrPackageAnnotation(final Method holder, final Class<T> api) {
return getIndirectAnnotation(api, holder::getDeclaringClass, () -> holder.getDeclaringClass().getPackage());
}
public static <T extends Annotation> T getClassOrPackageAnnotation(final Field holder, final Class<T> api) {
return getIndirectAnnotation(api, holder::getDeclaringClass, () -> holder.getDeclaringClass().getPackage());
}
private static <T extends Annotation> T getDirectAnnotation(final AnnotatedElement holder, final Class<T> api) {
final T annotation = holder.getAnnotation(api);
if (annotation != null) {
return annotation;
}
final T meta = findMeta(holder.getAnnotations(), api);
if (meta != null) {
return meta;
}
return null;
}
private static <T extends Annotation> T getIndirectAnnotation(final Class<T> api,
final Supplier<Class<?>> ownerSupplier,
final Supplier<Package> packageSupplier) {
final T ownerAnnotation = ownerSupplier.get().getAnnotation(api);
if (ownerAnnotation != null) {
return ownerAnnotation;
} // todo: meta?
final Package pck = packageSupplier.get();
if (pck != null) {
final T pckAnnotation = pck.getAnnotation(api);
if (pckAnnotation != null) {
return pckAnnotation;
}
} // todo: meta?
return null;
}
public static <T extends Annotation> T getAnnotation(final Class<?> clazz, final Class<T> api) {
Class<?> current = clazz;
final Set<Class<?>> visited = new HashSet<>();
while (current != null && current != Object.class) {
if (!visited.add(current)) {
return null;
}
final T annotation = current.getAnnotation(api);
if (annotation != null) {
return annotation;
}
final T meta = findMeta(clazz.getAnnotations(), api);
if (meta != null) {
return meta;
}
current = current.getSuperclass();
}
return null;
}
public static <T extends Annotation> T getAnnotation(final Package pck, final Class<T> api) {
final T annotation = pck.getAnnotation(api);
if (annotation != null) {
return annotation;
}
return findMeta(pck.getAnnotations(), api);
}
public static <T extends Annotation> T findMeta(final Annotation[] annotations, final Class<T> api) {
for (final Annotation a : annotations) {
final Class<? extends Annotation> userType = a.annotationType();
final T aa = userType.getAnnotation(api);
if (aa != null) {
boolean overriden = false;
final Map<String, Method> mapping = new HashMap<String, Method>();
for (final Class<?> cm : asList(api, userType)) {
for (final Method m : cm.getMethods()) {
overriden = mapping.put(m.getName(), m) != null || overriden;
}
}
if (!overriden) {
return aa;
}
return api.cast(newAnnotation(mapping, a, aa));
}
}
return null;
}
private static <T extends Annotation> T newAnnotation(final Map<String, Method> methodMapping, final Annotation user, final T johnzon) {
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{johnzon.annotationType()},
(proxy, method, args) -> {
final Method m = methodMapping.get(method.getName());
try {
if (m.getDeclaringClass() == user.annotationType()) {
return m.invoke(user, args);
}
return m.invoke(johnzon, args);
} catch (final InvocationTargetException ite) {
throw ite.getTargetException();
}
});
}
}