org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;

import java.util.Arrays;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.Annotation;

/**
 * Represents JSR 175 Annotation instances in the type-system.
 */
public class AnnotationBinding {
    // do not access directly - use getters instead (UnresolvedAnnotationBinding
    // resolves types for type and pair contents just in time)
    ReferenceBinding type;
    ElementValuePair[] pairs;

    /**
     * Add the standard annotations encoded in the tag bits to the recorded annotations.
     *
     * @param recordedAnnotations existing annotations already created
     * @param annotationTagBits
     * @param env
     * @return the combined list of annotations
     */
    public static AnnotationBinding[] addStandardAnnotations(AnnotationBinding[] recordedAnnotations,
            long annotationTagBits, LookupEnvironment env) {
        // NOTE: expect annotations to be requested just once so there is no need to store the standard annotations
        // and all of the standard annotations created by this method are fully resolved since the sender is expected to use them immediately
        if ((annotationTagBits & TagBits.AllStandardAnnotationsMask) == 0) {
            return recordedAnnotations;
        }
        boolean haveDeprecated = false;
        for (AnnotationBinding annotationBinding : recordedAnnotations) {
            if (annotationBinding.getAnnotationType().id == TypeIds.T_JavaLangDeprecated) {
                haveDeprecated = true;
                break;
            }
        }
        int count = 0;
        if ((annotationTagBits & TagBits.AnnotationTargetMASK) != 0)
            count++;
        if ((annotationTagBits & TagBits.AnnotationRetentionMASK) != 0)
            count++;
        if (!haveDeprecated && (annotationTagBits & TagBits.AnnotationDeprecated) != 0)
            count++;
        if ((annotationTagBits & TagBits.AnnotationDocumented) != 0)
            count++;
        if ((annotationTagBits & TagBits.AnnotationInherited) != 0)
            count++;
        if ((annotationTagBits & TagBits.AnnotationOverride) != 0)
            count++;
        if ((annotationTagBits & TagBits.AnnotationSuppressWarnings) != 0)
            count++;
        if ((annotationTagBits & TagBits.AnnotationPolymorphicSignature) != 0)
            count++;
        if ((annotationTagBits & TagBits.AnnotationSafeVarargs) != 0)
            count++;
        if (count == 0) {
            // this is possible if bits were set for null annotations
            return recordedAnnotations;
        }

        int index = recordedAnnotations.length;
        AnnotationBinding[] result = new AnnotationBinding[index + count];
        System.arraycopy(recordedAnnotations, 0, result, 0, index);
        if ((annotationTagBits & TagBits.AnnotationTargetMASK) != 0)
            result[index++] = buildTargetAnnotation(annotationTagBits, env);
        if ((annotationTagBits & TagBits.AnnotationRetentionMASK) != 0)
            result[index++] = buildRetentionAnnotation(annotationTagBits, env);
        if (!haveDeprecated && (annotationTagBits & TagBits.AnnotationDeprecated) != 0)
            result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_DEPRECATED, env.javaBaseModule(), env);
        if ((annotationTagBits & TagBits.AnnotationDocumented) != 0)
            result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_ANNOTATION_DOCUMENTED,
                    env.javaBaseModule(), env);
        if ((annotationTagBits & TagBits.AnnotationInherited) != 0)
            result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_ANNOTATION_INHERITED,
                    env.javaBaseModule(), env);
        if ((annotationTagBits & TagBits.AnnotationOverride) != 0)
            result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, env.javaBaseModule(), env);
        if ((annotationTagBits & TagBits.AnnotationSuppressWarnings) != 0)
            result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_SUPPRESSWARNINGS, env.javaBaseModule(),
                    env);
        if ((annotationTagBits & TagBits.AnnotationPolymorphicSignature) != 0)
            result[index++] = buildMarkerAnnotationForMemberType(
                    TypeConstants.JAVA_LANG_INVOKE_METHODHANDLE_$_POLYMORPHICSIGNATURE, env.javaBaseModule(), env);
        if ((annotationTagBits & TagBits.AnnotationSafeVarargs) != 0)
            result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_SAFEVARARGS, env.javaBaseModule(), env);
        return result;
    }

    private static AnnotationBinding buildMarkerAnnotationForMemberType(char[][] compoundName, ModuleBinding module,
            LookupEnvironment env) {
        ReferenceBinding type = env.getResolvedType(compoundName, module, null);
        // since this is a member type name using '$' the return binding is a
        // problem reference binding with reason ProblemReasons.InternalNameProvided
        if (!type.isValidBinding()) {
            type = ((ProblemReferenceBinding) type).closestMatch;
        }
        return env.createAnnotation(type, Binding.NO_ELEMENT_VALUE_PAIRS);
    }

    private static AnnotationBinding buildMarkerAnnotation(char[][] compoundName, ModuleBinding module,
            LookupEnvironment env) {
        ReferenceBinding type = env.getResolvedType(compoundName, module, null);
        return env.createAnnotation(type, Binding.NO_ELEMENT_VALUE_PAIRS);
    }

    private static AnnotationBinding buildRetentionAnnotation(long bits, LookupEnvironment env) {
        ReferenceBinding retentionPolicy = env
                .getResolvedJavaBaseType(TypeConstants.JAVA_LANG_ANNOTATION_RETENTIONPOLICY, null);
        Object value = null;
        if ((bits & TagBits.AnnotationRuntimeRetention) == TagBits.AnnotationRuntimeRetention) {
            value = retentionPolicy.getField(TypeConstants.UPPER_RUNTIME, true);
        } else if ((bits & TagBits.AnnotationClassRetention) != 0) {
            value = retentionPolicy.getField(TypeConstants.UPPER_CLASS, true);
        } else if ((bits & TagBits.AnnotationSourceRetention) != 0) {
            value = retentionPolicy.getField(TypeConstants.UPPER_SOURCE, true);
        }
        return env.createAnnotation(env.getResolvedJavaBaseType(TypeConstants.JAVA_LANG_ANNOTATION_RETENTION, null),
                new ElementValuePair[] { new ElementValuePair(TypeConstants.VALUE, value, null) });
    }

    private static AnnotationBinding buildTargetAnnotation(long bits, LookupEnvironment env) {
        ReferenceBinding target = env.getResolvedJavaBaseType(TypeConstants.JAVA_LANG_ANNOTATION_TARGET, null);
        if ((bits & TagBits.AnnotationTarget) != 0)
            return new AnnotationBinding(target, Binding.NO_ELEMENT_VALUE_PAIRS);

        int arraysize = 0;
        if ((bits & TagBits.AnnotationForAnnotationType) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForConstructor) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForField) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForLocalVariable) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForMethod) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForPackage) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForParameter) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForType) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForTypeUse) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForTypeParameter) != 0)
            arraysize++;
        if ((bits & TagBits.AnnotationForModule) != 0)
            arraysize++;

        Object[] value = new Object[arraysize];
        if (arraysize > 0) {
            ReferenceBinding elementType = env.getResolvedType(TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE,
                    null);
            int index = 0;
            if ((bits & TagBits.AnnotationForTypeUse) != 0)
                value[index++] = elementType.getField(TypeConstants.TYPE_USE_TARGET, true);
            if ((bits & TagBits.AnnotationForAnnotationType) != 0)
                value[index++] = elementType.getField(TypeConstants.UPPER_ANNOTATION_TYPE, true);
            if ((bits & TagBits.AnnotationForConstructor) != 0)
                value[index++] = elementType.getField(TypeConstants.UPPER_CONSTRUCTOR, true);
            if ((bits & TagBits.AnnotationForField) != 0)
                value[index++] = elementType.getField(TypeConstants.UPPER_FIELD, true);
            if ((bits & TagBits.AnnotationForMethod) != 0)
                value[index++] = elementType.getField(TypeConstants.UPPER_METHOD, true);
            if ((bits & TagBits.AnnotationForPackage) != 0)
                value[index++] = elementType.getField(TypeConstants.UPPER_PACKAGE, true);
            if ((bits & TagBits.AnnotationForParameter) != 0)
                value[index++] = elementType.getField(TypeConstants.UPPER_PARAMETER, true);
            if ((bits & TagBits.AnnotationForTypeParameter) != 0)
                value[index++] = elementType.getField(TypeConstants.TYPE_PARAMETER_TARGET, true);
            if ((bits & TagBits.AnnotationForType) != 0)
                value[index++] = elementType.getField(TypeConstants.TYPE, true);
            if ((bits & TagBits.AnnotationForLocalVariable) != 0)
                value[index++] = elementType.getField(TypeConstants.UPPER_LOCAL_VARIABLE, true);
        }
        return env.createAnnotation(target,
                new ElementValuePair[] { new ElementValuePair(TypeConstants.VALUE, value, null) });
    }

    public AnnotationBinding(ReferenceBinding type, ElementValuePair[] pairs) {
        this.type = type;
        this.pairs = pairs;
    }

    AnnotationBinding(Annotation astAnnotation) {
        this((ReferenceBinding) astAnnotation.resolvedType, astAnnotation.computeElementValuePairs());
    }

    /*
     * Computes a key that uniquely identifies this binding, using the given recipient's unique key.
     * recipientKey @ typeKey
     * @MyAnnot void bar() --> Lp/X;.bar()V@Lp/MyAnnot;
     */
    public char[] computeUniqueKey(char[] recipientKey) {
        char[] typeKey = this.type.computeUniqueKey(false);
        int recipientKeyLength = recipientKey.length;
        char[] uniqueKey = new char[recipientKeyLength + 1 + typeKey.length];
        System.arraycopy(recipientKey, 0, uniqueKey, 0, recipientKeyLength);
        uniqueKey[recipientKeyLength] = '@';
        System.arraycopy(typeKey, 0, uniqueKey, recipientKeyLength + 1, typeKey.length);
        return uniqueKey;
    }

    public ReferenceBinding getAnnotationType() {
        return this.type;
    }

    public void resolve() {
        // Nothing to do, this is already resolved.
    }

    public ElementValuePair[] getElementValuePairs() {
        return this.pairs;
    }

    public static void setMethodBindings(ReferenceBinding type, ElementValuePair[] pairs) {
        // set the method bindings of each element value pair
        for (int i = pairs.length; --i >= 0;) {
            ElementValuePair pair = pairs[i];
            MethodBinding[] methods = type.getMethods(pair.getName());
            // there should be exactly one since the type is an annotation type.
            if (methods != null && methods.length == 1)
                pair.setMethodBinding(methods[0]);
        }
    }

    @Override
    public String toString() {
        StringBuffer buffer = new StringBuffer(5);
        buffer.append('@').append(this.type.sourceName);
        if (this.pairs != null && this.pairs.length > 0) {
            buffer.append('(');
            if (this.pairs.length == 1 && CharOperation.equals(this.pairs[0].getName(), TypeConstants.VALUE)) {
                buffer.append(this.pairs[0].value);
            } else {
                for (int i = 0, max = this.pairs.length; i < max; i++) {
                    if (i > 0)
                        buffer.append(", "); //$NON-NLS-1$
                    buffer.append(this.pairs[i]);
                }
            }
            buffer.append(')');
        }
        return buffer.toString();
    }

    @Override
    public int hashCode() {
        int result = 17;
        int c = this.getAnnotationType().hashCode();
        result = 31 * result + c;
        c = Arrays.hashCode(this.getElementValuePairs());
        result = 31 * result + c;
        return result;
    }

    @Override
    public boolean equals(Object object) {
        if (this == object)
            return true;
        if (!(object instanceof AnnotationBinding))
            return false;

        AnnotationBinding that = (AnnotationBinding) object;
        if (this.getAnnotationType() != that.getAnnotationType()) //$IDENTITY-COMPARISON$
            return false;

        final ElementValuePair[] thisElementValuePairs = this.getElementValuePairs();
        final ElementValuePair[] thatElementValuePairs = that.getElementValuePairs();
        final int length = thisElementValuePairs.length;
        if (length != thatElementValuePairs.length)
            return false;
        loop: for (int i = 0; i < length; i++) {
            ElementValuePair thisPair = thisElementValuePairs[i];
            for (int j = 0; j < length; j++) {
                ElementValuePair thatPair = thatElementValuePairs[j];
                if (thisPair.binding == thatPair.binding) {
                    if (thisPair.value == null) {
                        if (thatPair.value == null) {
                            continue loop;
                        }
                        return false;
                    } else {
                        if (thatPair.value == null)
                            return false;
                        if (thatPair.value instanceof Object[] && thisPair.value instanceof Object[]) {
                            if (!Arrays.equals((Object[]) thisPair.value, (Object[]) thatPair.value)) {
                                return false;
                            }
                        } else if (!thatPair.value.equals(thisPair.value)) {
                            return false;
                        }
                    }
                    continue loop;
                }
            }
            return false;
        }
        return true;
    }
}