public static boolean equalOnEquivalentClasses(@NotNull PsiSubstitutor s1,
                                                 @NotNull PsiClass aClass,
                                                 @NotNull PsiSubstitutor s2,
                                                 @NotNull PsiClass bClass) {
    if (s1 == s2 && aClass == bClass) return true;
    // assume generic class equals to non-generic
    if (aClass.hasTypeParameters() != bClass.hasTypeParameters()) return true;
    PsiTypeParameter[] typeParameters1 = aClass.getTypeParameters();
    PsiTypeParameter[] typeParameters2 = bClass.getTypeParameters();
    if (typeParameters1.length != typeParameters2.length) return false;
    for (int i = 0; i < typeParameters1.length; i++) {
      PsiType substituted2 = s2.substitute(typeParameters2[i]);
      PsiType substituted1 = s1.substitute(typeParameters1[i]);
      if (!Comparing.equal(substituted1, substituted2)) return false;
    }
    if (aClass.hasModifierProperty(PsiModifier.STATIC)) return true;
    PsiClass containingClass1 = aClass.getContainingClass();
    PsiClass containingClass2 = bClass.getContainingClass();

    if (containingClass1 != null && containingClass2 != null) {
      return equalOnEquivalentClasses(s1, containingClass1, s2, containingClass2);
    }

    if (containingClass1 == null && containingClass2 == null) {
      if (aClass == bClass && isLocalClass(aClass)) {
        PsiClass containingClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class);
        return containingClass != null && equalOnEquivalentClasses(s1, containingClass, s2, containingClass);
      }
      return true;
    }

    return false;

  }
public static boolean equalOnEquivalentClasses(@NotNull PsiSubstitutor s1,
                                                 @NotNull PsiClass aClass,
                                                 @NotNull PsiSubstitutor s2,
                                                 @NotNull PsiClass bClass)