Package org.eclipse.jdt.internal.codeassist

Source Code of org.eclipse.jdt.internal.codeassist.Visitor

/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.codeassist;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.TypeNameMatch;
import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
import org.eclipse.jdt.internal.codeassist.impl.*;
import org.eclipse.jdt.internal.codeassist.select.*;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.problem.*;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.core.BinaryTypeConverter;
import org.eclipse.jdt.internal.core.ClassFile;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.eclipse.jdt.internal.core.SelectionRequestor;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
import org.eclipse.jdt.internal.core.search.TypeNameMatchRequestorWrapper;
import org.eclipse.jdt.internal.core.util.ASTNodeFinder;
import org.eclipse.jdt.internal.core.util.HashSetOfCharArrayArray;

/**
* The selection engine is intended to infer the nature of a selected name in some
* source code. This name can be qualified.
*
* Selection is resolving context using a name environment (no need to search), assuming
* the source where selection occurred is correct and will not perform any completion
* attempt. If this was the desired behavior, a call to the CompletionEngine should be
* performed instead.
*/
public final class SelectionEngine extends Engine implements ISearchRequestor {
 
  private static class SelectionTypeNameMatchRequestorWrapper extends TypeNameMatchRequestorWrapper {
   
    class AcceptedType {
      public int modifiers;
      public char[] packageName;
      public char[] simpleTypeName;
      public String path;
      public AccessRestriction access;
     
      public AcceptedType(int modifiers, char[] packageName, char[] simpleTypeName, String path, AccessRestriction access) {
        this.modifiers = modifiers;
        this.packageName = packageName;
        this.simpleTypeName = simpleTypeName;
        this.path = path;
        this.access = access;
      }
    }
   
    private ImportReference[] importReferences;
   
    private boolean importCachesNodeInitialized = false;
    private ImportReference[] onDemandImportsNodeCache;
    private int onDemandImportsNodeCacheCount;
    private char[][][] importsNodeCache;
    private int importsNodeCacheCount;
   
    private HashtableOfObject onDemandFound = new HashtableOfObject();
    private ObjectVector notImportedFound = new ObjectVector();
   
    public SelectionTypeNameMatchRequestorWrapper(TypeNameMatchRequestor requestor, IJavaSearchScope scope, ImportReference[] importReferences) {
      super(requestor, scope);
      this.importReferences = importReferences;
    }
   
    public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path, AccessRestriction access) {
      if (enclosingTypeNames != null && enclosingTypeNames.length > 0) return;
     
      if (!this.importCachesNodeInitialized) initializeImportNodeCaches();
     
      char[] fullyQualifiedTypeName = CharOperation.concat(packageName, simpleTypeName, '.');
     
      for (int i = 0; i < this.importsNodeCacheCount; i++) {
        char[][] importName = this.importsNodeCache[i];
        if (CharOperation.equals(importName[0], simpleTypeName)) {
         
          if(CharOperation.equals(importName[1], fullyQualifiedTypeName)) {
            super.acceptType(modifiers, packageName, simpleTypeName, enclosingTypeNames, path, access);
          }
          return;
        }
      }
     
      for (int i = 0; i < this.onDemandImportsNodeCacheCount; i++) {
        char[][] importName = this.onDemandImportsNodeCache[i].tokens;
        char[] importFlatName = CharOperation.concatWith(importName, '.');
       
        if (CharOperation.equals(importFlatName, packageName)) {
         
          this.onDemandFound.put(simpleTypeName, simpleTypeName);
          super.acceptType(modifiers, packageName, simpleTypeName, enclosingTypeNames, path, access);
          return;
        }
      }
     
     
      this.notImportedFound.add(new AcceptedType(modifiers, packageName, simpleTypeName, path, access));
    }
   
    public void acceptNotImported() {
      int size = this.notImportedFound.size();
      for (int i = 0; i < size; i++) {
        AcceptedType acceptedType = (AcceptedType)this.notImportedFound.elementAt(i);
       
        if (this.onDemandFound.get(acceptedType.simpleTypeName) == null) {
          super.acceptType(
              acceptedType.modifiers,
              acceptedType.packageName,
              acceptedType.simpleTypeName,
              null,
              acceptedType.path,
              acceptedType.access);
        }
      }
    }
   
