@Override
  protected void addCompletions(
      @NotNull final CompletionParameters parameters,
      final ProcessingContext processingContext,
      @NotNull final CompletionResultSet resultSet) {
    final PsiElement context = parameters.getPosition();

    final Pair<PsiClass, Integer> pair = getTypeParameterInfo(context);
    if (pair == null) return;

    PsiExpression expression = PsiTreeUtil.getContextOfType(context, PsiExpression.class, true);
    if (expression != null) {
      ExpectedTypeInfo[] types =
          ExpectedTypesProvider.getExpectedTypes(expression, true, false, false);
      if (types.length > 0) {
        for (ExpectedTypeInfo info : types) {
          PsiType type = info.getType();
          if (type instanceof PsiClassType && !type.equals(expression.getType())) {
            fillExpectedTypeArgs(
                resultSet,
                context,
                pair.first,
                pair.second,
                ((PsiClassType) type).resolveGenerics(),
                mySmart ? info.getTailType() : TailType.NONE);
          }
        }
        return;
      }
    }

    if (mySmart) {
      addInheritors(parameters, resultSet, pair.first, pair.second);
    }
  }
  private static boolean shouldInsertParentheses(PsiClass psiClass, PsiElement position) {
    final PsiJavaCodeReferenceElement ref =
        PsiTreeUtil.getParentOfType(position, PsiJavaCodeReferenceElement.class);
    if (ref == null) {
      return false;
    }

    final PsiReferenceParameterList parameterList = ref.getParameterList();
    if (parameterList != null && parameterList.getTextLength() > 0) {
      return false;
    }

    final PsiElement prevElement = FilterPositionUtil.searchNonSpaceNonCommentBack(ref);
    if (prevElement != null && prevElement.getParent() instanceof PsiNewExpression) {

      Set<PsiType> expectedTypes = new HashSet<PsiType>();
      for (ExpectedTypeInfo info :
          ExpectedTypesProvider.getExpectedTypes((PsiExpression) prevElement.getParent(), true)) {
        expectedTypes.add(info.getType());
      }

      return JavaCompletionUtil.isDefinitelyExpected(psiClass, expectedTypes, position);
    }

    return false;
  }
  public void setupTypeElement(
      PsiTypeElement typeElement,
      ExpectedTypeInfo[] infos,
      PsiSubstitutor substitutor,
      TemplateBuilder builder,
      @Nullable PsiElement context,
      PsiClass targetClass) {
    LOG.assertTrue(typeElement.isValid());
    ApplicationManager.getApplication().assertWriteAccessAllowed();

    PsiManager manager = typeElement.getManager();
    GlobalSearchScope scope = typeElement.getResolveScope();
    Project project = manager.getProject();

    if (infos.length == 1 && substitutor != null && substitutor != PsiSubstitutor.EMPTY) {
      ExpectedTypeInfo info = infos[0];
      Map<PsiTypeParameter, PsiType> map = substitutor.getSubstitutionMap();
      PsiType[] vals = map.values().toArray(PsiType.createArray(map.size()));
      PsiTypeParameter[] params = map.keySet().toArray(new PsiTypeParameter[map.size()]);

      List<PsiType> types = matchingTypeParameters(vals, params, info);
      if (!types.isEmpty()) {
        ContainerUtil.addAll(
            types,
            ExpectedTypesProvider.processExpectedTypes(
                infos, new MyTypeVisitor(manager, scope), project));
        builder.replaceElement(
            typeElement,
            new TypeExpression(project, types.toArray(PsiType.createArray(types.size()))));
        return;
      } else {
        PsiElementFactory factory =
            JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
        PsiType type = info.getType();
        PsiType defaultType = info.getDefaultType();
        try {
          PsiTypeElement inplaceTypeElement =
              ((PsiVariable)
                      factory.createVariableDeclarationStatement("foo", type, null)
                          .getDeclaredElements()[0])
                  .getTypeElement();

          PsiSubstitutor rawingSubstitutor = getRawingSubstitutor(context, targetClass);
          int substitionResult =
              substituteToTypeParameters(
                  typeElement, inplaceTypeElement, vals, params, builder, rawingSubstitutor, true);
          if (substitionResult != SUBSTITUTED_NONE) {
            if (substitionResult == SUBSTITUTED_IN_PARAMETERS) {
              PsiJavaCodeReferenceElement refElement =
                  typeElement.getInnermostComponentReferenceElement();
              LOG.assertTrue(refElement != null && refElement.getReferenceNameElement() != null);
              type = getComponentType(type);
              LOG.assertTrue(type != null);
              defaultType = getComponentType(defaultType);
              LOG.assertTrue(defaultType != null);
              ExpectedTypeInfo info1 =
                  ExpectedTypesProvider.createInfo(
                      ((PsiClassType) defaultType).rawType(),
                      ExpectedTypeInfo.TYPE_STRICTLY,
                      ((PsiClassType) defaultType).rawType(),
                      info.getTailType());
              MyTypeVisitor visitor = new MyTypeVisitor(manager, scope);
              builder.replaceElement(
                  refElement.getReferenceNameElement(),
                  new TypeExpression(
                      project,
                      ExpectedTypesProvider.processExpectedTypes(
                          new ExpectedTypeInfo[] {info1}, visitor, project)));
            }

            return;
          }
        } catch (IncorrectOperationException e) {
          LOG.error(e);
        }
      }
    }

    PsiType[] types =
        infos.length == 0
            ? new PsiType[] {typeElement.getType()}
            : ExpectedTypesProvider.processExpectedTypes(
                infos, new MyTypeVisitor(manager, scope), project);
    builder.replaceElement(typeElement, new TypeExpression(project, types));
  }