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;
  }
 private static boolean mayExpectBoolean(CompletionParameters parameters) {
   for (ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
     PsiType type = info.getType();
     if (type instanceof PsiClassType || type == PsiType.BOOLEAN) return true;
   }
   return false;
 }
  @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 List<PsiType> matchingTypeParameters(
      PsiType[] paramVals, PsiTypeParameter[] params, ExpectedTypeInfo info) {
    PsiType type = info.getType();
    int kind = info.getKind();

    List<PsiType> result = new ArrayList<>();
    for (int i = 0; i < paramVals.length; i++) {
      PsiType val = paramVals[i];
      if (val != null) {
        switch (kind) {
          case ExpectedTypeInfo.TYPE_STRICTLY:
            if (val.equals(type)) result.add(myFactory.createType(params[i]));
            break;
          case ExpectedTypeInfo.TYPE_OR_SUBTYPE:
            if (type.isAssignableFrom(val)) result.add(myFactory.createType(params[i]));
            break;
          case ExpectedTypeInfo.TYPE_OR_SUPERTYPE:
            if (val.isAssignableFrom(type)) result.add(myFactory.createType(params[i]));
            break;
        }
      }
    }

    return result;
  }
  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;

    ExpectedTypeInfo[] types = JavaSmartCompletionContributor.getExpectedTypes(parameters, false);
    if (types.length > 0) {
      for (ExpectedTypeInfo info : types) {
        PsiType type = info.getType();
        if (type instanceof PsiClassType) {
          fillExpectedTypeArgs(
              resultSet,
              context,
              pair.first,
              pair.second,
              ((PsiClassType) type).resolveGenerics(),
              mySmart ? info.getTailType() : TailType.NONE);
        }
      }
    } else if (mySmart) {
      addInheritors(parameters, resultSet, pair.first, pair.second);
    }
  }
 private static boolean hasNonVoid(ExpectedTypeInfo[] expectedInfos) {
   boolean hasNonVoid = false;
   for (ExpectedTypeInfo info : expectedInfos) {
     if (!PsiType.VOID.equals(info.getType())) {
       hasNonVoid = true;
     }
   }
   return hasNonVoid;
 }
 static void addExpectedTypeMembers(
     CompletionParameters parameters, final CompletionResultSet result) {
   if (parameters.getInvocationCount()
       <= 1) { // on second completion, StaticMemberProcessor will suggest those
     for (final ExpectedTypeInfo info :
         JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
       new JavaMembersGetter(info.getDefaultType(), parameters).addMembers(false, result);
     }
   }
 }
 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);
     }
   }
 }
    @NotNull
    @Override
    public MyResult weigh(@NotNull LookupElement item) {
      final Object object = item.getObject();

      if (object instanceof PsiClass) {
        if (object instanceof PsiTypeParameter) return MyResult.typeParameter;

        if (myTypeParameter != null
            && object.equals(
                PsiUtil.resolveClassInType(
                    TypeConversionUtil.typeParameterErasure(myTypeParameter)))) {
          return MyResult.exactlyExpected;
        }
      }

      if (myExpectedTypes == null) return MyResult.normal;

      PsiType itemType = JavaCompletionUtil.getLookupElementType(item);
      if (itemType == null || !itemType.isValid()) return MyResult.normal;

      if (object instanceof PsiClass) {
        for (final ExpectedTypeInfo info : myExpectedTypes) {
          if (TypeConversionUtil.erasure(info.getType().getDeepComponentType())
              .equals(TypeConversionUtil.erasure(itemType))) {
            return AbstractExpectedTypeSkipper.skips(item, myLocation)
                ? MyResult.expectedNoSelect
                : MyResult.exactlyExpected;
          }
        }
      }

      for (final ExpectedTypeInfo expectedInfo : myExpectedTypes) {
        final PsiType defaultType = expectedInfo.getDefaultType();
        final PsiType expectedType = expectedInfo.getType();
        if (!expectedType.isValid()) {
          return MyResult.normal;
        }

        if (defaultType != expectedType) {
          if (defaultType.equals(itemType)) {
            return MyResult.exactlyDefault;
          }

          if (defaultType.isAssignableFrom(itemType)) {
            return MyResult.ofDefaultType;
          }
        }
        if (PsiType.VOID.equals(itemType) && PsiType.VOID.equals(expectedType)) {
          return MyResult.exactlyExpected;
        }
      }

      return MyResult.normal;
    }
 @Nullable
 public static Set<PsiType> getExpectedTypes(final CompletionParameters parameters) {
   final PsiExpression expr =
       PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true);
   if (expr != null) {
     final Set<PsiType> set = new THashSet<PsiType>();
     for (final ExpectedTypeInfo expectedInfo :
         JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
       set.add(expectedInfo.getType());
     }
     return set;
   }
   return null;
 }
    public PreferExpected(
        boolean constructorPossible, ExpectedTypeInfo[] expectedTypes, PsiElement position) {
      super("expectedType");
      myConstructorPossible = constructorPossible;
      myExpectedTypes = expectedTypes;
      for (ExpectedTypeInfo info : expectedTypes) {
        ContainerUtil.addIfNotNull(
            myExpectedClasses,
            PsiUtil.substituteTypeParameter(
                info.getDefaultType(), CommonClassNames.JAVA_LANG_CLASS, 0, false));
      }

      myExpectedMemberName = calcExpectedMemberNameByParentCall(position);
    }
  static void addCollectConversion(
      PsiReferenceExpression ref,
      Collection<ExpectedTypeInfo> expectedTypes,
      Consumer<LookupElement> consumer) {
    final PsiExpression qualifier = ref.getQualifierExpression();
    PsiType component =
        qualifier == null
            ? null
            : PsiUtil.substituteTypeParameter(
                qualifier.getType(), JAVA_UTIL_STREAM_STREAM, 0, true);
    if (component == null) return;

    JavaPsiFacade facade = JavaPsiFacade.getInstance(ref.getProject());
    GlobalSearchScope scope = ref.getResolveScope();
    PsiClass list = facade.findClass(JAVA_UTIL_LIST, scope);
    PsiClass set = facade.findClass(JAVA_UTIL_SET, scope);
    if (facade.findClass(JAVA_UTIL_STREAM_COLLECTORS, scope) == null || list == null || set == null)
      return;

    boolean hasList = false;
    boolean hasSet = false;
    for (ExpectedTypeInfo info : expectedTypes) {
      PsiType type = info.getDefaultType();
      PsiClass expectedClass = PsiUtil.resolveClassInClassTypeOnly(type);
      PsiType expectedComponent = PsiUtil.extractIterableTypeParameter(type, true);
      if (expectedClass == null
          || expectedComponent == null
          || !TypeConversionUtil.isAssignable(expectedComponent, component)) continue;

      if (!hasList && InheritanceUtil.isInheritorOrSelf(list, expectedClass, true)) {
        hasList = true;
        consumer.consume(new MyLookupElement("toList", type));
      }

      if (!hasSet && InheritanceUtil.isInheritorOrSelf(set, expectedClass, true)) {
        hasSet = true;
        consumer.consume(new MyLookupElement("toSet", type));
      }
    }
  }
  private static ExpectedTypeMatching getExpectedTypeMatching(
      LookupElement item, ExpectedTypeInfo[] expectedInfos) {
    PsiType itemType = JavaCompletionUtil.getLookupElementType(item);

    if (itemType != null) {
      for (final ExpectedTypeInfo expectedInfo : expectedInfos) {
        final PsiType defaultType = expectedInfo.getDefaultType();
        final PsiType expectedType = expectedInfo.getType();

        if (defaultType != expectedType && defaultType.isAssignableFrom(itemType)) {
          return ExpectedTypeMatching.ofDefaultType;
        }
        if (expectedType.isAssignableFrom(itemType)) {
          return ExpectedTypeMatching.expected;
        }
      }
    }

    boolean hasNonVoid = false;
    for (ExpectedTypeInfo info : expectedInfos) {
      if (!PsiType.VOID.equals(info.getType())) {
        hasNonVoid = true;
      }
    }

    if (hasNonVoid) {
      if (item.getObject() instanceof PsiKeyword) {
        String keyword = ((PsiKeyword) item.getObject()).getText();
        if (PsiKeyword.NEW.equals(keyword) || PsiKeyword.NULL.equals(keyword)) {
          return ExpectedTypeMatching.maybeExpected;
        }
      }
    } else if (expectedInfos.length > 0) {
      return ExpectedTypeMatching.unexpected;
    }

    return ExpectedTypeMatching.normal;
  }
  private static ExpectedTypeMatching getExpectedTypeMatching(
      LookupElement item, ExpectedTypeInfo[] expectedInfos, @Nullable String expectedMemberName) {
    PsiType itemType = JavaCompletionUtil.getLookupElementType(item);

    if (itemType != null) {
      PsiUtil.ensureValidType(itemType);

      for (final ExpectedTypeInfo expectedInfo : expectedInfos) {
        final PsiType defaultType = expectedInfo.getDefaultType();
        final PsiType expectedType = expectedInfo.getType();

        assert expectedType.isValid();
        assert defaultType.isValid();

        if (defaultType != expectedType && defaultType.isAssignableFrom(itemType)) {
          return ExpectedTypeMatching.ofDefaultType;
        }
        if (expectedType.isAssignableFrom(itemType)) {
          return ExpectedTypeMatching.expected;
        }
      }
    }

    if (hasNonVoid(expectedInfos)) {
      if (item.getObject() instanceof PsiKeyword) {
        String keyword = ((PsiKeyword) item.getObject()).getText();
        if (PsiKeyword.NEW.equals(keyword) || PsiKeyword.NULL.equals(keyword)) {
          return ExpectedTypeMatching.maybeExpected;
        }
      }
    } else if (expectedInfos.length > 0) {
      return ExpectedTypeMatching.unexpected;
    }

    return preferByMemberName(expectedMemberName, itemType);
  }
  public Result weigh(
      @NotNull final LookupElement element, @NotNull final CompletionLocation location) {
    if (location == null) {
      return null;
    }
    if (location.getCompletionType() != CompletionType.BASIC
        && location.getCompletionType() != CompletionType.SMART) return Result.normal;

    final Object object = element.getObject();
    if (!(object instanceof PsiModifierListOwner) && !(object instanceof PsiExpression))
      return Result.normal;

    final PsiMethod positionMethod = JavaCompletionUtil.POSITION_METHOD.getValue(location);
    if (positionMethod == null) return Result.normal;

    final PsiElement position = location.getCompletionParameters().getPosition();
    final ElementFilter filter = JavaCompletionUtil.recursionFilter(position);
    if (filter != null && !filter.isAcceptable(object, position)) {
      return Result.recursive;
    }

    final PsiMethodCallExpression expression =
        PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class, true, PsiClass.class);
    final PsiReferenceExpression reference =
        expression != null
            ? expression.getMethodExpression()
            : PsiTreeUtil.getParentOfType(position, PsiReferenceExpression.class);
    if (reference == null) return Result.normal;

    final PsiExpression qualifier = reference.getQualifierExpression();
    boolean isDelegate = qualifier != null && !(qualifier instanceof PsiThisExpression);

    if (isPassingObjectToItself(object, qualifier, isDelegate)) {
      return Result.passingObjectToItself;
    }

    if (expression != null) {
      final ExpectedTypeInfo[] expectedInfos = JavaCompletionUtil.EXPECTED_TYPES.getValue(location);
      if (expectedInfos != null) {
        final PsiType itemType = JavaCompletionUtil.getLookupElementType(element);
        if (itemType != null) {
          for (final ExpectedTypeInfo expectedInfo : expectedInfos) {
            if (positionMethod.equals(expectedInfo.getCalledMethod())
                && expectedInfo.getType().isAssignableFrom(itemType)) {
              return isDelegate ? Result.delegation : Result.recursive;
            }
          }
        }
      }
      return Result.normal;
    }

    if (object instanceof PsiMethod) {
      final PsiMethod method = (PsiMethod) object;
      if (PsiTreeUtil.isAncestor(reference, position, false)
          && Comparing.equal(method.getName(), positionMethod.getName())
          && method.getParameterList().getParametersCount()
              == positionMethod.getParameterList().getParametersCount()) {
        if (findDeepestSuper(method).equals(findDeepestSuper(positionMethod))) {
          return isDelegate ? Result.delegation : Result.recursive;
        }
      }
    }

    return Result.normal;
  }
  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));
  }