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 structure for parsing recovery
16 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
17 import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
18 import net.sourceforge.phpdt.internal.compiler.ast.Block;
19 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
20 import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
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;
25 public class RecoveredElement {
27 public RecoveredElement parent;
28 public int bracketBalance;
29 public boolean foundOpeningBrace;
30 protected Parser recoveringParser;
31 public RecoveredElement(RecoveredElement parent, int bracketBalance){
32 this(parent, bracketBalance, null);
34 public RecoveredElement(RecoveredElement parent, int bracketBalance, Parser parser){
36 this.bracketBalance = bracketBalance;
37 this.recoveringParser = parser;
40 * Record a method declaration
42 public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalance) {
44 /* default behavior is to delegate recording to parent if any */
46 return this; // ignore
48 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(methodDeclaration.declarationSourceStart - 1));
49 return this.parent.add(methodDeclaration, bracketBalance);
53 * Record a nested block declaration
55 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) {
57 /* default behavior is to delegate recording to parent if any */
59 return this; // ignore
61 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(nestedBlockDeclaration.sourceStart - 1));
62 return this.parent.add(nestedBlockDeclaration, bracketBalance);
66 * Record a field declaration
68 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
70 /* default behavior is to delegate recording to parent if any */
72 return this; // ignore
74 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
75 return this.parent.add(fieldDeclaration, bracketBalance);
79 * Record an import reference
81 public RecoveredElement add(ImportReference importReference, int bracketBalance){
83 /* default behavior is to delegate recording to parent if any */
85 return this; // ignore
87 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(importReference.declarationSourceStart - 1));
88 return this.parent.add(importReference, bracketBalance);
92 * Record a local declaration
94 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance) {
96 /* default behavior is to delegate recording to parent if any */
98 return this; // ignore
100 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
101 return this.parent.add(localDeclaration, bracketBalance);
107 public RecoveredElement add(Statement statement, int bracketBalance) {
109 /* default behavior is to delegate recording to parent if any */
110 if (parent == null) {
111 return this; // ignore
113 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(statement.sourceStart - 1));
114 return this.parent.add(statement, bracketBalance);
118 * Record a type declaration
120 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance){
122 /* default behavior is to delegate recording to parent if any */
123 if (parent == null) {
124 return this; // ignore
126 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1));
127 return this.parent.add(typeDeclaration, bracketBalance);
131 * Answer the depth of this element, considering the parent link.
135 RecoveredElement current = this;
136 while ((current = current.parent) != null) depth++;
140 * Answer the enclosing method node, or null if none
142 public RecoveredInitializer enclosingInitializer(){
143 RecoveredElement current = this;
144 while (current != null){
145 if (current instanceof RecoveredInitializer){
146 return (RecoveredInitializer) current;
148 current = current.parent;
153 * Answer the enclosing method node, or null if none
155 public RecoveredMethod enclosingMethod(){
156 RecoveredElement current = this;
157 while (current != null){
158 if (current instanceof RecoveredMethod){
159 return (RecoveredMethod) current;
161 current = current.parent;
166 * Answer the enclosing type node, or null if none
168 public RecoveredType enclosingType(){
169 RecoveredElement current = this;
170 while (current != null){
171 if (current instanceof RecoveredType){
172 return (RecoveredType) current;
174 current = current.parent;
179 * Answer the closest specified parser
181 public Parser parser(){
182 RecoveredElement current = this;
183 while (current != null){
184 if (current.recoveringParser != null){
185 return current.recoveringParser;
187 current = current.parent;
192 * Answer the associated parsed structure
194 public AstNode parseTree(){
198 * Iterate the enclosing blocks and tag them so as to preserve their content
200 public void preserveEnclosingBlocks(){
201 RecoveredElement current = this;
202 while (current != null){
203 if (current instanceof RecoveredBlock){
204 ((RecoveredBlock)current).preserveContent = true;
206 if (current instanceof RecoveredType){ // for anonymous types
207 ((RecoveredType)current).preserveContent = true;
209 current = current.parent;
213 * Answer the position of the previous line end if
214 * there is nothing but spaces in between it and the
215 * line end. Used to trim spaces on unclosed elements.
217 public int previousAvailableLineEnd(int position){
219 Parser parser = this.parser();
220 if (parser == null) return position;
222 Scanner scanner = parser.scanner;
223 if (scanner.lineEnds == null) return position;
225 int index = scanner.getLineNumber(position);
226 if (index < 2) return position;
227 int previousLineEnd = scanner.lineEnds[index-2];
229 char[] source = scanner.source;
230 for (int i = previousLineEnd+1; i < position; i++){
231 if (!(source[i] == ' ' || source[i] == '\t')) return position;
233 return previousLineEnd;
236 * Answer the very source end of the corresponding parse node
238 public int sourceEnd(){
241 protected String tabString(int tab) {
242 StringBuffer result = new StringBuffer();
243 for (int i = tab; i > 0; i--) {
244 result.append(" "); //$NON-NLS-1$
246 return result.toString();
249 * Answer the top node
251 public RecoveredElement topElement(){
252 RecoveredElement current = this;
253 while (current.parent != null){
254 current = current.parent;
258 public String toString() {
261 public String toString(int tab) {
262 return super.toString();
265 * Answer the enclosing type node, or null if none
267 public RecoveredType type(){
268 RecoveredElement current = this;
269 while (current != null){
270 if (current instanceof RecoveredType){
271 return (RecoveredType) current;
273 current = current.parent;
278 * Update the bodyStart of the corresponding parse node
280 public void updateBodyStart(int bodyStart){
281 this.foundOpeningBrace = true;
284 * Update the corresponding parse node from parser state which
285 * is about to disappear because of restarting recovery
287 public void updateFromParserState(){
290 * A closing brace got consumed, might have closed the current element,
291 * in which case both the currentElement is exited
293 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
294 if ((--bracketBalance <= 0) && (parent != null)){
295 this.updateSourceEndIfNecessary(braceEnd);
301 * An opening brace got consumed, might be the expected opening one of the current element,
302 * in which case the bodyStart is updated.
304 public RecoveredElement updateOnOpeningBrace(int braceEnd){
306 if (bracketBalance++ == 0){
307 this.updateBodyStart(braceEnd + 1);
310 return null; // no update is necessary
313 * Final update the corresponding parse node
315 public void updateParseTree(){
318 * Update the declarationSourceEnd of the corresponding parse node
320 public void updateSourceEndIfNecessary(int sourceEnd){