javax.enterprise.util.AnnotationLiteral 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 javax.enterprise.util;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
/**
* Annotation literal utility.
*
* @param wrapped annotation class
* @version $Rev$ $Date$
*/
@SuppressWarnings("unchecked")
public abstract class AnnotationLiteral implements Annotation, Serializable
{
private static final long serialVersionUID = -1885320698638161810L;
private Class annotationType;
// cached values
private transient Method[] _meths;
private transient String _toString;
private transient Integer _hashCode;
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
protected AnnotationLiteral()
{
this.annotationType = getAnnotationType(getClass());
}
public Class extends Annotation> annotationType()
{
return annotationType;
}
private Class getAnnotationType(Class> definedClazz)
{
Type superClazz = definedClazz.getGenericSuperclass();
Class clazz;
if (superClazz.equals(Object.class))
{
throw new RuntimeException("Super class must be parametrized type!");
}
else if (superClazz instanceof ParameterizedType)
{
ParameterizedType paramType = (ParameterizedType) superClazz;
Type[] actualArgs = paramType.getActualTypeArguments();
if (actualArgs.length == 1)
{
//Actual annotation type
Type type = actualArgs[0];
if (type instanceof Class)
{
clazz = (Class) type;
return clazz;
}
else
{
throw new RuntimeException("Not class type!");
}
}
else
{
throw new RuntimeException("More than one parametric type!");
}
}
else
{
return getAnnotationType((Class>) superClazz);
}
}
@Override
public boolean equals(Object other)
{
// use a private reference to prevent instance swapping as we don't use synchronization nor volatile on meths.
Method[] methods = getMethods();
if(other == this)
{
return true;
}
if(other == null)
{
return false;
}
if (other instanceof Annotation)
{
Annotation annotOther = (Annotation) other;
if (this.annotationType().equals(annotOther.annotationType()))
{
for (Method method : methods)
{
Object value = callMethod(this, method);
Object annotValue = callMethod(annotOther, method);
if((value == null && annotValue != null) || (value != null && annotValue == null))
{
return false;
}
if(value == null)
{
continue;
}
Class> valueClass = value.getClass();
Class> annotValueClass = annotValue.getClass();
if(valueClass.isPrimitive() && annotValueClass.isPrimitive())
{
if((valueClass != Float.TYPE && annotValue != Float.TYPE)
|| (valueClass != Double.TYPE && annotValue != Double.TYPE))
{
if(value != annotValue)
{
return false;
}
}
}
else if(valueClass.isArray() && annotValueClass.isArray())
{
Class> type = valueClass.getComponentType();
if(type.isPrimitive())
{
if(Long.TYPE == type)
{
if(!Arrays.equals(((Long[])value),(Long[])annotValue)) return false;
}
else if(Integer.TYPE == type)
{
if(!Arrays.equals(((Integer[])value),(Integer[])annotValue)) return false;
}
else if(Short.TYPE == type)
{
if(!Arrays.equals(((Short[])value),(Short[])annotValue)) return false;
}
else if(Double.TYPE == type)
{
if(!Arrays.equals(((Double[])value),(Double[])annotValue)) return false;
}
else if(Float.TYPE == type)
{
if(!Arrays.equals(((Float[])value),(Float[])annotValue)) return false;
}
else if(Boolean.TYPE == type)
{
if(!Arrays.equals(((Boolean[])value),(Boolean[])annotValue)) return false;
}
else if(Byte.TYPE == type)
{
if(!Arrays.equals(((Byte[])value),(Byte[])annotValue)) return false;
}
else if(Character.TYPE == type)
{
if(!Arrays.equals(((Character[])value),(Character[])annotValue)) return false;
}
}
else
{
if(!Arrays.equals(((Object[])value),(Object[])annotValue)) return false;
}
}
else if (annotValue != null)
{
if (!value.equals(annotValue))
{
return false;
}
}
}
return true;
}
}
return false;
}
private Object callMethod(Object instance, Method method)
{
boolean access = method.isAccessible();
try
{
if (!method.isAccessible())
{
AccessController.doPrivileged(new PrivilegedActionForAccessibleObject(method, true));
}
return method.invoke(instance, EMPTY_OBJECT_ARRAY);
}
catch (Exception e)
{
throw new RuntimeException("Exception in method call : " + method.getName(), e);
}
finally
{
AccessController.doPrivileged(new PrivilegedActionForAccessibleObject(method, access));
}
}
@Override
public int hashCode()
{
if (_hashCode != null) {
return _hashCode;
}
Method[] methods = getMethods();
int hashCode = 0;
for (Method method : methods)
{
// Member name
int name = 127 * method.getName().hashCode();
// Member value
Object object = callMethod(this, method);
int value = 0;
if(object.getClass().isArray())
{
Class> type = object.getClass().getComponentType();
if(type.isPrimitive())
{
if(Long.TYPE == type)
{
value = Arrays.hashCode((Long[])object);
}
else if(Integer.TYPE == type)
{
value = Arrays.hashCode((Integer[])object);
}
else if(Short.TYPE == type)
{
value = Arrays.hashCode((Short[])object);
}
else if(Double.TYPE == type)
{
value = Arrays.hashCode((Double[])object);
}
else if(Float.TYPE == type)
{
value = Arrays.hashCode((Float[])object);
}
else if(Boolean.TYPE == type)
{
value = Arrays.hashCode((Boolean[])object);
}
else if(Byte.TYPE == type)
{
value = Arrays.hashCode((Byte[])object);
}
else if(Character.TYPE == type)
{
value = Arrays.hashCode((Character[])object);
}
}
else
{
value = Arrays.hashCode((Object[])object);
}
}
else
{
value = object.hashCode();
}
hashCode += name ^ value;
}
_hashCode = hashCode;
return hashCode;
}
@Override
public String toString()
{
if (_toString != null) {
return _toString;
}
Method[] methods = getMethods();
StringBuilder sb = new StringBuilder("@" + annotationType().getName() + "(");
int lenght = methods.length;
for (int i = 0; i < lenght; i++)
{
// Member name
sb.append(methods[i].getName()).append("=");
// Member value
sb.append(callMethod(this, methods[i]));
if (i < lenght - 1)
{
sb.append(",");
}
}
sb.append(")");
_toString = sb.toString();
return _toString;
}
private Method[] getMethods() {
if (_meths == null) {
// no need to have meths volatile nor synchronized.
// if invoked in parallel we only might call getMethods() a bit too often.
_meths = (Method[]) AccessController.doPrivileged((PrivilegedAction) () -> annotationType.getDeclaredMethods());
}
return _meths;
}
protected static class PrivilegedActionForAccessibleObject implements PrivilegedAction