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