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