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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.parser;
13 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
14 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
15 import org.eclipse.jdt.internal.compiler.ast.Block;
16 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
17 import org.eclipse.jdt.internal.compiler.ast.Initializer;
18 import org.eclipse.jdt.internal.compiler.ast.Statement;
19 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
20 import org.eclipse.jdt.internal.compiler.ast.TypeReference;
21 import org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers;
24 * Internal type structure for parsing recovery
27 public class RecoveredType extends RecoveredStatement implements TerminalTokens, CompilerModifiers {
28 public TypeDeclaration typeDeclaration;
30 public RecoveredType[] memberTypes;
31 public int memberTypeCount;
32 public RecoveredField[] fields;
33 public int fieldCount;
34 public RecoveredMethod[] methods;
35 public int methodCount;
37 public boolean preserveContent = false; // only used for anonymous types
40 public RecoveredType(TypeDeclaration typeDeclaration, RecoveredElement parent, int bracketBalance){
41 super(typeDeclaration, parent, bracketBalance);
42 this.typeDeclaration = typeDeclaration;
43 this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
44 if(this.foundOpeningBrace) {
45 this.bracketBalance++;
48 public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalanceValue) {
50 /* do not consider a method starting passed the type end (if set)
51 it must be belonging to an enclosing type */
52 if (typeDeclaration.declarationSourceEnd != 0
53 && methodDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd){
54 return this.parent.add(methodDeclaration, bracketBalanceValue);
57 if (methods == null) {
58 methods = new RecoveredMethod[5];
61 if (methodCount == methods.length) {
65 (methods = new RecoveredMethod[2 * methodCount]),
70 RecoveredMethod element = new RecoveredMethod(methodDeclaration, this, bracketBalanceValue, this.recoveringParser);
71 methods[methodCount++] = element;
73 /* consider that if the opening brace was not found, it is there */
74 if (!foundOpeningBrace){
75 foundOpeningBrace = true;
76 this.bracketBalance++;
78 /* if method not finished, then method becomes current */
79 if (methodDeclaration.declarationSourceEnd == 0) return element;
82 public RecoveredElement add(Block nestedBlockDeclaration,int bracketBalanceValue) {
83 int modifiers = AccDefault;
84 if(this.parser().recoveredStaticInitializerStart != 0) {
85 modifiers = AccStatic;
87 return this.add(new Initializer(nestedBlockDeclaration, modifiers), bracketBalanceValue);
89 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
91 /* do not consider a field starting passed the type end (if set)
92 it must be belonging to an enclosing type */
93 if (typeDeclaration.declarationSourceEnd != 0
94 && fieldDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd) {
95 return this.parent.add(fieldDeclaration, bracketBalanceValue);
98 fields = new RecoveredField[5];
101 if (fieldCount == fields.length) {
105 (fields = new RecoveredField[2 * fieldCount]),
110 RecoveredField element = fieldDeclaration.isField()
111 ? new RecoveredField(fieldDeclaration, this, bracketBalanceValue)
112 : new RecoveredInitializer(fieldDeclaration, this, bracketBalanceValue);
113 fields[fieldCount++] = element;
115 /* consider that if the opening brace was not found, it is there */
116 if (!foundOpeningBrace){
117 foundOpeningBrace = true;
118 this.bracketBalance++;
120 /* if field not finished, then field becomes current */
121 if (fieldDeclaration.declarationSourceEnd == 0) return element;
124 public RecoveredElement add(TypeDeclaration memberTypeDeclaration, int bracketBalanceValue) {
126 /* do not consider a type starting passed the type end (if set)
127 it must be belonging to an enclosing type */
128 if (typeDeclaration.declarationSourceEnd != 0
129 && memberTypeDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd){
130 return this.parent.add(memberTypeDeclaration, bracketBalanceValue);
133 if ((memberTypeDeclaration.bits & ASTNode.IsAnonymousTypeMASK) != 0){
134 if (this.methodCount > 0) {
135 // add it to the last method body
136 RecoveredMethod lastMethod = this.methods[this.methodCount-1];
137 lastMethod.methodDeclaration.bodyEnd = 0; // reopen method
138 lastMethod.methodDeclaration.declarationSourceEnd = 0; // reopen method
139 lastMethod.bracketBalance++; // expect one closing brace
140 return lastMethod.add(memberTypeDeclaration, bracketBalanceValue);
147 if (memberTypes == null) {
148 memberTypes = new RecoveredType[5];
151 if (memberTypeCount == memberTypes.length) {
155 (memberTypes = new RecoveredType[2 * memberTypeCount]),
160 RecoveredType element = new RecoveredType(memberTypeDeclaration, this, bracketBalanceValue);
161 memberTypes[memberTypeCount++] = element;
163 /* consider that if the opening brace was not found, it is there */
164 if (!foundOpeningBrace){
165 foundOpeningBrace = true;
166 this.bracketBalance++;
168 /* if member type not finished, then member type becomes current */
169 if (memberTypeDeclaration.declarationSourceEnd == 0) return element;
173 * Answer the body end of the corresponding parse node
175 public int bodyEnd(){
176 if (bodyEnd == 0) return typeDeclaration.declarationSourceEnd;
179 public boolean bodyStartsAtHeaderEnd(){
180 if (typeDeclaration.superInterfaces == null){
181 if (typeDeclaration.superclass == null){
182 return typeDeclaration.bodyStart == typeDeclaration.sourceEnd+1;
184 return typeDeclaration.bodyStart == typeDeclaration.superclass.sourceEnd+1;
187 return typeDeclaration.bodyStart
188 == typeDeclaration.superInterfaces[typeDeclaration.superInterfaces.length-1].sourceEnd+1;
192 * Answer the enclosing type node, or null if none
194 public RecoveredType enclosingType(){
195 RecoveredElement current = parent;
196 while (current != null){
197 if (current instanceof RecoveredType){
198 return (RecoveredType) current;
200 current = current.parent;
204 public char[] name(){
205 return typeDeclaration.name;
208 * Answer the associated parsed structure
210 public ASTNode parseTree(){
211 return typeDeclaration;
214 * Answer the very source end of the corresponding parse node
216 public int sourceEnd(){
217 return this.typeDeclaration.declarationSourceEnd;
219 public String toString(int tab) {
220 StringBuffer result = new StringBuffer(tabString(tab));
221 result.append("Recovered type:\n"); //$NON-NLS-1$
222 if ((typeDeclaration.bits & ASTNode.IsAnonymousTypeMASK) != 0) {
223 result.append(tabString(tab));
224 result.append(" "); //$NON-NLS-1$
226 typeDeclaration.print(tab + 1, result);
227 if (this.memberTypes != null) {
228 for (int i = 0; i < this.memberTypeCount; i++) {
229 result.append("\n"); //$NON-NLS-1$
230 result.append(this.memberTypes[i].toString(tab + 1));
233 if (this.fields != null) {
234 for (int i = 0; i < this.fieldCount; i++) {
235 result.append("\n"); //$NON-NLS-1$
236 result.append(this.fields[i].toString(tab + 1));
239 if (this.methods != null) {
240 for (int i = 0; i < this.methodCount; i++) {
241 result.append("\n"); //$NON-NLS-1$
242 result.append(this.methods[i].toString(tab + 1));
245 return result.toString();
248 * Update the bodyStart of the corresponding parse node
250 public void updateBodyStart(int bodyStart){
251 this.foundOpeningBrace = true;
252 this.typeDeclaration.bodyStart = bodyStart;
254 public Statement updatedStatement(){
256 // ignore closed anonymous type
257 if ((typeDeclaration.bits & ASTNode.IsAnonymousTypeMASK) != 0 && !this.preserveContent){
261 TypeDeclaration updatedType = this.updatedTypeDeclaration();
262 if ((updatedType.bits & ASTNode.IsAnonymousTypeMASK) != 0){
263 /* in presence of an anonymous type, we want the full allocation expression */
264 return updatedType.allocation;
268 public TypeDeclaration updatedTypeDeclaration(){
270 /* update member types */
271 if (memberTypeCount > 0){
272 int existingCount = typeDeclaration.memberTypes == null ? 0 : typeDeclaration.memberTypes.length;
273 TypeDeclaration[] memberTypeDeclarations = new TypeDeclaration[existingCount + memberTypeCount];
274 if (existingCount > 0){
275 System.arraycopy(typeDeclaration.memberTypes, 0, memberTypeDeclarations, 0, existingCount);
277 // may need to update the declarationSourceEnd of the last type
278 if (memberTypes[memberTypeCount - 1].typeDeclaration.declarationSourceEnd == 0){
279 int bodyEndValue = bodyEnd();
280 memberTypes[memberTypeCount - 1].typeDeclaration.declarationSourceEnd = bodyEndValue;
281 memberTypes[memberTypeCount - 1].typeDeclaration.bodyEnd = bodyEndValue;
283 for (int i = 0; i < memberTypeCount; i++){
284 memberTypeDeclarations[existingCount + i] = memberTypes[i].updatedTypeDeclaration();
286 typeDeclaration.memberTypes = memberTypeDeclarations;
290 int existingCount = typeDeclaration.fields == null ? 0 : typeDeclaration.fields.length;
291 FieldDeclaration[] fieldDeclarations = new FieldDeclaration[existingCount + fieldCount];
292 if (existingCount > 0){
293 System.arraycopy(typeDeclaration.fields, 0, fieldDeclarations, 0, existingCount);
295 // may need to update the declarationSourceEnd of the last field
296 if (fields[fieldCount - 1].fieldDeclaration.declarationSourceEnd == 0){
297 int temp = bodyEnd();
298 fields[fieldCount - 1].fieldDeclaration.declarationSourceEnd = temp;
299 fields[fieldCount - 1].fieldDeclaration.declarationEnd = temp;
301 for (int i = 0; i < fieldCount; i++){
302 fieldDeclarations[existingCount + i] = fields[i].updatedFieldDeclaration();
304 typeDeclaration.fields = fieldDeclarations;
307 int existingCount = typeDeclaration.methods == null ? 0 : typeDeclaration.methods.length;
308 boolean hasConstructor = false, hasRecoveredConstructor = false;
309 int defaultConstructorIndex = -1;
310 if (methodCount > 0){
311 AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[existingCount + methodCount];
312 for (int i = 0; i < existingCount; i++){
313 AbstractMethodDeclaration m = typeDeclaration.methods[i];
314 if (m.isDefaultConstructor()) defaultConstructorIndex = i;
315 methodDeclarations[i] = m;
317 // may need to update the declarationSourceEnd of the last method
318 if (methods[methodCount - 1].methodDeclaration.declarationSourceEnd == 0){
319 int bodyEndValue = bodyEnd();
320 methods[methodCount - 1].methodDeclaration.declarationSourceEnd = bodyEndValue;
321 methods[methodCount - 1].methodDeclaration.bodyEnd = bodyEndValue;
323 for (int i = 0; i < methodCount; i++){
324 AbstractMethodDeclaration updatedMethod = methods[i].updatedMethodDeclaration();
325 if (updatedMethod.isConstructor()) hasRecoveredConstructor = true;
326 methodDeclarations[existingCount + i] = updatedMethod;
328 typeDeclaration.methods = methodDeclarations;
329 hasConstructor = typeDeclaration.checkConstructors(this.parser());
331 for (int i = 0; i < existingCount; i++){
332 if (typeDeclaration.methods[i].isConstructor()) hasConstructor = true;
336 if (typeDeclaration.needClassInitMethod()){
337 boolean alreadyHasClinit = false;
338 for (int i = 0; i < existingCount; i++){
339 if (typeDeclaration.methods[i].isClinit()){
340 alreadyHasClinit = true;
344 if (!alreadyHasClinit) typeDeclaration.addClinit();
346 /* add default constructor ? */
347 if (defaultConstructorIndex >= 0 && hasRecoveredConstructor){
348 /* should discard previous default construtor */
349 AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[typeDeclaration.methods.length - 1];
350 if (defaultConstructorIndex != 0){
351 System.arraycopy(typeDeclaration.methods, 0, methodDeclarations, 0, defaultConstructorIndex);
353 if (defaultConstructorIndex != typeDeclaration.methods.length-1){
355 typeDeclaration.methods,
356 defaultConstructorIndex+1,
358 defaultConstructorIndex,
359 typeDeclaration.methods.length - defaultConstructorIndex - 1);
361 typeDeclaration.methods = methodDeclarations;
363 if (!hasConstructor && !typeDeclaration.isInterface()) {// if was already reduced, then constructor
364 boolean insideFieldInitializer = false;
365 RecoveredElement parentElement = this.parent;
366 while (parentElement != null){
367 if (parentElement instanceof RecoveredField){
368 insideFieldInitializer = true;
371 parentElement = parentElement.parent;
373 typeDeclaration.createsInternalConstructor(!parser().diet || insideFieldInitializer, true);
376 if (parent instanceof RecoveredType){
377 typeDeclaration.bits |= ASTNode.IsMemberTypeMASK;
378 } else if (parent instanceof RecoveredMethod){
379 typeDeclaration.bits |= ASTNode.IsLocalTypeMASK;
381 return typeDeclaration;
384 * Update the corresponding parse node from parser state which
385 * is about to disappear because of restarting recovery
387 public void updateFromParserState(){
389 if(this.bodyStartsAtHeaderEnd()){
390 Parser parser = this.parser();
391 /* might want to recover implemented interfaces */
392 // protection for bugs 15142
393 if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
394 int length = parser.astLengthStack[parser.astLengthPtr];
395 int astPtr = parser.astPtr - length;
396 boolean canConsume = astPtr >= 0;
398 if((!(parser.astStack[astPtr] instanceof TypeDeclaration))) {
401 for (int i = 1, max = length + 1; i < max; i++) {
402 if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
408 parser.consumeClassHeaderImplements();
409 // will reset typeListLength to zero
410 // thus this check will only be performed on first errorCheck after class X implements Y,Z,
416 * A closing brace got consumed, might have closed the current element,
417 * in which case both the currentElement is exited
419 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
420 if ((--bracketBalance <= 0) && (parent != null)){
421 this.updateSourceEndIfNecessary(braceStart, braceEnd);
422 this.bodyEnd = braceStart - 1;
428 * An opening brace got consumed, might be the expected opening one of the current element,
429 * in which case the bodyStart is updated.
431 public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
432 /* in case the opening brace is not close enough to the signature, ignore it */
433 if (bracketBalance == 0){
435 if (parser.scanner.searchLineNumber(typeDeclaration.sourceEnd)
436 != parser.scanner.searchLineNumber(braceEnd)){
438 Parser parser = this.parser();
439 switch(parser.lastIgnoredToken){
441 case TokenNameextends :
442 case TokenNameimplements :
443 if (parser.recoveredStaticInitializerStart == 0) break;
445 this.foundOpeningBrace = true;
446 bracketBalance = 1; // pretend the brace was already there
449 // might be an initializer
450 if (this.bracketBalance == 1){
451 Block block = new Block(0);
452 Parser parser = this.parser();
453 block.sourceStart = parser.scanner.startPosition;
455 if (parser.recoveredStaticInitializerStart == 0){
456 init = new Initializer(block, AccDefault);
458 init = new Initializer(block, AccStatic);
459 init.declarationSourceStart = parser.recoveredStaticInitializerStart;
461 init.bodyStart = parser.scanner.currentPosition;
462 return this.add(init, 1);
464 return super.updateOnOpeningBrace(braceStart, braceEnd);
466 public void updateParseTree(){
467 this.updatedTypeDeclaration();
470 * Update the declarationSourceEnd of the corresponding parse node
472 public void updateSourceEndIfNecessary(int start, int end){
473 if (this.typeDeclaration.declarationSourceEnd == 0){
475 this.typeDeclaration.declarationSourceEnd = end;
476 this.typeDeclaration.bodyEnd = end;