blob: cfd1fa78142b29c917d6f40de7a056703f21be82 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}