Refactory: phphelp plugin.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / MemberValuePair.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.internal.compiler.ASTVisitor;
14 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
15 import net.sourceforge.phpdt.internal.compiler.lookup.BaseTypeBinding;
16 import net.sourceforge.phpdt.internal.compiler.lookup.Binding;
17 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
18 import net.sourceforge.phpdt.internal.compiler.lookup.ElementValuePair;
19 import net.sourceforge.phpdt.internal.compiler.lookup.FieldBinding;
20 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
21 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
22
23 /**
24  * MemberValuePair node
25  */
26 public class MemberValuePair extends ASTNode {
27         
28         public char[] name;
29         public Expression value;
30         public MethodBinding binding;
31         /** 
32          *  The representation of this pair in the type system. 
33          */
34         public ElementValuePair compilerElementPair = null;
35         
36         public MemberValuePair(char[] token, int sourceStart, int sourceEnd, Expression value) {
37                 this.name = token;
38                 this.sourceStart = sourceStart;
39                 this.sourceEnd = sourceEnd;
40                 this.value = value;
41                 if (value instanceof ArrayInitializer) {
42                         value.bits |= IsAnnotationDefaultValue;
43                 }
44         }
45         
46         /* (non-Javadoc)
47          * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer)
48          */
49         public StringBuffer print(int indent, StringBuffer output) {
50                 output
51                         .append(name)
52                         .append(" = "); //$NON-NLS-1$
53                 value.print(0, output);
54                 return output;
55         }
56         
57         public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) {
58                 
59                 if (this.value == null) {
60                         this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding);
61                         return;
62                 }
63                 if (requiredType == null) {
64                         // fault tolerance: keep resolving
65                         if (this.value instanceof ArrayInitializer) {
66                                 this.value.resolveTypeExpecting(scope, null);
67                         } else {
68                                 this.value.resolveType(scope);
69                         }
70                         this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding);
71                         return;
72                 }
73
74                 this.value.setExpectedType(requiredType); // needed in case of generic method invocation
75                 TypeBinding valueType;
76                 if (this.value instanceof ArrayInitializer) {
77                         ArrayInitializer initializer = (ArrayInitializer) this.value;
78                         valueType = initializer.resolveTypeExpecting(scope, this.binding.returnType);
79                 } else if (this.value instanceof ArrayAllocationExpression) {
80                         scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value);
81                         this.value.resolveType(scope);
82                         valueType = null; // no need to pursue
83                 } else {
84                         valueType = this.value.resolveType(scope);
85                 }
86                 this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding);
87                 if (valueType == null)
88                         return;
89
90                 TypeBinding leafType = requiredType.leafComponentType();
91                 if (!((this.value.isConstantValueOfTypeAssignableToType(valueType, requiredType)
92                                 || (requiredType.isBaseType() && BaseTypeBinding.isWidening(requiredType.id, valueType.id)))
93                                 || valueType.isCompatibleWith(requiredType))) {
94
95                         if (!(requiredType.isArrayType() 
96                                         && requiredType.dimensions() == 1 
97                                         && (this.value.isConstantValueOfTypeAssignableToType(valueType, leafType)
98                                                         || (leafType.isBaseType() && BaseTypeBinding.isWidening(leafType.id, valueType.id)))
99                                                         || valueType.isCompatibleWith(leafType))) {
100                                 
101                                 if (leafType.isAnnotationType() && !valueType.isAnnotationType()) {
102                                         scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType);                          
103                                 } else {
104                                         scope.problemReporter().typeMismatchError(valueType, requiredType, this.value, null);
105                                 }
106                                 return; // may allow to proceed to find more errors at once
107                         }
108                 } else {
109                         scope.compilationUnitScope().recordTypeConversion(requiredType.leafComponentType(), valueType.leafComponentType());
110                         this.value.computeConversion(scope, requiredType, valueType);                           
111                 }
112                 
113                 // annotation methods can only return base types, String, Class, enum type, annotation types and arrays of these
114                 checkAnnotationMethodType: {
115                         switch (leafType.erasure().id) {
116                                 case T_byte :
117                                 case T_short :
118                                 case T_char :
119                                 case T_int :
120                                 case T_long :
121                                 case T_float :
122                                 case T_double :
123                                 case T_boolean :
124                                 case T_JavaLangString :
125                                         if (this.value instanceof ArrayInitializer) {
126                                                 ArrayInitializer initializer = (ArrayInitializer) this.value;
127                                                 final Expression[] expressions = initializer.expressions;
128                                                 if (expressions != null) {
129                                                         for (int i =0, max = expressions.length; i < max; i++) {
130                                                                 Expression expression = expressions[i];
131                                                                 if (expression.resolvedType == null) continue; // fault-tolerance
132                                                                 if (expression.constant == Constant.NotAConstant) {
133                                                                         scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, expressions[i], false);
134                                                                 }
135                                                         }
136                                                 }
137                                         } else if (this.value.constant == Constant.NotAConstant) {
138                                                 if (valueType.isArrayType()) {
139                                                         scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value);
140                                                 } else {
141                                                         scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, false);
142                                                 }
143                                         }
144                                         break checkAnnotationMethodType;
145                                 case T_JavaLangClass :
146                                         if (this.value instanceof ArrayInitializer) {
147                                                 ArrayInitializer initializer = (ArrayInitializer) this.value;
148                                                 final Expression[] expressions = initializer.expressions;
149                                                 if (expressions != null) {
150                                                         for (int i =0, max = expressions.length; i < max; i++) {
151                                                                 Expression currentExpression = expressions[i];
152                                                                 if (!(currentExpression instanceof ClassLiteralAccess)) {
153                                                                         scope.problemReporter().annotationValueMustBeClassLiteral(this.binding.declaringClass, this.name, currentExpression);
154                                                                 }
155                                                         }
156                                                 }
157                                         } else if (!(this.value instanceof ClassLiteralAccess)) {
158                                                 scope.problemReporter().annotationValueMustBeClassLiteral(this.binding.declaringClass, this.name, this.value);
159                                         }
160                                         break checkAnnotationMethodType;
161                         }
162                         if (leafType.isEnum()) {
163                                 if (this.value instanceof NullLiteral) {
164                                         scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, true);
165                                 } else if (this.value instanceof ArrayInitializer) {
166                                         ArrayInitializer initializer = (ArrayInitializer) this.value;
167                                         final Expression[] expressions = initializer.expressions;
168                                         if (expressions != null) {
169                                                 for (int i =0, max = expressions.length; i < max; i++) {
170                                                         Expression currentExpression = expressions[i];
171                                                         if (currentExpression instanceof NullLiteral) {
172                                                                 scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, currentExpression, true);
173                                                         } else if (currentExpression instanceof NameReference) {
174                                                                 NameReference nameReference = (NameReference) currentExpression;
175                                                                 final Binding nameReferenceBinding = nameReference.binding;
176                                                                 if (nameReferenceBinding.kind() == Binding.FIELD) {
177                                                                         FieldBinding fieldBinding = (FieldBinding) nameReferenceBinding;
178                                                                         if (!fieldBinding.declaringClass.isEnum()) {
179                                                                                 scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, currentExpression, true);
180                                                                         }
181                                                                 }
182                                                         }
183                                                 }
184                                         }
185                                 } else if (this.value instanceof NameReference) {
186                                         NameReference nameReference = (NameReference) this.value;
187                                         final Binding nameReferenceBinding = nameReference.binding;
188                                         if (nameReferenceBinding.kind() == Binding.FIELD) {
189                                                 FieldBinding fieldBinding = (FieldBinding) nameReferenceBinding;
190                                                 if (!fieldBinding.declaringClass.isEnum()) {
191                                                         if (!fieldBinding.type.isArrayType()) {
192                                                                 scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, true);
193                                                         } else {
194                                                                 scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value);
195                                                         }
196                                                 }
197                                         }
198                                 }
199                                 break checkAnnotationMethodType;
200                         }
201                         if (leafType.isAnnotationType()) {
202                                 if (!valueType.leafComponentType().isAnnotationType()) { // check annotation type and also reject null literal
203                                         scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType);
204                                 } else if (this.value instanceof ArrayInitializer) {
205                                         ArrayInitializer initializer = (ArrayInitializer) this.value;
206                                         final Expression[] expressions = initializer.expressions;
207                                         if (expressions != null) {
208                                                 for (int i =0, max = expressions.length; i < max; i++) {
209                                                         Expression currentExpression = expressions[i];
210                                                         if (currentExpression instanceof NullLiteral || !(currentExpression instanceof Annotation)) {
211                                                                 scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, currentExpression, leafType);
212                                                         }
213                                                 }
214                                         }
215                                 } else if (!(this.value instanceof Annotation)) {
216                                         scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType);
217                                 }
218                                 break checkAnnotationMethodType;
219                         }
220                 }
221         }
222         
223         public void traverse(ASTVisitor visitor, BlockScope scope) {
224                 if (visitor.visit(this, scope)) {
225                         if (this.value != null) {
226                                 this.value.traverse(visitor, scope);
227                         }
228                 }
229                 visitor.endVisit(this, scope);
230         }
231 }