added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / parser / RecoveredType.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.internal.compiler.ast.AbstractMethodDeclaration;
14 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
15 import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
16 import org.eclipse.jdt.internal.compiler.ast.Block;
17 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
18 import org.eclipse.jdt.internal.compiler.ast.Initializer;
19 import org.eclipse.jdt.internal.compiler.ast.Statement;
20 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
21 import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
22 import org.eclipse.jdt.internal.compiler.ast.TypeReference;
23 import org.eclipse.jdt.internal.compiler.env.IGenericType;
24 import org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers;
25
26 /**
27  * Internal type structure for parsing recovery 
28  */
29
30 public class RecoveredType extends RecoveredStatement implements TerminalTokens, CompilerModifiers {
31         public TypeDeclaration typeDeclaration;
32
33         public RecoveredType[] memberTypes;
34         public int memberTypeCount;
35         public RecoveredField[] fields;
36         public int fieldCount;
37         public RecoveredMethod[] methods;
38         public int methodCount;
39
40         public boolean preserveContent = false; // only used for anonymous types
41         public int bodyEnd;
42         
43         public boolean insideEnumConstantPart = false;
44         
45 public RecoveredType(TypeDeclaration typeDeclaration, RecoveredElement parent, int bracketBalance){
46         super(typeDeclaration, parent, bracketBalance);
47         this.typeDeclaration = typeDeclaration;
48         this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
49         this.insideEnumConstantPart = typeDeclaration.kind() == IGenericType.ENUM_DECL;
50         if(this.foundOpeningBrace) {
51                 this.bracketBalance++;
52         }
53 }
54 public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalanceValue) {
55
56         /* do not consider a method starting passed the type end (if set)
57                 it must be belonging to an enclosing type */
58         if (typeDeclaration.declarationSourceEnd != 0 
59                 && methodDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd){
60                 return this.parent.add(methodDeclaration, bracketBalanceValue);
61         }
62
63         if (methods == null) {
64                 methods = new RecoveredMethod[5];
65                 methodCount = 0;
66         } else {
67                 if (methodCount == methods.length) {
68                         System.arraycopy(
69                                 methods, 
70                                 0, 
71                                 (methods = new RecoveredMethod[2 * methodCount]), 
72                                 0, 
73                                 methodCount); 
74                 }
75         }
76         RecoveredMethod element = new RecoveredMethod(methodDeclaration, this, bracketBalanceValue, this.recoveringParser);
77         methods[methodCount++] = element;
78         
79         this.insideEnumConstantPart = false;
80
81         /* consider that if the opening brace was not found, it is there */
82         if (!foundOpeningBrace){
83                 foundOpeningBrace = true;
84                 this.bracketBalance++;
85         }
86         /* if method not finished, then method becomes current */
87         if (methodDeclaration.declarationSourceEnd == 0) return element;
88         return this;
89 }
90 public RecoveredElement add(Block nestedBlockDeclaration,int bracketBalanceValue) {
91         int modifiers = AccDefault;
92         if(this.parser().recoveredStaticInitializerStart != 0) {
93                 modifiers = AccStatic;
94         }
95         return this.add(new Initializer(nestedBlockDeclaration, modifiers), bracketBalanceValue);
96 }
97 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
98         
99         /* do not consider a field starting passed the type end (if set)
100         it must be belonging to an enclosing type */
101         if (typeDeclaration.declarationSourceEnd != 0
102                 && fieldDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd) {
103                 return this.parent.add(fieldDeclaration, bracketBalanceValue);
104         }
105         if (fields == null) {
106                 fields = new RecoveredField[5];
107                 fieldCount = 0;
108         } else {
109                 if (fieldCount == fields.length) {
110                         System.arraycopy(
111                                 fields, 
112                                 0, 
113                                 (fields = new RecoveredField[2 * fieldCount]), 
114                                 0, 
115                                 fieldCount); 
116                 }
117         }
118         RecoveredField element;
119         switch (fieldDeclaration.getKind()) {
120                 case AbstractVariableDeclaration.FIELD:
121                 case AbstractVariableDeclaration.ENUM_CONSTANT:
122                         element = new RecoveredField(fieldDeclaration, this, bracketBalanceValue);
123                         break;
124                 case AbstractVariableDeclaration.INITIALIZER:
125                         element = new RecoveredInitializer(fieldDeclaration, this, bracketBalanceValue);
126                         break;
127                 default:
128                         // never happens, as field is always identified
129                         return this;
130         }
131         fields[fieldCount++] = element;
132
133         /* consider that if the opening brace was not found, it is there */
134         if (!foundOpeningBrace){
135                 foundOpeningBrace = true;
136                 this.bracketBalance++;
137         }
138         /* if field not finished, then field becomes current */
139         if (fieldDeclaration.declarationSourceEnd == 0) return element;
140         return this;
141 }
142 public RecoveredElement add(TypeDeclaration memberTypeDeclaration, int bracketBalanceValue) {
143
144         /* do not consider a type starting passed the type end (if set)
145                 it must be belonging to an enclosing type */
146         if (typeDeclaration.declarationSourceEnd != 0 
147                 && memberTypeDeclaration.declarationSourceStart > typeDeclaration.declarationSourceEnd){
148                 return this.parent.add(memberTypeDeclaration, bracketBalanceValue);
149         }
150         
151         this.insideEnumConstantPart = false;
152         
153         if ((memberTypeDeclaration.bits & ASTNode.IsAnonymousTypeMASK) != 0){
154                 if (this.methodCount > 0) {
155                         // add it to the last method body
156                         RecoveredMethod lastMethod = this.methods[this.methodCount-1];
157                         lastMethod.methodDeclaration.bodyEnd = 0; // reopen method
158                         lastMethod.methodDeclaration.declarationSourceEnd = 0; // reopen method
159                         lastMethod.bracketBalance++; // expect one closing brace
160                         return lastMethod.add(memberTypeDeclaration, bracketBalanceValue);
161                 } else {
162                         // ignore
163                         return this;
164                 }
165         }       
166                 
167         if (memberTypes == null) {
168                 memberTypes = new RecoveredType[5];
169                 memberTypeCount = 0;
170         } else {
171                 if (memberTypeCount == memberTypes.length) {
172                         System.arraycopy(
173                                 memberTypes, 
174                                 0, 
175                                 (memberTypes = new RecoveredType[2 * memberTypeCount]), 
176                                 0, 
177                                 memberTypeCount); 
178                 }
179         }
180         RecoveredType element = new RecoveredType(memberTypeDeclaration, this, bracketBalanceValue);
181         memberTypes[memberTypeCount++] = element;
182
183         /* consider that if the opening brace was not found, it is there */
184         if (!foundOpeningBrace){
185                 foundOpeningBrace = true;
186                 this.bracketBalance++;
187         }
188         /* if member type not finished, then member type becomes current */
189         if (memberTypeDeclaration.declarationSourceEnd == 0) return element;
190         return this;
191 }
192 /*
193  * Answer the body end of the corresponding parse node
194  */
195 public int bodyEnd(){
196         if (bodyEnd == 0) return typeDeclaration.declarationSourceEnd;
197         return bodyEnd;
198 }
199 public boolean bodyStartsAtHeaderEnd(){
200         if (typeDeclaration.superInterfaces == null){
201                 if (typeDeclaration.superclass == null){
202                         if(typeDeclaration.typeParameters == null) {
203                                 return typeDeclaration.bodyStart == typeDeclaration.sourceEnd+1;
204                         } else {
205                                 return typeDeclaration.bodyStart == typeDeclaration.typeParameters[typeDeclaration.typeParameters.length-1].sourceEnd+1;
206                         }
207                 } else {
208                         return typeDeclaration.bodyStart == typeDeclaration.superclass.sourceEnd+1;
209                 }
210         } else {
211                 return typeDeclaration.bodyStart 
212                                 == typeDeclaration.superInterfaces[typeDeclaration.superInterfaces.length-1].sourceEnd+1;
213         }
214 }
215 /*
216  * Answer the enclosing type node, or null if none
217  */
218 public RecoveredType enclosingType(){
219         RecoveredElement current = parent;
220         while (current != null){
221                 if (current instanceof RecoveredType){
222                         return (RecoveredType) current;
223                 }
224                 current = current.parent;
225         }
226         return null;
227 }
228 public char[] name(){
229         return typeDeclaration.name;
230 }
231 /* 
232  * Answer the associated parsed structure
233  */
234 public ASTNode parseTree(){
235         return typeDeclaration;
236 }
237 /*
238  * Answer the very source end of the corresponding parse node
239  */
240 public int sourceEnd(){
241         return this.typeDeclaration.declarationSourceEnd;
242 }
243 public String toString(int tab) {
244         StringBuffer result = new StringBuffer(tabString(tab));
245         result.append("Recovered type:\n"); //$NON-NLS-1$
246         if ((typeDeclaration.bits & ASTNode.IsAnonymousTypeMASK) != 0) {
247                 result.append(tabString(tab));
248                 result.append(" "); //$NON-NLS-1$
249         }
250         typeDeclaration.print(tab + 1, result);
251         if (this.memberTypes != null) {
252                 for (int i = 0; i < this.memberTypeCount; i++) {
253                         result.append("\n"); //$NON-NLS-1$
254                         result.append(this.memberTypes[i].toString(tab + 1));
255                 }
256         }
257         if (this.fields != null) {
258                 for (int i = 0; i < this.fieldCount; i++) {
259                         result.append("\n"); //$NON-NLS-1$
260                         result.append(this.fields[i].toString(tab + 1));
261                 }
262         }
263         if (this.methods != null) {
264                 for (int i = 0; i < this.methodCount; i++) {
265                         result.append("\n"); //$NON-NLS-1$
266                         result.append(this.methods[i].toString(tab + 1));
267                 }
268         }
269         return result.toString();
270 }
271 /*
272  * Update the bodyStart of the corresponding parse node
273  */
274 public void updateBodyStart(int bodyStart){
275         this.foundOpeningBrace = true;
276         this.typeDeclaration.bodyStart = bodyStart;
277 }
278 public Statement updatedStatement(){
279
280         // ignore closed anonymous type
281         if ((typeDeclaration.bits & ASTNode.IsAnonymousTypeMASK) != 0 && !this.preserveContent){
282                 return null;
283         }
284                 
285         TypeDeclaration updatedType = this.updatedTypeDeclaration();
286         if ((updatedType.bits & ASTNode.IsAnonymousTypeMASK) != 0){
287                 /* in presence of an anonymous type, we want the full allocation expression */
288                 return updatedType.allocation;
289         }
290         return updatedType;
291 }
292 public TypeDeclaration updatedTypeDeclaration(){
293
294         /* update member types */
295         if (memberTypeCount > 0){
296                 int existingCount = typeDeclaration.memberTypes == null ? 0 : typeDeclaration.memberTypes.length;
297                 TypeDeclaration[] memberTypeDeclarations = new TypeDeclaration[existingCount + memberTypeCount];
298                 if (existingCount > 0){
299                         System.arraycopy(typeDeclaration.memberTypes, 0, memberTypeDeclarations, 0, existingCount);
300                 }
301                 // may need to update the declarationSourceEnd of the last type
302                 if (memberTypes[memberTypeCount - 1].typeDeclaration.declarationSourceEnd == 0){
303                         int bodyEndValue = bodyEnd();
304                         memberTypes[memberTypeCount - 1].typeDeclaration.declarationSourceEnd = bodyEndValue;
305                         memberTypes[memberTypeCount - 1].typeDeclaration.bodyEnd =  bodyEndValue;
306                 }
307                 for (int i = 0; i < memberTypeCount; i++){
308                         memberTypeDeclarations[existingCount + i] = memberTypes[i].updatedTypeDeclaration();
309                 }
310                 typeDeclaration.memberTypes = memberTypeDeclarations;
311         }
312         /* update fields */
313         if (fieldCount > 0){
314                 int existingCount = typeDeclaration.fields == null ? 0 : typeDeclaration.fields.length;
315                 FieldDeclaration[] fieldDeclarations = new FieldDeclaration[existingCount + fieldCount];
316                 if (existingCount > 0){
317                         System.arraycopy(typeDeclaration.fields, 0, fieldDeclarations, 0, existingCount);
318                 }
319                 // may need to update the declarationSourceEnd of the last field
320                 if (fields[fieldCount - 1].fieldDeclaration.declarationSourceEnd == 0){
321                         int temp = bodyEnd();
322                         fields[fieldCount - 1].fieldDeclaration.declarationSourceEnd = temp;
323                         fields[fieldCount - 1].fieldDeclaration.declarationEnd = temp;
324                 }
325                 for (int i = 0; i < fieldCount; i++){
326                         fieldDeclarations[existingCount + i] = fields[i].updatedFieldDeclaration();
327                 }
328                 typeDeclaration.fields = fieldDeclarations;
329         }
330         /* update methods */
331         int existingCount = typeDeclaration.methods == null ? 0 : typeDeclaration.methods.length;
332         boolean hasConstructor = false, hasRecoveredConstructor = false;
333         boolean hasAbstractMethods = false;
334         int defaultConstructorIndex = -1;
335         if (methodCount > 0){
336                 AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[existingCount + methodCount];
337                 for (int i = 0; i < existingCount; i++){
338                         AbstractMethodDeclaration m = typeDeclaration.methods[i];
339                         if (m.isDefaultConstructor()) defaultConstructorIndex = i;
340                         if (m.isAbstract()) hasAbstractMethods = true;
341                         methodDeclarations[i] = m;
342                 }
343                 // may need to update the declarationSourceEnd of the last method
344                 if (methods[methodCount - 1].methodDeclaration.declarationSourceEnd == 0){
345                         int bodyEndValue = bodyEnd();
346                         methods[methodCount - 1].methodDeclaration.declarationSourceEnd = bodyEndValue;
347                         methods[methodCount - 1].methodDeclaration.bodyEnd = bodyEndValue;
348                 }
349                 for (int i = 0; i < methodCount; i++){
350                         AbstractMethodDeclaration updatedMethod = methods[i].updatedMethodDeclaration();                        
351                         if (updatedMethod.isConstructor()) hasRecoveredConstructor = true;
352                         if (updatedMethod.isAbstract()) hasAbstractMethods = true;
353                         methodDeclarations[existingCount + i] = updatedMethod;                  
354                 }
355                 typeDeclaration.methods = methodDeclarations;
356                 if (hasAbstractMethods) typeDeclaration.bits |= ASTNode.HasAbstractMethods;
357                 hasConstructor = typeDeclaration.checkConstructors(this.parser());
358         } else {
359                 for (int i = 0; i < existingCount; i++){
360                         if (typeDeclaration.methods[i].isConstructor()) hasConstructor = true;
361                 }               
362         }
363         /* add clinit ? */
364         if (typeDeclaration.needClassInitMethod()){
365                 boolean alreadyHasClinit = false;
366                 for (int i = 0; i < existingCount; i++){
367                         if (typeDeclaration.methods[i].isClinit()){
368                                 alreadyHasClinit = true;
369                                 break;
370                         }
371                 }
372                 if (!alreadyHasClinit) typeDeclaration.addClinit();
373         }
374         /* add default constructor ? */
375         if (defaultConstructorIndex >= 0 && hasRecoveredConstructor){
376                 /* should discard previous default construtor */
377                 AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[typeDeclaration.methods.length - 1];
378                 if (defaultConstructorIndex != 0){
379                         System.arraycopy(typeDeclaration.methods, 0, methodDeclarations, 0, defaultConstructorIndex);
380                 }
381                 if (defaultConstructorIndex != typeDeclaration.methods.length-1){
382                         System.arraycopy(
383                                 typeDeclaration.methods, 
384                                 defaultConstructorIndex+1, 
385                                 methodDeclarations, 
386                                 defaultConstructorIndex, 
387                                 typeDeclaration.methods.length - defaultConstructorIndex - 1);
388                 }
389                 typeDeclaration.methods = methodDeclarations;
390         } else {
391                 if (!hasConstructor && typeDeclaration.kind() != IGenericType.INTERFACE_DECL && typeDeclaration.kind() != IGenericType.ANNOTATION_TYPE_DECL) {// if was already reduced, then constructor
392                         boolean insideFieldInitializer = false;
393                         RecoveredElement parentElement = this.parent; 
394                         while (parentElement != null){
395                                 if (parentElement instanceof RecoveredField){
396                                                 insideFieldInitializer = true;
397                                                 break; 
398                                 }
399                                 parentElement = parentElement.parent;
400                         }
401                         typeDeclaration.createDefaultConstructor(!parser().diet || insideFieldInitializer, true);
402                 } 
403         }
404         if (parent instanceof RecoveredType){
405                 typeDeclaration.bits |= ASTNode.IsMemberTypeMASK;
406         } else if (parent instanceof RecoveredMethod){
407                 typeDeclaration.bits |= ASTNode.IsLocalTypeMASK;
408         }
409         return typeDeclaration;
410 }
411 /*
412  * Update the corresponding parse node from parser state which
413  * is about to disappear because of restarting recovery
414  */
415 public void updateFromParserState(){
416
417         if(this.bodyStartsAtHeaderEnd()){
418                 Parser parser = this.parser();
419                 /* might want to recover implemented interfaces */
420                 // protection for bugs 15142
421                 if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
422                         int length = parser.astLengthStack[parser.astLengthPtr];
423                         int astPtr = parser.astPtr - length;
424                         boolean canConsume = astPtr >= 0;
425                         if(canConsume) {
426                                 if((!(parser.astStack[astPtr] instanceof TypeDeclaration))) {
427                                         canConsume = false;
428                                 }
429                                 for (int i = 1, max = length + 1; i < max; i++) {
430                                         if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
431                                                 canConsume = false;
432                                         }
433                                 }
434                         }
435                         if(canConsume) {
436                                 parser.consumeClassHeaderImplements(); 
437                                 // will reset typeListLength to zero
438                                 // thus this check will only be performed on first errorCheck after class X implements Y,Z,
439                         }
440                 } else if (parser.listTypeParameterLength > 0) {
441                         int length = parser.listTypeParameterLength;
442                         int genericsPtr = parser.genericsPtr;
443                         boolean canConsume = genericsPtr + 1 >= length && parser.astPtr > -1;
444                         if(canConsume) {
445                                 if (!(parser.astStack[parser.astPtr] instanceof TypeDeclaration)) {
446                                         canConsume = false;
447                                 }
448                                 while(genericsPtr + 1 > length && !(parser.genericsStack[genericsPtr] instanceof TypeParameter)) {
449                                         genericsPtr--;
450                                 }
451                                 for (int i = 0; i < length; i++) {
452                                         if(!(parser.genericsStack[genericsPtr - i] instanceof TypeParameter)) {
453                                                 canConsume = false;
454                                         }
455                                 }
456                         }
457                         if(canConsume) {
458                                 TypeDeclaration typeDecl = (TypeDeclaration)parser.astStack[parser.astPtr];
459                                 System.arraycopy(parser.genericsStack, genericsPtr - length + 1, typeDecl.typeParameters = new TypeParameter[length], 0, length);
460                                 typeDecl.bodyStart = typeDecl.typeParameters[length-1].declarationSourceEnd + 1;
461                                 parser.listTypeParameterLength = 0;
462                                 parser.lastCheckPoint = typeDecl.bodyStart;
463                         }
464                 }
465         }
466 }
467 /*
468  * A closing brace got consumed, might have closed the current element,
469  * in which case both the currentElement is exited
470  */
471 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
472         if ((--bracketBalance <= 0) && (parent != null)){
473                 this.updateSourceEndIfNecessary(braceStart, braceEnd);
474                 this.bodyEnd = braceStart - 1;
475                 return parent;
476         }
477         return this;
478 }
479 /*
480  * An opening brace got consumed, might be the expected opening one of the current element,
481  * in which case the bodyStart is updated.
482  */
483 public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
484         /* in case the opening brace is not close enough to the signature, ignore it */
485         if (bracketBalance == 0){
486                 /*
487                         if (parser.scanner.searchLineNumber(typeDeclaration.sourceEnd) 
488                                 != parser.scanner.searchLineNumber(braceEnd)){
489                  */
490                 Parser parser = this.parser();
491                 switch(parser.lastIgnoredToken){
492                         case -1 :
493                         case TokenNameextends :
494                         case TokenNameimplements :
495                         case TokenNameGREATER :
496                         case TokenNameRIGHT_SHIFT :
497                         case TokenNameUNSIGNED_RIGHT_SHIFT :
498                                 if (parser.recoveredStaticInitializerStart == 0) break;
499                         default:
500                                 this.foundOpeningBrace = true;                          
501                                 bracketBalance = 1; // pretend the brace was already there
502                 }
503         }       
504         // might be an initializer
505         if (this.bracketBalance == 1){
506                 Block block = new Block(0);
507                 Parser parser = this.parser();
508                 block.sourceStart = parser.scanner.startPosition;
509                 Initializer init;
510                 if (parser.recoveredStaticInitializerStart == 0){
511                         init = new Initializer(block, AccDefault);
512                 } else {
513                         init = new Initializer(block, AccStatic);
514                         init.declarationSourceStart = parser.recoveredStaticInitializerStart;
515                 }
516                 init.bodyStart = parser.scanner.currentPosition;
517                 return this.add(init, 1);
518         }
519         return super.updateOnOpeningBrace(braceStart, braceEnd);
520 }
521 public void updateParseTree(){
522         this.updatedTypeDeclaration();
523 }
524 /*
525  * Update the declarationSourceEnd of the corresponding parse node
526  */
527 public void updateSourceEndIfNecessary(int start, int end){
528         if (this.typeDeclaration.declarationSourceEnd == 0){
529                 this.bodyEnd = 0;
530                 this.typeDeclaration.declarationSourceEnd = end;
531                 this.typeDeclaration.bodyEnd = end;
532         }
533 }
534 }