added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / parser / RecoveredMethod.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.parser;
12
13 import org.eclipse.jdt.core.compiler.*;
14 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
15 import org.eclipse.jdt.internal.compiler.ast.Argument;
16 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
17 import org.eclipse.jdt.internal.compiler.ast.Block;
18 import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
19 import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
20 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
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.SuperReference;
24 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
25 import org.eclipse.jdt.internal.compiler.ast.TypeReference;
26 import org.eclipse.jdt.internal.compiler.env.IGenericType;
27 import org.eclipse.jdt.internal.compiler.lookup.BaseTypes;
28 import org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers;
29
30 /**
31  * Internal method structure for parsing recovery 
32  */
33
34 public class RecoveredMethod extends RecoveredElement implements CompilerModifiers, TerminalTokens, BaseTypes {
35
36         public AbstractMethodDeclaration methodDeclaration;
37
38         public RecoveredType[] localTypes;
39         public int localTypeCount;
40
41         public RecoveredBlock methodBody;
42         public boolean discardBody = true;
43
44 public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){
45         super(parent, bracketBalance, parser);
46         this.methodDeclaration = methodDeclaration;
47         this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
48         if(this.foundOpeningBrace) {
49                 this.bracketBalance++;
50         }
51 }
52 /*
53  * Record a nested block declaration
54  */
55 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
56
57         /* default behavior is to delegate recording to parent if any,
58         do not consider elements passed the known end (if set)
59         it must be belonging to an enclosing element 
60         */
61         if (methodDeclaration.declarationSourceEnd > 0
62                 && nestedBlockDeclaration.sourceStart
63                         > methodDeclaration.declarationSourceEnd){
64                                 if (this.parent == null){
65                                         return this; // ignore
66                                 } else {
67                                         return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
68                                 }
69         }
70         /* consider that if the opening brace was not found, it is there */
71         if (!foundOpeningBrace){
72                 foundOpeningBrace = true;
73                 this.bracketBalance++;
74         }
75
76         methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue);
77         if (nestedBlockDeclaration.sourceEnd == 0) return methodBody;
78         return this;
79 }
80 /*
81  * Record a field declaration
82  */
83 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
84
85         /* local variables inside method can only be final and non void */
86         char[][] fieldTypeName; 
87         if ((fieldDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final 
88                 || (fieldDeclaration.type == null) // initializer
89                 || ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
90                         && CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){ 
91
92                 if (this.parent == null){
93                         return this; // ignore
94                 } else {
95                         this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
96                         return this.parent.add(fieldDeclaration, bracketBalanceValue);
97                 }
98         }
99         /* default behavior is to delegate recording to parent if any,
100         do not consider elements passed the known end (if set)
101         it must be belonging to an enclosing element 
102         */
103         if (methodDeclaration.declarationSourceEnd > 0
104                 && fieldDeclaration.declarationSourceStart
105                         > methodDeclaration.declarationSourceEnd){
106                 if (this.parent == null){
107                         return this; // ignore
108                 } else {
109                         return this.parent.add(fieldDeclaration, bracketBalanceValue);
110                 }
111         }
112         /* consider that if the opening brace was not found, it is there */
113         if (!foundOpeningBrace){
114                 foundOpeningBrace = true;
115                 this.bracketBalance++;
116         }
117         // still inside method, treat as local variable
118         return this; // ignore
119 }
120 /*
121  * Record a local declaration - regular method should have been created a block body
122  */
123 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
124
125         /* local variables inside method can only be final and non void */
126 /*      
127         char[][] localTypeName; 
128         if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final 
129                 || (localDeclaration.type == null) // initializer
130                 || ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
131                         && CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){ 
132
133                 if (this.parent == null){
134                         return this; // ignore
135                 } else {
136                         this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
137                         return this.parent.add(localDeclaration, bracketBalance);
138                 }
139         }
140 */
141         /* do not consider a type starting passed the type end (if set)
142                 it must be belonging to an enclosing type */
143         if (methodDeclaration.declarationSourceEnd != 0 
144                 && localDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
145                         
146                 if (this.parent == null) {
147                         return this; // ignore
148                 } else {
149                         return this.parent.add(localDeclaration, bracketBalanceValue);
150                 }
151         }
152         if (methodBody == null){
153                 Block block = new Block(0);
154                 block.sourceStart = methodDeclaration.bodyStart;
155                 RecoveredElement currentBlock = this.add(block, 1);
156                 if (this.bracketBalance > 0){
157                         for (int i = 0; i < this.bracketBalance - 1; i++){
158                                 currentBlock = currentBlock.add(new Block(0), 1);
159                         }
160                         this.bracketBalance = 1;
161                 }
162                 return currentBlock.add(localDeclaration, bracketBalanceValue);
163         }
164         return methodBody.add(localDeclaration, bracketBalanceValue, true);
165 }
166 /*
167  * Record a statement - regular method should have been created a block body
168  */
169 public RecoveredElement add(Statement statement, int bracketBalanceValue) {
170
171         /* do not consider a type starting passed the type end (if set)
172                 it must be belonging to an enclosing type */
173         if (methodDeclaration.declarationSourceEnd != 0 
174                 && statement.sourceStart > methodDeclaration.declarationSourceEnd){
175
176                 if (this.parent == null) {
177                         return this; // ignore
178                 } else {
179                         return this.parent.add(statement, bracketBalanceValue);
180                 }
181         }
182         if (methodBody == null){
183                 Block block = new Block(0);
184                 block.sourceStart = methodDeclaration.bodyStart;
185                 RecoveredElement currentBlock = this.add(block, 1);
186                 if (this.bracketBalance > 0){
187                         for (int i = 0; i < this.bracketBalance - 1; i++){
188                                 currentBlock = currentBlock.add(new Block(0), 1);
189                         }
190                         this.bracketBalance = 1;
191                 }
192                 return currentBlock.add(statement, bracketBalanceValue);
193         }
194         return methodBody.add(statement, bracketBalanceValue, true);    
195 }
196 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {
197
198         /* do not consider a type starting passed the type end (if set)
199                 it must be belonging to an enclosing type */
200         if (methodDeclaration.declarationSourceEnd != 0 
201                 && typeDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
202                         
203                 if (this.parent == null) {
204                         return this; // ignore
205                 }
206                 return this.parent.add(typeDeclaration, bracketBalanceValue);
207         }
208         if ((typeDeclaration.bits & ASTNode.IsLocalTypeMASK) != 0){
209                 if (methodBody == null){
210                         Block block = new Block(0);
211                         block.sourceStart = methodDeclaration.bodyStart;
212                         this.add(block, 1);
213                 }
214                 return methodBody.add(typeDeclaration, bracketBalanceValue, true);      
215         }
216         if (typeDeclaration.kind() == IGenericType.INTERFACE_DECL) {
217                 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1));
218                 if (this.parent == null) {
219                         return this; // ignore
220                 }
221                 // close the constructor
222                 return this.parent.add(typeDeclaration, bracketBalanceValue);
223         }
224         if (localTypes == null) {
225                 localTypes = new RecoveredType[5];
226                 localTypeCount = 0;
227         } else {
228                 if (localTypeCount == localTypes.length) {
229                         System.arraycopy(
230                                 localTypes, 
231                                 0, 
232                                 (localTypes = new RecoveredType[2 * localTypeCount]), 
233                                 0, 
234                                 localTypeCount); 
235                 }
236         }
237         RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue);
238         localTypes[localTypeCount++] = element;
239
240         /* consider that if the opening brace was not found, it is there */
241         if (!foundOpeningBrace){
242                 foundOpeningBrace = true;
243                 this.bracketBalance++;
244         }
245         return element;
246 }
247 public boolean bodyStartsAtHeaderEnd(){
248         return methodDeclaration.bodyStart == methodDeclaration.sourceEnd+1;
249 }
250 /* 
251  * Answer the associated parsed structure
252  */
253 public ASTNode parseTree(){
254         return methodDeclaration;
255 }
256 /*
257  * Answer the very source end of the corresponding parse node
258  */
259 public int sourceEnd(){
260         return this.methodDeclaration.declarationSourceEnd;
261 }
262 public String toString(int tab) {
263         StringBuffer result = new StringBuffer(tabString(tab));
264         result.append("Recovered method:\n"); //$NON-NLS-1$
265         this.methodDeclaration.print(tab + 1, result);
266         if (this.localTypes != null) {
267                 for (int i = 0; i < this.localTypeCount; i++) {
268                         result.append("\n"); //$NON-NLS-1$
269                         result.append(this.localTypes[i].toString(tab + 1));
270                 }
271         }
272         if (this.methodBody != null) {
273                 result.append("\n"); //$NON-NLS-1$
274                 result.append(this.methodBody.toString(tab + 1));
275         }
276         return result.toString();
277 }
278 /*
279  * Update the bodyStart of the corresponding parse node
280  */
281 public void updateBodyStart(int bodyStart){
282         this.foundOpeningBrace = true;          
283         this.methodDeclaration.bodyStart = bodyStart;
284 }
285 public AbstractMethodDeclaration updatedMethodDeclaration(){
286
287         if (methodBody != null){
288                 Block block = methodBody.updatedBlock();
289                 if (block != null){
290                         methodDeclaration.statements = block.statements;
291
292                         /* first statement might be an explict constructor call destinated to a special slot */
293                         if (methodDeclaration.isConstructor()) {
294                                 ConstructorDeclaration constructor = (ConstructorDeclaration)methodDeclaration;
295                                 if (methodDeclaration.statements != null
296                                         && methodDeclaration.statements[0] instanceof ExplicitConstructorCall){
297                                         constructor.constructorCall = (ExplicitConstructorCall)methodDeclaration.statements[0];
298                                         int length = methodDeclaration.statements.length;
299                                         System.arraycopy(
300                                                 methodDeclaration.statements, 
301                                                 1, 
302                                                 (methodDeclaration.statements = new Statement[length-1]),
303                                                 0,
304                                                 length-1);
305                                         }
306                                         if (constructor.constructorCall == null){ // add implicit constructor call
307                                                 constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
308                                         }
309                         }
310                 }
311         }
312         if (localTypeCount > 0) methodDeclaration.bits |= ASTNode.HasLocalTypeMASK;
313         return methodDeclaration;
314 }
315 /*
316  * Update the corresponding parse node from parser state which
317  * is about to disappear because of restarting recovery
318  */
319 public void updateFromParserState(){
320
321         if(this.bodyStartsAtHeaderEnd()){
322                 Parser parser = this.parser();
323                 /* might want to recover arguments or thrown exceptions */
324                 if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
325                         /* has consumed the arguments - listed elements must be thrown exceptions */
326                         if (methodDeclaration.sourceEnd == parser.rParenPos) {
327                                 
328                                 // protection for bugs 15142
329                                 int length = parser.astLengthStack[parser.astLengthPtr];
330                                 int astPtr = parser.astPtr - length;
331                                 boolean canConsume = astPtr >= 0;
332                                 if(canConsume) {
333                                         if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
334                                                 canConsume = false;
335                                         }
336                                         for (int i = 1, max = length + 1; i < max; i++) {
337                                                 if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
338                                                         canConsume = false;
339                                                 }
340                                         }
341                                 }
342                                 if (canConsume){
343                                         parser.consumeMethodHeaderThrowsClause(); 
344                                         // will reset typeListLength to zero
345                                         // thus this check will only be performed on first errorCheck after void foo() throws X, Y,
346                                 } else {
347                                         parser.listLength = 0;
348                                 }
349                         } else {
350                                 /* has not consumed arguments yet, listed elements must be arguments */
351                                 if (parser.currentToken == TokenNameLPAREN || parser.currentToken == TokenNameSEMICOLON){
352                                         /* if currentToken is parenthesis this last argument is a method/field signature */
353                                         parser.astLengthStack[parser.astLengthPtr] --; 
354                                         parser.astPtr --; 
355                                         parser.listLength --;
356                                         parser.currentToken = 0;
357                                 }
358                                 int argLength = parser.astLengthStack[parser.astLengthPtr];
359                                 int argStart = parser.astPtr - argLength + 1;
360                                 boolean needUpdateRParenPos = parser.rParenPos < parser.lParenPos; // 12387 : rParenPos will be used
361                                 // to compute bodyStart, and thus used to set next checkpoint.
362                                 int count;
363                                 for (count = 0; count < argLength; count++){
364                                         Argument argument = (Argument)parser.astStack[argStart+count];
365                                         /* cannot be an argument if non final */
366                                         char[][] argTypeName = argument.type.getTypeName();
367                                         if ((argument.modifiers & ~AccFinal) != 0
368                                                 || (argTypeName.length == 1
369                                                         && CharOperation.equals(argTypeName[0], VoidBinding.sourceName()))){
370                                                 parser.astLengthStack[parser.astLengthPtr] = count; 
371                                                 parser.astPtr = argStart+count-1; 
372                                                 parser.listLength = count;
373                                                 parser.currentToken = 0;
374                                                 break;
375                                         }
376                                         if (needUpdateRParenPos) parser.rParenPos = argument.sourceEnd + 1;
377                                 }
378                                 if (parser.listLength > 0 && parser.astLengthPtr > 0){
379                                         
380                                         // protection for bugs 15142
381                                         int length = parser.astLengthStack[parser.astLengthPtr];
382                                         int astPtr = parser.astPtr - length;
383                                         boolean canConsume = astPtr >= 0;
384                                         if(canConsume) {
385                                                 if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
386                                                         canConsume = false;
387                                                 }
388                                                 for (int i = 1, max = length + 1; i < max; i++) {
389                                                         if(!(parser.astStack[astPtr + i ] instanceof Argument)) {
390                                                                 canConsume = false;
391                                                         }
392                                                 }
393                                         }
394                                         if(canConsume) {
395                                                 parser.consumeMethodHeaderRightParen();
396                                                 /* fix-up positions, given they were updated against rParenPos, which did not get set */
397                                                 if (parser.currentElement == this){ // parameter addition might have added an awaiting (no return type) method - see 1FVXQZ4 */
398                                                         methodDeclaration.sourceEnd = methodDeclaration.arguments[methodDeclaration.arguments.length-1].sourceEnd;
399                                                         methodDeclaration.bodyStart = methodDeclaration.sourceEnd+1;
400                                                         parser.lastCheckPoint = methodDeclaration.bodyStart;
401                                                 }
402                                         }
403                                 }
404                         }
405                 }
406         }
407 }
408 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
409         if(this.methodDeclaration.isAnnotationMethod()) {
410                 this.updateSourceEndIfNecessary(braceStart, braceEnd);
411                 if(!this.foundOpeningBrace && this.parent != null) {
412                         return this.parent.updateOnClosingBrace(braceStart, braceEnd);
413                 }
414                 return this;
415         }
416         return super.updateOnClosingBrace(braceStart, braceEnd);
417 }
418 /*
419  * An opening brace got consumed, might be the expected opening one of the current element,
420  * in which case the bodyStart is updated.
421  */
422 public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
423
424         /* in case the opening brace is close enough to the signature */
425         if (bracketBalance == 0){
426                 /*
427                         if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd) 
428                                 != parser.scanner.searchLineNumber(braceEnd)){
429                  */
430                 switch(parser().lastIgnoredToken){
431                         case -1 :
432                         case TokenNamethrows :
433                                 break;
434                         default:
435                                 this.foundOpeningBrace = true;                          
436                                 bracketBalance = 1; // pretend the brace was already there
437                 }
438         }       
439         return super.updateOnOpeningBrace(braceStart, braceEnd);
440 }
441 public void updateParseTree(){
442         this.updatedMethodDeclaration();
443 }
444 /*
445  * Update the declarationSourceEnd of the corresponding parse node
446  */
447 public void updateSourceEndIfNecessary(int braceStart, int braceEnd){
448         if (this.methodDeclaration.declarationSourceEnd == 0) {
449                 if(parser().rBraceSuccessorStart >= braceEnd) {
450                         this.methodDeclaration.declarationSourceEnd = parser().rBraceEnd;
451                         this.methodDeclaration.bodyEnd = parser().rBraceStart;
452                 } else {
453                         this.methodDeclaration.declarationSourceEnd = braceEnd;
454                         this.methodDeclaration.bodyEnd  = braceStart - 1;
455                 }
456         }
457 }
458 }