import eclipse 3.1 M4 compiler
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / Annotation.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.core.compiler.CharOperation;
14 import org.eclipse.jdt.internal.compiler.ASTVisitor;
15 import org.eclipse.jdt.internal.compiler.lookup.*;
16
17 /**
18  * Annotation
19  */
20 public abstract class Annotation extends Expression {
21         
22         public TypeReference type;
23         public int declarationSourceEnd;
24         public Binding recipient;
25         
26         final static MemberValuePair[] NoValuePairs = new MemberValuePair[0];
27         
28         public static long getRetentionPolicy(char[] policyName) {
29                 if (policyName == null || policyName.length == 0)
30                         return 0;
31                 switch(policyName[0]) {
32                         case 'C' :
33                                 if (CharOperation.equals(policyName, TypeConstants.UPPER_CLASS)) 
34                                         return TagBits.AnnotationClassRetention;
35                                 break;
36                         case 'S' :
37                                 if (CharOperation.equals(policyName, TypeConstants.UPPER_SOURCE)) 
38                                         return TagBits.AnnotationSourceRetention;
39                                 break;
40                         case 'R' :
41                                 if (CharOperation.equals(policyName, TypeConstants.UPPER_RUNTIME)) 
42                                         return TagBits.AnnotationRuntimeRetention;
43                                 break;
44                 }
45                 return 0; // unknown
46         }
47         
48         public static long getTargetElementType(char[] elementName) {
49                 if (elementName == null || elementName.length == 0)
50                         return 0;
51                 switch(elementName[0]) {
52                         case 'A' :
53                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_ANNOTATION_TYPE)) 
54                                         return TagBits.AnnotationForAnnotationType;
55                                 break;
56                         case 'C' :
57                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_CONSTRUCTOR)) 
58                                         return TagBits.AnnotationForConstructor;
59                                 break;
60                         case 'F' :
61                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_FIELD)) 
62                                         return TagBits.AnnotationForField;
63                                 break;
64                         case 'L' :
65                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_LOCAL_VARIABLE)) 
66                                         return TagBits.AnnotationForLocalVariable;
67                                 break;
68                         case 'M' :
69                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_METHOD)) 
70                                         return TagBits.AnnotationForMethod;
71                                 break;
72                         case 'P' :
73                                 if (CharOperation.equals(elementName, TypeConstants.UPPER_PARAMETER)) 
74                                         return TagBits.AnnotationForParameter;
75                                 else if (CharOperation.equals(elementName, TypeConstants.UPPER_PACKAGE)) 
76                                         return TagBits.AnnotationForPackage;
77                                 break;
78                         case 'T' :
79                                 if (CharOperation.equals(elementName, TypeConstants.TYPE)) 
80                                         return TagBits.AnnotationForType;
81                                 break;
82                 }
83                 return 0; // unknown
84         }               
85         
86         /**
87          * Compute the bit pattern for recognized standard annotations the compiler may need to act upon
88          */
89         private long detectStandardAnnotation(Scope scope, ReferenceBinding annotationType, MemberValuePair valueAttribute) {
90                 long tagBits = 0;
91                 switch (annotationType.id) {
92                         // retention annotation
93                         case TypeIds.T_JavaLangAnnotationRetention :
94                                 if (valueAttribute != null) {
95                                         Expression expr = valueAttribute.value;
96                                         if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) {
97                                                 FieldBinding field = ((Reference)expr).fieldBinding();
98                                                 if (field != null && field.declaringClass.id == T_JavaLangAnnotationRetentionPolicy) {
99                                                         tagBits |= getRetentionPolicy(field.name);
100                                                 }
101                                         }
102                                 }
103                                 break;
104                         // target annotation
105                         case TypeIds.T_JavaLangAnnotationTarget :               
106                                 tagBits |= TagBits.AnnotationTarget; // target specified (could be empty)
107                                 if (valueAttribute != null) {
108                                         Expression expr = valueAttribute.value;
109                                         if (expr instanceof ArrayInitializer) {
110                                                 ArrayInitializer initializer = (ArrayInitializer) expr;
111                                                 final Expression[] expressions = initializer.expressions;
112                                                 if (expressions != null) {
113                                                         for (int i = 0, length = expressions.length; i < length; i++) {
114                                                                 Expression initExpr = expressions[i];
115                                                                 if ((initExpr.bits & Binding.VARIABLE) == Binding.FIELD) {
116                                                                         FieldBinding field = ((Reference) initExpr).fieldBinding();
117                                                                         if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) {
118                                                                                 long element = getTargetElementType(field.name);
119                                                                                 if ((tagBits & element) != 0) {
120                                                                                         scope.problemReporter().duplicateTargetInTargetAnnotation(annotationType, (NameReference)initExpr);
121                                                                                 } else {
122                                                                                         tagBits |= element;
123                                                                                 }
124                                                                         }                                                       
125                                                                 }
126                                                         }
127                                                 }
128                                         } else if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) {
129                                                 FieldBinding field = ((Reference) expr).fieldBinding();
130                                                 if (field != null && field.declaringClass.id == T_JavaLangAnnotationElementType) {
131                                                         tagBits |= getTargetElementType(field.name);
132                                                 }
133                                         }
134                                 }
135                                 break;
136                         // marker annotations
137                         case TypeIds.T_JavaLangDeprecated :
138                                 tagBits |= TagBits.AnnotationDeprecated;
139                                 break;
140                         case TypeIds.T_JavaLangAnnotationDocumented :
141                                 tagBits |= TagBits.AnnotationDocumented;
142                                 break;
143                         case TypeIds.T_JavaLangAnnotationInherited :
144                                 tagBits |= TagBits.AnnotationInherited;
145                                 break;
146                         case TypeIds.T_JavaLangOverride :
147                                 tagBits |= TagBits.AnnotationOverride;
148                                 break;
149                         case TypeIds.T_JavaLangSuppressWarnings :
150                                 tagBits |= TagBits.AnnotationSuppressWarnings;
151                                 break;
152                 }
153                 return tagBits;
154         }
155         
156         public StringBuffer printExpression(int indent, StringBuffer output) {
157                 output.append('@');
158                 this.type.printExpression(0, output);
159                 return output;
160         }
161         
162         public abstract MemberValuePair[] memberValuePairs();
163         
164         public TypeBinding resolveType(BlockScope scope) {
165                 
166                 this.constant = NotAConstant;
167                 
168                 TypeBinding typeBinding = this.type.resolveType(scope);
169                 if (typeBinding == null)
170                         return null;
171                 this.resolvedType = typeBinding;
172                 // ensure type refers to an annotation type
173                 if (!typeBinding.isAnnotationType()) {
174                         scope.problemReporter().typeMismatchError(typeBinding, scope.getJavaLangAnnotationAnnotation(), this.type);
175                         return null;
176                 }
177
178                 ReferenceBinding annotationType = (ReferenceBinding) this.resolvedType;
179                 MethodBinding[] methods = annotationType.methods();
180                 // clone valuePairs to keep track of unused ones
181                 MemberValuePair[] valuePairs = memberValuePairs();
182                 MemberValuePair valueAttribute = null; // remember the first 'value' pair
183                 MemberValuePair[] usedValuePairs;
184                 int pairsLength = valuePairs.length;
185                 System.arraycopy(valuePairs, 0, usedValuePairs = new MemberValuePair[pairsLength], 0, pairsLength);
186                 
187                 nextMember: for (int i = 0, requiredLength = methods.length; i < requiredLength; i++) {
188                         MethodBinding method = methods[i];
189                         char[] selector = method.selector;
190                         boolean foundValue = false;
191                         nextPair: for (int j = 0; j < pairsLength; j++) {
192                                 MemberValuePair valuePair = usedValuePairs[j];
193                                 if (valuePair == null) continue nextPair;
194                                 char[] memberName = valuePair.name;
195                                 if (CharOperation.equals(memberName, selector)) {
196                                         if (valueAttribute == null && CharOperation.equals(memberName, TypeConstants.VALUE)) {
197                                                 valueAttribute = valuePair;
198                                         }
199                                         valuePair.binding = method;
200                                         usedValuePairs[j] = null; // consumed
201                                         foundValue = true;
202                                         boolean foundDuplicate = false;
203                                         for (int k = j+1; k < pairsLength; k++) {
204                                                 if (CharOperation.equals(usedValuePairs[k].name, selector)) {
205                                                         foundDuplicate = true;
206                                                         scope.problemReporter().duplicateAnnotationValue(annotationType, usedValuePairs[k]);
207                                                         usedValuePairs[k].binding = method;
208                                                         usedValuePairs[k] = null;
209                                                 }
210                                         }
211                                         if (foundDuplicate) {
212                                                 scope.problemReporter().duplicateAnnotationValue(annotationType, valuePair);
213                                                 continue nextMember;
214                                         }
215                                         valuePair.resolveTypeExpecting(scope, method.returnType);
216                                 }
217                         }
218                         if (!foundValue && (method.modifiers & AccAnnotationDefault) == 0) {
219                                 scope.problemReporter().missingValueForAnnotationMember(this, method.selector);
220                         }
221                 }
222                 // check unused pairs
223                 for (int i = 0; i < pairsLength; i++) {
224                         if (usedValuePairs[i] != null) {
225                                 scope.problemReporter().undefinedAnnotationValue(annotationType, usedValuePairs[i]);
226                         }
227                 }
228                 // recognize standard annotations ?
229                 long tagBits = detectStandardAnnotation(scope, annotationType, valueAttribute);
230                 if (this.recipient != null) {
231                         if (tagBits != 0) {
232                                 // tag bits onto recipient
233                                 switch (this.recipient.kind()) {
234                                         case Binding.PACKAGE :
235                                                 // TODO (philippe) need support for package annotations
236                                                 break;
237                                         case Binding.TYPE :
238                                         case Binding.GENERIC_TYPE :
239                                         case Binding.TYPE_PARAMETER :
240                                                 ((ReferenceBinding)this.recipient).tagBits |= tagBits;
241                                                 break;
242                                         case Binding.METHOD :
243                                                 ((MethodBinding)this.recipient).tagBits |= tagBits;
244                                                 break;
245                                         case Binding.FIELD :
246                                                 ((FieldBinding)this.recipient).tagBits |= tagBits;
247                                                 break;
248                                         case Binding.LOCAL :
249                                                 ((LocalVariableBinding)this.recipient).tagBits |= tagBits;
250                                                 break;
251                                 }                       
252                         }
253                         // check (meta)target compatibility
254                         checkTargetCompatibility: {
255                                 long metaTagBits = annotationType.getAnnotationTagBits();
256                                 if ((metaTagBits & TagBits.AnnotationTargetMASK) == 0) // does not specify any target restriction
257                                         break checkTargetCompatibility;
258                                         
259                                 switch (recipient.kind()) {
260                                         case Binding.PACKAGE :
261                                                 if ((metaTagBits & TagBits.AnnotationForPackage) != 0)
262                                                         break checkTargetCompatibility;
263                                                 break;
264                                         case Binding.TYPE :
265                                         case Binding.GENERIC_TYPE :
266                                                 if (((ReferenceBinding)this.recipient).isAnnotationType()) {
267                                                         if ((metaTagBits & (TagBits.AnnotationForAnnotationType|TagBits.AnnotationForType)) != 0)
268                                                         break checkTargetCompatibility;
269                                                 } else if ((metaTagBits & TagBits.AnnotationForType) != 0) 
270                                                         break checkTargetCompatibility;
271                                                 break;
272                                         case Binding.METHOD :
273                                                 if (((MethodBinding)this.recipient).isConstructor()) {
274                                                         if ((metaTagBits & TagBits.AnnotationForConstructor) != 0)
275                                                                 break checkTargetCompatibility;
276                                                 } else  if ((metaTagBits & TagBits.AnnotationForMethod) != 0)
277                                                         break checkTargetCompatibility;
278                                                 break;
279                                         case Binding.FIELD :
280                                                 if ((metaTagBits & TagBits.AnnotationForField) != 0)
281                                                         break checkTargetCompatibility;
282                                                 break;
283                                         case Binding.LOCAL :
284                                                 if (((LocalVariableBinding)this.recipient).isArgument) {
285                                                         if ((metaTagBits & TagBits.AnnotationForParameter) != 0)
286                                                                 break checkTargetCompatibility;
287                                                 } else  if ((annotationType.tagBits & TagBits.AnnotationForLocalVariable) != 0)
288                                                         break checkTargetCompatibility;
289                                                 break;
290                                 }                       
291                                 scope.problemReporter().disallowedTargetForAnnotation(this);
292                         }
293                 }
294                 return this.resolvedType;
295         }
296         
297         public abstract void traverse(ASTVisitor visitor, BlockScope scope);
298         public abstract void traverse(ASTVisitor visitor, CompilationUnitScope scope);
299 }