Refactory: xdebug.ui plugin.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / Annotation.java
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
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
12
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.*;
19
20 /**
21  * Annotation
22  */
23 public abstract class Annotation extends Expression {
24         
25         final static MemberValuePair[] NoValuePairs = new MemberValuePair[0];
26         public int declarationSourceEnd;
27         public Binding recipient;
28         
29         public TypeReference type;
30         /** 
31          *  The representation of this annotation in the type system. 
32          */
33         private AnnotationBinding compilerAnnotation = null;
34         
35         public static long getRetentionPolicy(char[] policyName) {
36                 if (policyName == null || policyName.length == 0)
37                         return 0;
38                 switch(policyName[0]) {
39                         case 'C' :
40                                 if (CharOperation.equals(policyName, TypeConstants.UPPER_CLASS)) 
41                                         return TagBits.AnnotationClassRetention;
42                                 break;
43                         case 'S' :
44                                 if (CharOperation.equals(policyName, TypeConstants.UPPER_SOURCE)) 
45                                         return TagBits.AnnotationSourceRetention;
46                                 break;
47                         case 'R' :
48                                 if (CharOperation.equals(policyName, TypeConstants.UPPER_RUNTIME)) 
49                                         return TagBits.AnnotationRuntimeRetention;
50                                 break;
51                 }
52                 return 0; // unknown
53         }
54         
55         public static long getTargetElementType(char[] elementName) {
56                 if (elementName == null || elementName.length == 0)
57                         return 0;
58                 switch(elementName[0]) {
59                         case 'A' :
60                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_ANNOTATION_TYPE)) 
61                                         return TagBits.AnnotationForAnnotationType;
62                                 break;
63                         case 'C' :
64                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_CONSTRUCTOR)) 
65                                         return TagBits.AnnotationForConstructor;
66                                 break;
67                         case 'F' :
68                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_FIELD)) 
69                                         return TagBits.AnnotationForField;
70                                 break;
71                         case 'L' :
72                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_LOCAL_VARIABLE)) 
73                                         return TagBits.AnnotationForLocalVariable;
74                                 break;
75                         case 'M' :
76                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_METHOD)) 
77                                         return TagBits.AnnotationForMethod;
78                                 break;
79                         case 'P' :
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;
84                                 break;
85                         case 'T' :
86                                 if (CharOperation.equals(elementName, TypeConstants.TYPE)) 
87                                         return TagBits.AnnotationForType;
88                                 break;
89                 }
90                 return 0; // unknown
91         }               
92
93         public ElementValuePair[] computeElementValuePairs() {
94                 return Binding.NO_ELEMENT_VALUE_PAIRS;
95         }
96
97         /**
98          * Compute the bit pattern for recognized standard annotations the compiler may need to act upon
99          */
100         private long detectStandardAnnotation(Scope scope, ReferenceBinding annotationType, MemberValuePair valueAttribute) {
101                 long tagBits = 0;
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);
111                                                 }
112                                         }
113                                 }
114                                 break;
115                         // target annotation
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);
132                                                                                 } else {
133                                                                                         tagBits |= element;
134                                                                                 }
135                                                                         }                                                       
136                                                                 }
137                                                         }
138                                                 }
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);
143                                                 }
144                                         }
145                                 }
146                                 break;
147                         // marker annotations
148                         case TypeIds.T_JavaLangDeprecated :
149                                 tagBits |= TagBits.AnnotationDeprecated;
150                                 break;
151                         case TypeIds.T_JavaLangAnnotationDocumented :
152                                 tagBits |= TagBits.AnnotationDocumented;
153                                 break;
154                         case TypeIds.T_JavaLangAnnotationInherited :
155                                 tagBits |= TagBits.AnnotationInherited;
156                                 break;
157                         case TypeIds.T_JavaLangOverride :
158                                 tagBits |= TagBits.AnnotationOverride;
159                                 break;
160                         case TypeIds.T_JavaLangSuppressWarnings :
161                                 tagBits |= TagBits.AnnotationSuppressWarnings;
162                                 break;
163                 }
164                 return tagBits;
165         }
166
167         public AnnotationBinding getCompilerAnnotation() {
168                 return this.compilerAnnotation;
169         }
170
171         public abstract MemberValuePair[] memberValuePairs();
172         
173         public StringBuffer printExpression(int indent, StringBuffer output) {
174                 output.append('@');
175                 this.type.printExpression(0, output);
176                 return output;
177         }
178         
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;
189                                         if (inits != null) {
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]);
197                                                                         } else {
198                                                                                 suppressWarningIrritants |= irritants;
199                                                                         }
200                                                                 } else {
201                                                                         scope.problemReporter().unhandledWarningToken(inits[j]);
202                                                                 }
203                                                         }
204                                                 }
205                                         }
206                                 } else {
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;
212                                                 } else {
213                                                         scope.problemReporter().unhandledWarningToken(value);
214                                                 }
215                                         }       
216                                 }
217                                 break pairLoop;
218                         }
219                 }
220                 if (isSuppressingWarnings && suppressWarningIrritants != 0) {
221                         scope.referenceCompilationUnit().recordSuppressWarnings(suppressWarningIrritants, this, startSuppresss, endSuppress);
222                 }
223         }
224         
225         public TypeBinding resolveType(BlockScope scope) {
226
227                 if (this.compilerAnnotation != null)
228                         return this.resolvedType;
229                 this.constant = Constant.NotAConstant;
230                 
231                 TypeBinding typeBinding = this.type.resolveType(scope);
232                 if (typeBinding == null) {
233                         return null;
234                 }
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);
239                         return null;
240                 }
241
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);
251                 } else {
252                         pairs = originalValuePairs;
253                 }               
254                 
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;
266                                         }
267                                         pair.binding = method;
268                                         pair.resolveTypeExpecting(scope, method.returnType);
269                                         pairs[j] = null; // consumed
270                                         foundValue = true;
271                                         
272                                         // check duplicates
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);
282                                                         pairs[k] = null;
283                                                 }
284                                         }
285                                         if (foundDuplicate) {
286                                                 scope.problemReporter().duplicateAnnotationValue(annotationType, pair);
287                                                 continue nextMember;
288                                         }
289                                 }
290                         }
291                         if (!foundValue &&
292                                         (method.modifiers & ClassFileConstants.AccAnnotationDefault) == 0 &&
293                                         (this.bits & IsRecovered) == 0) {
294                                 scope.problemReporter().missingValueForAnnotationMember(this, selector);
295                         }
296                 }
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
302                         }
303                 }
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);
308
309                 // record annotation positions in the compilation result
310                 scope.referenceCompilationUnit().recordSuppressWarnings(CompilerOptions.NonExternalizedString, null, this.sourceStart, this.declarationSourceEnd);
311                 if (this.recipient != null) {
312                         if (tagBits != 0) {
313                                 // tag bits onto recipient
314                                 switch (this.recipient.kind()) {
315                                         case Binding.PACKAGE :
316                                                 ((PackageBinding)this.recipient).tagBits |= tagBits;
317                                                 break;
318                                         case Binding.TYPE :
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;
324                                                         int start;
325                                                         if (scope.referenceCompilationUnit().types[0] == typeDeclaration) {
326                                                                 start = 0;
327                                                         } else {
328                                                                 start = typeDeclaration.declarationSourceStart;
329                                                         }
330                                                         recordSuppressWarnings(scope, start, typeDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
331                                                 }
332                                                 break;
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);
340                                                 }
341                                                 break;
342                                         case Binding.FIELD :
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);
349                                                 }                                               
350                                                 break;
351                                         case Binding.LOCAL :
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);
357                                                 }                                                                       
358                                                 break;
359                                 }                       
360                         }
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;
366                                         
367                                 switch (recipient.kind()) {
368                                         case Binding.PACKAGE :
369                                                 if ((metaTagBits & TagBits.AnnotationForPackage) != 0)
370                                                         break checkTargetCompatibility;
371                                                 break;
372                                         case Binding.TYPE :
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;
379                                                 break;
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;
386                                                 break;
387                                         case Binding.FIELD :
388                                                 if ((metaTagBits & TagBits.AnnotationForField) != 0)
389                                                         break checkTargetCompatibility;
390                                                 break;
391                                         case Binding.LOCAL :
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;
397                                                 break;
398                                 }                       
399                                 scope.problemReporter().disallowedTargetForAnnotation(this);
400                         }
401                 }
402                 return this.resolvedType;
403         }
404         
405         public abstract void traverse(ASTVisitor visitor, BlockScope scope);
406         
407 }