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
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.parser;
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;
28 * Internal type structure for parsing recovery
31 public class RecoveredType extends RecoveredStatement implements ITerminalSymbols, CompilerModifiers {
32 public TypeDeclaration typeDeclaration;
34 public RecoveredType[] memberTypes;
35 public int memberTypeCount;
36 public RecoveredField[] fields;
37 public int fieldCount;
38 public RecoveredMethod[] methods;
39 public int methodCount;
41 public boolean preserveContent = false; // only used for anonymous types
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++;
52 public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalance) {
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);
61 if (methods == null) {
62 methods = new RecoveredMethod[5];
65 if (methodCount == methods.length) {
69 (methods = new RecoveredMethod[2 * methodCount]),
74 RecoveredMethod element = new RecoveredMethod(methodDeclaration, this, bracketBalance, this.recoveringParser);
75 methods[methodCount++] = element;
77 /* consider that if the opening brace was not found, it is there */
78 if (!foundOpeningBrace){
79 foundOpeningBrace = true;
80 this.bracketBalance++;
82 /* if method not finished, then method becomes current */
83 if (methodDeclaration.declarationSourceEnd == 0) return element;
86 public RecoveredElement add(Block nestedBlockDeclaration,int bracketBalance) {
87 int modifiers = AccDefault;
88 if(this.parser().recoveredStaticInitializerStart != 0) {
89 modifiers = AccStatic;
91 return this.add(new Initializer(nestedBlockDeclaration, modifiers), bracketBalance);
93 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
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);
101 if (fields == null) {
102 fields = new RecoveredField[5];
105 if (fieldCount == fields.length) {
109 (fields = new RecoveredField[2 * fieldCount]),
114 RecoveredField element = fieldDeclaration.isField()
115 ? new RecoveredField(fieldDeclaration, this, bracketBalance)
116 : new RecoveredInitializer(fieldDeclaration, this, bracketBalance);
117 fields[fieldCount++] = element;
119 /* consider that if the opening brace was not found, it is there */
120 if (!foundOpeningBrace){
121 foundOpeningBrace = true;
122 this.bracketBalance++;
124 /* if field not finished, then field becomes current */
125 if (fieldDeclaration.declarationSourceEnd == 0) return element;
128 public RecoveredElement add(TypeDeclaration memberTypeDeclaration, int bracketBalance) {
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);
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);
151 if (memberTypes == null) {
152 memberTypes = new RecoveredType[5];
155 if (memberTypeCount == memberTypes.length) {
159 (memberTypes = new RecoveredType[2 * memberTypeCount]),
164 RecoveredType element = new RecoveredType(memberTypeDeclaration, this, bracketBalance);
165 memberTypes[memberTypeCount++] = element;
167 /* consider that if the opening brace was not found, it is there */
168 if (!foundOpeningBrace){
169 foundOpeningBrace = true;
170 this.bracketBalance++;
172 /* if member type not finished, then member type becomes current */
173 if (memberTypeDeclaration.declarationSourceEnd == 0) return element;
177 * Answer the body end of the corresponding parse node
179 public int bodyEnd(){
180 if (bodyEnd == 0) return typeDeclaration.declarationSourceEnd;
183 public boolean bodyStartsAtHeaderEnd(){
184 if (typeDeclaration.superInterfaces == null){
185 if (typeDeclaration.superclass == null){
186 return typeDeclaration.bodyStart == typeDeclaration.sourceEnd+1;
188 return typeDeclaration.bodyStart == typeDeclaration.superclass.sourceEnd+1;
191 return typeDeclaration.bodyStart
192 == typeDeclaration.superInterfaces[typeDeclaration.superInterfaces.length-1].sourceEnd+1;
196 * Answer the enclosing type node, or null if none
198 public RecoveredType enclosingType(){
199 RecoveredElement current = parent;
200 while (current != null){
201 if (current instanceof RecoveredType){
202 return (RecoveredType) current;
204 current = current.parent;
208 public char[] name(){
209 return typeDeclaration.name;
212 * Answer the associated parsed structure
214 public AstNode parseTree(){
215 return typeDeclaration;
218 * Answer the very source end of the corresponding parse node
220 public int sourceEnd(){
221 return this.typeDeclaration.declarationSourceEnd;
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$
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));
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));
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));
249 return result.toString();
252 * Update the bodyStart of the corresponding parse node
254 public void updateBodyStart(int bodyStart){
255 this.foundOpeningBrace = true;
256 this.typeDeclaration.bodyStart = bodyStart;
258 public Statement updatedStatement(){
260 // ignore closed anonymous type
261 if (typeDeclaration instanceof AnonymousLocalTypeDeclaration
262 && !this.preserveContent){
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;
273 public TypeDeclaration updatedTypeDeclaration(){
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);
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;
288 for (int i = 0; i < memberTypeCount; i++){
289 memberTypeDeclarations[existingCount + i] = (MemberTypeDeclaration)memberTypes[i].updatedTypeDeclaration();
291 typeDeclaration.memberTypes = memberTypeDeclarations;
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);
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;
306 for (int i = 0; i < fieldCount; i++){
307 fieldDeclarations[existingCount + i] = fields[i].updatedFieldDeclaration();
309 typeDeclaration.fields = fieldDeclarations;
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;
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;
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;
333 typeDeclaration.methods = methodDeclarations;
334 hasConstructor = typeDeclaration.checkConstructors(this.parser());
336 for (int i = 0; i < existingCount; i++){
337 if (typeDeclaration.methods[i].isConstructor()) hasConstructor = true;
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;
349 if (!alreadyHasClinit) typeDeclaration.addClinit();
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);
358 if (defaultConstructorIndex != typeDeclaration.methods.length-1){
360 typeDeclaration.methods,
361 defaultConstructorIndex+1,
363 defaultConstructorIndex,
364 typeDeclaration.methods.length - defaultConstructorIndex - 1);
366 typeDeclaration.methods = methodDeclarations;
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;
376 parent = parent.parent;
378 typeDeclaration.createsInternalConstructor(!parser().diet || insideFieldInitializer, true);
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);
386 if ((typeDeclaration instanceof TypeDeclaration) && (parent instanceof RecoveredMethod)){
387 newTypeDeclaration = new LocalTypeDeclaration(typeDeclaration.compilationResult);
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;
408 return typeDeclaration;
411 * Update the corresponding parse node from parser state which
412 * is about to disappear because of restarting recovery
414 public void updateFromParserState(){
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;
425 if((!(parser.astStack[astPtr] instanceof TypeDeclaration))) {
428 for (int i = 1, max = length + 1; i < max; i++) {
429 if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
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,
443 * A closing brace got consumed, might have closed the current element,
444 * in which case both the currentElement is exited
446 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
447 if ((--bracketBalance <= 0) && (parent != null)){
448 this.updateSourceEndIfNecessary(braceEnd);
449 this.bodyEnd = braceStart - 1;
455 * An opening brace got consumed, might be the expected opening one of the current element,
456 * in which case the bodyStart is updated.
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){
462 if (parser.scanner.searchLineNumber(typeDeclaration.sourceEnd)
463 != parser.scanner.searchLineNumber(braceEnd)){
465 Parser parser = this.parser();
466 switch(parser.lastIgnoredToken){
468 case TokenNameextends :
469 // case TokenNameimplements :
470 if (parser.recoveredStaticInitializerStart == 0) break;
472 this.foundOpeningBrace = true;
473 bracketBalance = 1; // pretend the brace was already there
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;
482 if (parser.recoveredStaticInitializerStart == 0){
483 init = new Initializer(block, AccDefault);
485 init = new Initializer(block, AccStatic);
486 init.declarationSourceStart = parser.recoveredStaticInitializerStart;
488 return this.add(init, 1);
490 return super.updateOnOpeningBrace(braceEnd);
492 public void updateParseTree(){
493 this.updatedTypeDeclaration();
496 * Update the declarationSourceEnd of the corresponding parse node
498 public void updateSourceEndIfNecessary(int sourceEnd){
499 if (this.typeDeclaration.declarationSourceEnd == 0){
501 this.typeDeclaration.declarationSourceEnd = sourceEnd;
502 this.typeDeclaration.bodyEnd = sourceEnd;