private static MethodsChain createChainFromFirstElement(
     final MethodsChain chain, final PsiClass newQualifierClass) {
   final String qualifiedClassName = newQualifierClass.getQualifiedName();
   if (qualifiedClassName == null) {
     throw new IllegalArgumentException();
   }
   return new MethodsChain(chain.getFirst(), chain.getChainWeight(), qualifiedClassName);
 }
 public void add(final MethodsChain newChain) {
   if (myResult.isEmpty()) {
     myResult.add(newChain);
     return;
   }
   boolean doAdd = true;
   final Stack<Integer> indexesToRemove = new Stack<>();
   for (int i = 0; i < myResult.size(); i++) {
     final MethodsChain chain = myResult.get(i);
     //
     final MethodsChain.CompareResult r = MethodsChain.compare(chain, newChain, myContext);
     switch (r) {
       case LEFT_CONTAINS_RIGHT:
         indexesToRemove.add(i);
         break;
       case RIGHT_CONTAINS_LEFT:
       case EQUAL:
         doAdd = false;
         break;
       case NOT_EQUAL:
         break;
     }
   }
   while (!indexesToRemove.empty()) {
     myResult.remove((int) indexesToRemove.pop());
   }
   if (doAdd) {
     myResult.add(newChain);
   }
 }
  @NotNull
  private static List<MethodsChain> search(
      final MethodsUsageIndexReader indexReader,
      final SearchInitializer initializer,
      final Set<String> toSet,
      final int pathMaximalLength,
      final int maxResultSize,
      final String targetQName,
      final ChainCompletionContext context) {
    final Set<String> allExcludedNames =
        MethodChainsSearchUtil.joinToHashSet(context.getExcludedQNames(), targetQName);
    final SearchInitializer.InitResult initResult =
        initializer.init(Collections.<String>emptySet());

    final Map<MethodIncompleteSignature, MethodsChain> knownDistance = initResult.getChains();

    final List<WeightAware<MethodIncompleteSignature>> allInitialVertexes =
        initResult.getVertexes();

    final LinkedList<WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>> q =
        new LinkedList<>(
            ContainerUtil.map(
                allInitialVertexes,
                methodIncompleteSignatureWeightAware -> {
                  final MethodIncompleteSignature underlying =
                      methodIncompleteSignatureWeightAware.getUnderlying();
                  return new WeightAware<>(
                      Pair.create(
                          underlying,
                          new MethodsChain(
                              context.resolveNotDeprecated(underlying),
                              methodIncompleteSignatureWeightAware.getWeight(),
                              underlying.getOwner())),
                      methodIncompleteSignatureWeightAware.getWeight());
                }));

    int maxWeight = 0;
    for (final MethodsChain methodsChain : knownDistance.values()) {
      if (methodsChain.getChainWeight() > maxWeight) {
        maxWeight = methodsChain.getChainWeight();
      }
    }

    final ResultHolder result = new ResultHolder(context.getPsiManager());
    while (!q.isEmpty()) {
      ProgressManager.checkCanceled();
      final WeightAware<Pair<MethodIncompleteSignature, MethodsChain>> currentVertex = q.poll();
      final int currentVertexDistance = currentVertex.getWeight();
      final Pair<MethodIncompleteSignature, MethodsChain> currentVertexUnderlying =
          currentVertex.getUnderlying();
      final MethodsChain currentVertexMethodsChain =
          knownDistance.get(currentVertexUnderlying.getFirst());
      if (currentVertexDistance != currentVertexMethodsChain.getChainWeight()) {
        continue;
      }
      if (currentVertex.getUnderlying().getFirst().isStatic()
          || toSet.contains(currentVertex.getUnderlying().getFirst().getOwner())) {
        result.add(currentVertex.getUnderlying().getSecond());
        continue;
      }
      final String currentReturnType = currentVertexUnderlying.getFirst().getOwner();
      final SortedSet<UsageIndexValue> nextMethods = indexReader.getMethods(currentReturnType);
      final MaxSizeTreeSet<WeightAware<MethodIncompleteSignature>> currentSignatures =
          new MaxSizeTreeSet<>(maxResultSize);
      for (final UsageIndexValue indexValue : nextMethods) {
        final MethodIncompleteSignature vertex = indexValue.getMethodIncompleteSignature();
        final int occurrences = indexValue.getOccurrences();
        if (vertex.isStatic() || !vertex.getOwner().equals(targetQName)) {
          final int vertexDistance = Math.min(currentVertexDistance, occurrences);
          final MethodsChain knownVertexMethodsChain = knownDistance.get(vertex);
          if ((knownVertexMethodsChain == null
              || knownVertexMethodsChain.getChainWeight() < vertexDistance)) {
            if (currentSignatures.isEmpty()
                || currentSignatures.last().getWeight() < vertexDistance) {
              if (currentVertexMethodsChain.size() < pathMaximalLength - 1) {
                final MethodIncompleteSignature methodInvocation =
                    indexValue.getMethodIncompleteSignature();
                final PsiMethod[] psiMethods = context.resolveNotDeprecated(methodInvocation);
                if (psiMethods.length != 0
                    && MethodChainsSearchUtil.checkParametersForTypesQNames(
                        psiMethods, allExcludedNames)) {
                  final MethodsChain newBestMethodsChain =
                      currentVertexMethodsChain.addEdge(
                          psiMethods,
                          indexValue.getMethodIncompleteSignature().getOwner(),
                          vertexDistance);
                  currentSignatures.add(
                      new WeightAware<>(indexValue.getMethodIncompleteSignature(), vertexDistance));
                  knownDistance.put(vertex, newBestMethodsChain);
                }
              }
            }
          } else {
            break;
          }
        }
      }
      boolean updated = false;
      if (!currentSignatures.isEmpty()) {
        boolean isBreak = false;
        for (final WeightAware<MethodIncompleteSignature> sign : currentSignatures) {
          final PsiMethod[] resolved = context.resolveNotDeprecated(sign.getUnderlying());
          if (!isBreak) {
            if (sign.getWeight() * NEXT_METHOD_IN_CHAIN_RATIO > currentVertex.getWeight()) {
              final boolean stopChain =
                  sign.getUnderlying().isStatic()
                      || toSet.contains(sign.getUnderlying().getOwner());
              if (stopChain) {
                updated = true;
                result.add(
                    currentVertex
                        .getUnderlying()
                        .getSecond()
                        .addEdge(resolved, sign.getUnderlying().getOwner(), sign.getWeight()));
                continue;
              } else {
                updated = true;
                final MethodsChain methodsChain =
                    currentVertexUnderlying.second.addEdge(
                        resolved, sign.getUnderlying().getOwner(), sign.getWeight());
                q.add(
                    new WeightAware<>(
                        Pair.create(sign.getUnderlying(), methodsChain), sign.getWeight()));
                continue;
              }
            }
          }
          final MethodsChain methodsChain =
              currentVertexUnderlying.second.addEdge(
                  resolved, sign.getUnderlying().getOwner(), sign.getWeight());
          final ParametersMatcher.MatchResult parametersMatchResult =
              ParametersMatcher.matchParameters(methodsChain, context);
          if (parametersMatchResult.noUnmatchedAndHasMatched()
              && parametersMatchResult.hasTarget()) {
            updated = true;
            q.addFirst(
                new WeightAware<>(
                    Pair.create(sign.getUnderlying(), methodsChain), sign.getWeight()));
          }
          isBreak = true;
        }
      }
      if (!updated
          && (currentVertex.getUnderlying().getFirst().isStatic()
              || !targetQName.equals(currentVertex.getUnderlying().getFirst().getOwner()))) {
        result.add(currentVertex.getUnderlying().getSecond());
      }
      if (result.size() > maxResultSize) {
        return result.getResult();
      }
    }
    return result.getResult();
  }