    public void initializeImportNodeCaches() {
      int length = this.importReferences == null ? 0 : this.importReferences.length;
     
      for (int i = 0; i < length; i++) {
        ImportReference importReference = this.importReferences[i];
        if((importReference.bits & ASTNode.OnDemand) != 0) {
          if(this.onDemandImportsNodeCache == null) {
            this.onDemandImportsNodeCache = new ImportReference[length - i];
          }
          this.onDemandImportsNodeCache[this.onDemandImportsNodeCacheCount++] =
            importReference;
        } else {
          if(this.importsNodeCache == null) {
            this.importsNodeCache = new char[length - i][][];
          }
         
         
          this.importsNodeCache[this.importsNodeCacheCount++] = new char[][]{
              importReference.tokens[importReference.tokens.length - 1],
              CharOperation.concatWith(importReference.tokens, '.')
            };
        }
      }
     
      this.importCachesNodeInitialized = true;
    }
  }

  public static boolean DEBUG = false;
  public static boolean PERF = false;

  SelectionParser parser;
  ISelectionRequestor requestor;
  WorkingCopyOwner owner;

  boolean acceptedAnswer;

  private int actualSelectionStart;
  private int actualSelectionEnd;
  private char[] selectedIdentifier;

  private char[][][] acceptedClasses;
  private int[] acceptedClassesModifiers;
  private char[][][] acceptedInterfaces;
  private int[] acceptedInterfacesModifiers;
  private char[][][] acceptedEnums;
  private int[] acceptedEnumsModifiers;
  private char[][][] acceptedAnnotations;
  private int[] acceptedAnnotationsModifiers;
  int acceptedClassesCount;
  int acceptedInterfacesCount;
  int acceptedEnumsCount;
  int acceptedAnnotationsCount;

  boolean noProposal = true;
  CategorizedProblem problem = null;

  /**
   * The SelectionEngine is responsible for computing the selected object.
   *
   * It requires a searchable name environment, which supports some
   * specific search APIs, and a requestor to feed back the results to a UI.
   *
   *  @param nameEnvironment org.eclipse.jdt.internal.core.SearchableEnvironment
   *      used to resolve type/package references and search for types/packages
   *      based on partial names.
   *
   *  @param requestor org.eclipse.jdt.internal.codeassist.ISelectionRequestor
   *      since the engine might produce answers of various forms, the engine
   *      is associated with a requestor able to accept all possible completions.
   *
   *  @param settings java.util.Map
   *    set of options used to configure the code assist engine.
   */
  public SelectionEngine(
    SearchableEnvironment nameEnvironment,
    ISelectionRequestor requestor,
    Map settings,
    WorkingCopyOwner owner) {

    super(settings);

    this.requestor = requestor;
    this.nameEnvironment = nameEnvironment;

    ProblemReporter problemReporter =
      new ProblemReporter(
        DefaultErrorHandlingPolicies.proceedWithAllProblems(),
        this.compilerOptions,
        new DefaultProblemFactory(Locale.getDefault())) {

      public CategorizedProblem createProblem(
        char[] fileName,
        int problemId,
        String[] problemArguments,
        String[] messageArguments,
        int severity,
        int problemStartPosition,
        int problemEndPosition,
        int lineNumber,
        int columnNumber) {
        CategorizedProblem pb =  super.createProblem(
          fileName,
          problemId,
          problemArguments,
          messageArguments,
          severity,
          problemStartPosition,
          problemEndPosition,
          lineNumber,
          columnNumber);
          if(SelectionEngine.this.problem == null && pb.isError() && (pb.getID() & IProblem.Syntax) == 0) {
            SelectionEngine.this.problem = pb;
          }

          return pb;
      }
    };
    this.lookupEnvironment =
      new LookupEnvironment(this, this.compilerOptions, problemReporter, nameEnvironment);
    this.parser = new SelectionParser(problemReporter);
    this.owner = owner;
  }
 
  public void acceptConstructor(
      int modifiers,
      char[] simpleTypeName,
      int parameterCount,
      char[] signature,
      char[][] parameterTypes,
      char[][] parameterNames,
      int typeModifiers,
      char[] packageName,
      int extraFlags,
      String path,
      AccessRestriction access) {
    // constructors aren't searched
  }

  public void acceptType(char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, int modifiers, AccessRestriction accessRestriction) {
    char[] typeName = enclosingTypeNames == null ?
        simpleTypeName :
          CharOperation.concat(
            CharOperation.concatWith(enclosingTypeNames, '.'),
            simpleTypeName,
            '.');

    if (CharOperation.equals(simpleTypeName, this.selectedIdentifier)) {
      char[] flatEnclosingTypeNames =
        enclosingTypeNames == null || enclosingTypeNames.length == 0 ?
            null :
              CharOperation.concatWith(enclosingTypeNames, '.');
      if(mustQualifyType(packageName, simpleTypeName, flatEnclosingTypeNames, modifiers)) {
        int length = 0;
        int kind = modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation);
        switch (kind) {
          case ClassFileConstants.AccAnnotation:
          case ClassFileConstants.AccAnnotation | ClassFileConstants.AccInterface:
            char[][] acceptedAnnotation = new char[2][];
            acceptedAnnotation[0] = packageName;
            acceptedAnnotation[1] = typeName;

            if(this.acceptedAnnotations == null) {
              this.acceptedAnnotations = new char[10][][];
              this.acceptedAnnotationsModifiers = new int[10];
              this.acceptedAnnotationsCount = 0;
            }
            length = this.acceptedAnnotations.length;
            if(length == this.acceptedAnnotationsCount) {
              int newLength = (length + 1)* 2;
              System.arraycopy(this.acceptedAnnotations, 0, this.acceptedAnnotations = new char[newLength][][], 0, length);
              System.arraycopy(this.acceptedAnnotationsModifiers, 0, this.acceptedAnnotationsModifiers = new int[newLength], 0, length);
            }
            this.acceptedAnnotationsModifiers[this.acceptedAnnotationsCount] = modifiers;
            this.acceptedAnnotations[this.acceptedAnnotationsCount++] = acceptedAnnotation;
            break;
          case ClassFileConstants.AccEnum:
            char[][] acceptedEnum = new char[2][];
            acceptedEnum[0] = packageName;
            acceptedEnum[1] = typeName;

            if(this.acceptedEnums == null) {
              this.acceptedEnums = new char[10][][];
              this.acceptedEnumsModifiers = new int[10];
              this.acceptedEnumsCount = 0;
            }
            length = this.acceptedEnums.length;
            if(length == this.acceptedEnumsCount) {
              int newLength = (length + 1)* 2;
              System.arraycopy(this.acceptedEnums, 0, this.acceptedEnums = new char[newLength][][], 0, length);
              System.arraycopy(this.acceptedEnumsModifiers, 0, this.acceptedEnumsModifiers = new int[newLength], 0, length);
            }
            this.acceptedEnumsModifiers[this.acceptedEnumsCount] = modifiers;
            this.acceptedEnums[this.acceptedEnumsCount++] = acceptedEnum;
            break;
          case ClassFileConstants.AccInterface:
            char[][] acceptedInterface= new char[2][];
            acceptedInterface[0] = packageName;
            acceptedInterface[1] = typeName;

            if(this.acceptedInterfaces == null) {
              this.acceptedInterfaces = new char[10][][];
              this.acceptedInterfacesModifiers = new int[10];
              this.acceptedInterfacesCount = 0;
            }
            length = this.acceptedInterfaces.length;
            if(length == this.acceptedInterfacesCount) {
              int newLength = (length + 1)* 2;
              System.arraycopy(this.acceptedInterfaces, 0, this.acceptedInterfaces = new char[newLength][][], 0, length);
              System.arraycopy(this.acceptedInterfacesModifiers, 0, this.acceptedInterfacesModifiers = new int[newLength], 0, length);
            }
            this.acceptedInterfacesModifiers[this.acceptedInterfacesCount] = modifiers;
            this.acceptedInterfaces[this.acceptedInterfacesCount++] = acceptedInterface;
            break;
          default:
            char[][] acceptedClass = new char[2][];
            acceptedClass[0] = packageName;
            acceptedClass[1] = typeName;

            if(this.acceptedClasses == null) {
              this.acceptedClasses = new char[10][][];
              this.acceptedClassesModifiers = new int[10];
              this.acceptedClassesCount = 0;
            }
            length = this.acceptedClasses.length;
            if(length == this.acceptedClassesCount) {
              int newLength = (length + 1)* 2;
              System.arraycopy(this.acceptedClasses, 0, this.acceptedClasses = new char[newLength][][], 0, length);
              System.arraycopy(this.acceptedClassesModifiers, 0, this.acceptedClassesModifiers = new int[newLength], 0, length);
            }
            this.acceptedClassesModifiers[this.acceptedClassesCount] = modifiers;
            this.acceptedClasses[this.acceptedClassesCount++] = acceptedClass;
            break;
        }
      } else {
        this.noProposal = false;
        this.requestor.acceptType(
          packageName,
          typeName,
          modifiers,
          false,
          null,
          this.actualSelectionStart,
          this.actualSelectionEnd);
        this.acceptedAnswer = true;
      }
    }
  }

  /**
   * One result of the search consists of a new package.
   * @param packageName char[]
   *
   * NOTE - All package names are presented in their readable form:
   *    Package names are in the form "a.b.c".
   *    The default package is represented by an empty array.
   */
  public void acceptPackage(char[] packageName) {
    // implementation of interface method
  }

  private void acceptQualifiedTypes() {
    if(this.acceptedClasses != null){
      this.acceptedAnswer = true;
      for (int i = 0; i < this.acceptedClassesCount; i++) {
        this.noProposal = false;
        this.requestor.acceptType(
          this.acceptedClasses[i][0],
          this.acceptedClasses[i][1],
          this.acceptedClassesModifiers[i],
          false,
          null,
          this.actualSelectionStart,
          this.actualSelectionEnd);
      }
      this.acceptedClasses = null;
      this.acceptedClassesModifiers = null;
      this.acceptedClassesCount = 0;
    }
    if(this.acceptedInterfaces != null){
      this.acceptedAnswer = true;
      for (int i = 0; i < this.acceptedInterfacesCount; i++) {
        this.noProposal = false;
        this.requestor.acceptType(
          this.acceptedInterfaces[i][0],
          this.acceptedInterfaces[i][1],
          this.acceptedInterfacesModifiers[i],
          false,
          null,
          this.actualSelectionStart,
          this.actualSelectionEnd);
      }
      this.acceptedInterfaces = null;
      this.acceptedInterfacesModifiers = null;
      this.acceptedInterfacesCount = 0;
    }
    if(this.acceptedAnnotations != null){
      this.acceptedAnswer = true;
      for (int i = 0; i < this.acceptedAnnotationsCount; i++) {
        this.noProposal = false;
        this.requestor.acceptType(
          this.acceptedAnnotations[i][0],
          this.acceptedAnnotations[i][1],
          this.acceptedAnnotationsModifiers[i],
          false,
          null,
          this.actualSelectionStart,
          this.actualSelectionEnd);
      }
      this.acceptedAnnotations = null;
      this.acceptedAnnotationsModifiers = null;
      this.acceptedAnnotationsCount = 0;
    }
    if(this.acceptedEnums != null){
      this.acceptedAnswer = true;
      for (int i = 0; i < this.acceptedEnumsCount; i++) {
        this.noProposal = false;
        this.requestor.acceptType(
          this.acceptedEnums[i][0],
          this.acceptedEnums[i][1],
          this.acceptedEnumsModifiers[i],
          false,
          null,
          this.actualSelectionStart,
          this.actualSelectionEnd);
      }
      this.acceptedEnums = null;
      this.acceptedEnumsModifiers = null;
      this.acceptedEnumsCount = 0;
    }
  }
  private boolean checkSelection(
      char[] source,
      int selectionStart,
      int selectionEnd) {

    Scanner scanner =
      new Scanner(
        false /*comment*/,
        false /*whitespace*/,
        false /*nls*/,
        this.compilerOptions.sourceLevel,
        this.compilerOptions.complianceLevel,
        null/*taskTag*/,
        null/*taskPriorities*/,
        true /*taskCaseSensitive*/);
    scanner.setSource(source);

    int lastIdentifierStart = -1;
    int lastIdentifierEnd = -1;
    char[] lastIdentifier = null;
    int token;

    if(selectionStart > selectionEnd){
      int end = source.length - 1;

      // compute start position of current line
      int currentPosition = selectionStart - 1;
      int nextCharacterPosition = selectionStart;
      char currentCharacter = ' ';
      try {
        lineLoop: while(currentPosition > 0){

          if(source[currentPosition] == '\\' && source[currentPosition+1] == 'u') {
            int pos = currentPosition + 2;
            int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
            while (source[pos] == 'u') {
              pos++;
            }

            int endOfUnicode = pos + 3;
            if (end < endOfUnicode) {
              if (endOfUnicode < source.length) {
                end = endOfUnicode;
              } else {
                return false; // not enough characters to decode an unicode
              }
            }

            if ((c1 = ScannerHelper.getNumericValue(source[pos++])) > 15
              || c1 < 0
              || (c2 = ScannerHelper.getNumericValue(source[pos++])) > 15
              || c2 < 0
              || (c3 = ScannerHelper.getNumericValue(source[pos++])) > 15
              || c3 < 0
              || (c4 = ScannerHelper.getNumericValue(source[pos++])) > 15
              || c4 < 0) {
              return false;
            } else {
              currentCharacter = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
              nextCharacterPosition = pos;
            }
          } else {
            currentCharacter = source[currentPosition];
            nextCharacterPosition = currentPosition+1;
          }

          switch(currentCharacter) {
            case '\r':
            case '\n':
            case '/':
            case '"':
            case '\'':
              break lineLoop;
          }
          currentPosition--;
        }
      } catch (ArrayIndexOutOfBoundsException e) {
        return false;
      }

      // compute start and end of the last token
      scanner.resetTo(nextCharacterPosition, end);
      isolateLastName: do {
        try {
          token = scanner.getNextToken();
        } catch (InvalidInputException e) {
          return false;
        }
        switch (token) {
          case TerminalTokens.TokenNamethis:
          case TerminalTokens.TokenNamesuper:
          case TerminalTokens.TokenNameIdentifier:
            if (scanner.startPosition <= selectionStart && selectionStart <= scanner.currentPosition) {
              if (scanner.currentPosition == scanner.eofPosition) {
                int temp = scanner.eofPosition;
                scanner.eofPosition = scanner.source.length;
                 while(scanner.getNextCharAsJavaIdentifierPart()){/*empty*/}
                 scanner.eofPosition = temp;
              }
              lastIdentifierStart = scanner.startPosition;
              lastIdentifierEnd = scanner.currentPosition - 1;
              lastIdentifier = scanner.getCurrentTokenSource();
              break isolateLastName;
            }
            break;
        }
      } while (token != TerminalTokens.TokenNameEOF);
    } else {
      scanner.resetTo(selectionStart, selectionEnd);

      boolean expectingIdentifier = true;
      do {
        try {
          token = scanner.getNextToken();
        } catch (InvalidInputException e) {
          return false;
        }
        switch (token) {
          case TerminalTokens.TokenNamethis :
          case TerminalTokens.TokenNamesuper :
          case TerminalTokens.TokenNameIdentifier :
            if (!expectingIdentifier)
              return false;
            lastIdentifier = scanner.getCurrentTokenSource();
            lastIdentifierStart = scanner.startPosition;
            lastIdentifierEnd = scanner.currentPosition - 1;
            if(lastIdentifierEnd > selectionEnd) {
              lastIdentifierEnd = selectionEnd;
              lastIdentifier = CharOperation.subarray(lastIdentifier, 0,lastIdentifierEnd - lastIdentifierStart + 1);
            }
            expectingIdentifier = false;
            break;
          case TerminalTokens.TokenNameDOT :
            if (expectingIdentifier)
              return false;
            expectingIdentifier = true;
            break;
          case TerminalTokens.TokenNameEOF :
            if (expectingIdentifier)
              return false;
            break;
          case TerminalTokens.TokenNameLESS :
            if(!checkTypeArgument(scanner))
              return false;
            break;
          case TerminalTokens.TokenNameAT:
            if(scanner.startPosition != scanner.initialPosition)
              return false;
            break;
          default :
            return false;
        }
      } while (token != TerminalTokens.TokenNameEOF);
    }
    if (lastIdentifierStart > 0) {
      this.actualSelectionStart = lastIdentifierStart;
      this.actualSelectionEnd = lastIdentifierEnd;
      this.selectedIdentifier = lastIdentifier;
      return true;
    }
    return false;
  }
  private boolean checkTypeArgument(Scanner scanner) {
    int depth = 1;
    int token;
    StringBuffer buffer = new StringBuffer();
    do {
      try {
        token = scanner.getNextToken();
      } catch (InvalidInputException e) {
        return false;
      }
      switch(token) {
        case TerminalTokens.TokenNameLESS :
          depth++;
          buffer.append(scanner.getCurrentTokenSource());
          break;
        case TerminalTokens.TokenNameGREATER :
          depth--;
          buffer.append(scanner.getCurrentTokenSource());
          break;
        case TerminalTokens.TokenNameRIGHT_SHIFT :
          depth-=2;
          buffer.append(scanner.getCurrentTokenSource());
          break;
        case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT :
          depth-=3;
          buffer.append(scanner.getCurrentTokenSource());
          break;
        case TerminalTokens.TokenNameextends :
        case TerminalTokens.TokenNamesuper :
          buffer.append(' ');
          buffer.append(scanner.getCurrentTokenSource());
          buffer.append(' ');
          break;
        case TerminalTokens.TokenNameCOMMA :
          if(depth == 1) {
            int length = buffer.length();
            char[] typeRef = new char[length];
            buffer.getChars(0, length, typeRef, 0);
            try {
              Signature.createTypeSignature(typeRef, true);
              buffer = new StringBuffer();
            } catch(IllegalArgumentException e) {
              return false;
            }
          }
          break;
        default :
          buffer.append(scanner.getCurrentTokenSource());
          break;

      }
      if(depth < 0) {
        return false;
      }
    } while (depth != 0 && token != TerminalTokens.TokenNameEOF);

    if(depth == 0) {
      int length = buffer.length() - 1;
      char[] typeRef = new char[length];
      buffer.getChars(0, length, typeRef, 0);
      try {
        Signature.createTypeSignature(typeRef, true);
        return true;
      } catch(IllegalArgumentException e) {
        return false;
      }
    }

    return false;
  }
 
  /*
   * find all types outside the project scope
   */
  private void findAllTypes(char[] prefix) {
    try {
      IProgressMonitor progressMonitor = new IProgressMonitor() {
        boolean isCanceled = false;
        public void beginTask(String name, int totalWork) {
          // implements interface method
        }
        public void done() {
          // implements interface method
        }
        public void internalWorked(double work) {
          // implements interface method
        }
        public boolean isCanceled() {
          return this.isCanceled;
        }
        public void setCanceled(boolean value) {
          this.isCanceled = value;
        }
        public void setTaskName(String name) {
          // implements interface method
        }
        public void subTask(String name) {
          // implements interface method
        }
        public void worked(int work) {
          // implements interface method
        }
      };
     
      TypeNameMatchRequestor typeNameMatchRequestor = new TypeNameMatchRequestor() {
        public void acceptTypeNameMatch(TypeNameMatch match) {
          if (SelectionEngine.this.requestor instanceof SelectionRequestor) {
            SelectionEngine.this.noProposal = false;
            ((SelectionRequestor)SelectionEngine.this.requestor).acceptType(match.getType());
          }
        }
      };
     
      IJavaSearchScope scope = BasicSearchEngine.createWorkspaceScope();
     
      SelectionTypeNameMatchRequestorWrapper requestorWrapper =
        new SelectionTypeNameMatchRequestorWrapper(
            typeNameMatchRequestor,
            scope,
            this.unitScope == null ? null : this.unitScope.referenceContext.imports);
     
      org.eclipse.jdt.core.ICompilationUnit[] workingCopies = this.owner == null ? null : JavaModelManager.getJavaModelManager().getWorkingCopies(this.owner, true/*add primary WCs*/);
     
      try {
        new BasicSearchEngine(workingCopies).searchAllTypeNames(
          null,
          SearchPattern.R_EXACT_MATCH,
          prefix,
          SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
          IJavaSearchConstants.TYPE,
          scope,
          requestorWrapper,
          IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH,
          progressMonitor);
      } catch (OperationCanceledException e) {
        // do nothing
      }
      requestorWrapper.acceptNotImported();
    } catch (JavaModelException e) {
      // do nothing
    }
  }

  public AssistParser getParser() {
    return this.parser;
  }

  /*
   * Returns whether the given binding is a local/anonymous reference binding, or if its declaring class is
   * local.
   */
  private boolean isLocal(ReferenceBinding binding) {
    if(binding instanceof ParameterizedTypeBinding) {
      return isLocal(((ParameterizedTypeBinding)binding).genericType());
    }
    if (!(binding instanceof SourceTypeBinding)) return false;
    if (binding instanceof LocalTypeBinding) return true;
    if (binding instanceof MemberTypeBinding) {
      return isLocal(((MemberTypeBinding)binding).enclosingType);
    }
    return false;
  }

  /**
   * Ask the engine to compute the selection at the specified position
   * of the given compilation unit.

   *  @param sourceUnit org.eclipse.jdt.internal.compiler.env.ICompilationUnit
   *      the source of the current compilation unit.
   *
   *  @param selectionSourceStart int
   *  @param selectionSourceEnd int
   *      a range in the source where the selection is.
   */
  public void select(
    ICompilationUnit sourceUnit,
    int selectionSourceStart,
    int selectionSourceEnd) {

    char[] source = sourceUnit.getContents();

    if(DEBUG) {
      System.out.print("SELECTION IN "); //$NON-NLS-1$
      System.out.print(sourceUnit.getFileName());
      System.out.print(" FROM "); //$NON-NLS-1$
      System.out.print(selectionSourceStart);
      System.out.print(" TO "); //$NON-NLS-1$
      System.out.println(selectionSourceEnd);
      System.out.println("SELECTION - Source :"); //$NON-NLS-1$
      System.out.println(source);
    }
    if (!checkSelection(source, selectionSourceStart, selectionSourceEnd)) {
      return;
    }
    if (DEBUG) {
      System.out.print("SELECTION - Checked : \""); //$NON-NLS-1$
      System.out.print(new String(source, this.actualSelectionStart, this.actualSelectionEnd-this.actualSelectionStart+1));
      System.out.println('"');
    }
    try {
      this.acceptedAnswer = false;
      CompilationResult result = new CompilationResult(sourceUnit, 1, 1, this.compilerOptions.maxProblemsPerUnit);
      CompilationUnitDeclaration parsedUnit =
        this.parser.dietParse(sourceUnit, result, this.actualSelectionStart, this.actualSelectionEnd);

      if (parsedUnit != null) {
        if(DEBUG) {
          System.out.println("SELECTION - Diet AST :"); //$NON-NLS-1$
          System.out.println(parsedUnit.toString());
        }

        // scan the package & import statements first
        if (parsedUnit.currentPackage instanceof SelectionOnPackageReference) {
          char[][] tokens =
            ((SelectionOnPackageReference) parsedUnit.currentPackage).tokens;
          this.noProposal = false;
          this.requestor.acceptPackage(CharOperation.concatWith(tokens, '.'));
          return;
        }
        ImportReference[] imports = parsedUnit.imports;
        if (imports != null) {
          for (int i = 0, length = imports.length; i < length; i++) {
            ImportReference importReference = imports[i];
            if (importReference instanceof SelectionOnImportReference) {
              char[][] tokens = ((SelectionOnImportReference) importReference).tokens;
              this.noProposal = false;
              this.requestor.acceptPackage(CharOperation.concatWith(tokens, '.'));
              this.nameEnvironment.findTypes(CharOperation.concatWith(tokens, '.'), false, false, IJavaSearchConstants.TYPE, this);

              this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
              if ((this.unitScope = parsedUnit.scope) != null) {
                int tokenCount = tokens.length;
                char[] lastToken = tokens[tokenCount - 1];
                char[][] qualifierTokens = CharOperation.subarray(tokens, 0, tokenCount - 1);

                if(qualifierTokens != null && qualifierTokens.length > 0) {
                  Binding binding = this.unitScope.getTypeOrPackage(qualifierTokens);
                  if(binding != null && binding instanceof ReferenceBinding) {
                    ReferenceBinding ref = (ReferenceBinding) binding;
                    selectMemberTypeFromImport(parsedUnit, lastToken, ref, importReference.isStatic());
                    if(importReference.isStatic()) {
                      selectStaticFieldFromStaticImport(parsedUnit, lastToken, ref);
                      selectStaticMethodFromStaticImport(parsedUnit, lastToken, ref);
                    }
                  }
                }
              }

              // accept qualified types only if no unqualified type was accepted
              if(!this.acceptedAnswer) {
                acceptQualifiedTypes();
                if (!this.acceptedAnswer) {
                  this.nameEnvironment.findTypes(this.selectedIdentifier, false, false, IJavaSearchConstants.TYPE, this);
                  // try with simple type name
                  if(!this.acceptedAnswer) {
                    acceptQualifiedTypes();
                  }
                }
              }
              if(this.noProposal && this.problem != null) {
                this.requestor.acceptError(this.problem);
              }
              return;
            }
          }
        }
        if (parsedUnit.types != null || parsedUnit.isPackageInfo()) {
          if(selectDeclaration(parsedUnit))
            return;
          this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
          if ((this.unitScope = parsedUnit.scope!= null) {
            try {
              this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
             
              CompilationUnitDeclaration previousUnitBeingCompleted = this.lookupEnvironment.unitBeingCompleted;
              this.lookupEnvironment.unitBeingCompleted = parsedUnit;
              parsedUnit.scope.faultInTypes();
              this.lookupEnvironment.unitBeingCompleted = previousUnitBeingCompleted;
              ASTNode node = null;
              if (parsedUnit.types != null)
                node = parseBlockStatements(parsedUnit, selectionSourceStart);
              if(DEBUG) {
                System.out.println("SELECTION - AST :"); //$NON-NLS-1$
                System.out.println(parsedUnit.toString());
              }
              parsedUnit.resolve();
              if (node != null) {
                selectLocalDeclaration(node);
              }
            } catch (SelectionNodeFound e) {
              if (e.binding != null) {
                if(DEBUG) {
                  System.out.println("SELECTION - Selection binding:"); //$NON-NLS-1$
                  System.out.println(e.binding.toString());
                }
                // if null then we found a problem in the selection node
                selectFrom(e.binding, parsedUnit, e.isDeclaration);
              }
            }
          }
        }
      }
      // only reaches here if no selection could be derived from the parsed tree
      // thus use the selected source and perform a textual type search
      if (!this.acceptedAnswer) {
        this.nameEnvironment.findTypes(this.selectedIdentifier, false, false, IJavaSearchConstants.TYPE, this);

        // accept qualified types only if no unqualified type was accepted
        if(!this.acceptedAnswer) {
          acceptQualifiedTypes();
         
          // accept types from all the workspace only if no type was found in the project scope
          if (this.noProposal) {
            findAllTypes(this.selectedIdentifier);
          }
        }
      }
      if(this.noProposal && this.problem != null) {
        this.requestor.acceptError(this.problem);
      }
    } catch (IndexOutOfBoundsException e) { // work-around internal failure - 1GEMF6D
      if(DEBUG) {
        System.out.println("Exception caught by SelectionEngine:"); //$NON-NLS-1$
        e.printStackTrace(System.out);
      }
    } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object
      if(DEBUG) {
        System.out.println("Exception caught by SelectionEngine:"); //$NON-NLS-1$
        e.printStackTrace(System.out);
      }
    } finally {
      reset(true);
    }
  }

  private void selectMemberTypeFromImport(CompilationUnitDeclaration parsedUnit, char[] lastToken, ReferenceBinding ref, boolean staticOnly) {
    int fieldLength = lastToken.length;
    ReferenceBinding[] memberTypes = ref.memberTypes();
    next : for (int j = 0; j < memberTypes.length; j++) {
      ReferenceBinding memberType = memberTypes[j];

      if (fieldLength > memberType.sourceName.length)
        continue next;

      if (staticOnly && !memberType.isStatic())
        continue next;

      if (!CharOperation.equals(lastToken, memberType.sourceName, true))
        continue next;

      selectFrom(memberType, parsedUnit, false);
    }
  }

  private void selectStaticFieldFromStaticImport(CompilationUnitDeclaration parsedUnit, char[] lastToken, ReferenceBinding ref) {
    int fieldLength = lastToken.length;
    FieldBinding[] fields = ref.availableFields();
    next : for (int j = 0; j < fields.length; j++) {
      FieldBinding field = fields[j];

      if (fieldLength > field.name.length)
        continue next;

      if (field.isSynthetic())
        continue next;

      if (!field.isStatic())
        continue next;

      if (!CharOperation.equals(lastToken, field.name, true))
        continue next;

      selectFrom(field, parsedUnit, false);
    }
  }

  private void selectStaticMethodFromStaticImport(CompilationUnitDeclaration parsedUnit, char[] lastToken, ReferenceBinding ref) {
    int methodLength = lastToken.length;
    MethodBinding[] methods = ref.availableMethods();
    next : for (int j = 0; j < methods.length; j++) {
      MethodBinding method = methods[j];

      if (method.isSynthetic()) continue next;

      if (method.isDefaultAbstract())  continue next;

      if (method.isConstructor()) continue next;

      if (!method.isStatic()) continue next;

      if (methodLength > method.selector.length)
        continue next;

      if (!CharOperation.equals(lastToken, method.selector, true))
        continue next;

      selectFrom(method, parsedUnit, false);
    }
  }

  private void selectFrom(Binding binding, CompilationUnitDeclaration parsedUnit, boolean isDeclaration) {
    if(binding instanceof TypeVariableBinding) {
      TypeVariableBinding typeVariableBinding = (TypeVariableBinding) binding;
      Binding enclosingElement = typeVariableBinding.declaringElement;
      this.noProposal = false;

      if(enclosingElement instanceof SourceTypeBinding) {
        SourceTypeBinding enclosingType = (SourceTypeBinding) enclosingElement;
        if (isLocal(enclosingType) && this.requestor instanceof SelectionRequestor) {
          ((SelectionRequestor)this.requestor).acceptLocalTypeParameter(typeVariableBinding);
        } else {
          this.requestor.acceptTypeParameter(
            enclosingType.qualifiedPackageName(),
            enclosingType.qualifiedSourceName(),
            typeVariableBinding.sourceName(),
            false,
            this.actualSelectionStart,
            this.actualSelectionEnd);
        }
      } else if(enclosingElement instanceof MethodBinding) {
        MethodBinding enclosingMethod = (MethodBinding) enclosingElement;
        if (isLocal(enclosingMethod.declaringClass) && this.requestor instanceof SelectionRequestor) {
          ((SelectionRequestor)this.requestor).acceptLocalMethodTypeParameter(typeVariableBinding);
        } else {
          this.requestor.acceptMethodTypeParameter(
            enclosingMethod.declaringClass.qualifiedPackageName(),
            enclosingMethod.declaringClass.qualifiedSourceName(),
            enclosingMethod.isConstructor()
                ? enclosingMethod.declaringClass.sourceName()
                : enclosingMethod.selector,
            enclosingMethod.sourceStart(),
            enclosingMethod.sourceEnd(),
            typeVariableBinding.sourceName(),
            false,
            this.actualSelectionStart,
            this.actualSelectionEnd);
        }
      }
      this.acceptedAnswer = true;
    } else if (binding instanceof ReferenceBinding) {
      ReferenceBinding typeBinding = (ReferenceBinding) binding;
      if(typeBinding instanceof ProblemReferenceBinding) {
        TypeBinding closestMatch = typeBinding.closestMatch();
        if (closestMatch instanceof ReferenceBinding) {
          typeBinding = (ReferenceBinding) closestMatch;
        } else {
          typeBinding = null;
        }
      }
      if (typeBinding == null) return;
      if (isLocal(typeBinding) && this.requestor instanceof SelectionRequestor) {
        this.noProposal = false;
        ((SelectionRequestor)this.requestor).acceptLocalType(typeBinding);
      } else {
        this.noProposal = false;

        this.requestor.acceptType(
          typeBinding.qualifiedPackageName(),
          typeBinding.qualifiedSourceName(),
          typeBinding.modifiers,
          false,
          typeBinding.computeUniqueKey(),
          this.actualSelectionStart,
          this.actualSelectionEnd);
      }
      this.acceptedAnswer = true;
    } else if (binding instanceof MethodBinding) {
      MethodBinding methodBinding = getCorrectMethodBinding((MethodBinding) binding);
      this.noProposal = false;

      boolean isValuesOrValueOf = false;
      if(binding instanceof SyntheticMethodBinding) {
        SyntheticMethodBinding syntheticMethodBinding = (SyntheticMethodBinding) binding;
        if(syntheticMethodBinding.purpose  == SyntheticMethodBinding.EnumValues
            || syntheticMethodBinding.purpose  == SyntheticMethodBinding.EnumValueOf) {
          isValuesOrValueOf =  true;
        }
      }

      if(!isValuesOrValueOf && !methodBinding.isSynthetic()) {
        TypeBinding[] parameterTypes = methodBinding.original().parameters;
        int length = parameterTypes.length;
        char[][] parameterPackageNames = new char[length][];
        char[][] parameterTypeNames = new char[length][];
        String[] parameterSignatures = new String[length];
        for (int i = 0; i < length; i++) {
          parameterPackageNames[i] = parameterTypes[i].qualifiedPackageName();
          parameterTypeNames[i] = parameterTypes[i].qualifiedSourceName();
          parameterSignatures[i] = new String(getSignature(parameterTypes[i])).replace('/', '.');
        }

        TypeVariableBinding[] typeVariables = methodBinding.original().typeVariables;
        length = typeVariables == null ? 0 : typeVariables.length;
        char[][] typeParameterNames = new char[length][];
        char[][][] typeParameterBoundNames = new char[length][][];
        for (int i = 0; i < length; i++) {
          TypeVariableBinding typeVariable = typeVariables[i];
          typeParameterNames[i] = typeVariable.sourceName;
          if (typeVariable.firstBound == null) {
            typeParameterBoundNames[i] = new char[0][];
          } else if (typeVariable.firstBound == typeVariable.superclass) {
            int boundCount = 1 + (typeVariable.superInterfaces == null ? 0 : typeVariable.superInterfaces.length);
            typeParameterBoundNames[i] = new char[boundCount][];
            typeParameterBoundNames[i][0] = typeVariable.superclass.sourceName;
            for (int j = 1; j < boundCount; j++) {
              typeParameterBoundNames[i][j] = typeVariables[i].superInterfaces[j - 1].sourceName;
            }
          } else {
            int boundCount = typeVariable.superInterfaces == null ? 0 : typeVariable.superInterfaces.length;
            typeParameterBoundNames[i] = new char[boundCount][];
            for (int j = 0; j < boundCount; j++) {
              typeParameterBoundNames[i][j] = typeVariables[i].superInterfaces[j].sourceName;
            }
          }
        }

        ReferenceBinding declaringClass = methodBinding.declaringClass;
        if (isLocal(declaringClass) && this.requestor instanceof SelectionRequestor) {
          ((SelectionRequestor)this.requestor).acceptLocalMethod(methodBinding);
        } else {
          this.requestor.acceptMethod(
            declaringClass.qualifiedPackageName(),
            declaringClass.qualifiedSourceName(),
            declaringClass.enclosingType() == null ? null : new String(getSignature(declaringClass.enclosingType())),
            methodBinding.isConstructor()
              ? declaringClass.sourceName()
              : methodBinding.selector,
            parameterPackageNames,
            parameterTypeNames,
            parameterSignatures,
            typeParameterNames,
            typeParameterBoundNames,
            methodBinding.isConstructor(),
            isDeclaration,
            methodBinding.computeUniqueKey(),
            this.actualSelectionStart,
            this.actualSelectionEnd);
        }
      }
      this.acceptedAnswer = true;
    } else if (binding instanceof FieldBinding) {
      FieldBinding fieldBinding = (FieldBinding) binding;
      ReferenceBinding declaringClass = fieldBinding.declaringClass;
      if (declaringClass != null) { // arraylength
        this.noProposal = false;
        if (isLocal(declaringClass) && this.requestor instanceof SelectionRequestor) {
          ((SelectionRequestor)this.requestor).acceptLocalField(fieldBinding);
        } else {
          // if the binding is a problem field binding, we want to make sure
          // we can retrieve the closestMatch if the problem reason is NotVisible
          FieldBinding currentFieldBinding = fieldBinding;
          while (currentFieldBinding instanceof ProblemFieldBinding) {
            ProblemFieldBinding problemFieldBinding = (ProblemFieldBinding) currentFieldBinding;
            if (problemFieldBinding.problemId() == ProblemReasons.NotVisible) {
              currentFieldBinding = problemFieldBinding.closestMatch;
            } else {
              currentFieldBinding = null;
            }
          }
          char[] fieldName = null;
          char[] key = null;
          if (currentFieldBinding != null) {
            fieldName = currentFieldBinding.name;
            key = currentFieldBinding.computeUniqueKey();
          } else {
            fieldName = fieldBinding.name;
            key = fieldBinding.computeUniqueKey();
          }
          this.requestor.acceptField(
            declaringClass.qualifiedPackageName(),
            declaringClass.qualifiedSourceName(),
            fieldName,
            false,
            key,
            this.actualSelectionStart,
            this.actualSelectionEnd);
        }
        this.acceptedAnswer = true;
      }
    } else if (binding instanceof LocalVariableBinding) {
      if (this.requestor instanceof SelectionRequestor) {
        ((SelectionRequestor)this.requestor).acceptLocalVariable((LocalVariableBinding)binding);
        this.acceptedAnswer = true;
      } else {
        // open on the type of the variable
        selectFrom(((LocalVariableBinding) binding).type, parsedUnit, false);
      }
    } else if (binding instanceof ArrayBinding) {
      selectFrom(((ArrayBinding) binding).leafComponentType, parsedUnit, false);
      // open on the type of the array
    } else if (binding instanceof PackageBinding) {
      PackageBinding packageBinding = (PackageBinding) binding;
      this.noProposal = false;
      this.requestor.acceptPackage(packageBinding.readableName());
      this.acceptedAnswer = true;
    } else if(binding instanceof BaseTypeBinding) {
      this.acceptedAnswer = true;
    }
  }
  /*
   * Checks if a local declaration got selected in this method/initializer/field.
   */
  private void selectLocalDeclaration(ASTNode node) {
    // the selected identifier is not identical to the parser one (equals but not identical),
    // for traversing the parse tree, the parser assist identifier is necessary for identitiy checks
    final char[] assistIdentifier = getParser().assistIdentifier();
    if (assistIdentifier == null) return;

    class Visitor extends ASTVisitor {
      public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
        if (constructorDeclaration.selector == assistIdentifier){
          if (constructorDeclaration.binding != null) {
            throw new SelectionNodeFound(constructorDeclaration.binding);
          } else {
            if (constructorDeclaration.scope != null) {
              throw new SelectionNodeFound(new MethodBinding(constructorDeclaration.modifiers, constructorDeclaration.selector, null, null, null, constructorDeclaration.scope.referenceType().binding));
            }
          }
        }
        return true;
      }
      public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
        if (fieldDeclaration.name == assistIdentifier){
          throw new SelectionNodeFound(fieldDeclaration.binding);
        }
        return true;
      }
      public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
        if (localTypeDeclaration.name == assistIdentifier) {
          throw new SelectionNodeFound(localTypeDeclaration.binding);
        }
        return true;
      }
      public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
        if (memberTypeDeclaration.name == assistIdentifier) {
          throw new SelectionNodeFound(memberTypeDeclaration.binding);
        }
        return true;
      }
      public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
        if (methodDeclaration.selector == assistIdentifier){
          if (methodDeclaration.binding != null) {
            throw new SelectionNodeFound(methodDeclaration.binding);
          } else {
            if (methodDeclaration.scope != null) {
              throw new SelectionNodeFound(new MethodBinding(methodDeclaration.modifiers, methodDeclaration.selector, null, null, null, methodDeclaration.scope.referenceType().binding));
            }
          }
        }
        return true;
      }
      public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
        if (typeDeclaration.name == assistIdentifier) {
          throw new SelectionNodeFound(typeDeclaration.binding);
        }
        return true;
      }
      public boolean visit(TypeParameter typeParameter, BlockScope scope) {
        if (typeParameter.name == assistIdentifier) {
          throw new SelectionNodeFound(typeParameter.binding);
        }
        return true;
      }
      public boolean visit(TypeParameter typeParameter, ClassScope scope) {
        if (typeParameter.name == assistIdentifier) {
          throw new SelectionNodeFound(typeParameter.binding);
        }
        return true;
      }
    }

    if (node instanceof AbstractMethodDeclaration) {
      ((AbstractMethodDeclaration)node).traverse(new Visitor(), (ClassScope)null);
    } else {
      ((FieldDeclaration)node).traverse(new Visitor(), (MethodScope)null);
    }
  }

  /**
   * Asks the engine to compute the selection of the given type
   * from the given context
   *
   *  @param typeName char[]
   *      a type name which is to be resolved in the context of a compilation unit.
   *    NOTE: the type name is supposed to be correctly reduced (no whitespaces, no unicodes left)
   *
   *  @param context org.eclipse.jdt.core.IType
   *      the context in which code assist is invoked.
   */
  public void selectType(char[] typeName, IType context) throws JavaModelException {
    try {
      this.acceptedAnswer = false;

      // only the type erasure are returned by IType.resolvedType(...)
      if (CharOperation.indexOf('<', typeName) != -1) {
        char[] typeSig = Signature.createCharArrayTypeSignature(typeName, false/*not resolved*/);
        typeSig = Signature.getTypeErasure(typeSig);
        typeName = Signature.toCharArray(typeSig);
      }

      // find the outer most type
      IType outerType = context;
      IType parent = context.getDeclaringType();
      while (parent != null) {
        outerType = parent;
        parent = parent.getDeclaringType();
      }

      // compute parse tree for this most outer type
      CompilationUnitDeclaration parsedUnit = null;
      TypeDeclaration typeDeclaration = null;
      org.eclipse.jdt.core.ICompilationUnit cu = context.getCompilationUnit();
      if (cu != null) {
         IType[] topLevelTypes = cu.getTypes();
         int length = topLevelTypes.length;
         SourceTypeElementInfo[] topLevelInfos = new SourceTypeElementInfo[length];
         for (int i = 0; i < length; i++) {
          topLevelInfos[i] = (SourceTypeElementInfo) ((SourceType)topLevelTypes[i]).getElementInfo();
        }
        ISourceType outerTypeInfo = (ISourceType) ((SourceType) outerType).getElementInfo();
        CompilationResult result = new CompilationResult(outerTypeInfo.getFileName(), 1, 1, this.compilerOptions.maxProblemsPerUnit);
        int flags = SourceTypeConverter.FIELD_AND_METHOD | SourceTypeConverter.MEMBER_TYPE;
        if (context.isAnonymous() || context.isLocal())
          flags |= SourceTypeConverter.LOCAL_TYPE;
        parsedUnit =
          SourceTypeConverter.buildCompilationUnit(
              topLevelInfos,
              flags,
              this.parser.problemReporter(),
              result);
        if (parsedUnit != null && parsedUnit.types != null) {
          if(DEBUG) {
            System.out.println("SELECTION - Diet AST :"); //$NON-NLS-1$
            System.out.println(parsedUnit.toString());
          }
          // find the type declaration that corresponds to the original source type
          typeDeclaration = new ASTNodeFinder(parsedUnit).findType(context);
        }
      } else { // binary type
        ClassFile classFile = (ClassFile) context.getClassFile();
        ClassFileReader reader = (ClassFileReader) classFile.getBinaryTypeInfo((IFile) classFile.resource(), false/*don't fully initialize so as to keep constant pool (used below)*/);
        CompilationResult result = new CompilationResult(reader.getFileName(), 1, 1, this.compilerOptions.maxProblemsPerUnit);
        parsedUnit = new CompilationUnitDeclaration(this.parser.problemReporter(), result, 0);
        HashSetOfCharArrayArray typeNames = new HashSetOfCharArrayArray();

        BinaryTypeConverter converter = new BinaryTypeConverter(this.parser.problemReporter(), result, typeNames);
        typeDeclaration = converter.buildTypeDeclaration(context, parsedUnit);
        parsedUnit.imports = converter.buildImports(reader);
      }

      if (typeDeclaration != null) {

        // add fake field with the type we're looking for
        // note: since we didn't ask for fields above, there is no field defined yet
        FieldDeclaration field = new FieldDeclaration();
        int dot;
        if ((dot = CharOperation.lastIndexOf('.', typeName)) == -1) {
          this.selectedIdentifier = typeName;
          field.type = new SelectionOnSingleTypeReference(typeName, -1);
          // position not used
        } else {
          char[][] previousIdentifiers = CharOperation.splitOn('.', typeName, 0, dot);
          char[] selectionIdentifier =
            CharOperation.subarray(typeName, dot + 1, typeName.length);
          this.selectedIdentifier = selectionIdentifier;
          field.type =
            new SelectionOnQualifiedTypeReference(
              previousIdentifiers,
              selectionIdentifier,
              new long[previousIdentifiers.length + 1]);
        }
        field.name = "<fakeField>".toCharArray(); //$NON-NLS-1$
        typeDeclaration.fields = new FieldDeclaration[] { field };

        // build bindings
        this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
        if ((this.unitScope = parsedUnit.scope) != null) {
          try {
            // build fields
            // note: this builds fields only in the parsed unit (the buildFieldsAndMethods flag is not passed along)
            this.lookupEnvironment.completeTypeBindings(parsedUnit, true);

            // resolve
            parsedUnit.scope.faultInTypes();
            parsedUnit.resolve();
          } catch (SelectionNodeFound e) {
            if (e.binding != null) {
              if(DEBUG) {
                System.out.println("SELECTION - Selection binding :"); //$NON-NLS-1$
                System.out.println(e.binding.toString());
              }
              // if null then we found a problem in the selection node
              selectFrom(e.binding, parsedUnit, e.isDeclaration);
            }
          }
        }
      }
      if(this.noProposal && this.problem != null) {
        this.requestor.acceptError(this.problem);
      }
    } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object
    } finally {
      reset(true);
    }
  }

  // Check if a declaration got selected in this unit
  private boolean selectDeclaration(CompilationUnitDeclaration compilationUnit){

    // the selected identifier is not identical to the parser one (equals but not identical),
    // for traversing the parse tree, the parser assist identifier is necessary for identitiy checks
    char[] assistIdentifier = getParser().assistIdentifier();
    if (assistIdentifier == null) return false;

    ImportReference currentPackage = compilationUnit.currentPackage;
    char[] packageName = currentPackage == null ? CharOperation.NO_CHAR : CharOperation.concatWith(currentPackage.tokens, '.');
    // iterate over the types
    TypeDeclaration[] types = compilationUnit.types;
    for (int i = 0, length = types == null ? 0 : types.length; i < length; i++){
      if(selectDeclaration(types[i], assistIdentifier, packageName))
        return true;
    }
    return false;
  }

  // Check if a declaration got selected in this type
  private boolean selectDeclaration(TypeDeclaration typeDeclaration, char[] assistIdentifier, char[] packageName){

    if (typeDeclaration.name == assistIdentifier){
      char[] qualifiedSourceName = null;

      TypeDeclaration enclosingType = typeDeclaration;
      while(enclosingType != null) {
        qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
        enclosingType = enclosingType.enclosingType;
      }
      char[] uniqueKey = typeDeclaration.binding != null ? typeDeclaration.binding.computeUniqueKey() : null;

      this.requestor.acceptType(
        packageName,
        qualifiedSourceName,
        typeDeclaration.modifiers,
        true,
        uniqueKey,
        this.actualSelectionStart,
        this.actualSelectionEnd);

      this.noProposal = false;
      return true;
    }
    TypeDeclaration[] memberTypes = typeDeclaration.memberTypes;
    for (int i = 0, length = memberTypes == null ? 0 : memberTypes.length; i < length; i++){
      if(selectDeclaration(memberTypes[i], assistIdentifier, packageName))
        return true;
    }
    FieldDeclaration[] fields = typeDeclaration.fields;
    for (int i = 0, length = fields == null ? 0 : fields.length; i < length; i++){
      if (fields[i].name == assistIdentifier){
        char[] qualifiedSourceName = null;

        TypeDeclaration enclosingType = typeDeclaration;
        while(enclosingType != null) {
          qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
          enclosingType = enclosingType.enclosingType;
        }
        FieldDeclaration field = fields[i];
        this.requestor.acceptField(
          packageName,
          qualifiedSourceName,
          field.name,
          true,
          field.binding != null ? field.binding.computeUniqueKey() : null,
          this.actualSelectionStart,
          this.actualSelectionEnd);

        this.noProposal = false;
        return true;
      }
    }
    AbstractMethodDeclaration[] methods = typeDeclaration.methods;
    for (int i = 0, length = methods == null ? 0 : methods.length; i < length; i++){
      AbstractMethodDeclaration method = methods[i];

      if (method.selector == assistIdentifier){
        char[] qualifiedSourceName = null;

        TypeDeclaration enclosingType = typeDeclaration;
        while(enclosingType != null) {
          qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
          enclosingType = enclosingType.enclosingType;
        }

        this.requestor.acceptMethod(
          packageName,
          qualifiedSourceName,
          null, // SelectionRequestor does not need of declaring type signature for method declaration
          method.selector,
          null, // SelectionRequestor does not need of parameters type for method declaration
          null, // SelectionRequestor does not need of parameters type for method declaration
          null, // SelectionRequestor does not need of parameters type for method declaration
          null, // SelectionRequestor does not need of type parameters name for method declaration
          null, // SelectionRequestor does not need of type parameters bounds for method declaration
          method.isConstructor(),
          true,
          method.binding != null ? method.binding.computeUniqueKey() : null,
          this.actualSelectionStart,
          this.actualSelectionEnd);

        this.noProposal = false;
        return true;
      }

      TypeParameter[] methodTypeParameters = method.typeParameters();
      for (int j = 0, length2 = methodTypeParameters == null ? 0 : methodTypeParameters.length; j < length2; j++){
        TypeParameter methodTypeParameter = methodTypeParameters[j];

        if(methodTypeParameter.name == assistIdentifier) {
          char[] qualifiedSourceName = null;

          TypeDeclaration enclosingType = typeDeclaration;
          while(enclosingType != null) {
            qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
            enclosingType = enclosingType.enclosingType;
          }

          this.requestor.acceptMethodTypeParameter(
            packageName,
            qualifiedSourceName,
            method.selector,
            method.sourceStart,
            method.sourceEnd,
            methodTypeParameter.name,
            true,
            this.actualSelectionStart,
            this.actualSelectionEnd);

          this.noProposal = false;
          return true;
        }
      }
    }

    TypeParameter[] typeParameters = typeDeclaration.typeParameters;
    for (int i = 0, length = typeParameters == null ? 0 : typeParameters.length; i < length; i++){
      TypeParameter typeParameter = typeParameters[i];
      if(typeParameter.name == assistIdentifier) {
        char[] qualifiedSourceName = null;

        TypeDeclaration enclosingType = typeDeclaration;
        while(enclosingType != null) {
          qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
          enclosingType = enclosingType.enclosingType;
        }

        this.requestor.acceptTypeParameter(
          packageName,
          qualifiedSourceName,
          typeParameter.name,
          true,
          this.actualSelectionStart,
          this.actualSelectionEnd);

        this.noProposal = false;
        return true;
      }
    }

    return false;
  }
 
  /*
   * Returns the correct method binding according to whether the selection is on the method declaration
   * or on the inheritDoc tag in its javadoc.
   */
  private MethodBinding getCorrectMethodBinding(MethodBinding binding) {
    if (this.parser.javadocParser instanceof SelectionJavadocParser) {
      if (((SelectionJavadocParser)this.parser.javadocParser).inheritDocTagSelected){
        try {
          Object res = findMethodWithAttachedDocInHierarchy(binding);
          if (res instanceof MethodBinding) {
            return (MethodBinding) res;
          }
        } catch (JavaModelException e) {
          return null;
        }
      }
    }
    return binding;
  }
 
  protected MethodBinding findOverriddenMethodInType(ReferenceBinding overriddenType, MethodBinding overriding) throws JavaModelException {
    if (overriddenType == null)
      return null;
    MethodBinding[] overriddenMethods= overriddenType.availableMethods();
    LookupEnvironment lookupEnv = this.lookupEnvironment;
    if (lookupEnv != null && overriddenMethods != null) {
      for (int i= 0; i < overriddenMethods.length; i++) {
        if (lookupEnv.methodVerifier().isMethodSubsignature(overriding, overriddenMethods[i])) {
          return overriddenMethods[i];
        }
      }
    }
    return null;
  }
 
  private Object findMethodWithAttachedDocInHierarchy(final MethodBinding method) throws JavaModelException {
    ReferenceBinding type= method.declaringClass;
    final SelectionRequestor requestor1 = (SelectionRequestor) this.requestor;
    return new InheritDocVisitor() {
      public Object visit(ReferenceBinding currType) throws JavaModelException {
        MethodBinding overridden =  findOverriddenMethodInType(currType, method);
        if (overridden == null)
          return InheritDocVisitor.CONTINUE;
        TypeBinding args[] = overridden.parameters;
        String names[] = new String[args.length];
        for (int i = 0; i < args.length; i++) {
          names[i] = Signature.createTypeSignature(args[i].sourceName(), false);
        }
        IMember member = (IMember) requestor1.findMethodFromBinding(overridden, names, overridden.declaringClass);
        if (member == null)
          return InheritDocVisitor.CONTINUE;
        if (member.getAttachedJavadoc(null) != null ) { 
          // for binary methods with attached javadoc and no source attached
          return overridden;
        }
        IOpenable openable = member.getOpenable();
        if (openable == null)
          return InheritDocVisitor.CONTINUE;
        IBuffer buf= openable.getBuffer();
        if (buf == null) {
          // no source attachment found. This method maybe the one. Stop.
          return InheritDocVisitor.STOP_BRANCH;
        }

        ISourceRange javadocRange= member.getJavadocRange();
        if (javadocRange == null)
          return InheritDocVisitor.CONTINUE;  // this method doesn't have javadoc, continue to look.
        String rawJavadoc= buf.getText(javadocRange.getOffset(), javadocRange.getLength());
        if (rawJavadoc != null) {
          return overridden;
        }
        return InheritDocVisitor.CONTINUE;
      }
    }.visitInheritDoc(type);
  }
 
  /**
   * Implements the "Algorithm for Inheriting Method Comments" as specified for
   * <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/javadoc.html#inheritingcomments">1.4.2</a>,
   * <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javadoc.html#inheritingcomments">1.5</a>, and
   * <a href="http://java.sun.com/javase/6/docs/technotes/tools/windows/javadoc.html#inheritingcomments">1.6</a>.
   *
   * <p>
   * Unfortunately, the implementation is broken in Javadoc implementations since 1.5, see
   * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6376959">Sun's bug</a>.
   * </p>
   *
   * <p>
   * We adhere to the spec.
   * </p>
   */
  static abstract class InheritDocVisitor {
    public static final Object STOP_BRANCH= new Object() {
      public String toString() { return "STOP_BRANCH"; } //$NON-NLS-1$
    };
    public static final Object CONTINUE= new Object() {
      public String toString() { return "CONTINUE"; } //$NON-NLS-1$
    };

    /**
     * Visits a type and decides how the visitor should proceed.
     *
     * @param currType the current type
     * @return <ul>
     *         <li>{@link #STOP_BRANCH} to indicate that no Javadoc has been found and visiting
     *         super types should stop here</li>
     *         <li>{@link #CONTINUE} to indicate that no Javadoc has been found and visiting
     *         super types should continue</li>
     *         <li>an {@link Object} or <code>null</code>, to indicate that visiting should be
     *         cancelled immediately. The returned value is the result of
     *         {@link #visitInheritDoc(ReferenceBinding)}</li>
     *         </ul>
     * @throws JavaModelException unexpected problem
     * @see #visitInheritDoc(ReferenceBinding)
     */
    public abstract Object visit(ReferenceBinding currType) throws JavaModelException;

    /**
     * Visits the super types of the given <code>currentType</code>.
     *
     * @param currentType the starting type
     * @return the result from a call to {@link #visit(ReferenceBinding)}, or <code>null</code> if none of
     *         the calls returned a result
     * @throws JavaModelException unexpected problem
     */
    public Object visitInheritDoc(ReferenceBinding currentType) throws JavaModelException {
      ArrayList visited= new ArrayList();
      visited.add(currentType);
      Object result= visitInheritDocInterfaces(visited, currentType);
      if (result != InheritDocVisitor.CONTINUE)
        return result;

      ReferenceBinding superClass= currentType.superclass();

      while (superClass != null && ! visited.contains(superClass)) {
        result= visit(superClass);
        if (result == InheritDocVisitor.STOP_BRANCH) {
          return null;
        } else if (result == InheritDocVisitor.CONTINUE) {
          visited.add(superClass);
          result= visitInheritDocInterfaces(visited, superClass);
          if (result != InheritDocVisitor.CONTINUE)
            return result;
          else
            superClass= superClass.superclass();
        } else {
          return result;
        }
      }

      return null;
    }

    /**
     * Visits the super interfaces of the given type in the given hierarchy, thereby skipping already visited types.
     *
     * @param visited set of visited types
     * @param currentType type whose super interfaces should be visited
     * @return the result, or {@link #CONTINUE} if no result has been found
     * @throws JavaModelException unexpected problem
     */
    private Object visitInheritDocInterfaces(ArrayList visited, ReferenceBinding currentType) throws JavaModelException {
      ArrayList toVisitChildren= new ArrayList();
      ReferenceBinding[] superInterfaces= currentType.superInterfaces();
      for (int i= 0; i < superInterfaces.length; i++) {
        ReferenceBinding superInterface= superInterfaces[i];
        if (visited.contains(superInterface))
          continue;
        visited.add(superInterface);
        Object result= visit(superInterface);
        if (result == InheritDocVisitor.STOP_BRANCH) {
          //skip
        } else if (result == InheritDocVisitor.CONTINUE) {
          toVisitChildren.add(superInterface);
        } else {
          return result;
        }
      }
      for (Iterator iter= toVisitChildren.iterator(); iter.hasNext(); ) {
        ReferenceBinding child= (ReferenceBinding) iter.next();
        Object result= visitInheritDocInterfaces(visited, child);
        if (result != InheritDocVisitor.CONTINUE)
          return result;
      }
      return InheritDocVisitor.CONTINUE;
    }
  }
}
TOP

Related Classes of org.eclipse.jdt.internal.codeassist.Visitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.