private static void addInheritors(
      CompletionParameters parameters,
      final CompletionResultSet resultSet,
      final PsiClass referencedClass,
      final int parameterIndex) {
    final List<PsiClassType> typeList =
        Collections.singletonList(
            (PsiClassType)
                TypeConversionUtil.typeParameterErasure(
                    referencedClass.getTypeParameters()[parameterIndex]));
    JavaInheritorsGetter.processInheritors(
        parameters,
        typeList,
        resultSet.getPrefixMatcher(),
        new Consumer<PsiType>() {
          @Override
          public void consume(final PsiType type) {
            final PsiClass psiClass = PsiUtil.resolveClassInType(type);
            if (psiClass == null) return;

            resultSet.addElement(
                TailTypeDecorator.withTail(
                    new JavaPsiClassReferenceElement(psiClass),
                    getTail(parameterIndex == referencedClass.getTypeParameters().length - 1)));
          }
        });
  }
  public static void addAllClasses(
      CompletionParameters parameters,
      final CompletionResultSet result,
      final InheritorsHolder inheritors) {
    if (!isClassNamePossible(parameters) || !mayStartClassName(result)) {
      return;
    }

    if (parameters.getInvocationCount() >= 2) {
      JavaClassNameCompletionContributor.addAllClasses(
          parameters,
          parameters.getInvocationCount() <= 2,
          result.getPrefixMatcher(),
          new Consumer<LookupElement>() {
            @Override
            public void consume(LookupElement element) {
              if (!inheritors.alreadyProcessed(element)) {
                result.addElement(element);
              }
            }
          });
    } else {
      advertiseSecondCompletion(parameters.getPosition().getProject(), result);
    }
  }
  private static void suggestChainedCalls(
      CompletionParameters parameters, CompletionResultSet result, PsiElement position) {
    PsiElement parent = position.getParent();
    if (!(parent instanceof PsiJavaCodeReferenceElement)) {
      return;
    }
    PsiElement qualifier = ((PsiJavaCodeReferenceElement) parent).getQualifier();
    if (!(qualifier instanceof PsiJavaCodeReferenceElement)
        || ((PsiJavaCodeReferenceElement) qualifier).isQualified()
        || ((PsiJavaCodeReferenceElement) qualifier).resolve() != null) {
      return;
    }

    String fullPrefix =
        position
            .getContainingFile()
            .getText()
            .substring(parent.getTextRange().getStartOffset(), parameters.getOffset());
    CompletionResultSet qualifiedCollector = result.withPrefixMatcher(fullPrefix);
    ElementFilter filter = JavaCompletionContributor.getReferenceFilter(position);
    for (LookupElement base :
        suggestQualifierItems(parameters, (PsiJavaCodeReferenceElement) qualifier, filter)) {
      PsiType type = JavaCompletionUtil.getLookupElementType(base);
      if (type != null && !PsiType.VOID.equals(type)) {
        PsiReferenceExpression ref =
            ReferenceExpressionCompletionContributor.createMockReference(position, type, base);
        for (final LookupElement item :
            JavaSmartCompletionContributor.completeReference(
                position, ref, filter, true, true, parameters, result.getPrefixMatcher())) {
          qualifiedCollector.addElement(new JavaChainLookupElement(base, item));
        }
      }
    }
  }
  public static CompletionResultSet addJavaSorting(
      final CompletionParameters parameters, CompletionResultSet result) {
    final PsiElement position = parameters.getPosition();
    final ExpectedTypeInfo[] expectedTypes =
        PsiJavaPatterns.psiElement()
                .beforeLeaf(PsiJavaPatterns.psiElement().withText("."))
                .accepts(position)
            ? ExpectedTypeInfo.EMPTY_ARRAY
            : JavaSmartCompletionContributor.getExpectedTypes(parameters);
    final CompletionType type = parameters.getCompletionType();
    final boolean smart = type == CompletionType.SMART;
    final boolean afterNew = JavaSmartCompletionContributor.AFTER_NEW.accepts(position);

    List<LookupElementWeigher> afterProximity = new ArrayList<LookupElementWeigher>();
    afterProximity.add(new PreferContainingSameWords(expectedTypes));
    if (smart) {
      afterProximity.add(new PreferFieldsAndGetters());
    }
    afterProximity.add(new PreferShorter(expectedTypes));

    CompletionSorter sorter = CompletionSorter.defaultSorter(parameters, result.getPrefixMatcher());
    if (!smart && afterNew) {
      sorter = sorter.weighBefore("liftShorter", new PreferExpected(true, expectedTypes));
    } else if (PsiTreeUtil.getParentOfType(position, PsiReferenceList.class) == null) {
      sorter =
          ((CompletionSorterImpl) sorter)
              .withClassifier("liftShorterClasses", true, new LiftShorterClasses(position));
    }
    if (smart) {
      sorter =
          sorter.weighAfter("priority", new PreferDefaultTypeWeigher(expectedTypes, parameters));
    }

    List<LookupElementWeigher> afterPrefix = ContainerUtil.newArrayList();
    if (!smart) {
      ContainerUtil.addIfNotNull(afterPrefix, preferStatics(position, expectedTypes));
    }
    ContainerUtil.addIfNotNull(afterPrefix, recursion(parameters, expectedTypes));
    if (!smart && !afterNew) {
      afterPrefix.add(new PreferExpected(false, expectedTypes));
    }
    Collections.addAll(
        afterPrefix,
        new PreferByKindWeigher(type, position),
        new PreferSimilarlyEnding(expectedTypes),
        new PreferNonGeneric(),
        new PreferAccessible(position),
        new PreferSimple(),
        new PreferEnumConstants(parameters));

    sorter =
        sorter.weighAfter(
            "prefix", afterPrefix.toArray(new LookupElementWeigher[afterPrefix.size()]));
    sorter =
        sorter.weighAfter(
            "proximity", afterProximity.toArray(new LookupElementWeigher[afterProximity.size()]));
    return result.withRelevanceSorter(sorter);
  }
 private static void suggestCollectionUtilities(
     CompletionParameters parameters, final CompletionResultSet result, PsiElement position) {
   if (StringUtil.isNotEmpty(result.getPrefixMatcher().getPrefix())) {
     for (ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
       new CollectionsUtilityMethodsProvider(
               position, info.getType(), info.getDefaultType(), result)
           .addCompletions(true);
     }
   }
 }
  private static void addKeywords(CompletionParameters parameters, CompletionResultSet result) {
    PsiElement position = parameters.getPosition();
    final Set<LookupElement> lookupSet = new LinkedHashSet<LookupElement>();
    final Set<CompletionVariant> keywordVariants = new HashSet<CompletionVariant>();
    final JavaCompletionData completionData = getCompletionData(PsiUtil.getLanguageLevel(position));
    completionData.addKeywordVariants(keywordVariants, position, parameters.getOriginalFile());
    completionData.completeKeywordsBySet(
        lookupSet,
        keywordVariants,
        position,
        result.getPrefixMatcher(),
        parameters.getOriginalFile());
    completionData.fillCompletions(parameters, result);

    for (final LookupElement item : lookupSet) {
      result.addElement(item);
    }
  }
 @Override
 protected void addCompletions(
     @NotNull CompletionParameters parameters,
     ProcessingContext context,
     @NotNull CompletionResultSet result) {
   UnicodeCharacterNames.iterate(
       name -> {
         if (result.getPrefixMatcher().prefixMatches(name)) {
           final String type =
               new String(new int[] {UnicodeCharacterNames.getCodePoint(name)}, 0, 1);
           if (myEmbrace) {
             result.addElement(createLookupElement("{" + name + "}", type, emptyIcon));
           } else {
             result.addElement(
                 TailTypeDecorator.withTail(
                     createLookupElement(name, type, emptyIcon),
                     TailType.createSimpleTailType('}')));
           }
         }
         ProgressManager.checkCanceled();
       });
 }
  private static void suggestNonImportedClasses(
      CompletionParameters parameters,
      CompletionResultSet result,
      @Nullable JavaCompletionSession session) {
    JavaClassNameCompletionContributor.addAllClasses(
        parameters,
        true,
        result.getPrefixMatcher(),
        element -> {
          if (session != null && session.alreadyProcessed(element)) {
            return;
          }
          JavaPsiClassReferenceElement classElement =
              element.as(JavaPsiClassReferenceElement.CLASS_CONDITION_KEY);
          if (classElement != null) {
            classElement.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE);
            element =
                JavaClassNameCompletionContributor.highlightIfNeeded(classElement, parameters);
          }

          result.addElement(element);
        });
  }
  private static void completeAnnotationAttributeName(
      CompletionResultSet result, PsiElement insertedElement, CompletionParameters parameters) {
    PsiNameValuePair pair = PsiTreeUtil.getParentOfType(insertedElement, PsiNameValuePair.class);
    PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList) pair.getParent();
    PsiAnnotation anno = (PsiAnnotation) parameterList.getParent();
    boolean showClasses = psiElement().afterLeaf("(").accepts(insertedElement);
    PsiClass annoClass = null;
    final PsiJavaCodeReferenceElement referenceElement = anno.getNameReferenceElement();
    if (referenceElement != null) {
      final PsiElement element = referenceElement.resolve();
      if (element instanceof PsiClass) {
        annoClass = (PsiClass) element;
        if (annoClass.findMethodsByName("value", false).length == 0) {
          showClasses = false;
        }
      }
    }

    if (showClasses && insertedElement.getParent() instanceof PsiReferenceExpression) {
      final Set<LookupElement> set =
          JavaCompletionUtil.processJavaReference(
              insertedElement,
              (PsiJavaReference) insertedElement.getParent(),
              new ElementExtractorFilter(createAnnotationFilter(insertedElement)),
              JavaCompletionProcessor.Options.DEFAULT_OPTIONS,
              result.getPrefixMatcher(),
              parameters);
      for (final LookupElement element : set) {
        result.addElement(element);
      }
      addAllClasses(parameters, result, new InheritorsHolder(insertedElement, result));
    }

    if (annoClass != null) {
      final PsiNameValuePair[] existingPairs = parameterList.getAttributes();

      methods:
      for (PsiMethod method : annoClass.getMethods()) {
        if (!(method instanceof PsiAnnotationMethod)) continue;

        final String attrName = method.getName();
        for (PsiNameValuePair existingAttr : existingPairs) {
          if (PsiTreeUtil.isAncestor(existingAttr, insertedElement, false)) break;
          if (Comparing.equal(existingAttr.getName(), attrName)
              || PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(attrName)
                  && existingAttr.getName() == null) continue methods;
        }
        LookupElementBuilder element =
            LookupElementBuilder.createWithIcon(method)
                .withInsertHandler(
                    new InsertHandler<LookupElement>() {
                      @Override
                      public void handleInsert(InsertionContext context, LookupElement item) {
                        final Editor editor = context.getEditor();
                        TailType.EQ.processTail(editor, editor.getCaretModel().getOffset());
                        context.setAddCompletionChar(false);

                        context.commitDocument();
                        PsiAnnotationParameterList paramList =
                            PsiTreeUtil.findElementOfClassAtOffset(
                                context.getFile(),
                                context.getStartOffset(),
                                PsiAnnotationParameterList.class,
                                false);
                        if (paramList != null
                            && paramList.getAttributes().length > 0
                            && paramList.getAttributes()[0].getName() == null) {
                          int valueOffset =
                              paramList.getAttributes()[0].getTextRange().getStartOffset();
                          context
                              .getDocument()
                              .insertString(
                                  valueOffset, PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
                          TailType.EQ.processTail(
                              editor,
                              valueOffset + PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.length());
                        }
                      }
                    });

        PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod) method).getDefaultValue();
        if (defaultValue != null) {
          Object constant =
              JavaPsiFacade.getInstance(method.getProject())
                  .getConstantEvaluationHelper()
                  .computeConstantExpression(defaultValue);
          if (constant != null) {
            element =
                element.withTailText(
                    " default " + (constant instanceof String ? "\"" + constant + "\"" : constant),
                    true);
          }
        }

        result.addElement(element);
      }
    }
  }
 public static boolean mayStartClassName(CompletionResultSet result) {
   return StringUtil.isNotEmpty(result.getPrefixMatcher().getPrefix());
 }
  @Override
  public void fillCompletionVariants(
      final CompletionParameters parameters, final CompletionResultSet _result) {
    if (parameters.getCompletionType() != CompletionType.BASIC) {
      return;
    }

    final PsiElement position = parameters.getPosition();
    if (!isInJavaContext(position)) {
      return;
    }

    if (AFTER_NUMBER_LITERAL.accepts(position)
        || UNEXPECTED_REFERENCE_AFTER_DOT.accepts(position)) {
      _result.stopHere();
      return;
    }

    final CompletionResultSet result = JavaCompletionSorting.addJavaSorting(parameters, _result);

    if (ANNOTATION_ATTRIBUTE_NAME.accepts(position)
        && !JavaCompletionData.isAfterPrimitiveOrArrayType(position)) {
      JavaCompletionData.addExpectedTypeMembers(parameters, result);
      completeAnnotationAttributeName(result, position, parameters);
      result.stopHere();
      return;
    }

    final InheritorsHolder inheritors = new InheritorsHolder(position, result);
    if (JavaSmartCompletionContributor.IN_TYPE_ARGS.accepts(position)) {
      new TypeArgumentCompletionProvider(false, inheritors)
          .addCompletions(parameters, new ProcessingContext(), result);
    }

    PrefixMatcher matcher = result.getPrefixMatcher();
    if (JavaSmartCompletionContributor.AFTER_NEW.accepts(position)) {
      new JavaInheritorsGetter(ConstructorInsertHandler.BASIC_INSTANCE)
          .generateVariants(parameters, matcher, inheritors);
    }

    if (IMPORT_REFERENCE.accepts(position)) {
      result.addElement(LookupElementBuilder.create("*"));
    }

    addKeywords(parameters, result);

    Set<String> usedWords = addReferenceVariants(parameters, result, inheritors);

    if (psiElement().inside(PsiLiteralExpression.class).accepts(position)) {
      PsiReference reference = position.getContainingFile().findReferenceAt(parameters.getOffset());
      if (reference == null || reference.isSoft()) {
        WordCompletionContributor.addWordCompletionVariants(result, parameters, usedWords);
      }
    }

    JavaGenerateMemberCompletionContributor.fillCompletionVariants(parameters, result);

    addAllClasses(parameters, result, inheritors);

    final PsiElement parent = position.getParent();
    if (parent instanceof PsiReferenceExpression
        && !((PsiReferenceExpression) parent).isQualified()
        && parameters.isExtendedCompletion()
        && StringUtil.isNotEmpty(matcher.getPrefix())) {
      new JavaStaticMemberProcessor(parameters).processStaticMethodsGlobally(matcher, result);
    }
    result.stopHere();
  }
  public static CompletionResultSet addJavaSorting(
      final CompletionParameters parameters, CompletionResultSet result) {
    String prefix = result.getPrefixMatcher().getPrefix();
    final PsiElement position = parameters.getPosition();
    final ExpectedTypeInfo[] expectedTypes =
        PsiJavaPatterns.psiElement()
                .beforeLeaf(PsiJavaPatterns.psiElement().withText("."))
                .accepts(position)
            ? ExpectedTypeInfo.EMPTY_ARRAY
            : JavaSmartCompletionContributor.getExpectedTypes(parameters);
    final CompletionType type = parameters.getCompletionType();
    final boolean smart = type == CompletionType.SMART;
    final boolean afterNew = JavaSmartCompletionContributor.AFTER_NEW.accepts(position);

    List<LookupElementWeigher> afterNegativeStats = new ArrayList<LookupElementWeigher>();
    if (!smart) {
      ContainerUtil.addIfNotNull(afterNegativeStats, preferStatics(position));
    }
    afterNegativeStats.add(new PreferLocalVariablesLiteralsAndAnnoMethodsWeigher(type, position));
    ContainerUtil.addIfNotNull(afterNegativeStats, recursion(parameters, expectedTypes));
    if (!smart && !afterNew) {
      afterNegativeStats.add(new PreferExpected(false, expectedTypes));
    }
    afterNegativeStats.add(new PreferSimilarlyEnding(expectedTypes, prefix));

    List<LookupElementWeigher> afterProximity = new ArrayList<LookupElementWeigher>();
    afterProximity.add(new PreferContainingSameWords(expectedTypes));
    if (smart) {
      afterProximity.add(new PreferFieldsAndGetters());
    }
    afterProximity.add(new PreferShorter(expectedTypes, prefix));

    CompletionSorter sorter = CompletionSorter.defaultSorter(parameters, result.getPrefixMatcher());
    if (!smart && afterNew) {
      sorter = sorter.weighBefore("liftShorter", new PreferExpected(true, expectedTypes));
    } else {
      final ProjectFileIndex fileIndex =
          ProjectRootManager.getInstance(position.getProject()).getFileIndex();
      sorter =
          ((CompletionSorterImpl) sorter)
              .withClassifier(
                  "liftShorter",
                  true,
                  new ClassifierFactory<LookupElement>("liftShorterClasses") {
                    @Override
                    public Classifier<LookupElement> createClassifier(
                        Classifier<LookupElement> next) {
                      return new LiftShorterItemsClassifier(
                          next,
                          new LiftShorterItemsClassifier.LiftingCondition() {
                            @Override
                            public boolean shouldLift(
                                LookupElement shorterElement,
                                LookupElement longerElement,
                                ProcessingContext context) {
                              if (super.shouldLift(shorterElement, longerElement, context)) {
                                return true;
                              }
                              Object object = shorterElement.getObject();
                              if (object instanceof PsiClass) {
                                PsiFile file = ((PsiClass) object).getContainingFile();
                                if (file != null) {
                                  VirtualFile vFile = file.getOriginalFile().getVirtualFile();
                                  if (vFile != null && fileIndex.isInSource(vFile)) {
                                    return true;
                                  }
                                }
                              }
                              return false;
                            }
                          });
                    }
                  });
    }
    if (smart) {
      sorter =
          sorter.weighBefore(
              "negativeStats", new PreferDefaultTypeWeigher(expectedTypes, parameters));
    }
    sorter =
        sorter.weighAfter(
            "negativeStats",
            afterNegativeStats.toArray(new LookupElementWeigher[afterNegativeStats.size()]));
    sorter =
        sorter.weighAfter(
            "prefix",
            new PreferNonGeneric(),
            new PreferAccessible(position),
            new PreferSimple(),
            new PreferEnumConstants(parameters));
    sorter =
        sorter.weighAfter(
            "proximity", afterProximity.toArray(new LookupElementWeigher[afterProximity.size()]));
    return result.withRelevanceSorter(sorter);
  }