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