package org.osgl.inject.util; /*- * #%L * OSGL Genie * %% * Copyright (C) 2017 OSGL (Open Source General Library) * %% * Licensed 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. * #L% */ import org.osgl.util.C; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Map; /** * Provides utility methods to deal with annotations. */ public class AnnotationUtil { /** * Return declared annotation with type `annoClass` from * an {@link AnnotatedElement}. * * @param annotatedElement * the annotated element (could be class, method or field) * @param annoClass * the annotation class * @param <T> * the generic type of the annotation class * @return * the annotation instance or `null` if not found */ public static <T extends Annotation> T declaredAnnotation( AnnotatedElement annotatedElement, Class<T> annoClass ) { Annotation[] aa = annotatedElement.getDeclaredAnnotations(); if (null == aa) { return null; } for (Annotation a : aa) { if (annoClass.isInstance(a)) { return (T) a; } } return null; } /** * Returns the {@link Annotation} tagged on another annotation instance. * * @param annotation * the annotation instance * @param tagClass * the expected annotation class * @param <T> * the generic type of the expected annotation * @return * the annotation tagged on annotation of type `tagClass` */ public static <T extends Annotation> T tagAnnotation(Annotation annotation, Class<T> tagClass) { Class<?> c = annotation.annotationType(); for (Annotation a : c.getAnnotations()) { if (tagClass.isInstance(a)) { return (T) a; } } return null; } /** * Create an annotation instance from annotation class. * * @param clazz * the annotation class * @param memberValues * the optional member values * @param <T> * the generic type of the annoation * @return * the annotation instance */ @SuppressWarnings("unchecked") public static <T extends Annotation> T createAnnotation(final Class<T> clazz, Map<String, Object> memberValues) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz, Annotation.class}, new SimpleAnnoInvocationHandler(clazz, memberValues)); } /** * Create an annotation instance from annotation class. * * @param clazz * the annotation class * @param <T> * the generic type of the annoation * @return * the annotation instance */ @SuppressWarnings("unchecked") public static <T extends Annotation> T createAnnotation(final Class<T> clazz) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz, Annotation.class}, new SimpleAnnoInvocationHandler(clazz, C.<String, Object>Map())); } /** * Generate a hash code for the given annotation using the algorithm * presented in the {@link Annotation#hashCode()} API docs. * * @param a * the Annotation for a hash code calculation is desired, not `null` * @return * the calculated hash code * @throws RuntimeException * if an {@code Exception} is encountered during annotation member access * * @throws IllegalStateException * if an annotation method invocation returns `null` */ public static int hashCode(Annotation a) { int result = 0; Class<? extends Annotation> type = a.annotationType(); for (Method m : type.getDeclaredMethods()) { try { Object value = m.invoke(a); if (value == null) { throw new IllegalStateException( String.format("Annotation method %s returned null", m)); } result += hashMember(m.getName(), value); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException(ex); } } return result; } /** * Helper method for generating a hash code for a member of an annotation. * * @param name * the name of the member * @param value * the value of the member * @return * a hash code for this member */ static int hashMember(String name, Object value) { int part1 = name.hashCode() * 127; if (value.getClass().isArray()) { return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value); } if (value instanceof Annotation) { return part1 ^ hashCode((Annotation) value); } return part1 ^ value.hashCode(); } /** * Helper method for generating a hash code for an array. * * @param componentType * the component type of the array * @param o * the array * @return * a hash code for the specified array */ private static int arrayMemberHash(Class<?> componentType, Object o) { if (componentType.equals(Byte.TYPE)) { return Arrays.hashCode((byte[]) o); } if (componentType.equals(Short.TYPE)) { return Arrays.hashCode((short[]) o); } if (componentType.equals(Integer.TYPE)) { return Arrays.hashCode((int[]) o); } if (componentType.equals(Character.TYPE)) { return Arrays.hashCode((char[]) o); } if (componentType.equals(Long.TYPE)) { return Arrays.hashCode((long[]) o); } if (componentType.equals(Float.TYPE)) { return Arrays.hashCode((float[]) o); } if (componentType.equals(Double.TYPE)) { return Arrays.hashCode((double[]) o); } if (componentType.equals(Boolean.TYPE)) { return Arrays.hashCode((boolean[]) o); } return Arrays.hashCode((Object[]) o); } }