All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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 
    {
        AccessibleObject object;
        boolean flag;

        protected PrivilegedActionForAccessibleObject(AccessibleObject object, boolean flag) 
        {
            this.object = object;
            this.flag = flag;
        }

        public Object run() 
        {
            object.setAccessible(flag);
            return null;
        }
    }

}