public static boolean isClassEquivalentTo(@NotNull PsiClass aClass, PsiElement another) {
    if (aClass == another) return true;
    if (!(another instanceof PsiClass)) return false;
    String name1 = aClass.getName();
    if (name1 == null) return false;
    if (!another.isValid()) return false;
    String name2 = ((PsiClass) another).getName();
    if (name2 == null) return false;
    if (name1.hashCode() != name2.hashCode()) return false;
    if (!name1.equals(name2)) return false;
    String qName1 = aClass.getQualifiedName();
    String qName2 = ((PsiClass) another).getQualifiedName();
    if (qName1 == null || qName2 == null) {
      //noinspection StringEquality
      if (qName1 != qName2) return false;

      if (aClass instanceof PsiTypeParameter && another instanceof PsiTypeParameter) {
        PsiTypeParameter p1 = (PsiTypeParameter) aClass;
        PsiTypeParameter p2 = (PsiTypeParameter) another;

        return p1.getIndex() == p2.getIndex()
            && aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner());
      } else {
        return false;
      }
    }
    if (qName1.hashCode() != qName2.hashCode() || !qName1.equals(qName2)) {
      return false;
    }

    if (originalElement(aClass).equals(originalElement((PsiClass) another))) {
      return true;
    }

    final PsiFile file1 = aClass.getContainingFile().getOriginalFile();
    final PsiFile file2 = another.getContainingFile().getOriginalFile();

    // see com.intellij.openapi.vcs.changes.PsiChangeTracker
    // see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile)
    final PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE);
    final PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE);
    if (original1 == original2 && original1 != null
        || original1 == file2
        || original2 == file1
        || file1 == file2) {
      return compareClassSeqNumber(aClass, (PsiClass) another);
    }

    final FileIndexFacade fileIndex =
        ServiceManager.getService(file1.getProject(), FileIndexFacade.class);
    final VirtualFile vfile1 = file1.getViewProvider().getVirtualFile();
    final VirtualFile vfile2 = file2.getViewProvider().getVirtualFile();
    boolean lib1 = fileIndex.isInLibraryClasses(vfile1);
    boolean lib2 = fileIndex.isInLibraryClasses(vfile2);

    return (fileIndex.isInSource(vfile1) || lib1) && (fileIndex.isInSource(vfile2) || lib2);
  }
 private JavaResolveResult advancedResolveImpl() {
   final PsiElement resolve = resolveElement();
   if (resolve instanceof PsiClass) {
     final Map<PsiTypeParameter, PsiType> substitutionMap =
         new HashMap<PsiTypeParameter, PsiType>();
     int index = 0;
     for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable((PsiClass) resolve)) {
       if (index >= myTypeParameters.length) {
         final PsiTypeParameterListOwner parameterOwner = parameter.getOwner();
         if (parameterOwner == resolve) {
           substitutionMap.put(parameter, null);
         } else if (parameterOwner instanceof PsiClass) {
           PsiElement containingClass = myParent;
           while ((containingClass =
                   PsiTreeUtil.getParentOfType(containingClass, PsiClass.class, true))
               != null) {
             final PsiSubstitutor superClassSubstitutor =
                 TypeConversionUtil.getClassSubstitutor(
                     (PsiClass) parameterOwner, (PsiClass) containingClass, PsiSubstitutor.EMPTY);
             if (superClassSubstitutor != null) {
               substitutionMap.put(parameter, superClassSubstitutor.substitute(parameter));
               break;
             }
           }
         }
       } else {
         substitutionMap.put(parameter, myTypeParameters[index].getType());
       }
       index++;
     }
     return new CandidateInfo(resolve, PsiSubstitutorImpl.createSubstitutor(substitutionMap));
   } else {
     return new CandidateInfo(resolve, PsiSubstitutor.EMPTY);
   }
 }
  private static void findTypeParameterExternalUsages(
      final PsiTypeParameter typeParameter, final Collection<UsageInfo> usages) {
    PsiTypeParameterListOwner owner = typeParameter.getOwner();
    if (owner != null) {
      final PsiTypeParameterList parameterList = owner.getTypeParameterList();
      if (parameterList != null) {
        final int paramsCount = parameterList.getTypeParameters().length;
        final int index = parameterList.getTypeParameterIndex(typeParameter);

        ReferencesSearch.search(owner)
            .forEach(
                reference -> {
                  if (reference instanceof PsiJavaCodeReferenceElement) {
                    final PsiReferenceParameterList parameterList1 =
                        ((PsiJavaCodeReferenceElement) reference).getParameterList();
                    if (parameterList1 != null) {
                      PsiTypeElement[] typeArgs = parameterList1.getTypeParameterElements();
                      if (typeArgs.length > index) {
                        if (typeArgs.length == 1
                            && paramsCount > 1
                            && typeArgs[0].getType() instanceof PsiDiamondType) return true;
                        usages.add(
                            new SafeDeleteReferenceJavaDeleteUsageInfo(
                                typeArgs[index], typeParameter, true));
                      }
                    }
                  }
                  return true;
                });
      }
    }
  }
 public String toString() {
   @NonNls StringBuilder buffer = new StringBuilder();
   final Set<Map.Entry<PsiTypeParameter, PsiType>> set = mySubstitutionMap.entrySet();
   for (Map.Entry<PsiTypeParameter, PsiType> entry : set) {
     final PsiTypeParameter typeParameter = entry.getKey();
     buffer.append(typeParameter.getName());
     final PsiElement owner = typeParameter.getOwner();
     if (owner instanceof PsiClass) {
       buffer.append(" of ");
       buffer.append(((PsiClass) owner).getQualifiedName());
     } else if (owner instanceof PsiMethod) {
       buffer.append(" of ");
       buffer.append(((PsiMethod) owner).getName());
       buffer.append(" in ");
       buffer.append(((PsiMethod) owner).getContainingClass().getQualifiedName());
     }
     buffer.append(" -> ");
     if (entry.getValue() != null) {
       buffer.append(entry.getValue().getCanonicalText());
     } else {
       buffer.append("null");
     }
     buffer.append('\n');
   }
   return buffer.toString();
 }
  @Nullable
  public static PsiType getSubstitutedType(@Nullable PsiParameter parameter) {
    if (parameter == null) return null;

    final PsiType type = getType(parameter);

    if (type instanceof PsiArrayType) {
      return type;
    }

    final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type);
    final PsiClass psiClass = result.getElement();
    if (psiClass == null) return type;

    final Set<PsiTypeParameter> usedTypeParameters = new HashSet<PsiTypeParameter>();
    RefactoringUtil.collectTypeParameters(usedTypeParameters, parameter);
    for (Iterator<PsiTypeParameter> iterator = usedTypeParameters.iterator();
        iterator.hasNext(); ) {
      PsiTypeParameter usedTypeParameter = iterator.next();
      if (parameter.getDeclarationScope() != usedTypeParameter.getOwner()) {
        iterator.remove();
      }
    }

    PsiSubstitutor subst = PsiSubstitutor.EMPTY;
    for (PsiTypeParameter usedTypeParameter : usedTypeParameters) {
      subst =
          subst.put(usedTypeParameter, TypeConversionUtil.typeParameterErasure(usedTypeParameter));
    }

    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    final Map<PsiTypeParameter, PsiType> typeMap = result.getSubstitutor().getSubstitutionMap();
    for (PsiTypeParameter typeParameter : typeMap.keySet()) {
      final PsiType psiType = typeMap.get(typeParameter);
      substitutor =
          substitutor.put(typeParameter, psiType != null ? subst.substitute(psiType) : null);
    }

    if (psiClass instanceof PsiTypeParameter) {
      return subst.substitute((PsiTypeParameter) psiClass);
    } else {
      return JavaPsiFacade.getElementFactory(parameter.getProject())
          .createType(psiClass, substitutor);
    }
  }
 @NotNull
 private static PsiSubstitutor calculateMethodSubstitutor(
     @NotNull PsiTypeParameter[] typeParameters,
     @NotNull PsiMethod method,
     @NotNull PsiSubstitutor siteSubstitutor,
     @NotNull PsiType[] types1,
     @NotNull PsiType[] types2,
     @NotNull LanguageLevel languageLevel) {
   PsiSubstitutor substitutor =
       PsiResolveHelper.SERVICE
           .getInstance(method.getProject())
           .inferTypeArguments(typeParameters, types1, types2, languageLevel);
   for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(method)) {
     ProgressManager.checkCanceled();
     LOG.assertTrue(typeParameter != null);
     if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
       PsiType type = siteSubstitutor.substitute(typeParameter);
       if (type instanceof PsiClassType && typeParameter.getOwner() == method) {
         final PsiClass aClass = ((PsiClassType) type).resolve();
         if (aClass instanceof PsiTypeParameter
             && ((PsiTypeParameter) aClass).getOwner() == method) {
           type = TypeConversionUtil.erasure(type, siteSubstitutor);
         }
       }
       substitutor = substitutor.put(typeParameter, type);
     } else {
       final PsiType type = substitutor.substitute(typeParameter);
       if (type instanceof PsiClassType) {
         final PsiClass aClass = ((PsiClassType) type).resolve();
         if (aClass instanceof PsiTypeParameter) {
           substitutor =
               substitutor.put(
                   typeParameter,
                   JavaPsiFacade.getElementFactory(aClass.getProject())
                       .createType(aClass, siteSubstitutor));
         }
       }
     }
   }
   return substitutor;
 }