--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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 net.sourceforge.phpdt.internal.compiler.ast;
+
+import net.sourceforge.phpdt.core.compiler.CharOperation;
+import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
+import net.sourceforge.phpdt.internal.compiler.classfmt.ClassFileConstants;
+import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
+import net.sourceforge.phpdt.internal.compiler.impl.Constant;
+import net.sourceforge.phpdt.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;
+ /**
+ * The representation of this annotation in the type system.
+ */
+ private AnnotationBinding compilerAnnotation = null;
+
+ 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
+ }
+
+ public ElementValuePair[] computeElementValuePairs() {
+ return Binding.NO_ELEMENT_VALUE_PAIRS;
+ }
+
+ /**
+ * 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 AnnotationBinding getCompilerAnnotation() {
+ return this.compilerAnnotation;
+ }
+
+ public abstract MemberValuePair[] memberValuePairs();
+
+ public StringBuffer printExpression(int indent, StringBuffer output) {
+ output.append('@');
+ this.type.printExpression(0, output);
+ return output;
+ }
+
+ public void recordSuppressWarnings(Scope scope, int startSuppresss, int endSuppress, boolean isSuppressingWarnings) {
+ long suppressWarningIrritants = 0;
+ MemberValuePair[] pairs = this.memberValuePairs();
+ pairLoop: for (int i = 0, length = pairs.length; i < length; i++) {
+ MemberValuePair pair = pairs[i];
+ if (CharOperation.equals(pair.name, TypeConstants.VALUE)) {
+ Expression value = pair.value;
+ if (value instanceof ArrayInitializer) {
+ ArrayInitializer initializer = (ArrayInitializer) value;
+ Expression[] inits = initializer.expressions;
+ if (inits != null) {
+ for (int j = 0, initsLength = inits.length; j < initsLength; j++) {
+ Constant cst = inits[j].constant;
+ if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) {
+ long irritants = CompilerOptions.warningTokenToIrritants(cst.stringValue());
+ if (irritants != 0) {
+ if ((suppressWarningIrritants & irritants) == irritants) {
+ scope.problemReporter().unusedWarningToken(inits[j]);
+ } else {
+ suppressWarningIrritants |= irritants;
+ }
+ } else {
+ scope.problemReporter().unhandledWarningToken(inits[j]);
+ }
+ }
+ }
+ }
+ } else {
+ Constant cst = value.constant;
+ if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) {
+ long irritants = CompilerOptions.warningTokenToIrritants(cst.stringValue());
+ if (irritants != 0) {
+ suppressWarningIrritants |= irritants;
+ } else {
+ scope.problemReporter().unhandledWarningToken(value);
+ }
+ }
+ }
+ break pairLoop;
+ }
+ }
+ if (isSuppressingWarnings && suppressWarningIrritants != 0) {
+ scope.referenceCompilationUnit().recordSuppressWarnings(suppressWarningIrritants, this, startSuppresss, endSuppress);
+ }
+ }
+
+ public TypeBinding resolveType(BlockScope scope) {
+
+ if (this.compilerAnnotation != null)
+ return this.resolvedType;
+ this.constant = 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() && typeBinding.isValidBinding()) {
+ scope.problemReporter().typeMismatchError(typeBinding, scope.getJavaLangAnnotationAnnotation(), this.type, null);
+ 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;
+ if (pairsLength > 0) {
+ System.arraycopy(originalValuePairs, 0, pairs = new MemberValuePair[pairsLength], 0, pairsLength);
+ } else {
+ pairs = originalValuePairs;
+ }
+
+ 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 & ClassFileConstants.AccAnnotationDefault) == 0 &&
+ (this.bits & IsRecovered) == 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]);
+ pairs[i].resolveTypeExpecting(scope, null); // resilient
+ }
+ }
+// if (scope.compilerOptions().storeAnnotations)
+ this.compilerAnnotation = scope.environment().createAnnotation((ReferenceBinding) this.resolvedType, this.computeElementValuePairs());
+ // recognize standard annotations ?
+ long tagBits = detectStandardAnnotation(scope, annotationType, valueAttribute);
+
+ // record annotation positions in the compilation result
+ scope.referenceCompilationUnit().recordSuppressWarnings(CompilerOptions.NonExternalizedString, null, this.sourceStart, this.declarationSourceEnd);
+ 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 :
+ SourceTypeBinding sourceType = (SourceTypeBinding) this.recipient;
+ sourceType.tagBits |= tagBits;
+ if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
+ TypeDeclaration typeDeclaration = sourceType.scope.referenceContext;
+ int start;
+ if (scope.referenceCompilationUnit().types[0] == typeDeclaration) {
+ start = 0;
+ } else {
+ start = typeDeclaration.declarationSourceStart;
+ }
+ recordSuppressWarnings(scope, start, typeDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
+ }
+ break;
+ case Binding.METHOD :
+ MethodBinding sourceMethod = (MethodBinding) this.recipient;
+ sourceMethod.tagBits |= tagBits;
+ if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
+ sourceType = (SourceTypeBinding) sourceMethod.declaringClass;
+ AbstractMethodDeclaration methodDeclaration = sourceType.scope.referenceContext.declarationOf(sourceMethod);
+ recordSuppressWarnings(scope, methodDeclaration.declarationSourceStart, methodDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
+ }
+ break;
+ case Binding.FIELD :
+ FieldBinding sourceField = (FieldBinding) this.recipient;
+ sourceField.tagBits |= tagBits;
+ if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
+ sourceType = (SourceTypeBinding) sourceField.declaringClass;
+ FieldDeclaration fieldDeclaration = sourceType.scope.referenceContext.declarationOf(sourceField);
+ recordSuppressWarnings(scope, fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
+ }
+ break;
+ case Binding.LOCAL :
+ LocalVariableBinding variable = (LocalVariableBinding) this.recipient;
+ variable.tagBits |= tagBits;
+ if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
+ LocalDeclaration localDeclaration = variable.declaration;
+ recordSuppressWarnings(scope, localDeclaration.declarationSourceStart, localDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
+ }
+ 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).tagBits & TagBits.IsArgument) != 0) {
+ 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);
+
+}