/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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; } int count = 0; if ((annotationTagBits & TagBits.AnnotationTargetMASK) != 0) count++; if ((annotationTagBits & TagBits.AnnotationRetentionMASK) != 0) count++; if ((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 ((annotationTagBits & TagBits.AnnotationDeprecated) != 0) result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_DEPRECATED, env); if ((annotationTagBits & TagBits.AnnotationDocumented) != 0) result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_ANNOTATION_DOCUMENTED, env); if ((annotationTagBits & TagBits.AnnotationInherited) != 0) result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_ANNOTATION_INHERITED, env); if ((annotationTagBits & TagBits.AnnotationOverride) != 0) result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, env); if ((annotationTagBits & TagBits.AnnotationSuppressWarnings) != 0) result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_SUPPRESSWARNINGS, env); if ((annotationTagBits & TagBits.AnnotationPolymorphicSignature) != 0) result[index++] = buildMarkerAnnotationForMemberType(TypeConstants.JAVA_LANG_INVOKE_METHODHANDLE_$_POLYMORPHICSIGNATURE, env); if ((annotationTagBits & TagBits.AnnotationSafeVarargs) != 0) result[index++] = buildMarkerAnnotation(TypeConstants.JAVA_LANG_SAFEVARARGS, env); return result; } private static AnnotationBinding buildMarkerAnnotationForMemberType(char[][] compoundName, LookupEnvironment env) { ReferenceBinding type = env.getResolvedType(compoundName, 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, LookupEnvironment env) { ReferenceBinding type = env.getResolvedType(compoundName, null); return env.createAnnotation(type, Binding.NO_ELEMENT_VALUE_PAIRS); } private static AnnotationBinding buildRetentionAnnotation(long bits, LookupEnvironment env) { ReferenceBinding retentionPolicy = env.getResolvedType(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.getResolvedType(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.getResolvedType(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++; Object[] value = new Object[arraysize]; if (arraysize > 0) { ReferenceBinding elementType = env.getResolvedType(TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE, null); int index = 0; 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.AnnotationForTypeUse) != 0) value[index++] = elementType.getField(TypeConstants.TYPE_USE_TARGET, 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]); } } 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(); } public int hashCode() { int result = 17; int c = this.type.hashCode(); result = 31 * result + c; c = Arrays.hashCode(this.pairs); result = 31 * result + c; return result; } 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; } }