99f38b009ef769378bcdc5d021380c2649d29869
[phpeclipse.git] /
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.codeassist.select;
12
13 /*
14  * Parser able to build specific completion parse nodes, given a cursorLocation.
15  *
16  * Cursor location denotes the position of the last character behind which completion
17  * got requested:
18  *  -1 means completion at the very beginning of the source
19  *      0  means completion behind the first character
20  *  n  means completion behind the n-th character
21  */
22  
23 import net.sourceforge.phpdt.internal.compiler.*;
24 import net.sourceforge.phpdt.internal.compiler.env.*;
25
26 import net.sourceforge.phpdt.internal.codeassist.impl.*;
27 import net.sourceforge.phpdt.internal.compiler.ast.*;
28 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
29 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
30 import net.sourceforge.phpdt.internal.compiler.parser.*;
31 import net.sourceforge.phpdt.internal.compiler.problem.*;
32 import net.sourceforge.phpdt.internal.compiler.util.CharOperation;
33
34 public class SelectionParser extends AssistParser {
35
36         /* public fields */
37
38         public int selectionStart, selectionEnd;
39
40         public static final char[] SUPER = "super".toCharArray(); //$NON-NLS-1$
41         public static final char[] THIS = "this".toCharArray(); //$NON-NLS-1$
42         
43 public SelectionParser(ProblemReporter problemReporter, boolean assertMode) {
44         super(problemReporter, assertMode);
45 }
46 public char[] assistIdentifier(){
47         return ((SelectionScanner)scanner).selectionIdentifier;
48 }
49 protected void attachOrphanCompletionNode(){
50         if (isOrphanCompletionNode){
51                 AstNode orphan = this.assistNode;
52                 isOrphanCompletionNode = false;
53                 
54                 
55                 /* if in context of a type, then persists the identifier into a fake field return type */
56                 if (currentElement instanceof RecoveredType){
57                         RecoveredType recoveredType = (RecoveredType)currentElement;
58                         /* filter out cases where scanner is still inside type header */
59                         if (recoveredType.foundOpeningBrace) {
60                                 /* generate a pseudo field with a completion on type reference */       
61                                 if (orphan instanceof TypeReference){
62                                         currentElement = currentElement.add(new SelectionOnFieldType((TypeReference)orphan), 0);
63                                         return;
64                                 }
65                         }
66                 }
67                 
68                 Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(orphan);
69                 currentElement = currentElement.add(statement, 0);
70                 currentToken = 0; // given we are not on an eof, we do not want side effects caused by looked-ahead token
71         }
72 }
73
74 private boolean checkRecoveredType() {
75         if (currentElement instanceof RecoveredType){
76                 /* check if current awaiting identifier is the completion identifier */
77                 if (this.indexOfAssistIdentifier() < 0) return false;
78
79                 if ((lastErrorEndPosition >= selectionStart)
80                         && (lastErrorEndPosition <= selectionEnd+1)){
81                         return false;
82                 }
83                 RecoveredType recoveredType = (RecoveredType)currentElement;
84                 /* filter out cases where scanner is still inside type header */
85                 if (recoveredType.foundOpeningBrace) {
86                         this.assistNode = this.getTypeReference(0);
87                         this.lastCheckPoint = this.assistNode.sourceEnd + 1;
88                         this.isOrphanCompletionNode = true;
89                         return true;
90                 }
91         }
92         return false;
93 }
94 protected void classInstanceCreation(boolean alwaysQualified) {
95         
96         // ClassInstanceCreationExpression ::= 'new' ClassType '(' ArgumentListopt ')' ClassBodyopt
97
98         // ClassBodyopt produces a null item on the astStak if it produces NO class body
99         // An empty class body produces a 0 on the length stack.....
100
101         int length;
102         if (((length = astLengthStack[astLengthPtr]) == 1)
103                 && (astStack[astPtr] == null)) {
104
105                 if (this.indexOfAssistIdentifier() < 0) {
106                         super.classInstanceCreation(alwaysQualified);
107                         return;
108                 }
109                 QualifiedAllocationExpression alloc;
110                 astPtr--;
111                 astLengthPtr--;
112                 alloc = new SelectionOnQualifiedAllocationExpression();
113                 alloc.sourceEnd = endPosition; //the position has been stored explicitly
114
115                 if ((length = expressionLengthStack[expressionLengthPtr--]) != 0) {
116                         expressionPtr -= length;
117                         System.arraycopy(
118                                 expressionStack, 
119                                 expressionPtr + 1, 
120                                 alloc.arguments = new Expression[length], 
121                                 0, 
122                                 length); 
123                 }
124                 // trick to avoid creating a selection on type reference
125                 char [] oldIdent = this.assistIdentifier();
126                 this.setAssistIdentifier(null);                 
127                 alloc.type = getTypeReference(0);
128                 this.setAssistIdentifier(oldIdent);
129                 
130                 //the default constructor with the correct number of argument
131                 //will be created and added by the TC (see createsInternalConstructorWithBinding)
132                 alloc.sourceStart = intStack[intPtr--];
133                 pushOnExpressionStack(alloc);
134
135                 this.assistNode = alloc;
136                 this.lastCheckPoint = alloc.sourceEnd + 1;
137                 if (!diet){
138                         this.restartRecovery    = true; // force to restart in recovery mode
139                         this.lastIgnoredToken = -1;     
140                 }
141                 this.isOrphanCompletionNode = true;
142         } else {
143                 super.classInstanceCreation(alwaysQualified);
144         }
145 }
146
147 protected void consumeArrayCreationExpression() {
148         // ArrayCreationExpression ::= 'new' PrimitiveType DimWithOrWithOutExprs ArrayInitializeropt
149         // ArrayCreationExpression ::= 'new' ClassOrInterfaceType DimWithOrWithOutExprs ArrayInitializeropt
150
151         super.consumeArrayCreationExpression();
152
153         ArrayAllocationExpression alloc = (ArrayAllocationExpression)expressionStack[expressionPtr];
154         if (alloc.type == assistNode){
155                 if (!diet){
156                         this.restartRecovery    = true; // force to restart in recovery mode
157                         this.lastIgnoredToken = -1;     
158                 }
159                 this.isOrphanCompletionNode = true;
160         }
161 }
162 protected void consumeEnterAnonymousClassBody() {
163         // EnterAnonymousClassBody ::= $empty
164
165         if (this.indexOfAssistIdentifier() < 0) {
166                 super.consumeEnterAnonymousClassBody();
167                 return;
168         }
169         QualifiedAllocationExpression alloc;
170         AnonymousLocalTypeDeclaration anonymousType = 
171                 new AnonymousLocalTypeDeclaration(this.compilationUnit.compilationResult); 
172         alloc = 
173                 anonymousType.allocation = new SelectionOnQualifiedAllocationExpression(anonymousType); 
174         markCurrentMethodWithLocalType();
175         pushOnAstStack(anonymousType);
176
177         alloc.sourceEnd = rParenPos; //the position has been stored explicitly
178         int argumentLength;
179         if ((argumentLength = expressionLengthStack[expressionLengthPtr--]) != 0) {
180                 expressionPtr -= argumentLength;
181                 System.arraycopy(
182                         expressionStack, 
183                         expressionPtr + 1, 
184                         alloc.arguments = new Expression[argumentLength], 
185                         0, 
186                         argumentLength); 
187         }
188         // trick to avoid creating a selection on type reference
189         char [] oldIdent = this.assistIdentifier();
190         this.setAssistIdentifier(null);                 
191         alloc.type = getTypeReference(0);
192         this.setAssistIdentifier(oldIdent);             
193
194         anonymousType.sourceEnd = alloc.sourceEnd;
195         //position at the type while it impacts the anonymous declaration
196         anonymousType.sourceStart = anonymousType.declarationSourceStart = alloc.type.sourceStart;
197         alloc.sourceStart = intStack[intPtr--];
198         pushOnExpressionStack(alloc);
199
200         assistNode = alloc;
201         this.lastCheckPoint = alloc.sourceEnd + 1;
202         if (!diet){
203                 this.restartRecovery    = true; // force to restart in recovery mode
204                 this.lastIgnoredToken = -1;     
205         }
206         this.isOrphanCompletionNode = true;
207                 
208         anonymousType.bodyStart = scanner.currentPosition;      
209         listLength = 0; // will be updated when reading super-interfaces
210         // recovery
211         if (currentElement != null){ 
212                 lastCheckPoint = anonymousType.bodyStart;
213                 currentElement = currentElement.add(anonymousType, 0); // the recoveryTokenCheck will deal with the open brace
214                 lastIgnoredToken = -1;          
215         }
216 }
217 protected void consumeEnterVariable() {
218         // EnterVariable ::= $empty
219         // do nothing by default
220
221         super.consumeEnterVariable();
222
223         AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
224         if (variable.type == assistNode){
225                 if (!diet){
226                         this.restartRecovery    = true; // force to restart in recovery mode
227                         this.lastIgnoredToken = -1;     
228                 }
229                 isOrphanCompletionNode = false; // already attached inside variable decl
230         }
231 }
232
233 protected void consumeExitVariableWithInitialization() {
234         super.consumeExitVariableWithInitialization();
235         
236         // does not keep the initialization if selection is not inside
237         AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
238         int start = variable.initialization.sourceStart;
239         int end =  variable.initialization.sourceEnd;
240         if ((selectionStart < start) &&  (selectionEnd < start) ||
241                 (selectionStart > end) && (selectionEnd > end)) {
242                 variable.initialization = null;
243         }
244 }
245
246 protected void consumeFieldAccess(boolean isSuperAccess) {
247         // FieldAccess ::= Primary '.' 'Identifier'
248         // FieldAccess ::= 'super' '.' 'Identifier'
249
250         if (this.indexOfAssistIdentifier() < 0) {
251                 super.consumeFieldAccess(isSuperAccess);
252                 return;
253         } 
254         FieldReference fieldReference = 
255                 new SelectionOnFieldReference(
256                         identifierStack[identifierPtr], 
257                         identifierPositionStack[identifierPtr--]);
258         identifierLengthPtr--;
259         if (isSuperAccess) { //considerates the fieldReferenceerence beginning at the 'super' ....      
260                 fieldReference.sourceStart = intStack[intPtr--];
261                 fieldReference.receiver = new SuperReference(fieldReference.sourceStart, endPosition);
262                 pushOnExpressionStack(fieldReference);
263         } else { //optimize push/pop
264                 if ((fieldReference.receiver = expressionStack[expressionPtr]).isThis()) { //fieldReferenceerence begins at the this
265                         fieldReference.sourceStart = fieldReference.receiver.sourceStart;
266                 }
267                 expressionStack[expressionPtr] = fieldReference;
268         }
269         assistNode = fieldReference;
270         this.lastCheckPoint = fieldReference.sourceEnd + 1;
271         if (!diet){
272                 this.restartRecovery    = true; // force to restart in recovery mode
273                 this.lastIgnoredToken = -1;
274         }
275         this.isOrphanCompletionNode = true;     
276 }
277 protected void consumeFormalParameter() {
278         if (this.indexOfAssistIdentifier() < 0) {
279                 super.consumeFormalParameter();
280         } else {
281
282                 identifierLengthPtr--;
283                 char[] name = identifierStack[identifierPtr];
284                 long namePositions = identifierPositionStack[identifierPtr--];
285                 TypeReference type = getTypeReference(intStack[intPtr--] + intStack[intPtr--]);
286                 intPtr -= 2;
287                 Argument arg = 
288                         new SelectionOnArgumentName(
289                                 name, 
290                                 namePositions, 
291                                 type, 
292                                 intStack[intPtr + 1] & ~AccDeprecated); // modifiers
293                 pushOnAstStack(arg);
294                 
295                 assistNode = arg;
296                 this.lastCheckPoint = (int) namePositions;
297                 isOrphanCompletionNode = true;
298                 
299                 if (!diet){
300                         this.restartRecovery    = true; // force to restart in recovery mode
301                         this.lastIgnoredToken = -1;     
302                 }
303
304                 /* if incomplete method header, listLength counter will not have been reset,
305                         indicating that some arguments are available on the stack */
306                 listLength++;
307         }       
308 }
309 protected void consumeInstanceOfExpression(int op) {
310         if (indexOfAssistIdentifier() < 0) {
311                 super.consumeInstanceOfExpression(op);
312         } else {
313                 getTypeReference(intStack[intPtr--]);
314                 this.isOrphanCompletionNode = true;
315                 this.restartRecovery = true;
316                 this.lastIgnoredToken = -1;
317         }
318 }
319 protected void consumeMethodInvocationName() {
320         // MethodInvocation ::= Name '(' ArgumentListopt ')'
321
322         // when the name is only an identifier...we have a message send to "this" (implicit)
323
324         char[] selector = identifierStack[identifierPtr];
325         int accessMode;
326         if(selector == this.assistIdentifier()) {
327                 if(CharOperation.equals(selector, SUPER)) {
328                         accessMode = ExplicitConstructorCall.Super;
329                 } else if(CharOperation.equals(selector, THIS)) {
330                         accessMode = ExplicitConstructorCall.This;
331                 } else {
332                         super.consumeMethodInvocationName();
333                         return;
334                 }
335         } else {
336                 super.consumeMethodInvocationName();
337                 return;
338         }
339         
340         final ExplicitConstructorCall constructorCall = new SelectionOnExplicitConstructorCall(accessMode);
341         constructorCall.sourceEnd = rParenPos;
342         constructorCall.sourceStart = (int) (identifierPositionStack[identifierPtr] >>> 32);
343         int length;
344         if ((length = expressionLengthStack[expressionLengthPtr--]) != 0) {
345                 expressionPtr -= length;
346                 System.arraycopy(expressionStack, expressionPtr + 1, constructorCall.arguments = new Expression[length], 0, length);
347         }
348
349         if (!diet){
350                 pushOnAstStack(constructorCall);
351                 this.restartRecovery    = true; // force to restart in recovery mode
352                 this.lastIgnoredToken = -1;
353         } else {
354                 pushOnExpressionStack(new Expression(){
355                         public TypeBinding resolveType(BlockScope scope) {
356                                 constructorCall.resolve(scope);
357                                 return null;
358                         }
359                 });
360         }
361         this.assistNode = constructorCall;      
362         this.lastCheckPoint = constructorCall.sourceEnd + 1;
363         this.isOrphanCompletionNode = true;
364 }
365 protected void consumeMethodInvocationPrimary() {
366         //optimize the push/pop
367         //MethodInvocation ::= Primary '.' 'Identifier' '(' ArgumentListopt ')'
368
369         char[] selector = identifierStack[identifierPtr];
370         int accessMode;
371         if(selector == this.assistIdentifier()) {
372                 if(CharOperation.equals(selector, SUPER)) {
373                         accessMode = ExplicitConstructorCall.Super;
374                 } else if(CharOperation.equals(selector, THIS)) {
375                         accessMode = ExplicitConstructorCall.This;
376                 } else {
377                         super.consumeMethodInvocationPrimary();
378                         return;
379                 }
380         } else {
381                 super.consumeMethodInvocationPrimary();
382                 return;
383         }
384         
385         final ExplicitConstructorCall constructorCall = new SelectionOnExplicitConstructorCall(accessMode);
386         constructorCall.sourceEnd = rParenPos;
387         int length;
388         if ((length = expressionLengthStack[expressionLengthPtr--]) != 0) {
389                 expressionPtr -= length;
390                 System.arraycopy(expressionStack, expressionPtr + 1, constructorCall.arguments = new Expression[length], 0, length);
391         }
392         constructorCall.qualification = expressionStack[expressionPtr--];
393         constructorCall.sourceStart = constructorCall.qualification.sourceStart;
394         
395         if (!diet){
396                 pushOnAstStack(constructorCall);
397                 this.restartRecovery    = true; // force to restart in recovery mode
398                 this.lastIgnoredToken = -1;
399         } else {
400                 pushOnExpressionStack(new Expression(){
401                         public TypeBinding resolveType(BlockScope scope) {
402                                 constructorCall.resolve(scope);
403                                 return null;
404                         }
405                 });
406         }
407         
408         this.assistNode = constructorCall;
409         this.lastCheckPoint = constructorCall.sourceEnd + 1;
410         this.isOrphanCompletionNode = true;
411 }
412 protected void consumeTypeImportOnDemandDeclarationName() {
413         // TypeImportOnDemandDeclarationName ::= 'import' Name '.' '*'
414         /* push an ImportRef build from the last name 
415         stored in the identifier stack. */
416
417         int index;
418
419         /* no need to take action if not inside assist identifiers */
420         if ((index = indexOfAssistIdentifier()) < 0) {
421                 super.consumeTypeImportOnDemandDeclarationName();
422                 return;
423         }
424         /* retrieve identifiers subset and whole positions, the assist node positions
425                 should include the entire replaced source. */
426         int length = identifierLengthStack[identifierLengthPtr];
427         char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
428         identifierLengthPtr--;
429         identifierPtr -= length;
430         long[] positions = new long[length];
431         System.arraycopy(
432                 identifierPositionStack, 
433                 identifierPtr + 1, 
434                 positions, 
435                 0, 
436                 length); 
437
438         /* build specific assist node on import statement */
439         ImportReference reference = this.createAssistImportReference(subset, positions);
440         reference.onDemand = true;
441         assistNode = reference;
442         this.lastCheckPoint = reference.sourceEnd + 1;
443         
444         pushOnAstStack(reference);
445
446         if (currentToken == TokenNameSEMICOLON){
447                 reference.declarationSourceEnd = scanner.currentPosition - 1;
448         } else {
449                 reference.declarationSourceEnd = (int) positions[length-1];
450         }
451         //endPosition is just before the ;
452         reference.declarationSourceStart = intStack[intPtr--];
453         // flush annotations defined prior to import statements
454         reference.declarationSourceEnd = this.flushAnnotationsDefinedPriorTo(reference.declarationSourceEnd);
455
456         // recovery
457         if (currentElement != null){
458                 lastCheckPoint = reference.declarationSourceEnd+1;
459                 currentElement = currentElement.add(reference, 0);
460                 lastIgnoredToken = -1;
461                 restartRecovery = true; // used to avoid branching back into the regular automaton              
462         }
463 }
464 public ImportReference createAssistImportReference(char[][] tokens, long[] positions){
465         return new SelectionOnImportReference(tokens, positions);
466 }
467 public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){
468         return new SelectionOnPackageReference(tokens, positions);
469 }
470 protected LocalDeclaration createLocalDeclaration(Expression initialization,char[] name,int sourceStart,int sourceEnd) {
471         if (this.indexOfAssistIdentifier() < 0) {
472                 return super.createLocalDeclaration(initialization, name, sourceStart, sourceEnd);
473         } else {
474                 SelectionOnLocalName local = new SelectionOnLocalName(initialization, name, sourceStart, sourceEnd);
475                 this.assistNode = local;
476                 this.lastCheckPoint = sourceEnd + 1;
477                 if (!diet){
478                         this.restartRecovery    = true; // force to restart in recovery mode
479                         this.lastIgnoredToken = -1;     
480                 }
481                 return local;
482         }
483 }
484 public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions){
485         return new SelectionOnQualifiedNameReference(
486                                         previousIdentifiers, 
487                                         name, 
488                                         positions);     
489 }
490 public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions){
491         return new SelectionOnQualifiedTypeReference(
492                                         previousIdentifiers, 
493                                         name, 
494                                         positions);     
495 }
496 public NameReference createSingleAssistNameReference(char[] name, long position) {
497         return new SelectionOnSingleNameReference(name, position);
498 }
499 public TypeReference createSingleAssistTypeReference(char[] name, long position) {
500         return new SelectionOnSingleTypeReference(name, position);
501 }
502 public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int selectionStart, int selectionEnd) {
503
504         this.selectionStart = selectionStart;
505         this.selectionEnd = selectionEnd;       
506         SelectionScanner selectionScanner = (SelectionScanner)this.scanner;
507         selectionScanner.selectionIdentifier = null;
508         selectionScanner.selectionStart = selectionStart;
509         selectionScanner.selectionEnd = selectionEnd;   
510         return this.dietParse(sourceUnit, compilationResult);
511 }
512 protected NameReference getUnspecifiedReference() {
513         /* build a (unspecified) NameReference which may be qualified*/
514
515         int completionIndex;
516
517         /* no need to take action if not inside completed identifiers */
518         if ((completionIndex = indexOfAssistIdentifier()) < 0) {
519                 return super.getUnspecifiedReference();
520         }
521
522         int length = identifierLengthStack[identifierLengthPtr];
523         if (CharOperation.equals(assistIdentifier(), SUPER)){
524                 Reference reference;
525                 if (completionIndex > 0){ // qualified super
526                         // discard 'super' from identifier stacks
527                         identifierLengthStack[identifierLengthPtr] = completionIndex;
528                         int ptr = identifierPtr -= (length - completionIndex);
529                         reference = 
530                                 new SelectionOnQualifiedSuperReference(
531                                         getTypeReference(0), 
532                                         (int)(identifierPositionStack[ptr+1] >>> 32),
533                                         (int) identifierPositionStack[ptr+1]);
534                 } else { // standard super
535                         identifierPtr -= length;
536                         identifierLengthPtr--;
537                         reference = new SelectionOnSuperReference((int)(identifierPositionStack[identifierPtr+1] >>> 32), (int) identifierPositionStack[identifierPtr+1]);
538                 }
539                 pushOnAstStack(reference);
540                 this.assistNode = reference;    
541                 this.lastCheckPoint = reference.sourceEnd + 1;
542                 if (!diet || dietInt != 0){
543                         this.restartRecovery    = true; // force to restart in recovery mode
544                         this.lastIgnoredToken = -1;             
545                 }
546                 this.isOrphanCompletionNode = true;
547                 return new SingleNameReference(new char[0], 0); // dummy reference
548         }
549         NameReference nameReference;
550         /* retrieve identifiers subset and whole positions, the completion node positions
551                 should include the entire replaced source. */
552         char[][] subset = identifierSubSet(completionIndex);
553         identifierLengthPtr--;
554         identifierPtr -= length;
555         long[] positions = new long[length];
556         System.arraycopy(
557                 identifierPositionStack, 
558                 identifierPtr + 1, 
559                 positions, 
560                 0, 
561                 length);
562         /* build specific completion on name reference */
563         if (completionIndex == 0) {
564                 /* completion inside first identifier */
565                 nameReference = this.createSingleAssistNameReference(assistIdentifier(), positions[0]);
566         } else {
567                 /* completion inside subsequent identifier */
568                 nameReference = this.createQualifiedAssistNameReference(subset, assistIdentifier(), positions);
569         }
570         assistNode = nameReference;
571         this.lastCheckPoint = nameReference.sourceEnd + 1;
572         if (!diet){
573                 this.restartRecovery    = true; // force to restart in recovery mode
574                 this.lastIgnoredToken = -1;     
575         }
576         this.isOrphanCompletionNode = true;
577         return nameReference;
578 }
579 /*
580  * Copy of code from superclass with the following change:
581  * In the case of qualified name reference if the cursor location is on the 
582  * qualified name reference, then create a CompletionOnQualifiedNameReference 
583  * instead.
584  */
585 protected NameReference getUnspecifiedReferenceOptimized() {
586
587         int index = indexOfAssistIdentifier();
588         NameReference reference = super.getUnspecifiedReferenceOptimized();
589
590         if (index >= 0){
591                 if (!diet){
592                         this.restartRecovery    = true; // force to restart in recovery mode
593                         this.lastIgnoredToken = -1;             
594                 }
595                 this.isOrphanCompletionNode = true;
596         }
597         return reference;
598 }
599 public void initializeScanner(){
600         this.scanner = new SelectionScanner(this.assertMode);
601 }
602 protected MessageSend newMessageSend() {
603         // '(' ArgumentListopt ')'
604         // the arguments are on the expression stack
605
606         char[] selector = identifierStack[identifierPtr];
607         if (selector != this.assistIdentifier()){
608                 return super.newMessageSend();
609         }       
610         MessageSend messageSend = new SelectionOnMessageSend();
611         int length;
612         if ((length = expressionLengthStack[expressionLengthPtr--]) != 0) {
613                 expressionPtr -= length;
614                 System.arraycopy(
615                         expressionStack, 
616                         expressionPtr + 1, 
617                         messageSend.arguments = new Expression[length], 
618                         0, 
619                         length); 
620         };
621         assistNode = messageSend;
622         if (!diet){
623                 this.restartRecovery    = true; // force to restart in recovery mode
624                 this.lastIgnoredToken = -1;     
625         }
626         
627         this.isOrphanCompletionNode = true;
628         return messageSend;
629 }
630 public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int selectionStart, int selectionEnd) {
631
632         this.selectionStart = selectionStart;
633         this.selectionEnd = selectionEnd;       
634         SelectionScanner selectionScanner = (SelectionScanner)this.scanner;
635         selectionScanner.selectionIdentifier = null;
636         selectionScanner.selectionStart = selectionStart;
637         selectionScanner.selectionEnd = selectionEnd;   
638         return this.parse(sourceUnit, compilationResult);
639 }
640 /*
641  * Reset context so as to resume to regular parse loop
642  * If unable to reset for resuming, answers false.
643  *
644  * Move checkpoint location, reset internal stacks and
645  * decide which grammar goal is activated.
646  */
647 protected boolean resumeAfterRecovery() {
648
649         /* if reached assist node inside method body, but still inside nested type,
650                 should continue in diet mode until the end of the method body */
651         if (this.assistNode != null
652                 && !(referenceContext instanceof CompilationUnitDeclaration)){
653                 currentElement.preserveEnclosingBlocks();
654                 if (currentElement.enclosingType() == null){
655                         this.resetStacks();
656                         return false;
657                 }
658         }
659         return super.resumeAfterRecovery();                     
660 }
661
662 public void selectionIdentifierCheck(){
663         if (checkRecoveredType()) return;
664 }
665 public void setAssistIdentifier(char[] assistIdent){
666         ((SelectionScanner)scanner).selectionIdentifier = assistIdent;
667 }
668 /*
669  * Update recovery state based on current parser/scanner state
670  */
671 protected void updateRecoveryState() {
672
673         /* expose parser state to recovery state */
674         currentElement.updateFromParserState();
675
676         /* may be able to retrieve completionNode as an orphan, and then attach it */
677         this.selectionIdentifierCheck();
678         this.attachOrphanCompletionNode();
679         
680         /* check and update recovered state based on current token,
681                 this action is also performed when shifting token after recovery
682                 got activated once. 
683         */
684         this.recoveryTokenCheck();
685 }
686 }