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;
14 * Internal block structure for parsing recovery
16 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
17 import net.sourceforge.phpdt.internal.compiler.ast.Argument;
18 import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
19 import net.sourceforge.phpdt.internal.compiler.ast.Block;
20 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
21 import net.sourceforge.phpdt.internal.compiler.ast.LocalDeclaration;
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.lookup.BaseTypes;
25 import net.sourceforge.phpdt.internal.compiler.lookup.CompilerModifiers;
26 import net.sourceforge.phpdt.internal.compiler.util.CharOperation;
28 public class RecoveredBlock extends RecoveredStatement implements CompilerModifiers, ITerminalSymbols, BaseTypes {
30 public Block blockDeclaration;
32 public RecoveredStatement[] statements;
33 public int statementCount;
35 public boolean preserveContent = false;
36 public RecoveredLocalVariable pendingArgument;
37 public RecoveredBlock(Block block, RecoveredElement parent, int bracketBalance){
38 super(block, parent, bracketBalance);
39 this.blockDeclaration = block;
40 this.foundOpeningBrace = true;
43 * Record a nested block declaration
45 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) {
47 /* do not consider a nested block starting passed the block end (if set)
48 it must be belonging to an enclosing block */
49 if (blockDeclaration.sourceEnd != 0
50 && nestedBlockDeclaration.sourceStart > blockDeclaration.sourceEnd){
51 return this.parent.add(nestedBlockDeclaration, bracketBalance);
54 RecoveredBlock element = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalance);
56 // if we have a pending Argument, promote it into the new block
57 if (pendingArgument != null){
58 element.attach(pendingArgument);
59 pendingArgument = null;
62 if (nestedBlockDeclaration.sourceEnd == 0) return element;
66 * Record a local declaration
68 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance) {
69 return this.add(localDeclaration, bracketBalance, false);
72 * Record a local declaration
74 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance, boolean delegatedByParent) {
76 /* local variables inside method can only be final and non void */
78 char[][] localTypeName;
79 if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
80 || (localDeclaration.type == null) // initializer
81 || ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
82 && CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
84 if (delegatedByParent){
87 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
88 return this.parent.add(localDeclaration, bracketBalance);
92 /* do not consider a local variable starting passed the block end (if set)
93 it must be belonging to an enclosing block */
94 if (blockDeclaration.sourceEnd != 0
95 && localDeclaration.declarationSourceStart > blockDeclaration.sourceEnd){
97 if (delegatedByParent){
100 return this.parent.add(localDeclaration, bracketBalance);
104 RecoveredLocalVariable element = new RecoveredLocalVariable(localDeclaration, this, bracketBalance);
106 if (localDeclaration instanceof Argument){
107 pendingArgument = element;
111 this.attach(element);
112 if (localDeclaration.declarationSourceEnd == 0) return element;
116 * Record a statement declaration
118 public RecoveredElement add(Statement statement, int bracketBalance) {
119 return this.add(statement, bracketBalance, false);
123 * Record a statement declaration
125 public RecoveredElement add(Statement statement, int bracketBalance, boolean delegatedByParent) {
127 /* do not consider a nested block starting passed the block end (if set)
128 it must be belonging to an enclosing block */
129 if (blockDeclaration.sourceEnd != 0
130 && statement.sourceStart > blockDeclaration.sourceEnd){
132 if (delegatedByParent){
133 return this; //ignore
135 return this.parent.add(statement, bracketBalance);
139 RecoveredStatement element = new RecoveredStatement(statement, this, bracketBalance);
140 this.attach(element);
141 if (statement.sourceEnd == 0) return element;
145 * Addition of a type to an initializer (act like inside method body)
147 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance) {
148 return this.add(typeDeclaration, bracketBalance, false);
151 * Addition of a type to an initializer (act like inside method body)
153 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance, boolean delegatedByParent) {
155 /* do not consider a type starting passed the block end (if set)
156 it must be belonging to an enclosing block */
157 if (blockDeclaration.sourceEnd != 0
158 && typeDeclaration.declarationSourceStart > blockDeclaration.sourceEnd){
159 if (delegatedByParent){
160 return this; //ignore
162 return this.parent.add(typeDeclaration, bracketBalance);
166 RecoveredStatement element = new RecoveredType(typeDeclaration, this, bracketBalance);
167 this.attach(element);
168 if (typeDeclaration.declarationSourceEnd == 0) return element;
172 * Attach a recovered statement
174 void attach(RecoveredStatement recoveredStatement) {
176 if (statements == null) {
177 statements = new RecoveredStatement[5];
180 if (statementCount == statements.length) {
184 (statements = new RecoveredStatement[2 * statementCount]),
189 statements[statementCount++] = recoveredStatement;
192 * Answer the associated parsed structure
194 public AstNode parseTree(){
195 return blockDeclaration;
197 public String toString(int tab) {
198 StringBuffer result = new StringBuffer(tabString(tab));
199 result.append("Recovered block:\n"); //$NON-NLS-1$
200 result.append(blockDeclaration.toString(tab + 1));
201 if (this.statements != null) {
202 for (int i = 0; i < this.statementCount; i++) {
203 result.append("\n"); //$NON-NLS-1$
204 result.append(this.statements[i].toString(tab + 1));
207 return result.toString();
210 * Rebuild a block from the nested structure which is in scope
212 public Block updatedBlock(){
214 // if block was not marked to be preserved or empty, then ignore it
215 if (!preserveContent || statementCount == 0) return null;
217 Statement[] updatedStatements = new Statement[statementCount];
218 int updatedCount = 0;
220 // only collect the non-null updated statements
221 for (int i = 0; i < statementCount; i++){
222 Statement updatedStatement = statements[i].updatedStatement();
223 if (updatedStatement != null){
224 updatedStatements[updatedCount++] = updatedStatement;
227 if (updatedCount == 0) return null; // not interesting block
229 // resize statement collection if necessary
230 if (updatedCount != statementCount){
231 blockDeclaration.statements = new Statement[updatedCount];
232 System.arraycopy(updatedStatements, 0, blockDeclaration.statements, 0, updatedCount);
234 blockDeclaration.statements = updatedStatements;
237 return blockDeclaration;
240 * Rebuild a statement from the nested structure which is in scope
242 public Statement updatedStatement(){
244 return this.updatedBlock();
247 * A closing brace got consumed, might have closed the current element,
248 * in which case both the currentElement is exited
250 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
251 if ((--bracketBalance <= 0) && (parent != null)){
252 this.updateSourceEndIfNecessary(braceEnd);
254 /* if the block is the method body, then it closes the method too */
255 RecoveredMethod method = enclosingMethod();
256 if (method != null && method.methodBody == this){
257 return parent.updateOnClosingBrace(braceStart, braceEnd);
259 RecoveredInitializer initializer = enclosingInitializer();
260 if (initializer != null && initializer.initializerBody == this){
261 return parent.updateOnClosingBrace(braceStart, braceEnd);
268 * An opening brace got consumed, might be the expected opening one of the current element,
269 * in which case the bodyStart is updated.
271 public RecoveredElement updateOnOpeningBrace(int currentPosition){
273 // create a nested block
274 Block block = new Block(0);
275 block.sourceStart = parser().scanner.startPosition;
276 return this.add(block, 1);
279 * Final update the corresponding parse node
281 public void updateParseTree(){
286 * Rebuild a flattened block from the nested structure which is in scope
288 public Statement updateStatement(){
290 // if block was closed or empty, then ignore it
291 if (this.blockDeclaration.sourceEnd != 0 || statementCount == 0) return null;
293 Statement[] updatedStatements = new Statement[statementCount];
294 int updatedCount = 0;
296 // only collect the non-null updated statements
297 for (int i = 0; i < statementCount; i++){
298 Statement updatedStatement = statements[i].updatedStatement();
299 if (updatedStatement != null){
300 updatedStatements[updatedCount++] = updatedStatement;
303 if (updatedCount == 0) return null; // not interesting block
305 // resize statement collection if necessary
306 if (updatedCount != statementCount){
307 blockDeclaration.statements = new Statement[updatedCount];
308 System.arraycopy(updatedStatements, 0, blockDeclaration.statements, 0, updatedCount);
310 blockDeclaration.statements = updatedStatements;
313 return blockDeclaration;
317 * Record a field declaration
319 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
321 /* local variables inside method can only be final and non void */
322 char[][] fieldTypeName;
323 if ((fieldDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
324 || (fieldDeclaration.type == null) // initializer
325 || ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
326 && CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){
327 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
328 return this.parent.add(fieldDeclaration, bracketBalance);
331 /* do not consider a local variable starting passed the block end (if set)
332 it must be belonging to an enclosing block */
333 if (blockDeclaration.sourceEnd != 0
334 && fieldDeclaration.declarationSourceStart > blockDeclaration.sourceEnd){
335 return this.parent.add(fieldDeclaration, bracketBalance);
338 // ignore the added field, since indicates a local variable behind recovery point
339 // which thus got parsed as a field reference. This can happen if restarting after
340 // having reduced an assistNode to get the following context (see 1GEK7SG)