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 structure for parsing recovery
16 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
17 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
18 import org.eclipse.jdt.internal.compiler.ast.Block;
19 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
20 import org.eclipse.jdt.internal.compiler.ast.ImportReference;
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;
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 bracketBalanceValue) {
44 /* default behavior is to delegate recording to parent if any */
45 if (this.parent == null) return this; // ignore
46 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(methodDeclaration.declarationSourceStart - 1));
47 return this.parent.add(methodDeclaration, bracketBalanceValue);
50 * Record a nested block declaration
52 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
54 /* default behavior is to delegate recording to parent if any */
55 if (this.parent == null) return this; // ignore
56 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(nestedBlockDeclaration.sourceStart - 1));
57 return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
60 * Record a field declaration
62 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
64 /* default behavior is to delegate recording to parent if any */
65 if (this.parent == null) return this; // ignore
66 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
67 return this.parent.add(fieldDeclaration, bracketBalanceValue);
70 * Record an import reference
72 public RecoveredElement add(ImportReference importReference, int bracketBalanceValue){
74 /* default behavior is to delegate recording to parent if any */
75 if (this.parent == null) return this; // ignore
76 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(importReference.declarationSourceStart - 1));
77 return this.parent.add(importReference, bracketBalanceValue);
80 * Record a local declaration
82 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
84 /* default behavior is to delegate recording to parent if any */
85 if (this.parent == null) return this; // ignore
86 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
87 return this.parent.add(localDeclaration, bracketBalanceValue);
92 public RecoveredElement add(Statement statement, int bracketBalanceValue) {
94 /* default behavior is to delegate recording to parent if any */
95 if (this.parent == null) return this; // ignore
96 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(statement.sourceStart - 1));
97 return this.parent.add(statement, bracketBalanceValue);
100 * Record a type declaration
102 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue){
104 /* default behavior is to delegate recording to parent if any */
105 if (this.parent == null) return this; // ignore
106 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1));
107 return this.parent.add(typeDeclaration, bracketBalanceValue);
110 * Answer the depth of this element, considering the parent link.
114 RecoveredElement current = this;
115 while ((current = current.parent) != null) depth++;
119 * Answer the enclosing method node, or null if none
121 public RecoveredInitializer enclosingInitializer(){
122 RecoveredElement current = this;
123 while (current != null){
124 if (current instanceof RecoveredInitializer){
125 return (RecoveredInitializer) current;
127 current = current.parent;
132 * Answer the enclosing method node, or null if none
134 public RecoveredMethod enclosingMethod(){
135 RecoveredElement current = this;
136 while (current != null){
137 if (current instanceof RecoveredMethod){
138 return (RecoveredMethod) current;
140 current = current.parent;
145 * Answer the enclosing type node, or null if none
147 public RecoveredType enclosingType(){
148 RecoveredElement current = this;
149 while (current != null){
150 if (current instanceof RecoveredType){
151 return (RecoveredType) current;
153 current = current.parent;
158 * Answer the closest specified parser
160 public Parser parser(){
161 RecoveredElement current = this;
162 while (current != null){
163 if (current.recoveringParser != null){
164 return current.recoveringParser;
166 current = current.parent;
171 * Answer the associated parsed structure
173 public ASTNode parseTree(){
177 * Iterate the enclosing blocks and tag them so as to preserve their content
179 public void preserveEnclosingBlocks(){
180 RecoveredElement current = this;
181 while (current != null){
182 if (current instanceof RecoveredBlock){
183 ((RecoveredBlock)current).preserveContent = true;
185 if (current instanceof RecoveredType){ // for anonymous types
186 ((RecoveredType)current).preserveContent = true;
188 current = current.parent;
192 * Answer the position of the previous line end if
193 * there is nothing but spaces in between it and the
194 * line end. Used to trim spaces on unclosed elements.
196 public int previousAvailableLineEnd(int position){
198 Parser parser = this.parser();
199 if (parser == null) return position;
201 Scanner scanner = parser.scanner;
202 if (scanner.lineEnds == null) return position;
204 int index = scanner.getLineNumber(position);
205 if (index < 2) return position;
206 int previousLineEnd = scanner.lineEnds[index-2];
208 char[] source = scanner.source;
209 for (int i = previousLineEnd+1; i < position; i++){
210 if (!(source[i] == ' ' || source[i] == '\t')) return position;
212 return previousLineEnd;
215 * Answer the very source end of the corresponding parse node
217 public int sourceEnd(){
220 protected String tabString(int tab) {
221 StringBuffer result = new StringBuffer();
222 for (int i = tab; i > 0; i--) {
223 result.append(" "); //$NON-NLS-1$
225 return result.toString();
228 * Answer the top node
230 public RecoveredElement topElement(){
231 RecoveredElement current = this;
232 while (current.parent != null){
233 current = current.parent;
237 public String toString() {
240 public String toString(int tab) {
241 return super.toString();
244 * Answer the enclosing type node, or null if none
246 public RecoveredType type(){
247 RecoveredElement current = this;
248 while (current != null){
249 if (current instanceof RecoveredType){
250 return (RecoveredType) current;
252 current = current.parent;
257 * Update the bodyStart of the corresponding parse node
259 public void updateBodyStart(int bodyStart){
260 this.foundOpeningBrace = true;
263 * Update the corresponding parse node from parser state which
264 * is about to disappear because of restarting recovery
266 public void updateFromParserState(){
267 // default implementation: do nothing
270 * A closing brace got consumed, might have closed the current element,
271 * in which case both the currentElement is exited
273 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
274 if ((--this.bracketBalance <= 0) && (this.parent != null)){
275 this.updateSourceEndIfNecessary(braceStart, braceEnd);
281 * An opening brace got consumed, might be the expected opening one of the current element,
282 * in which case the bodyStart is updated.
284 /*public RecoveredElement updateOnOpeningBrace(int braceEnd){return null;}*/
285 public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
287 if (this.bracketBalance++ == 0){
288 this.updateBodyStart(braceEnd + 1);
291 return null; // no update is necessary
294 * Final update the corresponding parse node
296 public void updateParseTree(){
297 // default implementation: do nothing
300 * Update the declarationSourceEnd of the corresponding parse node
302 public void updateSourceEndIfNecessary(int braceStart, int braceEnd){
303 // default implementation: do nothing
305 public void updateSourceEndIfNecessary(int sourceEnd){
306 this.updateSourceEndIfNecessary(sourceEnd + 1, sourceEnd);