| /******************************************************************************* |
| * Copyright (c) 2000, 2005 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.ast; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.lookup.*; |
| |
| /** |
| * Annotation |
| */ |
| public abstract class Annotation extends Expression { |
| |
| final static MemberValuePair[] NoValuePairs = new MemberValuePair[0]; |
| public int declarationSourceEnd; |
| public Binding recipient; |
| |
| public TypeReference type; |
| |
| public static long getRetentionPolicy(char[] policyName) { |
| if (policyName == null || policyName.length == 0) |
| return 0; |
| switch(policyName[0]) { |
| case 'C' : |
| if (CharOperation.equals(policyName, TypeConstants.UPPER_CLASS)) |
| return TagBits.AnnotationClassRetention; |
| break; |
| case 'S' : |
| if (CharOperation.equals(policyName, TypeConstants.UPPER_SOURCE)) |
| return TagBits.AnnotationSourceRetention; |
| break; |
| case 'R' : |
| if (CharOperation.equals(policyName, TypeConstants.UPPER_RUNTIME)) |
| return TagBits.AnnotationRuntimeRetention; |
| break; |
| } |
| return 0; // unknown |
| } |
| |
| public static long getTargetElementType(char[] elementName) { |
| if (elementName == null || elementName.length == 0) |
| return 0; |
| switch(elementName[0]) { |
| case 'A' : |
| if (CharOperation.equals(elementName, TypeConstants.UPPER_ANNOTATION_TYPE)) |
| return TagBits.AnnotationForAnnotationType; |
| break; |
| case 'C' : |
| if (CharOperation.equals(elementName, TypeConstants.UPPER_CONSTRUCTOR)) |
| return TagBits.AnnotationForConstructor; |
| break; |
| case 'F' : |
| if (CharOperation.equals(elementName, TypeConstants.UPPER_FIELD)) |
| return TagBits.AnnotationForField; |
| break; |
| case 'L' : |
| if (CharOperation.equals(elementName, TypeConstants.UPPER_LOCAL_VARIABLE)) |
| return TagBits.AnnotationForLocalVariable; |
| break; |
| case 'M' : |
| if (CharOperation.equals(elementName, TypeConstants.UPPER_METHOD)) |
| return TagBits.AnnotationForMethod; |
| break; |
| case 'P' : |
| if (CharOperation.equals(elementName, TypeConstants.UPPER_PARAMETER)) |
| return TagBits.AnnotationForParameter; |
| else if (CharOperation.equals(elementName, TypeConstants.UPPER_PACKAGE)) |
| return TagBits.AnnotationForPackage; |
| break; |
| case 'T' : |
| if (CharOperation.equals(elementName, TypeConstants.TYPE)) |
| return TagBits.AnnotationForType; |
| break; |
| } |
| return 0; // unknown |
| } |
| |
| /** |
| * Compute the bit pattern for recognized standard annotations the compiler may need to act upon |
| */ |
| private long detectStandardAnnotation(Scope scope, ReferenceBinding annotationType, MemberValuePair valueAttribute) { |
| long tagBits = 0; |
| switch (annotationType.id) { |
| // retention annotation |
| case TypeIds.T_JavaLangAnnotationRetention : |
| if (valueAttribute != null) { |
| Expression expr = valueAttribute.value; |
| if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) { |
| FieldBinding field = ((Reference)expr).fieldBinding(); |
| if (field != null && field.declaringClass.id == T_JavaLangAnnotationRetentionPolicy) { |
| tagBits |= getRetentionPolicy(field.name); |
| } |
| } |
| } |
| break; |
| // target annotation |
| case TypeIds.T_JavaLangAnnotationTarget : |
| tagBits |= TagBits.AnnotationTarget; // target specified (could be empty) |
| if (valueAttribute != null) { |
| Expression expr = valueAttribute.value; |
| if (expr instanceof ArrayInitializer) { |
| ArrayInitializer initializer = (ArrayInitializer) expr; |
| final Expression[] expressions = initializer.expressions; |
| if (expressions != null) { |
| for (int i = 0, length = expressions.length; i < length; i++) { |
| Expression initExpr = expressions[i]; |
| if ((initExpr.bits & Binding.VARIABLE) == Binding.FIELD) { |
| FieldBinding field = ((Reference) initExpr).fieldBinding(); |
| if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) { |
| long element = getTargetElementType(field.name); |
| if ((tagBits & element) != 0) { |
| scope.problemReporter().duplicateTargetInTargetAnnotation(annotationType, (NameReference)initExpr); |
| } else { |
| tagBits |= element; |
| } |
| } |
| } |
| } |
| } |
| } else if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) { |
| FieldBinding field = ((Reference) expr).fieldBinding(); |
| if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) { |
| tagBits |= getTargetElementType(field.name); |
| } |
| } |
| } |
| break; |
| // marker annotations |
| case TypeIds.T_JavaLangDeprecated : |
| tagBits |= TagBits.AnnotationDeprecated; |
| break; |
| case TypeIds.T_JavaLangAnnotationDocumented : |
| tagBits |= TagBits.AnnotationDocumented; |
| break; |
| case TypeIds.T_JavaLangAnnotationInherited : |
| tagBits |= TagBits.AnnotationInherited; |
| break; |
| case TypeIds.T_JavaLangOverride : |
| tagBits |= TagBits.AnnotationOverride; |
| break; |
| case TypeIds.T_JavaLangSuppressWarnings : |
| tagBits |= TagBits.AnnotationSuppressWarnings; |
| break; |
| } |
| return tagBits; |
| } |
| |
| public abstract MemberValuePair[] memberValuePairs(); |
| |
| public StringBuffer printExpression(int indent, StringBuffer output) { |
| output.append('@'); |
| this.type.printExpression(0, output); |
| return output; |
| } |
| |
| public TypeBinding resolveType(BlockScope scope) { |
| |
| this.constant = NotAConstant; |
| |
| TypeBinding typeBinding = this.type.resolveType(scope); |
| if (typeBinding == null) |
| return null; |
| this.resolvedType = typeBinding; |
| // ensure type refers to an annotation type |
| if (!typeBinding.isAnnotationType()) { |
| scope.problemReporter().typeMismatchError(typeBinding, scope.getJavaLangAnnotationAnnotation(), this.type); |
| return null; |
| } |
| |
| ReferenceBinding annotationType = (ReferenceBinding) this.resolvedType; |
| MethodBinding[] methods = annotationType.methods(); |
| // clone valuePairs to keep track of unused ones |
| MemberValuePair[] originalValuePairs = memberValuePairs(); |
| MemberValuePair valueAttribute = null; // remember the first 'value' pair |
| MemberValuePair[] pairs; |
| int pairsLength = originalValuePairs.length; |
| System.arraycopy(originalValuePairs, 0, pairs = new MemberValuePair[pairsLength], 0, pairsLength); |
| |
| nextMember: for (int i = 0, requiredLength = methods.length; i < requiredLength; i++) { |
| MethodBinding method = methods[i]; |
| char[] selector = method.selector; |
| boolean foundValue = false; |
| nextPair: for (int j = 0; j < pairsLength; j++) { |
| MemberValuePair pair = pairs[j]; |
| if (pair == null) continue nextPair; |
| char[] name = pair.name; |
| if (CharOperation.equals(name, selector)) { |
| if (valueAttribute == null && CharOperation.equals(name, TypeConstants.VALUE)) { |
| valueAttribute = pair; |
| } |
| pair.binding = method; |
| pair.resolveTypeExpecting(scope, method.returnType); |
| pairs[j] = null; // consumed |
| foundValue = true; |
| |
| // check duplicates |
| boolean foundDuplicate = false; |
| for (int k = j+1; k < pairsLength; k++) { |
| MemberValuePair otherPair = pairs[k]; |
| if (otherPair == null) continue; |
| if (CharOperation.equals(otherPair.name, selector)) { |
| foundDuplicate = true; |
| scope.problemReporter().duplicateAnnotationValue(annotationType, otherPair); |
| otherPair.binding = method; |
| otherPair.resolveTypeExpecting(scope, method.returnType); |
| pairs[k] = null; |
| } |
| } |
| if (foundDuplicate) { |
| scope.problemReporter().duplicateAnnotationValue(annotationType, pair); |
| continue nextMember; |
| } |
| } |
| } |
| if (!foundValue && (method.modifiers & AccAnnotationDefault) == 0) { |
| scope.problemReporter().missingValueForAnnotationMember(this, selector); |
| } |
| } |
| // check unused pairs |
| for (int i = 0; i < pairsLength; i++) { |
| if (pairs[i] != null) { |
| scope.problemReporter().undefinedAnnotationValue(annotationType, pairs[i]); |
| } |
| } |
| // recognize standard annotations ? |
| long tagBits = detectStandardAnnotation(scope, annotationType, valueAttribute); |
| if (this.recipient != null) { |
| if (tagBits != 0) { |
| // tag bits onto recipient |
| switch (this.recipient.kind()) { |
| case Binding.PACKAGE : |
| ((PackageBinding)this.recipient).tagBits |= tagBits; |
| break; |
| case Binding.TYPE : |
| case Binding.GENERIC_TYPE : |
| case Binding.TYPE_PARAMETER : |
| ((ReferenceBinding)this.recipient).tagBits |= tagBits; |
| break; |
| case Binding.METHOD : |
| ((MethodBinding)this.recipient).tagBits |= tagBits; |
| break; |
| case Binding.FIELD : |
| ((FieldBinding)this.recipient).tagBits |= tagBits; |
| break; |
| case Binding.LOCAL : |
| ((LocalVariableBinding)this.recipient).tagBits |= tagBits; |
| break; |
| } |
| } |
| // check (meta)target compatibility |
| checkTargetCompatibility: { |
| long metaTagBits = annotationType.getAnnotationTagBits(); // could be forward reference |
| if ((metaTagBits & TagBits.AnnotationTargetMASK) == 0) // does not specify any target restriction |
| break checkTargetCompatibility; |
| |
| switch (recipient.kind()) { |
| case Binding.PACKAGE : |
| if ((metaTagBits & TagBits.AnnotationForPackage) != 0) |
| break checkTargetCompatibility; |
| break; |
| case Binding.TYPE : |
| case Binding.GENERIC_TYPE : |
| if (((ReferenceBinding)this.recipient).isAnnotationType()) { |
| if ((metaTagBits & (TagBits.AnnotationForAnnotationType|TagBits.AnnotationForType)) != 0) |
| break checkTargetCompatibility; |
| } else if ((metaTagBits & TagBits.AnnotationForType) != 0) |
| break checkTargetCompatibility; |
| break; |
| case Binding.METHOD : |
| if (((MethodBinding)this.recipient).isConstructor()) { |
| if ((metaTagBits & TagBits.AnnotationForConstructor) != 0) |
| break checkTargetCompatibility; |
| } else if ((metaTagBits & TagBits.AnnotationForMethod) != 0) |
| break checkTargetCompatibility; |
| break; |
| case Binding.FIELD : |
| if ((metaTagBits & TagBits.AnnotationForField) != 0) |
| break checkTargetCompatibility; |
| break; |
| case Binding.LOCAL : |
| if (((LocalVariableBinding)this.recipient).isArgument) { |
| if ((metaTagBits & TagBits.AnnotationForParameter) != 0) |
| break checkTargetCompatibility; |
| } else if ((annotationType.tagBits & TagBits.AnnotationForLocalVariable) != 0) |
| break checkTargetCompatibility; |
| break; |
| } |
| scope.problemReporter().disallowedTargetForAnnotation(this); |
| } |
| } |
| return this.resolvedType; |
| } |
| |
| public abstract void traverse(ASTVisitor visitor, BlockScope scope); |
| public abstract void traverse(ASTVisitor visitor, CompilationUnitScope scope); |
| } |