Basic Reafctoring functionality adapted from Leif Frenzels sources in eclipse-magazin...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / QualifiedAllocationExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
14 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
16 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
17 import net.sourceforge.phpdt.internal.compiler.lookup.LocalTypeBinding;
18 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
19 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
20 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
21
22 /**
23  * Variation on allocation, where can be specified an enclosing instance and an anonymous type
24  */
25 public class QualifiedAllocationExpression extends AllocationExpression {
26         
27         //qualification may be on both side
28         public Expression enclosingInstance;
29         public AnonymousLocalTypeDeclaration anonymousType;
30         public ReferenceBinding superTypeBinding;
31         
32         public QualifiedAllocationExpression() {
33         }
34
35         public QualifiedAllocationExpression(AnonymousLocalTypeDeclaration anonymousType) {
36                 this.anonymousType = anonymousType;
37         }
38
39         public FlowInfo analyseCode(
40                 BlockScope currentScope,
41                 FlowContext flowContext,
42                 FlowInfo flowInfo) {
43
44                 // analyse the enclosing instance
45                 if (enclosingInstance != null) {
46                         flowInfo = enclosingInstance.analyseCode(currentScope, flowContext, flowInfo);
47                 }
48                 
49                 // check captured variables are initialized in current context (26134)
50                 checkCapturedLocalInitializationIfNecessary(
51                         this.superTypeBinding == null ? this.binding.declaringClass : this.superTypeBinding, 
52                         currentScope, 
53                         flowInfo);
54                 
55                 // process arguments
56                 if (arguments != null) {
57                         for (int i = 0, count = arguments.length; i < count; i++) {
58                                 flowInfo = arguments[i].analyseCode(currentScope, flowContext, flowInfo);
59                         }
60                 }
61
62                 // analyse the anonymous nested type
63                 if (anonymousType != null) {
64                         flowInfo = anonymousType.analyseCode(currentScope, flowContext, flowInfo);
65                 }
66
67                 // record some dependency information for exception types
68                 ReferenceBinding[] thrownExceptions;
69                 if (((thrownExceptions = binding.thrownExceptions).length) != 0) {
70                         // check exception handling
71                         flowContext.checkExceptionHandlers(
72                                 thrownExceptions,
73                                 this,
74                                 flowInfo,
75                                 currentScope);
76                 }
77                 manageEnclosingInstanceAccessIfNecessary(currentScope);
78                 manageSyntheticAccessIfNecessary(currentScope);
79                 return flowInfo;
80         }
81
82         public Expression enclosingInstance() {
83
84                 return enclosingInstance;
85         }
86
87 //      public void generateCode(
88 //              BlockScope currentScope,
89 //              CodeStream codeStream,
90 //              boolean valueRequired) {
91 //
92 //              int pc = codeStream.position;
93 //              ReferenceBinding allocatedType = binding.declaringClass;
94 //              codeStream.new_(allocatedType);
95 //              if (valueRequired) {
96 //                      codeStream.dup();
97 //              }
98 //              // better highlight for allocation: display the type individually
99 //              codeStream.recordPositionsFrom(pc, type.sourceStart);
100 //
101 //              // handling innerclass instance allocation - enclosing instance arguments
102 //              if (allocatedType.isNestedType()) {
103 //                      codeStream.generateSyntheticEnclosingInstanceValues(
104 //                              currentScope,
105 //                              allocatedType,
106 //                              enclosingInstance(),
107 //                              this);
108 //              }
109 //              // generate the arguments for constructor
110 //              if (arguments != null) {
111 //                      for (int i = 0, count = arguments.length; i < count; i++) {
112 //                              arguments[i].generateCode(currentScope, codeStream, true);
113 //                      }
114 //              }
115 //              // handling innerclass instance allocation - outer local arguments
116 //              if (allocatedType.isNestedType()) {
117 //                      codeStream.generateSyntheticOuterArgumentValues(
118 //                              currentScope,
119 //                              allocatedType,
120 //                              this);
121 //              }
122 //              
123 //              // invoke constructor
124 //              if (syntheticAccessor == null) {
125 //                      codeStream.invokespecial(binding);
126 //              } else {
127 //                      // synthetic accessor got some extra arguments appended to its signature, which need values
128 //                      for (int i = 0,
129 //                              max = syntheticAccessor.parameters.length - binding.parameters.length;
130 //                              i < max;
131 //                              i++) {
132 //                              codeStream.aconst_null();
133 //                      }
134 //                      codeStream.invokespecial(syntheticAccessor);
135 //              }
136 //              codeStream.recordPositionsFrom(pc, this.sourceStart);
137 //
138 //              if (anonymousType != null) {
139 //                      anonymousType.generateCode(currentScope, codeStream);
140 //              }
141 //      }
142         
143         public boolean isSuperAccess() {
144
145                 // necessary to lookup super constructor of anonymous type
146                 return anonymousType != null;
147         }
148         
149         /* Inner emulation consists in either recording a dependency 
150          * link only, or performing one level of propagation.
151          *
152          * Dependency mechanism is used whenever dealing with source target
153          * types, since by the time we reach them, we might not yet know their
154          * exact need.
155          */
156         public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope) {
157
158                 ReferenceBinding allocatedType;
159
160                 // perform some emulation work in case there is some and we are inside a local type only
161                 if ((allocatedType = binding.declaringClass).isNestedType()
162                         && currentScope.enclosingSourceType().isLocalType()) {
163
164                         if (allocatedType.isLocalType()) {
165                                 ((LocalTypeBinding) allocatedType).addInnerEmulationDependent(currentScope, enclosingInstance != null);
166                         } else {
167                                 // locally propagate, since we already now the desired shape for sure
168                                 currentScope.propagateInnerEmulation(allocatedType, enclosingInstance != null);
169                         }
170                 }
171         }
172
173         public TypeBinding resolveType(BlockScope scope) {
174
175                 // added for code assist...cannot occur with 'normal' code
176                 if (anonymousType == null && enclosingInstance == null) {
177                         return super.resolveType(scope);
178                 }
179
180                 // Propagate the type checking to the arguments, and checks if the constructor is defined.
181                 // ClassInstanceCreationExpression ::= Primary '.' 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt
182                 // ClassInstanceCreationExpression ::= Name '.' 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt
183                 // ==> by construction, when there is an enclosing instance the typename may NOT be qualified
184                 // ==> therefore by construction the type is always a SingleTypeReferenceType instead of being either 
185                 // sometime a SingleTypeReference and sometime a QualifedTypeReference
186
187                 constant = NotAConstant;
188                 TypeBinding enclosingInstanceType = null;
189                 TypeBinding receiverType = null;
190                 boolean hasError = false;
191                 if (anonymousType == null) { //----------------no anonymous class------------------------       
192                         if ((enclosingInstanceType = enclosingInstance.resolveType(scope)) == null){
193                                 hasError = true;
194                         } else if (enclosingInstanceType.isBaseType() || enclosingInstanceType.isArrayType()) {
195                                 scope.problemReporter().illegalPrimitiveOrArrayTypeForEnclosingInstance(
196                                         enclosingInstanceType,
197                                         enclosingInstance);
198                                 hasError = true;
199                         } else if ((this.resolvedType = receiverType = ((SingleTypeReference) type).resolveTypeEnclosing(
200                                                         scope,
201                                                         (ReferenceBinding) enclosingInstanceType)) == null) {
202                                 hasError = true;
203                         }
204                         // will check for null after args are resolved
205                         TypeBinding[] argumentTypes = NoParameters;
206                         if (arguments != null) {
207                                 int length = arguments.length;
208                                 argumentTypes = new TypeBinding[length];
209                                 for (int i = 0; i < length; i++)
210                                         if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null){
211                                                 hasError = true;
212                                         }
213                         }
214                         // limit of fault-tolerance
215                         if (hasError) return receiverType;
216
217                         if (!receiverType.canBeInstantiated()) {
218                                 scope.problemReporter().cannotInstantiate(type, receiverType);
219                                 return receiverType;
220                         }
221                         if ((this.binding = scope.getConstructor((ReferenceBinding) receiverType, argumentTypes, this))
222                                         .isValidBinding()) {
223                                 if (isMethodUseDeprecated(binding, scope))
224                                         scope.problemReporter().deprecatedMethod(this.binding, this);
225
226                                 if (arguments != null)
227                                         for (int i = 0; i < arguments.length; i++)
228                                                 arguments[i].implicitWidening(this.binding.parameters[i], argumentTypes[i]);
229                         } else {
230                                 if (this.binding.declaringClass == null)
231                                         this.binding.declaringClass = (ReferenceBinding) receiverType;
232                                 scope.problemReporter().invalidConstructor(this, this.binding);
233                                 return receiverType;
234                         }
235
236                         // The enclosing instance must be compatible with the innermost enclosing type
237                         ReferenceBinding expectedType = this.binding.declaringClass.enclosingType();
238                         if (enclosingInstanceType.isCompatibleWith(expectedType))
239                                 return receiverType;
240                         scope.problemReporter().typeMismatchErrorActualTypeExpectedType(
241                                 this.enclosingInstance,
242                                 enclosingInstanceType,
243                                 expectedType);
244                         return receiverType;
245                 }
246
247                 //--------------there is an anonymous type declaration-----------------
248                 if (this.enclosingInstance != null) {
249                         if ((enclosingInstanceType = this.enclosingInstance.resolveType(scope)) == null) {
250                                 hasError = true;
251                         } else if (enclosingInstanceType.isBaseType() || enclosingInstanceType.isArrayType()) {
252                                 scope.problemReporter().illegalPrimitiveOrArrayTypeForEnclosingInstance(
253                                         enclosingInstanceType,
254                                         this.enclosingInstance);
255                                 hasError = true;
256                         } else {
257                                 receiverType = ((SingleTypeReference) type).resolveTypeEnclosing(
258                                                                                 scope,
259                                                                                 (ReferenceBinding) enclosingInstanceType);                              
260                         }
261                 } else {
262                         receiverType = type.resolveType(scope);
263                 }
264                 if (receiverType == null) {
265                         hasError = true;
266                 } else if (((ReferenceBinding) receiverType).isFinal()) {
267                         scope.problemReporter().anonymousClassCannotExtendFinalClass(type, receiverType);
268                         hasError = true;
269                 }
270                 TypeBinding[] argumentTypes = NoParameters;
271                 if (arguments != null) {
272                         int length = arguments.length;
273                         argumentTypes = new TypeBinding[length];
274                         for (int i = 0; i < length; i++)
275                                 if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null) {
276                                         hasError = true;
277                                 }
278                 }
279                 // limit of fault-tolerance
280                 if (hasError) {
281                                 return receiverType;
282                 }
283
284                 // an anonymous class inherits from java.lang.Object when declared "after" an interface
285                 this.superTypeBinding =
286                         receiverType.isInterface() ? scope.getJavaLangObject() : (ReferenceBinding) receiverType;
287                 MethodBinding inheritedBinding =
288                         scope.getConstructor(this.superTypeBinding, argumentTypes, this);
289                 if (!inheritedBinding.isValidBinding()) {
290                         if (inheritedBinding.declaringClass == null)
291                                 inheritedBinding.declaringClass = this.superTypeBinding;
292                         scope.problemReporter().invalidConstructor(this, inheritedBinding);
293                         return null;
294                 }
295                 if (enclosingInstance != null) {
296                         if (!enclosingInstanceType.isCompatibleWith(inheritedBinding.declaringClass.enclosingType())) {
297                                 scope.problemReporter().typeMismatchErrorActualTypeExpectedType(
298                                         enclosingInstance,
299                                         enclosingInstanceType,
300                                         inheritedBinding.declaringClass.enclosingType());
301                                 return null;
302                         }
303                 }
304
305                 // this promotion has to be done somewhere: here or inside the constructor of the
306                 // anonymous class. We do it here while the constructor of the inner is then easier.
307                 if (arguments != null)
308                         for (int i = 0; i < arguments.length; i++)
309                                 arguments[i].implicitWidening(inheritedBinding.parameters[i], argumentTypes[i]);
310
311                 // Update the anonymous inner class : superclass, interface  
312                 scope.addAnonymousType(anonymousType, (ReferenceBinding) receiverType);
313                 anonymousType.resolve(scope);
314                 binding = anonymousType.createsInternalConstructorWithBinding(inheritedBinding);
315                 return anonymousType.binding; // 1.2 change
316         }
317         
318         public String toStringExpression() {
319                 return this.toStringExpression(0);
320         }
321
322         public String toStringExpression(int tab) {
323
324                 String s = ""; //$NON-NLS-1$
325                 if (enclosingInstance != null)
326                         s += enclosingInstance.toString() + "."; //$NON-NLS-1$
327                 s += super.toStringExpression();
328                 if (anonymousType != null) {
329                         s += anonymousType.toString(tab);
330                 } //allows to restart just after the } one line under ....
331                 return s;
332         }
333
334         public void traverse(ASTVisitor visitor, BlockScope scope) {
335
336                 if (visitor.visit(this, scope)) {
337                         if (enclosingInstance != null)
338                                 enclosingInstance.traverse(visitor, scope);
339                         type.traverse(visitor, scope);
340                         if (arguments != null) {
341                                 int argumentsLength = arguments.length;
342                                 for (int i = 0; i < argumentsLength; i++)
343                                         arguments[i].traverse(visitor, scope);
344                         }
345                         if (anonymousType != null)
346                                 anonymousType.traverse(visitor, scope);
347                 }
348                 visitor.endVisit(this, scope);
349         }
350 }