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;
14 * Internal block structure for parsing recovery
16 import org.eclipse.jdt.core.compiler.*;
17 import org.eclipse.jdt.internal.compiler.ast.Argument;
18 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
19 import org.eclipse.jdt.internal.compiler.ast.Block;
20 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
21 import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
22 import org.eclipse.jdt.internal.compiler.ast.Statement;
23 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
24 import org.eclipse.jdt.internal.compiler.lookup.BaseTypes;
25 import org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers;
27 public class RecoveredBlock extends RecoveredStatement implements CompilerModifiers, TerminalTokens, BaseTypes {
29 public Block blockDeclaration;
30 public RecoveredStatement[] statements;
31 public int statementCount;
32 public boolean preserveContent = false;
33 public RecoveredLocalVariable pendingArgument;
35 public RecoveredBlock(Block block, RecoveredElement parent, int bracketBalance){
36 super(block, parent, bracketBalance);
37 this.blockDeclaration = block;
38 this.foundOpeningBrace = true;
41 * Record a nested block declaration
43 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
45 /* do not consider a nested block starting passed the block end (if set)
46 it must be belonging to an enclosing block */
47 if (this.blockDeclaration.sourceEnd != 0
48 && nestedBlockDeclaration.sourceStart > this.blockDeclaration.sourceEnd){
49 return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
52 RecoveredBlock element = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue);
54 // if we have a pending Argument, promote it into the new block
55 if (this.pendingArgument != null){
56 element.attach(this.pendingArgument);
57 this.pendingArgument = null;
60 if (nestedBlockDeclaration.sourceEnd == 0) return element;
64 * Record a local declaration
66 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
67 return this.add(localDeclaration, bracketBalanceValue, false);
70 * Record a local declaration
72 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue, boolean delegatedByParent) {
74 /* local variables inside method can only be final and non void */
76 char[][] localTypeName;
77 if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
78 || (localDeclaration.type == null) // initializer
79 || ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
80 && CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
82 if (delegatedByParent){
85 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
86 return this.parent.add(localDeclaration, bracketBalance);
90 /* do not consider a local variable starting passed the block end (if set)
91 it must be belonging to an enclosing block */
92 if (this.blockDeclaration.sourceEnd != 0
93 && localDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd){
94 if (delegatedByParent) return this; //ignore
95 return this.parent.add(localDeclaration, bracketBalanceValue);
98 RecoveredLocalVariable element = new RecoveredLocalVariable(localDeclaration, this, bracketBalanceValue);
100 if (localDeclaration instanceof Argument){
101 this.pendingArgument = element;
105 this.attach(element);
106 if (localDeclaration.declarationSourceEnd == 0) return element;
110 * Record a statement declaration
112 public RecoveredElement add(Statement stmt, int bracketBalanceValue) {
113 return this.add(stmt, bracketBalanceValue, false);
117 * Record a statement declaration
119 public RecoveredElement add(Statement stmt, int bracketBalanceValue, boolean delegatedByParent) {
121 /* do not consider a nested block starting passed the block end (if set)
122 it must be belonging to an enclosing block */
123 if (this.blockDeclaration.sourceEnd != 0
124 && stmt.sourceStart > this.blockDeclaration.sourceEnd){
125 if (delegatedByParent) return this; //ignore
126 return this.parent.add(stmt, bracketBalanceValue);
129 RecoveredStatement element = new RecoveredStatement(stmt, this, bracketBalanceValue);
130 this.attach(element);
131 if (stmt.sourceEnd == 0) return element;
135 * Addition of a type to an initializer (act like inside method body)
137 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {
138 return this.add(typeDeclaration, bracketBalanceValue, false);
141 * Addition of a type to an initializer (act like inside method body)
143 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue, boolean delegatedByParent) {
145 /* do not consider a type starting passed the block end (if set)
146 it must be belonging to an enclosing block */
147 if (this.blockDeclaration.sourceEnd != 0
148 && typeDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd){
149 if (delegatedByParent) return this; //ignore
150 return this.parent.add(typeDeclaration, bracketBalanceValue);
153 RecoveredStatement element = new RecoveredType(typeDeclaration, this, bracketBalanceValue);
154 this.attach(element);
155 if (typeDeclaration.declarationSourceEnd == 0) return element;
159 * Attach a recovered statement
161 void attach(RecoveredStatement recoveredStatement) {
163 if (this.statements == null) {
164 this.statements = new RecoveredStatement[5];
165 this.statementCount = 0;
167 if (this.statementCount == this.statements.length) {
171 (this.statements = new RecoveredStatement[2 * this.statementCount]),
173 this.statementCount);
176 this.statements[this.statementCount++] = recoveredStatement;
179 * Answer the associated parsed structure
181 public ASTNode parseTree(){
182 return this.blockDeclaration;
184 public String toString(int tab) {
185 StringBuffer result = new StringBuffer(tabString(tab));
186 result.append("Recovered block:\n"); //$NON-NLS-1$
187 this.blockDeclaration.print(tab + 1, result);
188 if (this.statements != null) {
189 for (int i = 0; i < this.statementCount; i++) {
190 result.append("\n"); //$NON-NLS-1$
191 result.append(this.statements[i].toString(tab + 1));
194 return result.toString();
197 * Rebuild a block from the nested structure which is in scope
199 public Block updatedBlock(){
201 // if block was not marked to be preserved or empty, then ignore it
202 if (!this.preserveContent || this.statementCount == 0) return null;
204 Statement[] updatedStatements = new Statement[this.statementCount];
205 int updatedCount = 0;
207 // only collect the non-null updated statements
208 for (int i = 0; i < this.statementCount; i++){
209 Statement updatedStatement = this.statements[i].updatedStatement();
210 if (updatedStatement != null){
211 updatedStatements[updatedCount++] = updatedStatement;
214 if (updatedCount == 0) return null; // not interesting block
216 // resize statement collection if necessary
217 if (updatedCount != this.statementCount){
218 this.blockDeclaration.statements = new Statement[updatedCount];
219 System.arraycopy(updatedStatements, 0, this.blockDeclaration.statements, 0, updatedCount);
221 this.blockDeclaration.statements = updatedStatements;
224 return this.blockDeclaration;
227 * Rebuild a statement from the nested structure which is in scope
229 public Statement updatedStatement(){
231 return this.updatedBlock();
234 * A closing brace got consumed, might have closed the current element,
235 * in which case both the currentElement is exited
237 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
238 if ((--this.bracketBalance <= 0) && (this.parent != null)){
239 this.updateSourceEndIfNecessary(braceStart, braceEnd);
241 /* if the block is the method body, then it closes the method too */
242 RecoveredMethod method = enclosingMethod();
243 if (method != null && method.methodBody == this){
244 return this.parent.updateOnClosingBrace(braceStart, braceEnd);
246 RecoveredInitializer initializer = enclosingInitializer();
247 if (initializer != null && initializer.initializerBody == this){
248 return this.parent.updateOnClosingBrace(braceStart, braceEnd);
255 * An opening brace got consumed, might be the expected opening one of the current element,
256 * in which case the bodyStart is updated.
258 public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
260 // create a nested block
261 Block block = new Block(0);
262 block.sourceStart = parser().scanner.startPosition;
263 return this.add(block, 1);
266 * Final update the corresponding parse node
268 public void updateParseTree(){
273 * Rebuild a flattened block from the nested structure which is in scope
275 public Statement updateStatement(){
277 // if block was closed or empty, then ignore it
278 if (this.blockDeclaration.sourceEnd != 0 || this.statementCount == 0) return null;
280 Statement[] updatedStatements = new Statement[this.statementCount];
281 int updatedCount = 0;
283 // only collect the non-null updated statements
284 for (int i = 0; i < this.statementCount; i++){
285 Statement updatedStatement = this.statements[i].updatedStatement();
286 if (updatedStatement != null){
287 updatedStatements[updatedCount++] = updatedStatement;
290 if (updatedCount == 0) return null; // not interesting block
292 // resize statement collection if necessary
293 if (updatedCount != this.statementCount){
294 this.blockDeclaration.statements = new Statement[updatedCount];
295 System.arraycopy(updatedStatements, 0, this.blockDeclaration.statements, 0, updatedCount);
297 this.blockDeclaration.statements = updatedStatements;
300 return this.blockDeclaration;
304 * Record a field declaration
306 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
308 /* local variables inside method can only be final and non void */
309 char[][] fieldTypeName;
310 if ((fieldDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
311 || (fieldDeclaration.type == null) // initializer
312 || ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
313 && CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){
314 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
315 return this.parent.add(fieldDeclaration, bracketBalanceValue);
318 /* do not consider a local variable starting passed the block end (if set)
319 it must be belonging to an enclosing block */
320 if (this.blockDeclaration.sourceEnd != 0
321 && fieldDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd){
322 return this.parent.add(fieldDeclaration, bracketBalanceValue);
325 // ignore the added field, since indicates a local variable behind recovery point
326 // which thus got parsed as a field reference. This can happen if restarting after
327 // having reduced an assistNode to get the following context (see 1GEK7SG)