@Override
  public String handleEmptyLookup(
      @NotNull final CompletionParameters parameters, final Editor editor) {
    if (!(parameters.getOriginalFile() instanceof PsiJavaFile)) return null;

    final String ad = advertise(parameters);
    final String suffix = ad == null ? "" : "; " + StringUtil.decapitalize(ad);
    if (parameters.getCompletionType() == CompletionType.SMART) {
      if (!ApplicationManager.getApplication().isUnitTestMode()) {

        final Project project = parameters.getPosition().getProject();
        final PsiFile file = parameters.getOriginalFile();

        PsiExpression expression =
            PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true);
        if (expression != null && expression.getParent() instanceof PsiExpressionList) {
          int lbraceOffset = expression.getParent().getTextRange().getStartOffset();
          ShowParameterInfoHandler.invoke(project, editor, file, lbraceOffset, null);
        }

        if (expression instanceof PsiLiteralExpression) {
          return LangBundle.message("completion.no.suggestions") + suffix;
        }

        if (expression instanceof PsiInstanceOfExpression) {
          final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression) expression;
          if (PsiTreeUtil.isAncestor(
              instanceOfExpression.getCheckType(), parameters.getPosition(), false)) {
            return LangBundle.message("completion.no.suggestions") + suffix;
          }
        }
      }

      final Set<PsiType> expectedTypes = JavaCompletionUtil.getExpectedTypes(parameters);
      if (expectedTypes != null) {
        PsiType type = expectedTypes.size() == 1 ? expectedTypes.iterator().next() : null;
        if (type != null) {
          final PsiType deepComponentType = type.getDeepComponentType();
          if (deepComponentType instanceof PsiClassType) {
            if (((PsiClassType) deepComponentType).resolve() != null) {
              return CompletionBundle.message(
                      "completion.no.suggestions.of.type", type.getPresentableText())
                  + suffix;
            }
            return CompletionBundle.message("completion.unknown.type", type.getPresentableText())
                + suffix;
          }
          if (!PsiType.NULL.equals(type)) {
            return CompletionBundle.message(
                    "completion.no.suggestions.of.type", type.getPresentableText())
                + suffix;
          }
        }
      }
    }
    return LangBundle.message("completion.no.suggestions") + suffix;
  }
  @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);
      }
    }
  }
  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);
      }
    }
  }
  private static Set<String> addReferenceVariants(
      final CompletionParameters parameters,
      CompletionResultSet result,
      final InheritorsHolder inheritors) {
    final Set<String> usedWords = new HashSet<String>();
    final PsiElement position = parameters.getPosition();
    final boolean first = parameters.getInvocationCount() <= 1;
    final boolean isSwitchLabel = SWITCH_LABEL.accepts(position);
    final boolean isAfterNew = JavaClassNameCompletionContributor.AFTER_NEW.accepts(position);
    final boolean pkgContext = JavaCompletionUtil.inSomePackage(position);
    LegacyCompletionContributor.processReferences(
        parameters,
        result,
        new PairConsumer<PsiReference, CompletionResultSet>() {
          @Override
          public void consume(final PsiReference reference, final CompletionResultSet result) {
            if (reference instanceof PsiJavaReference) {
              final ElementFilter filter = getReferenceFilter(position);
              if (filter != null) {
                final PsiFile originalFile = parameters.getOriginalFile();
                JavaCompletionProcessor.Options options =
                    JavaCompletionProcessor.Options.DEFAULT_OPTIONS
                        .withCheckAccess(first)
                        .withFilterStaticAfterInstance(first)
                        .withShowInstanceInStaticContext(!first);
                for (LookupElement element :
                    JavaCompletionUtil.processJavaReference(
                        position,
                        (PsiJavaReference) reference,
                        new ElementExtractorFilter(filter),
                        options,
                        result.getPrefixMatcher(),
                        parameters)) {
                  if (inheritors.alreadyProcessed(element)) {
                    continue;
                  }

                  if (isSwitchLabel) {
                    result.addElement(
                        TailTypeDecorator.withTail(element, TailType.createSimpleTailType(':')));
                  } else {
                    final LookupItem item = element.as(LookupItem.CLASS_CONDITION_KEY);
                    if (originalFile instanceof PsiJavaCodeReferenceCodeFragment
                        && !((PsiJavaCodeReferenceCodeFragment) originalFile).isClassesAccepted()
                        && item != null) {
                      item.setTailType(TailType.NONE);
                    }

                    result.addElement(element);
                  }
                }
              }
              return;
            }
            if (reference instanceof PsiLabelReference) {
              processLabelReference(result, (PsiLabelReference) reference);
              return;
            }

            final Object[] variants = reference.getVariants();
            if (variants == null) {
              LOG.error("Reference=" + reference);
            }
            for (Object completion : variants) {
              if (completion == null) {
                LOG.error(
                    "Position="
                        + position
                        + "\n;Reference="
                        + reference
                        + "\n;variants="
                        + Arrays.toString(variants));
              }
              if (completion instanceof LookupElement
                  && !inheritors.alreadyProcessed((LookupElement) completion)) {
                usedWords.add(((LookupElement) completion).getLookupString());
                result.addElement((LookupElement) completion);
              } else if (completion instanceof PsiClass) {
                for (JavaPsiClassReferenceElement item :
                    JavaClassNameCompletionContributor.createClassLookupItems(
                        (PsiClass) completion,
                        isAfterNew,
                        JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER,
                        new Condition<PsiClass>() {
                          @Override
                          public boolean value(PsiClass psiClass) {
                            return !inheritors.alreadyProcessed(psiClass)
                                && JavaCompletionUtil.isSourceLevelAccessible(
                                    position, psiClass, pkgContext);
                          }
                        })) {
                  usedWords.add(item.getLookupString());
                  result.addElement(item);
                }

              } else {
                LookupElement element = LookupItemUtil.objectToLookupItem(completion);
                usedWords.add(element.getLookupString());
                result.addElement(element);
              }
            }
          }
        });
    return usedWords;
  }