1dddbcd8249cc045ad73975a016d2caf4b3db1cd
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / parser / RecoveredType.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.parser;
12
13 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
14 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
15 import net.sourceforge.phpdt.internal.compiler.ast.AnonymousLocalTypeDeclaration;
16 import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
17 import net.sourceforge.phpdt.internal.compiler.ast.Block;
18 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
19 import net.sourceforge.phpdt.internal.compiler.ast.Initializer;
20 import net.sourceforge.phpdt.internal.compiler.ast.LocalTypeDeclaration;
21 import net.sourceforge.phpdt.internal.compiler.ast.MemberTypeDeclaration;
22 import net.sourceforge.phpdt.internal.compiler.ast.Statement;
23 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
24 import net.sourceforge.phpdt.internal.compiler.ast.TypeReference;
25 import net.sourceforge.phpdt.internal.compiler.lookup.CompilerModifiers;
26
27 /**
28  * Internal type structure for parsing recovery 
29  */
30
31 public class RecoveredType extends RecoveredStatement implements ITerminalSymbols, CompilerModifiers {
32         public TypeDeclaration typeDeclaration;
33
34         public RecoveredType[] memberTypes;
35         public int memberTypeCount;
36         public RecoveredField[] fields;
37         public int fieldCount;
38         public RecoveredMethod[] methods;
39         public int methodCount;
40
41         public boolean preserveContent = false; // only used for anonymous types
42         public int bodyEnd;
43         
44 public RecoveredType(TypeDeclaration typeDeclaration, RecoveredElement parent, int bracketBalance){
45         super(typeDeclaration, parent, bracketBalance);
46         this.typeDeclaration = typeDeclaration;
47         this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
48         if(this.foundOpeningBrace) {
49                 this.bracketBalance++;
50         }
51 }
52 public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalance) {
53
54         /* do not consider a method starting passed the type end (if set)
55                 it must be belonging to an enclosing type */
56         if (typeDeclaration.declarationSourceEnd != 0 
57                 && methodDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd){
58                 return this.parent.add(methodDeclaration, bracketBalance);
59         }
60
61         if (methods == null) {
62                 methods = new RecoveredMethod[5];
63                 methodCount = 0;
64         } else {
65                 if (methodCount == methods.length) {
66                         System.arraycopy(
67                                 methods, 
68                                 0, 
69                                 (methods = new RecoveredMethod[2 * methodCount]), 
70                                 0, 
71                                 methodCount); 
72                 }
73         }
74         RecoveredMethod element = new RecoveredMethod(methodDeclaration, this, bracketBalance, this.recoveringParser);
75         methods[methodCount++] = element;
76
77         /* consider that if the opening brace was not found, it is there */
78         if (!foundOpeningBrace){
79                 foundOpeningBrace = true;
80                 this.bracketBalance++;
81         }
82         /* if method not finished, then method becomes current */
83         if (methodDeclaration.declarationSourceEnd == 0) return element;
84         return this;
85 }
86 public RecoveredElement add(Block nestedBlockDeclaration,int bracketBalance) {
87         int modifiers = AccDefault;
88         if(this.parser().recoveredStaticInitializerStart != 0) {
89                 modifiers = AccStatic;
90         }
91         return this.add(new Initializer(nestedBlockDeclaration, modifiers), bracketBalance);
92 }
93 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
94         
95         /* do not consider a field starting passed the type end (if set)
96         it must be belonging to an enclosing type */
97         if (typeDeclaration.declarationSourceEnd != 0
98                 && fieldDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd) {
99                 return this.parent.add(fieldDeclaration, bracketBalance);
100         }
101         if (fields == null) {
102                 fields = new RecoveredField[5];
103                 fieldCount = 0;
104         } else {
105                 if (fieldCount == fields.length) {
106                         System.arraycopy(
107                                 fields, 
108                                 0, 
109                                 (fields = new RecoveredField[2 * fieldCount]), 
110                                 0, 
111                                 fieldCount); 
112                 }
113         }
114         RecoveredField element = fieldDeclaration.isField() 
115                                                                 ? new RecoveredField(fieldDeclaration, this, bracketBalance)
116                                                                 : new RecoveredInitializer(fieldDeclaration, this, bracketBalance);
117         fields[fieldCount++] = element;
118
119         /* consider that if the opening brace was not found, it is there */
120         if (!foundOpeningBrace){
121                 foundOpeningBrace = true;
122                 this.bracketBalance++;
123         }
124         /* if field not finished, then field becomes current */
125         if (fieldDeclaration.declarationSourceEnd == 0) return element;
126         return this;
127 }
128 public RecoveredElement add(TypeDeclaration memberTypeDeclaration, int bracketBalance) {
129
130         /* do not consider a type starting passed the type end (if set)
131                 it must be belonging to an enclosing type */
132         if (typeDeclaration.declarationSourceEnd != 0 
133                 && memberTypeDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd){
134                 return this.parent.add(memberTypeDeclaration, bracketBalance);
135         }
136         
137         if (memberTypeDeclaration instanceof AnonymousLocalTypeDeclaration){
138                 if (this.methodCount > 0) {
139                         // add it to the last method body
140                         RecoveredMethod lastMethod = this.methods[this.methodCount-1];
141                         lastMethod.methodDeclaration.bodyEnd = 0; // reopen method
142                         lastMethod.methodDeclaration.declarationSourceEnd = 0; // reopen method
143                         lastMethod.bracketBalance++; // expect one closing brace
144                         return lastMethod.add(typeDeclaration, bracketBalance);
145                 } else {
146                         // ignore
147                         return this;
148                 }
149         }       
150                 
151         if (memberTypes == null) {
152                 memberTypes = new RecoveredType[5];
153                 memberTypeCount = 0;
154         } else {
155                 if (memberTypeCount == memberTypes.length) {
156                         System.arraycopy(
157                                 memberTypes, 
158                                 0, 
159                                 (memberTypes = new RecoveredType[2 * memberTypeCount]), 
160                                 0, 
161                                 memberTypeCount); 
162                 }
163         }
164         RecoveredType element = new RecoveredType(memberTypeDeclaration, this, bracketBalance);
165         memberTypes[memberTypeCount++] = element;
166
167         /* consider that if the opening brace was not found, it is there */
168         if (!foundOpeningBrace){
169                 foundOpeningBrace = true;
170                 this.bracketBalance++;
171         }
172         /* if member type not finished, then member type becomes current */
173         if (memberTypeDeclaration.declarationSourceEnd == 0) return element;
174         return this;
175 }
176 /*
177  * Answer the body end of the corresponding parse node
178  */
179 public int bodyEnd(){
180         if (bodyEnd == 0) return typeDeclaration.declarationSourceEnd;
181         return bodyEnd;
182 }
183 public boolean bodyStartsAtHeaderEnd(){
184         if (typeDeclaration.superInterfaces == null){
185                 if (typeDeclaration.superclass == null){
186                         return typeDeclaration.bodyStart == typeDeclaration.sourceEnd+1;
187                 } else {
188                         return typeDeclaration.bodyStart == typeDeclaration.superclass.sourceEnd+1;
189                 }
190         } else {
191                 return typeDeclaration.bodyStart 
192                                 == typeDeclaration.superInterfaces[typeDeclaration.superInterfaces.length-1].sourceEnd+1;
193         }
194 }
195 /*
196  * Answer the enclosing type node, or null if none
197  */
198 public RecoveredType enclosingType(){
199         RecoveredElement current = parent;
200         while (current != null){
201                 if (current instanceof RecoveredType){
202                         return (RecoveredType) current;
203                 }
204                 current = current.parent;
205         }
206         return null;
207 }
208 public char[] name(){
209         return typeDeclaration.name;
210 }
211 /* 
212  * Answer the associated parsed structure
213  */
214 public AstNode parseTree(){
215         return typeDeclaration;
216 }
217 /*
218  * Answer the very source end of the corresponding parse node
219  */
220 public int sourceEnd(){
221         return this.typeDeclaration.declarationSourceEnd;
222 }
223 public String toString(int tab) {
224         StringBuffer result = new StringBuffer(tabString(tab));
225         result.append("Recovered type:\n"); //$NON-NLS-1$
226         if (typeDeclaration instanceof AnonymousLocalTypeDeclaration) {
227                 result.append(tabString(tab));
228                 result.append(" "); //$NON-NLS-1$
229         }
230         result.append(typeDeclaration.toString(tab + 1));
231         if (this.memberTypes != null) {
232                 for (int i = 0; i < this.memberTypeCount; i++) {
233                         result.append("\n"); //$NON-NLS-1$
234                         result.append(this.memberTypes[i].toString(tab + 1));
235                 }
236         }
237         if (this.fields != null) {
238                 for (int i = 0; i < this.fieldCount; i++) {
239                         result.append("\n"); //$NON-NLS-1$
240                         result.append(this.fields[i].toString(tab + 1));
241                 }
242         }
243         if (this.methods != null) {
244                 for (int i = 0; i < this.methodCount; i++) {
245                         result.append("\n"); //$NON-NLS-1$
246                         result.append(this.methods[i].toString(tab + 1));
247                 }
248         }
249         return result.toString();
250 }
251 /*
252  * Update the bodyStart of the corresponding parse node
253  */
254 public void updateBodyStart(int bodyStart){
255         this.foundOpeningBrace = true;
256         this.typeDeclaration.bodyStart = bodyStart;
257 }
258 public Statement updatedStatement(){
259
260         // ignore closed anonymous type
261         if (typeDeclaration instanceof AnonymousLocalTypeDeclaration
262                 && !this.preserveContent){
263                 return null;
264         }
265                 
266         TypeDeclaration updatedType = this.updatedTypeDeclaration();
267         if (updatedType instanceof AnonymousLocalTypeDeclaration){
268                 /* in presence of an anonymous type, we want the full allocation expression */
269                 return ((AnonymousLocalTypeDeclaration)updatedType).allocation;
270         }
271         return updatedType;
272 }
273 public TypeDeclaration updatedTypeDeclaration(){
274
275         /* update member types */
276         if (memberTypeCount > 0){
277                 int existingCount = typeDeclaration.memberTypes == null ? 0 : typeDeclaration.memberTypes.length;
278                 MemberTypeDeclaration[] memberTypeDeclarations = new MemberTypeDeclaration[existingCount + memberTypeCount];
279                 if (existingCount > 0){
280                         System.arraycopy(typeDeclaration.memberTypes, 0, memberTypeDeclarations, 0, existingCount);
281                 }
282                 // may need to update the declarationSourceEnd of the last type
283                 if (memberTypes[memberTypeCount - 1].typeDeclaration.declarationSourceEnd == 0){
284                         int bodyEnd = bodyEnd();
285                         memberTypes[memberTypeCount - 1].typeDeclaration.declarationSourceEnd = bodyEnd;
286                         memberTypes[memberTypeCount - 1].typeDeclaration.bodyEnd =  bodyEnd;
287                 }
288                 for (int i = 0; i < memberTypeCount; i++){
289                         memberTypeDeclarations[existingCount + i] = (MemberTypeDeclaration)memberTypes[i].updatedTypeDeclaration();
290                 }
291                 typeDeclaration.memberTypes = memberTypeDeclarations;
292         }
293         /* update fields */
294         if (fieldCount > 0){
295                 int existingCount = typeDeclaration.fields == null ? 0 : typeDeclaration.fields.length;
296                 FieldDeclaration[] fieldDeclarations = new FieldDeclaration[existingCount + fieldCount];
297                 if (existingCount > 0){
298                         System.arraycopy(typeDeclaration.fields, 0, fieldDeclarations, 0, existingCount);
299                 }
300                 // may need to update the declarationSourceEnd of the last field
301                 if (fields[fieldCount - 1].fieldDeclaration.declarationSourceEnd == 0){
302                         int temp = bodyEnd();
303                         fields[fieldCount - 1].fieldDeclaration.declarationSourceEnd = temp;
304                         fields[fieldCount - 1].fieldDeclaration.declarationEnd = temp;
305                 }
306                 for (int i = 0; i < fieldCount; i++){
307                         fieldDeclarations[existingCount + i] = fields[i].updatedFieldDeclaration();
308                 }
309                 typeDeclaration.fields = fieldDeclarations;
310         }
311         /* update methods */
312         int existingCount = typeDeclaration.methods == null ? 0 : typeDeclaration.methods.length;
313         boolean hasConstructor = false, hasRecoveredConstructor = false;
314         int defaultConstructorIndex = -1;
315         if (methodCount > 0){
316                 AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[existingCount + methodCount];
317                 for (int i = 0; i < existingCount; i++){
318                         AbstractMethodDeclaration m = typeDeclaration.methods[i];
319                         if (m.isDefaultConstructor()) defaultConstructorIndex = i;
320                         methodDeclarations[i] = m;
321                 }
322                 // may need to update the declarationSourceEnd of the last method
323                 if (methods[methodCount - 1].methodDeclaration.declarationSourceEnd == 0){
324                         int bodyEnd = bodyEnd();
325                         methods[methodCount - 1].methodDeclaration.declarationSourceEnd = bodyEnd;
326                         methods[methodCount - 1].methodDeclaration.bodyEnd = bodyEnd;
327                 }
328                 for (int i = 0; i < methodCount; i++){
329                         AbstractMethodDeclaration updatedMethod = methods[i].updatedMethodDeclaration();                        
330                         if (updatedMethod.isConstructor()) hasRecoveredConstructor = true;
331                         methodDeclarations[existingCount + i] = updatedMethod;                  
332                 }
333                 typeDeclaration.methods = methodDeclarations;
334                 hasConstructor = typeDeclaration.checkConstructors(this.parser());
335         } else {
336                 for (int i = 0; i < existingCount; i++){
337                         if (typeDeclaration.methods[i].isConstructor()) hasConstructor = true;
338                 }               
339         }
340         /* add clinit ? */
341         if (typeDeclaration.needClassInitMethod()){
342                 boolean alreadyHasClinit = false;
343                 for (int i = 0; i < existingCount; i++){
344                         if (typeDeclaration.methods[i].isClinit()){
345                                 alreadyHasClinit = true;
346                                 break;
347                         }
348                 }
349                 if (!alreadyHasClinit) typeDeclaration.addClinit();
350         }
351         /* add default constructor ? */
352         if (defaultConstructorIndex >= 0 && hasRecoveredConstructor){
353                 /* should discard previous default construtor */
354                 AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[typeDeclaration.methods.length - 1];
355                 if (defaultConstructorIndex != 0){
356                         System.arraycopy(typeDeclaration.methods, 0, methodDeclarations, 0, defaultConstructorIndex);
357                 }
358                 if (defaultConstructorIndex != typeDeclaration.methods.length-1){
359                         System.arraycopy(
360                                 typeDeclaration.methods, 
361                                 defaultConstructorIndex+1, 
362                                 methodDeclarations, 
363                                 defaultConstructorIndex, 
364                                 typeDeclaration.methods.length - defaultConstructorIndex - 1);
365                 }
366                 typeDeclaration.methods = methodDeclarations;
367         } else {
368                 if (!hasConstructor) {// if was already reduced, then constructor
369                         boolean insideFieldInitializer = false;
370                         RecoveredElement parent = this.parent; 
371                         while (parent != null){
372                                 if (parent instanceof RecoveredField){
373                                                 insideFieldInitializer = true;
374                                                 break; 
375                                 }
376                                 parent = parent.parent;
377                         }
378                         typeDeclaration.createsInternalConstructor(!parser().diet || insideFieldInitializer, true);
379                 } 
380         }
381         /* might need to cast itself into a MemberTypeDeclaration or a LocalTypeDeclaration */
382         TypeDeclaration newTypeDeclaration = null;
383         if ((typeDeclaration instanceof TypeDeclaration) && (parent instanceof RecoveredType)){
384                 newTypeDeclaration = new MemberTypeDeclaration(typeDeclaration.compilationResult);
385         } else {
386                 if ((typeDeclaration instanceof TypeDeclaration) && (parent instanceof RecoveredMethod)){
387                         newTypeDeclaration = new LocalTypeDeclaration(typeDeclaration.compilationResult);
388                 }
389         }
390         /* copy slots into new type */
391         if (newTypeDeclaration != null){
392                 newTypeDeclaration.modifiers = typeDeclaration.modifiers;
393                 newTypeDeclaration.modifiersSourceStart = typeDeclaration.modifiersSourceStart;
394                 newTypeDeclaration.name = typeDeclaration.name;
395                 newTypeDeclaration.superclass = typeDeclaration.superclass;
396                 newTypeDeclaration.superInterfaces = typeDeclaration.superInterfaces;
397                 newTypeDeclaration.fields = typeDeclaration.fields;
398                 newTypeDeclaration.methods = typeDeclaration.methods;
399                 newTypeDeclaration.memberTypes = typeDeclaration.memberTypes;
400                 newTypeDeclaration.ignoreFurtherInvestigation = typeDeclaration.ignoreFurtherInvestigation;
401                 newTypeDeclaration.maxFieldCount = typeDeclaration.maxFieldCount;
402                 newTypeDeclaration.declarationSourceStart = typeDeclaration.declarationSourceStart;
403                 newTypeDeclaration.declarationSourceEnd = typeDeclaration.declarationSourceEnd;
404                 newTypeDeclaration.bodyEnd = typeDeclaration.bodyEnd;
405                 newTypeDeclaration.bodyStart = typeDeclaration.bodyStart;
406                 typeDeclaration = newTypeDeclaration;
407         }
408         return typeDeclaration;
409 }
410 /*
411  * Update the corresponding parse node from parser state which
412  * is about to disappear because of restarting recovery
413  */
414 public void updateFromParserState(){
415
416         if(this.bodyStartsAtHeaderEnd()){
417                 Parser parser = this.parser();
418                 /* might want to recover implemented interfaces */
419                 // protection for bugs 15142
420                 if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
421                         int length = parser.astLengthStack[parser.astLengthPtr];
422                         int astPtr = parser.astPtr - length;
423                         boolean canConsume = astPtr >= 0;
424                         if(canConsume) {
425                                 if((!(parser.astStack[astPtr] instanceof TypeDeclaration))) {
426                                         canConsume = false;
427                                 }
428                                 for (int i = 1, max = length + 1; i < max; i++) {
429                                         if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
430                                                 canConsume = false;
431                                         }
432                                 }
433                         }
434                         if(canConsume) {
435                                 parser.consumeClassHeaderImplements(); 
436                                 // will reset typeListLength to zero
437                                 // thus this check will only be performed on first errorCheck after class X implements Y,Z,
438                         }
439                 }
440         }
441 }
442 /*
443  * A closing brace got consumed, might have closed the current element,
444  * in which case both the currentElement is exited
445  */
446 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
447         if ((--bracketBalance <= 0) && (parent != null)){
448                 this.updateSourceEndIfNecessary(braceEnd);
449                 this.bodyEnd = braceStart - 1;
450                 return parent;
451         }
452         return this;
453 }
454 /*
455  * An opening brace got consumed, might be the expected opening one of the current element,
456  * in which case the bodyStart is updated.
457  */
458 public RecoveredElement updateOnOpeningBrace(int braceEnd){
459         /* in case the opening brace is not close enough to the signature, ignore it */
460         if (bracketBalance == 0){
461                 /*
462                         if (parser.scanner.searchLineNumber(typeDeclaration.sourceEnd) 
463                                 != parser.scanner.searchLineNumber(braceEnd)){
464                  */
465                 Parser parser = this.parser();
466                 switch(parser.lastIgnoredToken){
467                         case -1 :
468                         case TokenNameextends :
469 //                      case TokenNameimplements :
470                                 if (parser.recoveredStaticInitializerStart == 0) break;
471                         default:
472                                 this.foundOpeningBrace = true;                          
473                                 bracketBalance = 1; // pretend the brace was already there
474                 }
475         }       
476         // might be an initializer
477         if (this.bracketBalance == 1){
478                 Block block = new Block(0);
479                 Parser parser = this.parser();
480                 block.sourceStart = parser.scanner.startPosition;
481                 Initializer init;
482                 if (parser.recoveredStaticInitializerStart == 0){
483                         init = new Initializer(block, AccDefault);
484                 } else {
485                         init = new Initializer(block, AccStatic);
486                         init.declarationSourceStart = parser.recoveredStaticInitializerStart;
487                 }
488                 return this.add(init, 1);
489         }
490         return super.updateOnOpeningBrace(braceEnd);
491 }
492 public void updateParseTree(){
493         this.updatedTypeDeclaration();
494 }
495 /*
496  * Update the declarationSourceEnd of the corresponding parse node
497  */
498 public void updateSourceEndIfNecessary(int sourceEnd){
499         if (this.typeDeclaration.declarationSourceEnd == 0){
500                 this.bodyEnd = 0;
501                 this.typeDeclaration.declarationSourceEnd = sourceEnd;
502                 this.typeDeclaration.bodyEnd = sourceEnd;
503         }
504 }
505 }