JDT codeassist module, nothing changed yet
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / codeassist / SelectionEngine.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;
12
13 import java.util.*;
14
15 import net.sourceforge.phpdt.core.compiler.*;
16 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
17 import net.sourceforge.phpdt.core.compiler.IProblem;
18 import net.sourceforge.phpdt.internal.codeassist.impl.*;
19 import net.sourceforge.phpdt.internal.codeassist.select.*;
20 import net.sourceforge.phpdt.internal.compiler.*;
21 import net.sourceforge.phpdt.internal.compiler.env.*;
22 import net.sourceforge.phpdt.internal.compiler.ast.*;
23 import net.sourceforge.phpdt.internal.compiler.lookup.*;
24 import net.sourceforge.phpdt.internal.compiler.parser.*;
25 import net.sourceforge.phpdt.internal.compiler.problem.*;
26 import net.sourceforge.phpdt.internal.compiler.util.*;
27 import net.sourceforge.phpdt.internal.compiler.impl.*;
28
29 /**
30  * The selection engine is intended to infer the nature of a selected name in some
31  * source code. This name can be qualified.
32  *
33  * Selection is resolving context using a name environment (no need to search), assuming
34  * the source where selection occurred is correct and will not perform any completion
35  * attempt. If this was the desired behavior, a call to the CompletionEngine should be
36  * performed instead.
37  */
38 public final class SelectionEngine extends Engine implements ISearchRequestor {
39
40         public static boolean DEBUG = false;
41         
42         SelectionParser parser;
43         ISelectionRequestor requestor;
44
45         boolean acceptedAnswer;
46
47         private int actualSelectionStart;
48         private int actualSelectionEnd;
49         private char[] qualifiedSelection;
50         private char[] selectedIdentifier;
51         
52         private char[][][] acceptedClasses;
53         private char[][][] acceptedInterfaces;
54         int acceptedClassesCount;
55         int acceptedInterfacesCount;
56
57         /**
58          * The SelectionEngine is responsible for computing the selected object.
59          *
60          * It requires a searchable name environment, which supports some
61          * specific search APIs, and a requestor to feed back the results to a UI.
62          *
63          *  @param nameEnvironment net.sourceforge.phpdt.internal.codeassist.ISearchableNameEnvironment
64          *      used to resolve type/package references and search for types/packages
65          *      based on partial names.
66          *
67          *  @param requestor net.sourceforge.phpdt.internal.codeassist.ISelectionRequestor
68          *      since the engine might produce answers of various forms, the engine 
69          *      is associated with a requestor able to accept all possible completions.
70          *
71          *  @param settings java.util.Map
72          *              set of options used to configure the code assist engine.
73          */
74         public SelectionEngine(
75                 ISearchableNameEnvironment nameEnvironment,
76                 ISelectionRequestor requestor,
77                 Map settings) {
78
79                 super(settings);
80
81                 this.requestor = requestor;
82                 this.nameEnvironment = nameEnvironment;
83
84                 ProblemReporter problemReporter =
85                         new ProblemReporter(
86                                 DefaultErrorHandlingPolicies.proceedWithAllProblems(),
87                                 this.compilerOptions,
88                                 new DefaultProblemFactory(Locale.getDefault())) {
89                         public void record(IProblem problem, CompilationResult unitResult, ReferenceContext referenceContext) {
90                                 unitResult.record(problem, referenceContext);
91                                 SelectionEngine.this.requestor.acceptError(problem);
92                         }
93                 };
94                 this.parser = new SelectionParser(problemReporter, this.compilerOptions.assertMode);
95                 this.lookupEnvironment =
96                         new LookupEnvironment(this, this.compilerOptions, problemReporter, nameEnvironment);
97         }
98
99         /**
100          * One result of the search consists of a new class.
101          * @param packageName char[]
102          * @param className char[]
103          * @param modifiers int
104          *
105          * NOTE - All package and type names are presented in their readable form:
106          *    Package names are in the form "a.b.c".
107          *    Nested type names are in the qualified form "A.M".
108          *    The default package is represented by an empty array.
109          */
110         public void acceptClass(char[] packageName, char[] className, int modifiers) {
111                 if (CharOperation.equals(className, selectedIdentifier)) {
112                         if (qualifiedSelection != null
113                                 && !CharOperation.equals(
114                                         qualifiedSelection,
115                                         CharOperation.concat(packageName, className, '.'))) {
116                                 return;
117                         }
118                         
119                         if(mustQualifyType(packageName, className)) {
120                                 char[][] acceptedClass = new char[2][];
121                                 acceptedClass[0] = packageName;
122                                 acceptedClass[1] = className;
123                                 
124                                 if(acceptedClasses == null) {
125                                         acceptedClasses = new char[10][][];
126                                         acceptedClassesCount = 0;
127                                 }
128                                 int length = acceptedClasses.length;
129                                 if(length == acceptedClassesCount) {
130                                         System.arraycopy(acceptedClasses, 0, acceptedClasses = new char[(length + 1)* 2][][], 0, length);
131                                 }
132                                 acceptedClasses[acceptedClassesCount++] = acceptedClass;
133                                 
134                         } else {
135                                 requestor.acceptClass(
136                                         packageName,
137                                         className,
138                                         false);
139                                 acceptedAnswer = true;
140                         }
141                 }
142         }
143
144         /**
145          * One result of the search consists of a new interface.
146          *
147          * NOTE - All package and type names are presented in their readable form:
148          *    Package names are in the form "a.b.c".
149          *    Nested type names are in the qualified form "A.I".
150          *    The default package is represented by an empty array.
151          */
152         public void acceptInterface(
153                 char[] packageName,
154                 char[] interfaceName,
155                 int modifiers) {
156
157                 if (CharOperation.equals(interfaceName, selectedIdentifier)) {
158                         if (qualifiedSelection != null
159                                 && !CharOperation.equals(
160                                         qualifiedSelection,
161                                         CharOperation.concat(packageName, interfaceName, '.'))) {
162                                 return;
163                         }
164                         
165                         if(mustQualifyType(packageName, interfaceName)) {
166                                 char[][] acceptedInterface= new char[2][];
167                                 acceptedInterface[0] = packageName;
168                                 acceptedInterface[1] = interfaceName;
169                                 
170                                 if(acceptedInterfaces == null) {
171                                         acceptedInterfaces = new char[10][][];
172                                         acceptedInterfacesCount = 0;
173                                 }
174                                 int length = acceptedInterfaces.length;
175                                 if(length == acceptedInterfacesCount) {
176                                         System.arraycopy(acceptedInterfaces, 0, acceptedInterfaces = new char[(length + 1) * 2][][], 0, length);
177                                 }
178                                 acceptedInterfaces[acceptedInterfacesCount++] = acceptedInterface;
179                                 
180                         } else {
181                                 requestor.acceptInterface(
182                                         packageName,
183                                         interfaceName,
184                                         false);
185                                 acceptedAnswer = true;
186                         }
187                 }
188         }
189
190         /**
191          * One result of the search consists of a new package.
192          * @param packageName char[]
193          * 
194          * NOTE - All package names are presented in their readable form:
195          *    Package names are in the form "a.b.c".
196          *    The default package is represented by an empty array.
197          */
198         public void acceptPackage(char[] packageName) {
199         }
200
201         private void acceptQualifiedTypes() {
202                 if(acceptedClasses != null){
203                         acceptedAnswer = true;
204                         for (int i = 0; i < acceptedClassesCount; i++) {
205                                 requestor.acceptClass(
206                                         acceptedClasses[i][0],
207                                         acceptedClasses[i][1],
208                                         true);
209                         }
210                         acceptedClasses = null;
211                         acceptedClassesCount = 0;
212                 }
213                 if(acceptedInterfaces != null){
214                         acceptedAnswer = true;
215                         for (int i = 0; i < acceptedInterfacesCount; i++) {
216                                 requestor.acceptInterface(
217                                         acceptedInterfaces[i][0],
218                                         acceptedInterfaces[i][1],
219                                         true);
220                         }
221                         acceptedInterfaces = null;
222                         acceptedInterfacesCount = 0;
223                 }
224         }
225         
226         /**
227          * One result of the search consists of a new type.
228          * @param packageName char[]
229          * @param typeName char[]
230          * 
231          * NOTE - All package and type names are presented in their readable form:
232          *    Package names are in the form "a.b.c".
233          *    Nested type names are in the qualified form "A.M".
234          *    The default package is represented by an empty array.
235          */
236         public void acceptType(char[] packageName, char[] typeName) {
237                 acceptClass(packageName, typeName, 0);
238         }
239
240         private boolean checkSelection(
241                 char[] source,
242                 int selectionStart,
243                 int selectionEnd) {
244
245                 Scanner scanner = new Scanner();
246                 scanner.setSource(source);
247                 
248                 int lastIdentifierStart = -1;
249                 int lastIdentifierEnd = -1;
250                 char[] lastIdentifier = null;
251                 int token, identCount = 0;
252                 StringBuffer entireSelection = new StringBuffer(selectionEnd - selectionStart + 1);
253                 
254                 if(selectionStart > selectionEnd){
255                         
256                         // compute start position of current line
257                         int currentPosition = selectionStart - 1;
258                         int nextCharacterPosition = selectionStart;
259                         char currentCharacter = ' ';
260                         try {
261                                 while(currentPosition > 0 || currentCharacter == '\r' || currentCharacter == '\n'){
262                                         
263                                         if(source[currentPosition] == '\\' && source[currentPosition+1] == 'u') {
264                                                 int pos = currentPosition + 2;
265                                                 int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
266                                                 while (source[pos] == 'u') {
267                                                         pos++;
268                                                 }
269                                                 if ((c1 = Character.getNumericValue(source[pos++])) > 15
270                                                         || c1 < 0
271                                                         || (c2 = Character.getNumericValue(source[pos++])) > 15
272                                                         || c2 < 0
273                                                         || (c3 = Character.getNumericValue(source[pos++])) > 15
274                                                         || c3 < 0
275                                                         || (c4 = Character.getNumericValue(source[pos++])) > 15
276                                                         || c4 < 0) {
277                                                         return false;
278                                                 } else {
279                                                         currentCharacter = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
280                                                         nextCharacterPosition = pos;
281                                                 }
282                                         } else {
283                                                 currentCharacter = source[currentPosition];
284                                                 nextCharacterPosition = currentPosition+1;
285                                         }
286                                         
287                                         if(currentCharacter == '\r' || currentCharacter == '\n') {
288                                                 break;
289                                         }
290                                         currentPosition--;
291                                 }
292                         }
293                         catch (ArrayIndexOutOfBoundsException e) {
294                                 return false;
295                         }
296                         
297                         // compute start and end of the last token
298                         scanner.resetTo(nextCharacterPosition, selectionEnd);
299                         do {
300                                 try {
301                                         token = scanner.getNextToken();
302                                 } catch (InvalidInputException e) {
303                                         return false;
304                                 }
305                                 if((
306         // token == ITerminalSymbols.TokenNamethis ||
307                                 // token == ITerminalSymbols.TokenNamesuper ||
308                                         token == ITerminalSymbols.TokenNameIdentifier) &&
309                                         scanner.startPosition <= selectionStart &&
310                                         selectionStart <= scanner.currentPosition) {
311                                         lastIdentifierStart = scanner.startPosition;
312                                         lastIdentifierEnd = scanner.currentPosition - 1;
313                                         lastIdentifier = scanner.getCurrentTokenSource();
314                                 }
315                         } while (token != ITerminalSymbols.TokenNameEOF);
316                 } else {
317                         scanner.resetTo(selectionStart, selectionEnd);
318         
319                         boolean expectingIdentifier = true;
320                         
321                         do {
322                                 try {
323                                         token = scanner.getNextToken();
324                                 } catch (InvalidInputException e) {
325                                         return false;
326                                 }
327                                 switch (token) {
328 //                                      case ITerminalSymbols.TokenNamethis :
329 //                                      case ITerminalSymbols.TokenNamesuper :
330                                         case ITerminalSymbols.TokenNameIdentifier :
331                                                 if (!expectingIdentifier)
332                                                         return false;
333                                                 lastIdentifier = scanner.getCurrentTokenSource();
334                                                 lastIdentifierStart = scanner.startPosition;
335                                                 lastIdentifierEnd = scanner.currentPosition - 1;
336                                                 if(lastIdentifierEnd > selectionEnd) {
337                                                         lastIdentifierEnd = selectionEnd;
338                                                         lastIdentifier = CharOperation.subarray(lastIdentifier, 0,lastIdentifierEnd - lastIdentifierStart + 1);
339                                                 }
340                                                 entireSelection.append(lastIdentifier);
341                                                         
342                                                 identCount++;
343                                                 expectingIdentifier = false;
344                                                 break;
345                                         case ITerminalSymbols.TokenNameDOT :
346                                                 if (expectingIdentifier)
347                                                         return false;
348                                                 entireSelection.append('.');
349                                                 expectingIdentifier = true;
350                                                 break;
351                                         case ITerminalSymbols.TokenNameEOF :
352                                                 if (expectingIdentifier)
353                                                         return false;
354                                                 break;
355                                         default :
356                                                 return false;
357                                 }
358                         } while (token != ITerminalSymbols.TokenNameEOF);
359                 }
360                 if (lastIdentifierStart > 0) {
361                         actualSelectionStart = lastIdentifierStart;
362                         actualSelectionEnd = lastIdentifierEnd;
363                         selectedIdentifier = lastIdentifier;
364                         if (identCount > 1)
365                                 qualifiedSelection = entireSelection.toString().toCharArray();
366                         return true;
367                 }
368                 return false;
369         }
370
371         public AssistParser getParser() {
372                 return parser;
373         }
374
375         /**
376          * Ask the engine to compute the selection at the specified position
377          * of the given compilation unit.
378
379          *  @param sourceUnit net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit
380          *      the source of the current compilation unit.
381          *
382          *  @param selectionSourceStart int
383          *  @param selectionSourceEnd int
384          *      a range in the source where the selection is.
385          */
386         public void select(
387                 ICompilationUnit sourceUnit,
388                 int selectionSourceStart,
389                 int selectionSourceEnd) {
390
391                 char[] source = sourceUnit.getContents();
392                 
393                 if(DEBUG) {
394                         System.out.print("SELECTION IN "); //$NON-NLS-1$
395                         System.out.print(sourceUnit.getFileName());
396                         System.out.print(" FROM "); //$NON-NLS-1$
397                         System.out.print(selectionSourceStart);
398                         System.out.print(" TO "); //$NON-NLS-1$
399                         System.out.println(selectionSourceEnd);
400                         System.out.println("SELECTION - Source :"); //$NON-NLS-1$
401                         System.out.println(source);
402                 }
403                 if (!checkSelection(source, selectionSourceStart, selectionSourceEnd))
404                         return;
405                 try {
406                         acceptedAnswer = false;
407                         CompilationResult result = new CompilationResult(sourceUnit, 1, 1, this.compilerOptions.maxProblemsPerUnit);
408                         CompilationUnitDeclaration parsedUnit =
409                                 parser.dietParse(sourceUnit, result, actualSelectionStart, actualSelectionEnd);
410
411                         if (parsedUnit != null) {
412                                 if(DEBUG) {
413                                         System.out.println("SELECTION - Diet AST :"); //$NON-NLS-1$
414                                         System.out.println(parsedUnit.toString());
415                                 }
416                                 
417                                 // scan the package & import statements first
418                                 if (parsedUnit.currentPackage instanceof SelectionOnPackageReference) {
419                                         char[][] tokens =
420                                                 ((SelectionOnPackageReference) parsedUnit.currentPackage).tokens;
421                                         requestor.acceptPackage(CharOperation.concatWith(tokens, '.'));
422                                         return;
423                                 }
424                                 ImportReference[] imports = parsedUnit.imports;
425                                 if (imports != null) {
426                                         for (int i = 0, length = imports.length; i < length; i++) {
427                                                 ImportReference importReference = imports[i];
428                                                 if (importReference instanceof SelectionOnImportReference) {
429                                                         char[][] tokens = ((SelectionOnImportReference) importReference).tokens;
430                                                         requestor.acceptPackage(CharOperation.concatWith(tokens, '.'));
431                                                         nameEnvironment.findTypes(CharOperation.concatWith(tokens, '.'), this);
432                                                         // accept qualified types only if no unqualified type was accepted
433                                                         if(!acceptedAnswer) {
434                                                                 acceptQualifiedTypes();
435                                                                 if (!acceptedAnswer) {
436                                                                         nameEnvironment.findTypes(selectedIdentifier, this);
437                                                                         // try with simple type name
438                                                                         if(!acceptedAnswer) {
439                                                                                 acceptQualifiedTypes();
440                                                                         }
441                                                                 }
442                                                         }
443                                                         return;
444                                                 }
445                                         }
446                                 }
447                                 if (parsedUnit.types != null) {
448                                         lookupEnvironment.buildTypeBindings(parsedUnit);
449                                         if ((this.unitScope = parsedUnit.scope)  != null) {
450                                                 try {
451                                                         lookupEnvironment.completeTypeBindings(parsedUnit, true);
452                                                         parsedUnit.scope.faultInTypes();
453                                                         selectDeclaration(parsedUnit);
454                                                         parseMethod(parsedUnit, selectionSourceStart);
455                                                         if(DEBUG) {
456                                                                 System.out.println("SELECTION - AST :"); //$NON-NLS-1$
457                                                                 System.out.println(parsedUnit.toString());
458                                                         }
459                                                         parsedUnit.resolve();
460                                                 } catch (SelectionNodeFound e) {
461                                                         if (e.binding != null) {
462                                                                 if(DEBUG) {
463                                                                         System.out.println("SELECTION - Selection binding:"); //$NON-NLS-1$
464                                                                         System.out.println(e.binding.toString());
465                                                                 }
466                                                                 // if null then we found a problem in the selection node
467                                                                 selectFrom(e.binding);
468                                                         }
469                                                 }
470                                         }
471                                 }
472                         }
473                         // only reaches here if no selection could be derived from the parsed tree
474                         // thus use the selected source and perform a textual type search
475                         if (!acceptedAnswer) {
476                                 nameEnvironment.findTypes(selectedIdentifier, this);
477                                 
478                                 // accept qualified types only if no unqualified type was accepted
479                                 if(!acceptedAnswer) {
480                                         acceptQualifiedTypes();
481                                 }
482                         }
483                 } catch (IndexOutOfBoundsException e) { // work-around internal failure - 1GEMF6D               
484                 } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object
485                 } finally {
486                         reset();
487                 }
488         }
489
490         private void selectFrom(Binding binding) {
491                 if (binding instanceof ReferenceBinding) {
492                         ReferenceBinding typeBinding = (ReferenceBinding) binding;
493                         if (qualifiedSelection != null
494                                 && !CharOperation.equals(qualifiedSelection, typeBinding.readableName())) {
495                                 return;
496                         }
497                         if (typeBinding.isInterface()) {
498                                 requestor.acceptInterface(
499                                         typeBinding.qualifiedPackageName(),
500                                         typeBinding.qualifiedSourceName(),
501                                         false);
502                         } else if(typeBinding instanceof ProblemReferenceBinding){
503                                 ProblemReferenceBinding problemBinding = (ProblemReferenceBinding)typeBinding;
504                                 if(problemBinding.original == null
505                                         || !(problemBinding.original instanceof ReferenceBinding)) {
506                                         return;
507                                 }
508                                 ReferenceBinding original = (ReferenceBinding) problemBinding.original;
509
510                                 requestor.acceptClass(
511                                         original.qualifiedPackageName(),
512                                         original.qualifiedSourceName(),
513                                         false);
514                         } else {
515                                 requestor.acceptClass(
516                                         typeBinding.qualifiedPackageName(),
517                                         typeBinding.qualifiedSourceName(),
518                                         false);
519                         }
520                         acceptedAnswer = true;
521                 } else
522                         if (binding instanceof MethodBinding) {
523                                 MethodBinding methodBinding = (MethodBinding) binding;
524                                 TypeBinding[] parameterTypes = methodBinding.parameters;
525                                 int length = parameterTypes.length;
526                                 char[][] parameterPackageNames = new char[length][];
527                                 char[][] parameterTypeNames = new char[length][];
528                                 for (int i = 0; i < length; i++) {
529                                         parameterPackageNames[i] = parameterTypes[i].qualifiedPackageName();
530                                         parameterTypeNames[i] = parameterTypes[i].qualifiedSourceName();
531                                 }
532                                 requestor.acceptMethod(
533                                         methodBinding.declaringClass.qualifiedPackageName(),
534                                         methodBinding.declaringClass.qualifiedSourceName(),
535                                         methodBinding.isConstructor()
536                                                 ? methodBinding.declaringClass.sourceName()
537                                                 : methodBinding.selector,
538                                         parameterPackageNames,
539                                         parameterTypeNames,
540                                         methodBinding.isConstructor());
541                                 acceptedAnswer = true;
542                         } else
543                                 if (binding instanceof FieldBinding) {
544                                         FieldBinding fieldBinding = (FieldBinding) binding;
545                                         if (fieldBinding.declaringClass != null) { // arraylength
546                                                 requestor.acceptField(
547                                                         fieldBinding.declaringClass.qualifiedPackageName(),
548                                                         fieldBinding.declaringClass.qualifiedSourceName(),
549                                                         fieldBinding.name);
550                                                 acceptedAnswer = true;
551                                         }
552                                 } else
553                                         if (binding instanceof LocalVariableBinding) {
554                                                 selectFrom(((LocalVariableBinding) binding).type);
555                                                 // open on the type of the variable
556                                         } else
557                                                 if (binding instanceof ArrayBinding) {
558                                                         selectFrom(((ArrayBinding) binding).leafComponentType);
559                                                         // open on the type of the array
560                                                 } else
561                                                         if (binding instanceof PackageBinding) {
562                                                                 PackageBinding packageBinding = (PackageBinding) binding;
563                                                                 requestor.acceptPackage(packageBinding.readableName());
564                                                                 acceptedAnswer = true;
565                                                         } else
566                                                                 if(binding instanceof BaseTypeBinding) {
567                                                                         acceptedAnswer = true;
568                                                                 }
569         }
570
571         /**
572          * Asks the engine to compute the selection of the given type
573          * from the source type.
574          *
575          *  @param sourceType net.sourceforge.phpdt.internal.compiler.env.ISourceType
576          *      a source form of the current type in which code assist is invoked.
577          *
578          *  @param typeName char[]
579          *      a type name which is to be resolved in the context of a compilation unit.
580          *              NOTE: the type name is supposed to be correctly reduced (no whitespaces, no unicodes left)
581          * 
582          *  @param searchInEnvironment
583          *      if <code>true</code> and no selection could be found in context then search type in environment.
584          */
585         public void selectType(ISourceType sourceType, char[] typeName, boolean searchInEnvironment) {
586                 try {
587                         acceptedAnswer = false;
588
589                         // find the outer most type
590                         ISourceType outerType = sourceType;
591                         ISourceType parent = sourceType.getEnclosingType();
592                         while (parent != null) {
593                                 outerType = parent;
594                                 parent = parent.getEnclosingType();
595                         }
596                         // compute parse tree for this most outer type
597                         CompilationResult result = new CompilationResult(outerType.getFileName(), 1, 1, this.compilerOptions.maxProblemsPerUnit);
598                         CompilationUnitDeclaration parsedUnit =
599                                 SourceTypeConverter
600                                         .buildCompilationUnit(
601                                                 new ISourceType[] { outerType },
602                                                 false,
603                         // don't need field and methods
604                         true, // by default get member types
605                         this.parser.problemReporter(), result);
606
607                         if (parsedUnit != null && parsedUnit.types != null) {
608                                 if(DEBUG) {
609                                         System.out.println("SELECTION - Diet AST :"); //$NON-NLS-1$
610                                         System.out.println(parsedUnit.toString());
611                                 }
612                                 // find the type declaration that corresponds to the original source type
613                                 char[] packageName = sourceType.getPackageName();
614                                 char[] sourceTypeName = sourceType.getQualifiedName();
615                                 // the fully qualified name without the package name
616                                 if (packageName != null) {
617                                         // remove the package name if necessary
618                                         sourceTypeName =
619                                                 CharOperation.subarray(
620                                                         sourceType.getQualifiedName(),
621                                                         packageName.length + 1,
622                                                         sourceTypeName.length);
623                                 };
624                                 TypeDeclaration typeDecl =
625                                         parsedUnit.declarationOfType(CharOperation.splitOn('.', sourceTypeName));
626                                 if (typeDecl != null) {
627
628                                         // add fake field with the type we're looking for
629                                         // note: since we didn't ask for fields above, there is no field defined yet
630                                         FieldDeclaration field = new FieldDeclaration();
631                                         int dot;
632                                         if ((dot = CharOperation.lastIndexOf('.', typeName)) == -1) {
633                                                 this.selectedIdentifier = typeName;
634                                                 field.type = new SelectionOnSingleTypeReference(typeName, -1);
635                                                 // position not used
636                                         } else {
637                                                 qualifiedSelection = typeName;
638                                                 char[][] previousIdentifiers = CharOperation.splitOn('.', typeName, 0, dot - 1);
639                                                 char[] selectionIdentifier =
640                                                         CharOperation.subarray(typeName, dot + 1, typeName.length);
641                                                 this.selectedIdentifier = selectionIdentifier;
642                                                 field.type =
643                                                         new SelectionOnQualifiedTypeReference(
644                                                                 previousIdentifiers,
645                                                                 selectionIdentifier,
646                                                                 new long[previousIdentifiers.length + 1]);
647                                         }
648                                         field.name = "<fakeField>".toCharArray(); //$NON-NLS-1$
649                                         typeDecl.fields = new FieldDeclaration[] { field };
650
651                                         // build bindings
652                                         lookupEnvironment.buildTypeBindings(parsedUnit);
653                                         if ((this.unitScope = parsedUnit.scope) != null) {
654                                                 try {
655                                                         // build fields
656                                                         // note: this builds fields only in the parsed unit (the buildFieldsAndMethods flag is not passed along)
657                                                         this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
658
659                                                         // resolve
660                                                         parsedUnit.scope.faultInTypes();
661                                                         parsedUnit.resolve();
662                                                 } catch (SelectionNodeFound e) {
663                                                         if (e.binding != null) {
664                                                                 if(DEBUG) {
665                                                                         System.out.println("SELECTION - Selection binding :"); //$NON-NLS-1$
666                                                                         System.out.println(e.binding.toString());
667                                                                 }
668                                                                 // if null then we found a problem in the selection node
669                                                                 selectFrom(e.binding);
670                                                         }
671                                                 }
672                                         }
673                                 }
674                         }
675                         // only reaches here if no selection could be derived from the parsed tree
676                         // thus use the selected source and perform a textual type search
677                         if (!acceptedAnswer && searchInEnvironment) {
678                                 if (this.selectedIdentifier != null) {
679                                         nameEnvironment.findTypes(typeName, this);
680                                         
681                                         // accept qualified types only if no unqualified type was accepted
682                                         if(!acceptedAnswer) {
683                                                 acceptQualifiedTypes();
684                                         }
685                                 }
686                         }
687                 } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object
688                 } finally {
689                         qualifiedSelection = null;
690                         reset();
691                 }
692         }
693
694         // Check if a declaration got selected in this unit
695         private void selectDeclaration(CompilationUnitDeclaration compilationUnit){
696
697                 // the selected identifier is not identical to the parser one (equals but not identical),
698                 // for traversing the parse tree, the parser assist identifier is necessary for identitiy checks
699                 char[] assistIdentifier = this.getParser().assistIdentifier();
700                 if (assistIdentifier == null) return;
701                 
702                 // iterate over the types
703                 TypeDeclaration[] types = compilationUnit.types;
704                 for (int i = 0, length = types == null ? 0 : types.length; i < length; i++){
705                         selectDeclaration(types[i], assistIdentifier);
706                 }
707         }
708
709         // Check if a declaration got selected in this type
710         private void selectDeclaration(TypeDeclaration typeDeclaration, char[] assistIdentifier){
711
712                 if (typeDeclaration.name == assistIdentifier){
713                         throw new SelectionNodeFound(typeDeclaration.binding);
714                 }
715                 TypeDeclaration[] memberTypes = typeDeclaration.memberTypes;
716                 for (int i = 0, length = memberTypes == null ? 0 : memberTypes.length; i < length; i++){
717                         selectDeclaration(memberTypes[i], assistIdentifier);
718                 }
719                 FieldDeclaration[] fields = typeDeclaration.fields;
720                 for (int i = 0, length = fields == null ? 0 : fields.length; i < length; i++){
721                         if (fields[i].name == assistIdentifier){
722                                 throw new SelectionNodeFound(fields[i].binding);
723                         }
724                 }
725                 AbstractMethodDeclaration[] methods = typeDeclaration.methods;
726                 for (int i = 0, length = methods == null ? 0 : methods.length; i < length; i++){
727                         AbstractMethodDeclaration method = methods[i];
728                         if (method.selector == assistIdentifier){
729                                 if(method.binding != null) {
730                                         throw new SelectionNodeFound(method.binding);
731                                 } else {
732                                         if(method.scope != null) {
733                                                 throw new SelectionNodeFound(new MethodBinding(method.modifiers, method.selector, null, null, null, method.scope.referenceType().binding));
734                                         }
735                                 }
736                 }
737                 }
738         }
739 }