8c38f8bad9d3b8afdea5d26fc7c12a7d53fa88e3
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / parser / RecoveredBlock.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 /**
14  * Internal block structure for parsing recovery 
15  */
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;
27
28 public class RecoveredBlock extends RecoveredStatement implements CompilerModifiers, ITerminalSymbols, BaseTypes {
29
30         public Block blockDeclaration;
31
32         public RecoveredStatement[] statements;
33         public int statementCount;
34
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;
41 }
42 /*
43  * Record a nested block declaration 
44  */
45 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) {
46
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);
52         }
53                         
54         RecoveredBlock element = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalance);
55
56         // if we have a pending Argument, promote it into the new block
57         if (pendingArgument != null){
58                 element.attach(pendingArgument);
59                 pendingArgument = null;
60         }
61         this.attach(element);
62         if (nestedBlockDeclaration.sourceEnd == 0) return element;
63         return this;    
64 }
65 /*
66  * Record a local declaration 
67  */
68 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance) {
69         return this.add(localDeclaration, bracketBalance, false);
70 }
71 /*
72  * Record a local declaration 
73  */
74 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance, boolean delegatedByParent) {
75
76         /* local variables inside method can only be final and non void */
77 /*      
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()))){ 
83
84                 if (delegatedByParent){
85                         return this; //ignore
86                 } else {
87                         this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
88                         return this.parent.add(localDeclaration, bracketBalance);
89                 }
90         }
91 */      
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){
96
97                 if (delegatedByParent){
98                         return this; //ignore
99                 } else {
100                         return this.parent.add(localDeclaration, bracketBalance);
101                 }
102         }
103
104         RecoveredLocalVariable element = new RecoveredLocalVariable(localDeclaration, this, bracketBalance);
105
106         if (localDeclaration instanceof Argument){
107                 pendingArgument = element;
108                 return this;
109         }
110         
111         this.attach(element);
112         if (localDeclaration.declarationSourceEnd == 0) return element;
113         return this;    
114 }
115 /*
116  * Record a statement declaration 
117  */
118 public RecoveredElement add(Statement statement, int bracketBalance) {
119         return this.add(statement, bracketBalance, false);
120 }
121
122 /*
123  * Record a statement declaration 
124  */
125 public RecoveredElement add(Statement statement, int bracketBalance, boolean delegatedByParent) {
126
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){
131                         
132                 if (delegatedByParent){
133                         return this; //ignore
134                 } else {
135                         return this.parent.add(statement, bracketBalance);
136                 }                       
137         }
138                         
139         RecoveredStatement element = new RecoveredStatement(statement, this, bracketBalance);
140         this.attach(element);
141         if (statement.sourceEnd == 0) return element;
142         return this;    
143 }
144 /*
145  * Addition of a type to an initializer (act like inside method body)
146  */
147 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance) {
148         return this.add(typeDeclaration, bracketBalance, false);
149 }
150 /*
151  * Addition of a type to an initializer (act like inside method body)
152  */
153 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance, boolean delegatedByParent) {
154
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
161                 } else {
162                         return this.parent.add(typeDeclaration, bracketBalance);
163                 }
164         }
165                         
166         RecoveredStatement element = new RecoveredType(typeDeclaration, this, bracketBalance);
167         this.attach(element);
168         if (typeDeclaration.declarationSourceEnd == 0) return element;
169         return this;
170 }
171 /*
172  * Attach a recovered statement
173  */
174 void attach(RecoveredStatement recoveredStatement) {
175
176         if (statements == null) {
177                 statements = new RecoveredStatement[5];
178                 statementCount = 0;
179         } else {
180                 if (statementCount == statements.length) {
181                         System.arraycopy(
182                                 statements, 
183                                 0, 
184                                 (statements = new RecoveredStatement[2 * statementCount]), 
185                                 0, 
186                                 statementCount); 
187                 }
188         }
189         statements[statementCount++] = recoveredStatement;
190 }
191 /* 
192  * Answer the associated parsed structure
193  */
194 public AstNode parseTree(){
195         return blockDeclaration;
196 }
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));
205                 }
206         }
207         return result.toString();
208 }
209 /*
210  * Rebuild a block from the nested structure which is in scope
211  */
212 public Block updatedBlock(){
213
214         // if block was not marked to be preserved or empty, then ignore it
215         if (!preserveContent || statementCount == 0) return null;
216
217         Statement[] updatedStatements = new Statement[statementCount];
218         int updatedCount = 0;
219         
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;
225                 }
226         }
227         if (updatedCount == 0) return null; // not interesting block
228
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);
233         } else {
234                 blockDeclaration.statements = updatedStatements;
235         }
236
237         return blockDeclaration;
238 }
239 /*
240  * Rebuild a statement from the nested structure which is in scope
241  */
242 public Statement updatedStatement(){
243
244         return this.updatedBlock();
245 }
246 /*
247  * A closing brace got consumed, might have closed the current element,
248  * in which case both the currentElement is exited
249  */
250 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
251         if ((--bracketBalance <= 0) && (parent != null)){
252                 this.updateSourceEndIfNecessary(braceEnd);
253
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);
258                 }
259                 RecoveredInitializer initializer = enclosingInitializer();
260                 if (initializer != null && initializer.initializerBody == this){
261                         return parent.updateOnClosingBrace(braceStart, braceEnd);
262                 }
263                 return parent;
264         }
265         return this;
266 }
267 /*
268  * An opening brace got consumed, might be the expected opening one of the current element,
269  * in which case the bodyStart is updated.
270  */
271 public RecoveredElement updateOnOpeningBrace(int currentPosition){
272
273         // create a nested block
274         Block block = new Block(0);
275         block.sourceStart = parser().scanner.startPosition;
276         return this.add(block, 1);
277 }
278 /*
279  * Final update the corresponding parse node
280  */
281 public void updateParseTree(){
282
283         this.updatedBlock();
284 }
285 /*
286  * Rebuild a flattened block from the nested structure which is in scope
287  */
288 public Statement updateStatement(){
289
290         // if block was closed or empty, then ignore it
291         if (this.blockDeclaration.sourceEnd != 0 || statementCount == 0) return null;
292
293         Statement[] updatedStatements = new Statement[statementCount];
294         int updatedCount = 0;
295         
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;
301                 }
302         }
303         if (updatedCount == 0) return null; // not interesting block
304
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);
309         } else {
310                 blockDeclaration.statements = updatedStatements;
311         }
312
313         return blockDeclaration;
314 }
315
316 /*
317  * Record a field declaration 
318  */
319 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
320
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);
329         }
330         
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);
336         }
337
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)
341         return this;    
342 }
343 }