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);
  }
  @Override
  public void beforeCompletion(@NotNull final CompletionInitializationContext context) {
    final PsiFile file = context.getFile();

    if (file instanceof PsiJavaFile) {
      JavaCompletionUtil.initOffsets(file, context.getOffsetMap());

      autoImport(file, context.getStartOffset() - 1, context.getEditor());

      if (context.getCompletionType() == CompletionType.BASIC) {
        if (semicolonNeeded(context.getEditor(), file, context.getStartOffset())) {
          context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER.trim() + ";");
          return;
        }

        final PsiJavaCodeReferenceElement ref =
            PsiTreeUtil.findElementOfClassAtOffset(
                file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false);
        if (ref != null && !(ref instanceof PsiReferenceExpression)) {
          if (ref.getParent() instanceof PsiTypeElement) {
            context.setDummyIdentifier(
                CompletionInitializationContext.DUMMY_IDENTIFIER.trim() + ";");
          }

          if (JavaSmartCompletionContributor.AFTER_NEW.accepts(ref)) {
            final PsiReferenceParameterList paramList = ref.getParameterList();
            if (paramList != null && paramList.getTextLength() > 0) {
              context
                  .getOffsetMap()
                  .addOffset(
                      ConstructorInsertHandler.PARAM_LIST_START,
                      paramList.getTextRange().getStartOffset());
              context
                  .getOffsetMap()
                  .addOffset(
                      ConstructorInsertHandler.PARAM_LIST_END,
                      paramList.getTextRange().getEndOffset());
            }
          }

          return;
        }

        final PsiElement element = file.findElementAt(context.getStartOffset());

        if (psiElement().inside(PsiAnnotation.class).accepts(element)) {
          return;
        }

        context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER_TRIMMED);
      }
    }
  }
  @Override
  public void fillCompletionVariants(
      @NotNull final CompletionParameters parameters, @NotNull final CompletionResultSet result) {
    final JavaCompletionSession session = new JavaCompletionSession(result);
    ResultTracker tracker =
        new ResultTracker(result) {
          @Override
          public void consume(CompletionResult plainResult) {
            super.consume(plainResult);

            LookupElement element = plainResult.getLookupElement();
            Object o = element.getObject();
            if (o instanceof PsiClass) {
              session.registerClass((PsiClass) o);
            }
            if (element instanceof TypeArgumentCompletionProvider.TypeArgsLookupElement) {
              ((TypeArgumentCompletionProvider.TypeArgsLookupElement) element)
                  .registerSingleClass(session);
            }
          }
        };
    result.runRemainingContributors(parameters, tracker);
    final boolean empty = tracker.containsOnlyPackages || suggestAllAnnotations(parameters);

    if (!empty && parameters.getInvocationCount() == 0) {
      result.restartCompletionWhenNothingMatches();
    }

    if (empty) {
      delegate(parameters, JavaCompletionSorting.addJavaSorting(parameters, result), session);
    } else if (Registry.is("ide.completion.show.better.matching.classes")) {
      if (parameters.getCompletionType() == CompletionType.BASIC
          && parameters.getInvocationCount() <= 1
          && JavaCompletionContributor.mayStartClassName(result)
          && JavaCompletionContributor.isClassNamePossible(parameters)
          && !JavaSmartCompletionContributor.AFTER_NEW.accepts(parameters.getPosition())) {
        suggestNonImportedClasses(
            parameters,
            JavaCompletionSorting.addJavaSorting(
                parameters, result.withPrefixMatcher(tracker.betterMatcher)),
            session);
      }
    }
  }
  @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();
  }
  @Nullable
  public static ElementFilter getReferenceFilter(PsiElement position) {
    // Completion after extends in interface, type parameter and implements in class
    final PsiClass containingClass =
        PsiTreeUtil.getParentOfType(
            position,
            PsiClass.class,
            false,
            PsiCodeBlock.class,
            PsiMethod.class,
            PsiExpressionList.class,
            PsiVariable.class,
            PsiAnnotation.class);
    if (containingClass != null
        && psiElement()
            .afterLeaf(PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS, ",", "&")
            .accepts(position)) {
      return new AndFilter(
          ElementClassFilter.CLASS, new NotFilter(new AssignableFromContextFilter()));
    }

    if (ANNOTATION_NAME.accepts(position)) {
      return new AnnotationTypeFilter();
    }

    if (JavaCompletionData.DECLARATION_START.accepts(position)
        || JavaCompletionData.isInsideParameterList(position)
        || psiElement()
            .inside(psiElement(PsiJavaCodeReferenceElement.class).withParent(psiAnnotation()))
            .accepts(position)) {
      return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.PACKAGE_FILTER);
    }

    if (psiElement().afterLeaf(PsiKeyword.INSTANCEOF).accepts(position)) {
      return new ElementExtractorFilter(ElementClassFilter.CLASS);
    }

    if (JavaCompletionData.VARIABLE_AFTER_FINAL.accepts(position)) {
      return ElementClassFilter.CLASS;
    }

    if (JavaCompletionData.AFTER_TRY_BLOCK.isAcceptable(position, position)
        || JavaCompletionData.START_SWITCH.accepts(position)
        || JavaCompletionData.isInstanceofPlace(position)
        || JavaCompletionData.isAfterPrimitiveOrArrayType(position)) {
      return null;
    }

    if (JavaCompletionData.START_FOR.accepts(position)) {
      return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.VARIABLE);
    }

    if (JavaSmartCompletionContributor.AFTER_NEW.accepts(position)) {
      return ElementClassFilter.CLASS;
    }

    if (psiElement().inside(PsiReferenceParameterList.class).accepts(position)) {
      return ElementClassFilter.CLASS;
    }

    if (psiElement().inside(PsiAnnotationParameterList.class).accepts(position)) {
      return createAnnotationFilter(position);
    }

    if (psiElement().afterLeaf("=").inside(PsiVariable.class).accepts(position)) {
      return new OrFilter(
          new ClassFilter(PsiVariable.class, false),
          new ExcludeDeclaredFilter(new ClassFilter(PsiVariable.class)));
    }

    if (SWITCH_LABEL.accepts(position)) {
      return new ClassFilter(PsiField.class) {
        @Override
        public boolean isAcceptable(Object element, PsiElement context) {
          return element instanceof PsiEnumConstant;
        }
      };
    }

    return TrueFilter.INSTANCE;
  }
  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);
  }