private JavaElement getJavaElement(LocalVariableBinding binding) {
    LocalDeclaration local = binding.declaration;

    JavaElement parent = null;
    ReferenceContext referenceContext =
        binding.declaringScope.isLambdaSubscope()
            ? binding.declaringScope.namedMethodScope().referenceContext()
            : binding.declaringScope.referenceContext();
    if (referenceContext instanceof AbstractMethodDeclaration) {
      AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration) referenceContext;
      parent = this.getJavaElementOfCompilationUnit(methodDeclaration, methodDeclaration.binding);
    } else if (referenceContext instanceof TypeDeclaration) {
      // Local variable is declared inside an initializer
      TypeDeclaration typeDeclaration = (TypeDeclaration) referenceContext;

      JavaElement type =
          this.getJavaElementOfCompilationUnit(typeDeclaration, typeDeclaration.binding);
      parent = Util.getUnresolvedJavaElement(local.sourceStart, local.sourceEnd, type);
    }
    if (parent == null) return null;

    return new LocalVariable(
        parent,
        new String(local.name),
        local.declarationSourceStart,
        local.declarationSourceEnd,
        local.sourceStart,
        local.sourceEnd,
        local.type == null
            ? Signature.createTypeSignature(binding.type.signableName(), true)
            : Util.typeSignature(local.type),
        binding.declaration.annotations,
        local.modifiers,
        local.getKind() == AbstractVariableDeclaration.PARAMETER);
  }
  /**
   * Returns the qualified signature corresponding to <code>signature</code>.
   *
   * @param signature the signature to qualify
   * @param context the type inside which an unqualified type will be resolved to find the
   *     qualifier, or <code>null</code> if no context is available
   * @return the qualified signature
   */
  public static String qualifySignature(final String signature, final IType context) {
    if (context == null) return signature;

    String qualifier = Signature.getSignatureQualifier(signature);
    if (qualifier.length() > 0) return signature;

    String elementType = Signature.getElementType(signature);
    String erasure = Signature.getTypeErasure(elementType);
    String simpleName = Signature.getSignatureSimpleName(erasure);
    String genericSimpleName = Signature.getSignatureSimpleName(elementType);

    int dim = Signature.getArrayCount(signature);

    try {
      String[][] strings = context.resolveType(simpleName);
      if (strings != null && strings.length > 0) qualifier = strings[0][0];
    } catch (JavaModelException e) {
      // ignore - not found
    }

    if (qualifier.length() == 0) return signature;

    String qualifiedType = Signature.toQualifiedName(new String[] {qualifier, genericSimpleName});
    String qualifiedSignature = Signature.createTypeSignature(qualifiedType, true);
    String newSignature = Signature.createArraySignature(qualifiedSignature, dim);

    return newSignature;
  }
  public void switchToJavaEditor(IType type, String methodName, List<String> parameterTypes) {
    JavaEditor javaEditor = openJavaEditor(type);
    if (javaEditor == null) {
      return;
    }

    IMethod newMethod = null;
    try {
      String[] parameterTypeSignatures = new String[parameterTypes.size()];
      for (int i = 0; i < parameterTypeSignatures.length; i++) {
        String qualifiedName = parameterTypes.get(i);
        String signature = Signature.createTypeSignature(qualifiedName, true);
        parameterTypeSignatures[i] = signature;
      }

      newMethod = JavaModelUtil.findMethod(methodName, parameterTypeSignatures, false, type);
    } catch (JavaModelException e) {
      // ignore this
    }

    if (newMethod == null) {
      return;
    }

    javaEditor.setSelection(newMethod);
  }
  /*
   * Extract and store type signatures and arguments using unique key for parameterized types
   * and type parameters for non-generic ones
   */
  void storeTypeSignaturesAndArguments(IType type) {
    if (type.isResolved()) {
      BindingKey bindingKey = new BindingKey(type.getKey());
      if (bindingKey.isParameterizedType() || bindingKey.isRawType()) {
        String signature = bindingKey.toSignature();
        this.typeSignatures = Util.splitTypeLevelsSignature(signature);
        setTypeArguments(Util.getAllTypeArguments(this.typeSignatures));
      }
      return;
    }

    // Scan hierarchy to store type arguments at each level
    char[][][] typeParameters = new char[10][][];
    int ptr = -1;
    boolean hasParameters = false;
    try {
      IJavaElement parent = type;
      ITypeParameter[] parameters = null;
      while (parent != null && parent.getElementType() == IJavaElement.TYPE) {
        if (++ptr > typeParameters.length) {
          System.arraycopy(
              typeParameters, 0, typeParameters = new char[typeParameters.length + 10][][], 0, ptr);
        }
        IType parentType = (IType) parent;
        parameters = parentType.getTypeParameters();
        if (parameters != null) {
          int length = parameters.length;
          if (length > 0) {
            hasParameters = true;
            typeParameters[ptr] = new char[length][];
            for (int i = 0; i < length; i++)
              typeParameters[ptr][i] =
                  Signature.createTypeSignature(parameters[i].getElementName(), false)
                      .toCharArray();
          }
        }
        parent = parent.getParent();
      }
    } catch (JavaModelException jme) {
      return;
    }

    // Store type arguments if any
    if (hasParameters) {
      if (++ptr < typeParameters.length)
        System.arraycopy(typeParameters, 0, typeParameters = new char[ptr][][], 0, ptr);
      setTypeArguments(typeParameters);
    }
  }
  /*
   * Extract method arguments using unique key for parameterized methods
   * and type parameters for non-generic ones.
   */
  char[][] extractMethodArguments(IMethod method) {

    // Use bind key if the element is resolved
    if (method.isResolved()) {
      BindingKey bindingKey = new BindingKey(method.getKey());
      if (bindingKey.isParameterizedMethod()) {
        String[] argumentsSignatures = bindingKey.getTypeArguments();
        int length = argumentsSignatures.length;
        if (length > 0) {
          char[][] methodArguments = new char[length][];
          for (int i = 0; i < length; i++) {
            methodArguments[i] = argumentsSignatures[i].toCharArray();
            CharOperation.replace(methodArguments[i], new char[] {'$', '/'}, '.');
          }
          return methodArguments;
        }
      }
      return null;
    }

    // Try to get the argument using the JavaModel info
    try {
      ITypeParameter[] parameters = method.getTypeParameters();
      if (parameters != null) {
        int length = parameters.length;
        if (length > 0) {
          char[][] arguments = new char[length][];
          for (int i = 0; i < length; i++) {
            arguments[i] =
                Signature.createTypeSignature(parameters[i].getElementName(), false).toCharArray();
          }
          return arguments;
        }
      }
    } catch (JavaModelException jme) {
      // do nothing
    }
    return null;
  }
  protected void reportDeclaration(
      MethodBinding methodBinding, MatchLocator locator, SimpleSet knownMethods)
      throws CoreException {
    ReferenceBinding declaringClass = methodBinding.declaringClass;
    IType type = locator.lookupType(declaringClass);
    if (type == null) return; // case of a secondary type

    // Report match for binary
    if (type.isBinary()) {
      IMethod method = null;
      TypeBinding[] parameters = methodBinding.original().parameters;
      int parameterLength = parameters.length;
      char[][] parameterTypes = new char[parameterLength][];
      for (int i = 0; i < parameterLength; i++) {
        char[] typeName = parameters[i].qualifiedSourceName();
        for (int j = 0, dim = parameters[i].dimensions(); j < dim; j++) {
          typeName = CharOperation.concat(typeName, new char[] {'[', ']'});
        }
        parameterTypes[i] = typeName;
      }
      method = locator.createBinaryMethodHandle(type, methodBinding.selector, parameterTypes);
      if (method == null || knownMethods.addIfNotIncluded(method) == null) return;

      IResource resource = type.getResource();
      if (resource == null) resource = type.getJavaProject().getProject();
      IBinaryType info =
          locator.getBinaryInfo(
              (org.eclipse.jdt.internal.core.ClassFile) type.getClassFile(), resource);
      locator.reportBinaryMemberDeclaration(
          resource, method, methodBinding, info, SearchMatch.A_ACCURATE);
      return;
    }

    // When source is available, report match if method is found in the declaring type
    IResource resource = type.getResource();
    if (declaringClass instanceof ParameterizedTypeBinding)
      declaringClass = ((ParameterizedTypeBinding) declaringClass).genericType();
    ClassScope scope = ((SourceTypeBinding) declaringClass).scope;
    if (scope != null) {
      TypeDeclaration typeDecl = scope.referenceContext;
      AbstractMethodDeclaration methodDecl = typeDecl.declarationOf(methodBinding.original());
      if (methodDecl != null) {
        // Create method handle from method declaration
        String methodName = new String(methodBinding.selector);
        Argument[] arguments = methodDecl.arguments;
        int length = arguments == null ? 0 : arguments.length;
        String[] parameterTypes = new String[length];
        for (int i = 0; i < length; i++) {
          char[][] typeName = arguments[i].type.getParameterizedTypeName();
          parameterTypes[i] =
              Signature.createTypeSignature(CharOperation.concatWith(typeName, '.'), false);
        }
        IMethod method = type.getMethod(methodName, parameterTypes);
        if (method == null || knownMethods.addIfNotIncluded(method) == null) return;

        // Create and report corresponding match
        int offset = methodDecl.sourceStart;
        this.match =
            new MethodDeclarationMatch(
                method,
                SearchMatch.A_ACCURATE,
                offset,
                methodDecl.sourceEnd - offset + 1,
                locator.getParticipant(),
                resource);
        locator.report(this.match);
      }
    }
  }