/******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-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 { public TypeReference type; public int declarationSourceEnd; public Binding recipient; final static MemberValuePair[] NoValuePairs = new MemberValuePair[0]; 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 StringBuffer printExpression(int indent, StringBuffer output) { output.append('@'); this.type.printExpression(0, output); return output; } public abstract MemberValuePair[] memberValuePairs(); 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[] valuePairs = memberValuePairs(); MemberValuePair valueAttribute = null; // remember the first 'value' pair MemberValuePair[] usedValuePairs; int pairsLength = valuePairs.length; System.arraycopy(valuePairs, 0, usedValuePairs = 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 valuePair = usedValuePairs[j]; if (valuePair == null) continue nextPair; char[] memberName = valuePair.name; if (CharOperation.equals(memberName, selector)) { if (valueAttribute == null && CharOperation.equals(memberName, TypeConstants.VALUE)) { valueAttribute = valuePair; } valuePair.binding = method; usedValuePairs[j] = null; // consumed foundValue = true; boolean foundDuplicate = false; for (int k = j+1; k < pairsLength; k++) { if (CharOperation.equals(usedValuePairs[k].name, selector)) { foundDuplicate = true; scope.problemReporter().duplicateAnnotationValue(annotationType, usedValuePairs[k]); usedValuePairs[k].binding = method; usedValuePairs[k] = null; } } if (foundDuplicate) { scope.problemReporter().duplicateAnnotationValue(annotationType, valuePair); continue nextMember; } valuePair.resolveTypeExpecting(scope, method.returnType); } } if (!foundValue && (method.modifiers & AccAnnotationDefault) == 0) { scope.problemReporter().missingValueForAnnotationMember(this, method.selector); } } // check unused pairs for (int i = 0; i < pairsLength; i++) { if (usedValuePairs[i] != null) { scope.problemReporter().undefinedAnnotationValue(annotationType, usedValuePairs[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 : // TODO (philippe) need support for package annotations 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(); 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); }