1 /*******************************************************************************
2 * Copyright (c) 2000, 2008 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
13 import net.sourceforge.phpdt.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
15 import net.sourceforge.phpdt.internal.compiler.classfmt.ClassFileConstants;
16 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
17 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
18 import net.sourceforge.phpdt.internal.compiler.lookup.*;
23 public abstract class Annotation extends Expression {
25 final static MemberValuePair[] NoValuePairs = new MemberValuePair[0];
26 public int declarationSourceEnd;
27 public Binding recipient;
29 public TypeReference type;
31 * The representation of this annotation in the type system.
33 private AnnotationBinding compilerAnnotation = null;
35 public static long getRetentionPolicy(char[] policyName) {
36 if (policyName == null || policyName.length == 0)
38 switch(policyName[0]) {
40 if (CharOperation.equals(policyName, TypeConstants.UPPER_CLASS))
41 return TagBits.AnnotationClassRetention;
44 if (CharOperation.equals(policyName, TypeConstants.UPPER_SOURCE))
45 return TagBits.AnnotationSourceRetention;
48 if (CharOperation.equals(policyName, TypeConstants.UPPER_RUNTIME))
49 return TagBits.AnnotationRuntimeRetention;
55 public static long getTargetElementType(char[] elementName) {
56 if (elementName == null || elementName.length == 0)
58 switch(elementName[0]) {
60 if (CharOperation.equals(elementName, TypeConstants.UPPER_ANNOTATION_TYPE))
61 return TagBits.AnnotationForAnnotationType;
64 if (CharOperation.equals(elementName, TypeConstants.UPPER_CONSTRUCTOR))
65 return TagBits.AnnotationForConstructor;
68 if (CharOperation.equals(elementName, TypeConstants.UPPER_FIELD))
69 return TagBits.AnnotationForField;
72 if (CharOperation.equals(elementName, TypeConstants.UPPER_LOCAL_VARIABLE))
73 return TagBits.AnnotationForLocalVariable;
76 if (CharOperation.equals(elementName, TypeConstants.UPPER_METHOD))
77 return TagBits.AnnotationForMethod;
80 if (CharOperation.equals(elementName, TypeConstants.UPPER_PARAMETER))
81 return TagBits.AnnotationForParameter;
82 else if (CharOperation.equals(elementName, TypeConstants.UPPER_PACKAGE))
83 return TagBits.AnnotationForPackage;
86 if (CharOperation.equals(elementName, TypeConstants.TYPE))
87 return TagBits.AnnotationForType;
93 public ElementValuePair[] computeElementValuePairs() {
94 return Binding.NO_ELEMENT_VALUE_PAIRS;
98 * Compute the bit pattern for recognized standard annotations the compiler may need to act upon
100 private long detectStandardAnnotation(Scope scope, ReferenceBinding annotationType, MemberValuePair valueAttribute) {
102 switch (annotationType.id) {
103 // retention annotation
104 case TypeIds.T_JavaLangAnnotationRetention :
105 if (valueAttribute != null) {
106 Expression expr = valueAttribute.value;
107 if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) {
108 FieldBinding field = ((Reference)expr).fieldBinding();
109 if (field != null && field.declaringClass.id == T_JavaLangAnnotationRetentionPolicy) {
110 tagBits |= getRetentionPolicy(field.name);
116 case TypeIds.T_JavaLangAnnotationTarget :
117 tagBits |= TagBits.AnnotationTarget; // target specified (could be empty)
118 if (valueAttribute != null) {
119 Expression expr = valueAttribute.value;
120 if (expr instanceof ArrayInitializer) {
121 ArrayInitializer initializer = (ArrayInitializer) expr;
122 final Expression[] expressions = initializer.expressions;
123 if (expressions != null) {
124 for (int i = 0, length = expressions.length; i < length; i++) {
125 Expression initExpr = expressions[i];
126 if ((initExpr.bits & Binding.VARIABLE) == Binding.FIELD) {
127 FieldBinding field = ((Reference) initExpr).fieldBinding();
128 if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) {
129 long element = getTargetElementType(field.name);
130 if ((tagBits & element) != 0) {
131 scope.problemReporter().duplicateTargetInTargetAnnotation(annotationType, (NameReference)initExpr);
139 } else if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) {
140 FieldBinding field = ((Reference) expr).fieldBinding();
141 if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) {
142 tagBits |= getTargetElementType(field.name);
147 // marker annotations
148 case TypeIds.T_JavaLangDeprecated :
149 tagBits |= TagBits.AnnotationDeprecated;
151 case TypeIds.T_JavaLangAnnotationDocumented :
152 tagBits |= TagBits.AnnotationDocumented;
154 case TypeIds.T_JavaLangAnnotationInherited :
155 tagBits |= TagBits.AnnotationInherited;
157 case TypeIds.T_JavaLangOverride :
158 tagBits |= TagBits.AnnotationOverride;
160 case TypeIds.T_JavaLangSuppressWarnings :
161 tagBits |= TagBits.AnnotationSuppressWarnings;
167 public AnnotationBinding getCompilerAnnotation() {
168 return this.compilerAnnotation;
171 public abstract MemberValuePair[] memberValuePairs();
173 public StringBuffer printExpression(int indent, StringBuffer output) {
175 this.type.printExpression(0, output);
179 public void recordSuppressWarnings(Scope scope, int startSuppresss, int endSuppress, boolean isSuppressingWarnings) {
180 long suppressWarningIrritants = 0;
181 MemberValuePair[] pairs = this.memberValuePairs();
182 pairLoop: for (int i = 0, length = pairs.length; i < length; i++) {
183 MemberValuePair pair = pairs[i];
184 if (CharOperation.equals(pair.name, TypeConstants.VALUE)) {
185 Expression value = pair.value;
186 if (value instanceof ArrayInitializer) {
187 ArrayInitializer initializer = (ArrayInitializer) value;
188 Expression[] inits = initializer.expressions;
190 for (int j = 0, initsLength = inits.length; j < initsLength; j++) {
191 Constant cst = inits[j].constant;
192 if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) {
193 long irritants = CompilerOptions.warningTokenToIrritants(cst.stringValue());
194 if (irritants != 0) {
195 if ((suppressWarningIrritants & irritants) == irritants) {
196 scope.problemReporter().unusedWarningToken(inits[j]);
198 suppressWarningIrritants |= irritants;
201 scope.problemReporter().unhandledWarningToken(inits[j]);
207 Constant cst = value.constant;
208 if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) {
209 long irritants = CompilerOptions.warningTokenToIrritants(cst.stringValue());
210 if (irritants != 0) {
211 suppressWarningIrritants |= irritants;
213 scope.problemReporter().unhandledWarningToken(value);
220 if (isSuppressingWarnings && suppressWarningIrritants != 0) {
221 scope.referenceCompilationUnit().recordSuppressWarnings(suppressWarningIrritants, this, startSuppresss, endSuppress);
225 public TypeBinding resolveType(BlockScope scope) {
227 if (this.compilerAnnotation != null)
228 return this.resolvedType;
229 this.constant = Constant.NotAConstant;
231 TypeBinding typeBinding = this.type.resolveType(scope);
232 if (typeBinding == null) {
235 this.resolvedType = typeBinding;
236 // ensure type refers to an annotation type
237 if (!typeBinding.isAnnotationType() && typeBinding.isValidBinding()) {
238 scope.problemReporter().typeMismatchError(typeBinding, scope.getJavaLangAnnotationAnnotation(), this.type, null);
242 ReferenceBinding annotationType = (ReferenceBinding) this.resolvedType;
243 MethodBinding[] methods = annotationType.methods();
244 // clone valuePairs to keep track of unused ones
245 MemberValuePair[] originalValuePairs = memberValuePairs();
246 MemberValuePair valueAttribute = null; // remember the first 'value' pair
247 MemberValuePair[] pairs;
248 int pairsLength = originalValuePairs.length;
249 if (pairsLength > 0) {
250 System.arraycopy(originalValuePairs, 0, pairs = new MemberValuePair[pairsLength], 0, pairsLength);
252 pairs = originalValuePairs;
255 nextMember: for (int i = 0, requiredLength = methods.length; i < requiredLength; i++) {
256 MethodBinding method = methods[i];
257 char[] selector = method.selector;
258 boolean foundValue = false;
259 nextPair: for (int j = 0; j < pairsLength; j++) {
260 MemberValuePair pair = pairs[j];
261 if (pair == null) continue nextPair;
262 char[] name = pair.name;
263 if (CharOperation.equals(name, selector)) {
264 if (valueAttribute == null && CharOperation.equals(name, TypeConstants.VALUE)) {
265 valueAttribute = pair;
267 pair.binding = method;
268 pair.resolveTypeExpecting(scope, method.returnType);
269 pairs[j] = null; // consumed
273 boolean foundDuplicate = false;
274 for (int k = j+1; k < pairsLength; k++) {
275 MemberValuePair otherPair = pairs[k];
276 if (otherPair == null) continue;
277 if (CharOperation.equals(otherPair.name, selector)) {
278 foundDuplicate = true;
279 scope.problemReporter().duplicateAnnotationValue(annotationType, otherPair);
280 otherPair.binding = method;
281 otherPair.resolveTypeExpecting(scope, method.returnType);
285 if (foundDuplicate) {
286 scope.problemReporter().duplicateAnnotationValue(annotationType, pair);
292 (method.modifiers & ClassFileConstants.AccAnnotationDefault) == 0 &&
293 (this.bits & IsRecovered) == 0) {
294 scope.problemReporter().missingValueForAnnotationMember(this, selector);
297 // check unused pairs
298 for (int i = 0; i < pairsLength; i++) {
299 if (pairs[i] != null) {
300 scope.problemReporter().undefinedAnnotationValue(annotationType, pairs[i]);
301 pairs[i].resolveTypeExpecting(scope, null); // resilient
304 // if (scope.compilerOptions().storeAnnotations)
305 this.compilerAnnotation = scope.environment().createAnnotation((ReferenceBinding) this.resolvedType, this.computeElementValuePairs());
306 // recognize standard annotations ?
307 long tagBits = detectStandardAnnotation(scope, annotationType, valueAttribute);
309 // record annotation positions in the compilation result
310 scope.referenceCompilationUnit().recordSuppressWarnings(CompilerOptions.NonExternalizedString, null, this.sourceStart, this.declarationSourceEnd);
311 if (this.recipient != null) {
313 // tag bits onto recipient
314 switch (this.recipient.kind()) {
315 case Binding.PACKAGE :
316 ((PackageBinding)this.recipient).tagBits |= tagBits;
319 case Binding.GENERIC_TYPE :
320 SourceTypeBinding sourceType = (SourceTypeBinding) this.recipient;
321 sourceType.tagBits |= tagBits;
322 if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
323 TypeDeclaration typeDeclaration = sourceType.scope.referenceContext;
325 if (scope.referenceCompilationUnit().types[0] == typeDeclaration) {
328 start = typeDeclaration.declarationSourceStart;
330 recordSuppressWarnings(scope, start, typeDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
333 case Binding.METHOD :
334 MethodBinding sourceMethod = (MethodBinding) this.recipient;
335 sourceMethod.tagBits |= tagBits;
336 if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
337 sourceType = (SourceTypeBinding) sourceMethod.declaringClass;
338 AbstractMethodDeclaration methodDeclaration = sourceType.scope.referenceContext.declarationOf(sourceMethod);
339 recordSuppressWarnings(scope, methodDeclaration.declarationSourceStart, methodDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
343 FieldBinding sourceField = (FieldBinding) this.recipient;
344 sourceField.tagBits |= tagBits;
345 if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
346 sourceType = (SourceTypeBinding) sourceField.declaringClass;
347 FieldDeclaration fieldDeclaration = sourceType.scope.referenceContext.declarationOf(sourceField);
348 recordSuppressWarnings(scope, fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
352 LocalVariableBinding variable = (LocalVariableBinding) this.recipient;
353 variable.tagBits |= tagBits;
354 if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
355 LocalDeclaration localDeclaration = variable.declaration;
356 recordSuppressWarnings(scope, localDeclaration.declarationSourceStart, localDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
361 // check (meta)target compatibility
362 checkTargetCompatibility: {
363 long metaTagBits = annotationType.getAnnotationTagBits(); // could be forward reference
364 if ((metaTagBits & TagBits.AnnotationTargetMASK) == 0) // does not specify any target restriction
365 break checkTargetCompatibility;
367 switch (recipient.kind()) {
368 case Binding.PACKAGE :
369 if ((metaTagBits & TagBits.AnnotationForPackage) != 0)
370 break checkTargetCompatibility;
373 case Binding.GENERIC_TYPE :
374 if (((ReferenceBinding)this.recipient).isAnnotationType()) {
375 if ((metaTagBits & (TagBits.AnnotationForAnnotationType|TagBits.AnnotationForType)) != 0)
376 break checkTargetCompatibility;
377 } else if ((metaTagBits & TagBits.AnnotationForType) != 0)
378 break checkTargetCompatibility;
380 case Binding.METHOD :
381 if (((MethodBinding)this.recipient).isConstructor()) {
382 if ((metaTagBits & TagBits.AnnotationForConstructor) != 0)
383 break checkTargetCompatibility;
384 } else if ((metaTagBits & TagBits.AnnotationForMethod) != 0)
385 break checkTargetCompatibility;
388 if ((metaTagBits & TagBits.AnnotationForField) != 0)
389 break checkTargetCompatibility;
392 if ((((LocalVariableBinding)this.recipient).tagBits & TagBits.IsArgument) != 0) {
393 if ((metaTagBits & TagBits.AnnotationForParameter) != 0)
394 break checkTargetCompatibility;
395 } else if ((annotationType.tagBits & TagBits.AnnotationForLocalVariable) != 0)
396 break checkTargetCompatibility;
399 scope.problemReporter().disallowedTargetForAnnotation(this);
402 return this.resolvedType;
405 public abstract void traverse(ASTVisitor visitor, BlockScope scope);