org.apache.deltaspike.core.util.Annotateds Maven / Gradle / Ivy
/*
* 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.deltaspike.core.util;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedCallable;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.AnnotatedType;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* Utilities for working with {@link Annotated}s.
*
*
*
* Includes utilities to check the equality of and create unique id's for
* Annotated
instances.
*
*/
public final class Annotateds
{
private static final char SEPARATOR = ';';
private Annotateds()
{
// this is a utility class with statics only
}
/**
* Does the first stage of comparing AnnoatedCallables, however it cannot
* compare the method parameters
*/
private static class AnnotatedCallableComparator
implements Comparator>, Serializable
{
public int compare(AnnotatedCallable arg0, AnnotatedCallable arg1)
{
// compare the names first
int result = (arg0.getJavaMember().getName().compareTo(arg1.getJavaMember().getName()));
if (result != 0)
{
return result;
}
result = arg0.getJavaMember().getDeclaringClass().getName().compareTo(arg1.getJavaMember()
.getDeclaringClass().getName());
if (result != 0)
{
return result;
}
result = arg0.getParameters().size() - arg1.getParameters().size();
return result;
}
}
private static class AnnotatedMethodComparator
implements Comparator>, Serializable
{
private AnnotatedCallableComparator callableComparator = new AnnotatedCallableComparator();
public static Comparator> instance()
{
return new AnnotatedMethodComparator();
}
public int compare(AnnotatedMethod arg0, AnnotatedMethod arg1)
{
int result = callableComparator.compare(arg0, arg1);
if (result != 0)
{
return result;
}
for (int i = 0; i < arg0.getJavaMember().getParameterTypes().length; ++i)
{
Class p0 = arg0.getJavaMember().getParameterTypes()[i];
Class p1 = arg1.getJavaMember().getParameterTypes()[i];
result = p0.getName().compareTo(p1.getName());
if (result != 0)
{
return result;
}
}
return 0;
}
}
private static class AnnotatedConstructorComparator
implements Comparator>, Serializable
{
private AnnotatedCallableComparator callableComparator = new AnnotatedCallableComparator();
public static Comparator> instance()
{
return new AnnotatedConstructorComparator();
}
public int compare(AnnotatedConstructor arg0, AnnotatedConstructor arg1)
{
int result = callableComparator.compare(arg0, arg1);
if (result != 0)
{
return result;
}
for (int i = 0; i < arg0.getJavaMember().getParameterTypes().length; ++i)
{
Class p0 = arg0.getJavaMember().getParameterTypes()[i];
Class p1 = arg1.getJavaMember().getParameterTypes()[i];
result = p0.getName().compareTo(p1.getName());
if (result != 0)
{
return result;
}
}
return 0;
}
}
private static class AnnotatedFieldComparator
implements Comparator>, Serializable
{
public static Comparator> instance()
{
return new AnnotatedFieldComparator();
}
public int compare(AnnotatedField arg0, AnnotatedField arg1)
{
if (arg0.getJavaMember().getName().equals(arg1.getJavaMember().getName()))
{
return arg0.getJavaMember().getDeclaringClass().getName().compareTo(arg1.getJavaMember()
.getDeclaringClass().getName());
}
return arg0.getJavaMember().getName().compareTo(arg1.getJavaMember().getName());
}
}
private static class AnnotationComparator implements Comparator, Serializable
{
public static final Comparator INSTANCE = new AnnotationComparator();
public int compare(Annotation arg0, Annotation arg1)
{
return arg0.annotationType().getName().compareTo(arg1.annotationType().getName());
}
}
private static class MethodComparator implements Comparator
{
public static final Comparator INSTANCE = new MethodComparator();
public int compare(Method arg0, Method arg1)
{
return arg0.getName().compareTo(arg1.getName());
}
}
/**
* Generates a deterministic signature for an {@link AnnotatedType}. Two
* AnnotatedType
s that have the same annotations and underlying
* type will generate the same signature.
*
* This can be used to create a unique bean id for a passivation capable bean
* that is added directly through the SPI.
*
* @param annotatedType The type to generate a signature for
* @return A string representation of the annotated type
*/
public static String createTypeId(AnnotatedType annotatedType)
{
return createTypeId(annotatedType.getJavaClass(), annotatedType.getAnnotations(), annotatedType.getMethods(),
annotatedType.getFields(), annotatedType.getConstructors());
}
/**
* Generates a unique signature for a concrete class. Annotations are not
* read directly from the class, but are read from the
* annotations
, methods
, fields
and
* constructors
arguments
*
* @param clazz The java class type
* @param annotations Annotations present on the java class
* @param methods The AnnotatedMethods to include in the signature
* @param fields The AnnotatedFields to include in the signature
* @param constructors The AnnotatedConstructors to include in the signature
* @return A string representation of the type
*/
public static String createTypeId(Class clazz, Collection annotations,
Collection> methods,
Collection> fields,
Collection> constructors)
{
StringBuilder builder = new StringBuilder();
builder.append(clazz.getName());
builder.append(createAnnotationCollectionId(annotations));
builder.append("{");
// now deal with the fields
List> sortedFields = new ArrayList>();
sortedFields.addAll(fields);
Collections.sort(sortedFields, AnnotatedFieldComparator.instance());
for (AnnotatedField field : sortedFields)
{
if (!field.getAnnotations().isEmpty())
{
builder.append(createFieldId(field));
builder.append(SEPARATOR);
}
}
// methods
List> sortedMethods = new ArrayList>();
sortedMethods.addAll(methods);
Collections.sort(sortedMethods, AnnotatedMethodComparator.instance());
for (AnnotatedMethod method : sortedMethods)
{
if (!method.getAnnotations().isEmpty() || hasMethodParameters(method))
{
builder.append(createCallableId(method));
builder.append(SEPARATOR);
}
}
// constructors
List> sortedConstructors = new ArrayList>();
sortedConstructors.addAll(constructors);
Collections.sort(sortedConstructors, AnnotatedConstructorComparator.instance());
for (AnnotatedConstructor constructor : sortedConstructors)
{
if (!constructor.getAnnotations().isEmpty() || hasMethodParameters(constructor))
{
builder.append(createCallableId(constructor));
builder.append(SEPARATOR);
}
}
builder.append("}");
return builder.toString();
}
/**
* Generates a deterministic signature for an {@link AnnotatedField}. Two
* AnnotatedField
s that have the same annotations and
* underlying field will generate the same signature.
*/
public static String createFieldId(AnnotatedField field)
{
return createFieldId(field.getJavaMember(), field.getAnnotations());
}
/**
* Creates a deterministic signature for a {@link Field}.
*
* @param field The field to generate the signature for
* @param annotations The annotations to include in the signature
*/
public static String createFieldId(Field field, Collection annotations)
{
StringBuilder builder = new StringBuilder();
builder.append(field.getDeclaringClass().getName());
builder.append('.');
builder.append(field.getName());
builder.append(createAnnotationCollectionId(annotations));
return builder.toString();
}
/**
* Generates a deterministic signature for an {@link AnnotatedCallable}. Two
* AnnotatedCallable
s that have the same annotations and
* underlying callable will generate the same signature.
*/
public static String createCallableId(AnnotatedCallable method)
{
StringBuilder builder = new StringBuilder();
builder.append(method.getJavaMember().getDeclaringClass().getName());
builder.append('.');
builder.append(method.getJavaMember().getName());
builder.append(createAnnotationCollectionId(method.getAnnotations()));
builder.append(createParameterListId(method.getParameters()));
return builder.toString();
}
/**
* Creates a deterministic signature for a {@link Method}.
*
* @param method The method to generate the signature for
* @param annotations The annotations to include in the signature
* @param parameters The {@link AnnotatedParameter}s to include in the
* signature
*/
public static String createMethodId(Method method, Set annotations,
List> parameters)
{
StringBuilder builder = new StringBuilder();
builder.append(method.getDeclaringClass().getName());
builder.append('.');
builder.append(method.getName());
builder.append(createAnnotationCollectionId(annotations));
builder.append(createParameterListId(parameters));
return builder.toString();
}
/**
* Creates a deterministic signature for a {@link Constructor}.
*
* @param constructor The constructor to generate the signature for
* @param annotations The annotations to include in the signature
* @param parameters The {@link AnnotatedParameter}s to include in the
* signature
*/
public static String createConstructorId(Constructor constructor, Set annotations,
List> parameters)
{
StringBuilder builder = new StringBuilder();
builder.append(constructor.getDeclaringClass().getName());
builder.append('.');
builder.append(constructor.getName());
builder.append(createAnnotationCollectionId(annotations));
builder.append(createParameterListId(parameters));
return builder.toString();
}
/**
* Generates a unique string representation of a list of
* {@link AnnotatedParameter}s.
*/
public static String createParameterListId(List> parameters)
{
StringBuilder builder = new StringBuilder();
builder.append("(");
for (int i = 0; i < parameters.size(); ++i)
{
AnnotatedParameter ap = parameters.get(i);
builder.append(createParameterId(ap));
if (i + 1 != parameters.size())
{
builder.append(',');
}
}
builder.append(")");
return builder.toString();
}
/**
* Creates a string representation of an {@link AnnotatedParameter}.
*/
public static String createParameterId(AnnotatedParameter annotatedParameter)
{
return createParameterId(annotatedParameter.getBaseType(), annotatedParameter.getAnnotations());
}
/**
* Creates a string representation of a given type and set of annotations.
*/
public static String createParameterId(Type type, Set annotations)
{
StringBuilder builder = new StringBuilder();
if (type instanceof Class)
{
Class c = (Class) type;
builder.append(c.getName());
}
else
{
builder.append(type.toString());
}
builder.append(createAnnotationCollectionId(annotations));
return builder.toString();
}
/**
*
* Compares {@link AnnotatedField}s for equality.
*
*
* Two {@link AnnotatedField}s are considered equal if they have the same
* underlying field and annotations.
*
*/
public static boolean compareAnnotatedField(AnnotatedField f1, AnnotatedField f2)
{
if (!f1.getJavaMember().equals(f2.getJavaMember()))
{
return false;
}
return compareAnnotated(f1, f2);
}
/**
*
* Compare {@link AnnotatedCallable}s for equality.
*
*
*
* Two {@link AnnotatedCallable}s are considered equal if they have the same
* underlying callable and annotations.
*
*/
public static boolean compareAnnotatedCallable(AnnotatedCallable m1, AnnotatedCallable m2)
{
if (!m1.getJavaMember().equals(m2.getJavaMember()))
{
return false;
}
if (!compareAnnotated(m1, m2))
{
return false;
}
return compareAnnotatedParameters(m1.getParameters(), m2.getParameters());
}
/**
*
* Compares two {@link AnnotatedType}s for equality.
*
*
*
* Two {@link AnnotatedType}s are considered equal if they have the same
* underlying type and annotations, and all members have the same
* annotations.
*
*/
public static boolean compareAnnotatedTypes(AnnotatedType t1, AnnotatedType t2)
{
if (!t1.getJavaClass().equals(t2.getJavaClass()))
{
return false;
}
if (!compareAnnotated(t1, t2))
{
return false;
}
if (t1.getFields().size() != t2.getFields().size())
{
return false;
}
Map> fields = new HashMap>();
for (AnnotatedField f : t2.getFields())
{
fields.put(f.getJavaMember(), f);
}
for (AnnotatedField f : t1.getFields())
{
if (fields.containsKey(f.getJavaMember()))
{
if (!compareAnnotatedField(f, fields.get(f.getJavaMember())))
{
return false;
}
}
else
{
return false;
}
}
if (t1.getMethods().size() != t2.getMethods().size())
{
return false;
}
Map> methods = new HashMap>();
for (AnnotatedMethod f : t2.getMethods())
{
methods.put(f.getJavaMember(), f);
}
for (AnnotatedMethod f : t1.getMethods())
{
if (methods.containsKey(f.getJavaMember()))
{
if (!compareAnnotatedCallable(f, methods.get(f.getJavaMember())))
{
return false;
}
}
else
{
return false;
}
}
if (t1.getConstructors().size() != t2.getConstructors().size())
{
return false;
}
Map, AnnotatedConstructor> constructors =
new HashMap, AnnotatedConstructor>();
for (AnnotatedConstructor f : t2.getConstructors())
{
constructors.put(f.getJavaMember(), f);
}
for (AnnotatedConstructor f : t1.getConstructors())
{
if (constructors.containsKey(f.getJavaMember()))
{
if (!compareAnnotatedCallable(f, constructors.get(f.getJavaMember())))
{
return false;
}
}
else
{
return false;
}
}
return true;
}
private static boolean hasMethodParameters(AnnotatedCallable callable)
{
for (AnnotatedParameter parameter : callable.getParameters())
{
if (!parameter.getAnnotations().isEmpty())
{
return true;
}
}
return false;
}
private static String createAnnotationCollectionId(Collection annotations)
{
if (annotations.isEmpty())
{
return "";
}
StringBuilder builder = new StringBuilder();
builder.append('[');
List annotationList = new ArrayList(annotations.size());
annotationList.addAll(annotations);
Collections.sort(annotationList, AnnotationComparator.INSTANCE);
for (Annotation a : annotationList)
{
builder.append('@');
builder.append(a.annotationType().getName());
builder.append('(');
Method[] declaredMethods = a.annotationType().getDeclaredMethods();
List methods = new ArrayList(declaredMethods.length);
methods.addAll(Arrays.asList(declaredMethods));
Collections.sort(methods, MethodComparator.INSTANCE);
for (int i = 0; i < methods.size(); ++i)
{
Method method = methods.get(i);
try
{
Object value = method.invoke(a);
builder.append(method.getName());
builder.append('=');
builder.append(value.toString());
}
catch (NullPointerException e)
{
throw new RuntimeException("NullPointerException accessing annotation member, annotation:"
+ a.annotationType().getName() + " member: " + method.getName(), e);
}
catch (IllegalArgumentException e)
{
throw new RuntimeException("IllegalArgumentException accessing annotation member, annotation:"
+ a.annotationType().getName() + " member: " + method.getName(), e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException("IllegalAccessException accessing annotation member, annotation:"
+ a.annotationType().getName() + " member: " + method.getName(), e);
}
catch (InvocationTargetException e)
{
throw new RuntimeException("InvocationTargetException accessing annotation member, annotation:"
+ a.annotationType().getName() + " member: " + method.getName(), e);
}
if (i + 1 != methods.size())
{
builder.append(',');
}
}
builder.append(')');
}
builder.append(']');
return builder.toString();
}
/**
* Compares two annotated elements to see if they have the same annotations
*/
private static boolean compareAnnotated(Annotated a1, Annotated a2)
{
return a1.getAnnotations().equals(a2.getAnnotations());
}
/**
* Compares two annotated elements to see if they have the same annotations
*/
private static boolean compareAnnotatedParameters(List> p1,
List> p2)
{
if (p1.size() != p2.size())
{
return false;
}
for (int i = 0; i < p1.size(); ++i)
{
if (!compareAnnotated(p1.get(i), p2.get(i)))
{
return false;
}
}
return true;
}
}