Fix nasty bug #706. See trac.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / parser / RecoveredElement.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.parser;
12
13 import net.sourceforge.phpdt.internal.compiler.ast.ASTNode;
14 import net.sourceforge.phpdt.internal.compiler.ast.Block;
15 import net.sourceforge.phpdt.internal.compiler.ast.Statement;
16
17 /**
18  * Internal structure for parsing recovery
19  */
20 public class RecoveredElement {
21
22         public RecoveredElement parent;
23
24         public int bracketBalance;
25
26         public boolean foundOpeningBrace;
27
28         protected Parser recoveringParser;
29
30         public RecoveredElement(RecoveredElement parent, int bracketBalance) {
31                 this(parent, bracketBalance, null);
32         }
33
34         public RecoveredElement(RecoveredElement parent, int bracketBalance,
35                         Parser parser) {
36                 this.parent = parent;
37                 this.bracketBalance = bracketBalance;
38                 this.recoveringParser = parser;
39         }
40
41         /*
42          * Record a method declaration
43          */
44         // public RecoveredElement add(AbstractMethodDeclaration methodDeclaration,
45         // int bracketBalance) {
46         //
47         // /* default behavior is to delegate recording to parent if any */
48         // if (parent == null) {
49         // return this; // ignore
50         // } else {
51         // this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(methodDeclaration.declarationSourceStart
52         // - 1));
53         // return this.parent.add(methodDeclaration, bracketBalance);
54         // }
55         // }
56         /*
57          * Record a nested block declaration
58          */
59         public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) {
60
61                 /* default behavior is to delegate recording to parent if any */
62                 if (parent == null) {
63                         return this; // ignore
64                 } else {
65                         this
66                                         .updateSourceEndIfNecessary(this
67                                                         .previousAvailableLineEnd(nestedBlockDeclaration.sourceStart - 1));
68                         return this.parent.add(nestedBlockDeclaration, bracketBalance);
69                 }
70         }
71
72         /*
73          * Record a field declaration
74          */
75         // public RecoveredElement add(FieldDeclaration fieldDeclaration, int
76         // bracketBalance) {
77         //
78         // /* default behavior is to delegate recording to parent if any */
79         // if (parent == null) {
80         // return this; // ignore
81         // } else {
82         // this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart
83         // - 1));
84         // return this.parent.add(fieldDeclaration, bracketBalance);
85         // }
86         // }
87         // /*
88         // * Record an import reference
89         // */
90         // public RecoveredElement add(ImportReference importReference, int
91         // bracketBalance){
92         //
93         // /* default behavior is to delegate recording to parent if any */
94         // if (parent == null) {
95         // return this; // ignore
96         // } else {
97         // this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(importReference.declarationSourceStart
98         // - 1));
99         // return this.parent.add(importReference, bracketBalance);
100         // }
101         // }
102         // /*
103         // * Record a local declaration
104         // */
105         // public RecoveredElement add(LocalDeclaration localDeclaration, int
106         // bracketBalance) {
107         //
108         // /* default behavior is to delegate recording to parent if any */
109         // if (parent == null) {
110         // return this; // ignore
111         // } else {
112         // this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart
113         // - 1));
114         // return this.parent.add(localDeclaration, bracketBalance);
115         // }
116         // }
117         /*
118          * Record a statement
119          */
120         public RecoveredElement add(Statement statement, int bracketBalance) {
121
122                 /* default behavior is to delegate recording to parent if any */
123                 if (parent == null) {
124                         return this; // ignore
125                 } else {
126                         this.updateSourceEndIfNecessary(this
127                                         .previousAvailableLineEnd(statement.sourceStart - 1));
128                         return this.parent.add(statement, bracketBalance);
129                 }
130         }
131
132         /*
133          * Record a type declaration
134          */
135         // public RecoveredElement add(TypeDeclaration typeDeclaration, int
136         // bracketBalance){
137         //
138         // /* default behavior is to delegate recording to parent if any */
139         // if (parent == null) {
140         // return this; // ignore
141         // } else {
142         // this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart
143         // - 1));
144         // return this.parent.add(typeDeclaration, bracketBalance);
145         // }
146         // }
147         /*
148          * Answer the depth of this element, considering the parent link.
149          */
150         public int depth() {
151                 int depth = 0;
152                 RecoveredElement current = this;
153                 while ((current = current.parent) != null)
154                         depth++;
155                 return depth;
156         }
157
158         /*
159          * Answer the enclosing method node, or null if none
160          */
161         // public RecoveredInitializer enclosingInitializer(){
162         // RecoveredElement current = this;
163         // while (current != null){
164         // if (current instanceof RecoveredInitializer){
165         // return (RecoveredInitializer) current;
166         // }
167         // current = current.parent;
168         // }
169         // return null;
170         // }
171         /*
172          * Answer the enclosing method node, or null if none
173          */
174         // public RecoveredMethod enclosingMethod(){
175         // RecoveredElement current = this;
176         // while (current != null){
177         // if (current instanceof RecoveredMethod){
178         // return (RecoveredMethod) current;
179         // }
180         // current = current.parent;
181         // }
182         // return null;
183         // }
184         /*
185          * Answer the enclosing type node, or null if none
186          */
187         // public RecoveredType enclosingType(){
188         // RecoveredElement current = this;
189         // while (current != null){
190         // if (current instanceof RecoveredType){
191         // return (RecoveredType) current;
192         // }
193         // current = current.parent;
194         // }
195         // return null;
196         // }
197         /*
198          * Answer the closest specified parser
199          */
200         public Parser parser() {
201                 RecoveredElement current = this;
202                 while (current != null) {
203                         if (current.recoveringParser != null) {
204                                 return current.recoveringParser;
205                         }
206                         current = current.parent;
207                 }
208                 return null;
209         }
210
211         /*
212          * Answer the associated parsed structure
213          */
214         public ASTNode parseTree() {
215                 return null;
216         }
217
218         /*
219          * Iterate the enclosing blocks and tag them so as to preserve their content
220          */
221         // public void preserveEnclosingBlocks(){
222         // RecoveredElement current = this;
223         // while (current != null){
224         // if (current instanceof RecoveredBlock){
225         // ((RecoveredBlock)current).preserveContent = true;
226         // }
227         // if (current instanceof RecoveredType){ // for anonymous types
228         // ((RecoveredType)current).preserveContent = true;
229         // }
230         // current = current.parent;
231         // }
232         // }
233         /*
234          * Answer the position of the previous line end if there is nothing but
235          * spaces in between it and the line end. Used to trim spaces on unclosed
236          * elements.
237          */
238         public int previousAvailableLineEnd(int position) {
239
240                 Parser parser = this.parser();
241                 if (parser == null)
242                         return position;
243
244                 Scanner scanner = parser.scanner;
245                 if (scanner.lineEnds == null)
246                         return position;
247
248                 int index = scanner.getLineNumber(position);
249                 if (index < 2)
250                         return position;
251                 int previousLineEnd = scanner.lineEnds[index - 2];
252
253                 char[] source = scanner.source;
254                 for (int i = previousLineEnd + 1; i < position; i++) {
255                         if (!(source[i] == ' ' || source[i] == '\t'))
256                                 return position;
257                 }
258                 return previousLineEnd;
259         }
260
261         /*
262          * Answer the very source end of the corresponding parse node
263          */
264         public int sourceEnd() {
265                 return 0;
266         }
267
268         protected String tabString(int tab) {
269                 StringBuffer result = new StringBuffer();
270                 for (int i = tab; i > 0; i--) {
271                         result.append("  "); //$NON-NLS-1$
272                 }
273                 return result.toString();
274         }
275
276         /*
277          * Answer the top node
278          */
279         public RecoveredElement topElement() {
280                 RecoveredElement current = this;
281                 while (current.parent != null) {
282                         current = current.parent;
283                 }
284                 return current;
285         }
286
287         public String toString() {
288                 return toString(0);
289         }
290
291         public String toString(int tab) {
292                 return super.toString();
293         }
294
295         /*
296          * Answer the enclosing type node, or null if none
297          */
298         // public RecoveredType type(){
299         // RecoveredElement current = this;
300         // while (current != null){
301         // if (current instanceof RecoveredType){
302         // return (RecoveredType) current;
303         // }
304         // current = current.parent;
305         // }
306         // return null;
307         // }
308         /*
309          * Update the bodyStart of the corresponding parse node
310          */
311         public void updateBodyStart(int bodyStart) {
312                 this.foundOpeningBrace = true;
313         }
314
315         /*
316          * Update the corresponding parse node from parser state which is about to
317          * disappear because of restarting recovery
318          */
319         public void updateFromParserState() {
320         }
321
322         /*
323          * A closing brace got consumed, might have closed the current element, in
324          * which case both the currentElement is exited
325          */
326         public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd) {
327                 if ((--bracketBalance <= 0) && (parent != null)) {
328                         this.updateSourceEndIfNecessary(braceEnd);
329                         return parent;
330                 }
331                 return this;
332         }
333
334         /*
335          * An opening brace got consumed, might be the expected opening one of the
336          * current element, in which case the bodyStart is updated.
337          */
338         public RecoveredElement updateOnOpeningBrace(int braceEnd) {
339
340                 if (bracketBalance++ == 0) {
341                         this.updateBodyStart(braceEnd + 1);
342                         return this;
343                 }
344                 return null; // no update is necessary
345         }
346
347         /*
348          * Final update the corresponding parse node
349          */
350         public void updateParseTree() {
351         }
352
353         /*
354          * Update the declarationSourceEnd of the corresponding parse node
355          */
356         public void updateSourceEndIfNecessary(int sourceEnd) {
357         }
358 }