package org.jetbrains.plugins.groovy.lang.psi.typeEnhancers;

import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;

import java.util.Iterator;

/**
 * @author Max Medvedev
 */
public class GrGenericTypeConverter extends GrTypeConverter {
  @Override
  public boolean isAllowedInMethodCall() {
    return true;
  }

  @Override
  public Boolean isConvertible(@NotNull PsiType ltype, @NotNull PsiType rtype, @NotNull GroovyPsiElement context) {
    if (!(ltype instanceof PsiClassType && rtype instanceof PsiClassType)) {
      return null;
    }


    PsiClassType.ClassResolveResult lresult = ((PsiClassType)ltype).resolveGenerics();
    PsiClassType.ClassResolveResult rresult = ((PsiClassType)rtype).resolveGenerics();

    PsiClass lclass = lresult.getElement();
    PsiClass rclass = rresult.getElement();

    if (lclass == null || rclass == null) return null;

    if (lclass.getTypeParameters().length == 0) return null;

    if (!InheritanceUtil.isInheritorOrSelf(rclass, lclass, true)) return null;

    if (typeParametersAgree(lclass, rclass, lresult.getSubstitutor(), rresult.getSubstitutor(), context)) return Boolean.TRUE;

    return null;
  }

  private static boolean typeParametersAgree(@NotNull PsiClass leftClass,
                                             @NotNull PsiClass rightClass,
                                             @NotNull PsiSubstitutor leftSubstitutor,
                                             @NotNull PsiSubstitutor rightSubstitutor,
                                             @NotNull PsiElement context) {

    if (!leftClass.hasTypeParameters()) return true;

    if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) {
      rightSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor);
      rightClass = leftClass;
    }
    else if (!rightClass.hasTypeParameters()) return true;

    Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass);
    Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass);

    while (li.hasNext()) {
      if (!ri.hasNext()) return false;
      PsiTypeParameter lp = li.next();
      PsiTypeParameter rp = ri.next();
      final PsiType typeLeft = leftSubstitutor.substitute(lp);
      if (typeLeft == null) continue;
      final PsiType typeRight = rightSubstitutor.substituteWithBoundsPromotion(rp);
      if (typeRight == null) {
        // compatibility feature: allow to assign raw types to generic ones
        return true;
      }

      if (typeLeft instanceof PsiClassType && typeRight instanceof PsiClassType) {
        if (!TypesUtil.isAssignableByMethodCallConversion(typeLeft, typeRight, context)) {
          return false;
        }
      }
      else if (!TypeConversionUtil.typesAgree(typeLeft, typeRight, true)) {
        return false;
      }
    }

    return true;
  }
}

@@ -1,8 +1,609 @@
+/*%0A * Copyright 2000-2014 JetBrains s.r.o.%0A *%0A * Licensed under the Apache License, Version 2.0 (the %22License%22);%0A * you may not use this file except in compliance with the License.%0A * You may obtain a copy of the License at%0A *%0A * http://www.apache.org/licenses/LICENSE-2.0%0A *%0A * Unless required by applicable law or agreed to in writing, software%0A * distributed under the License is distributed on an %22AS IS%22 BASIS,%0A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.%0A * See the License for the specific language governing permissions and%0A * limitations under the License.%0A */%0A
 package 
@@ -1436,17 +1436,16 @@
 %0A    %7D%0A%0A
-%0A
     PsiC
@@ -1452,39 +1452,15 @@
 lass
-Type.ClassResolveResult lresult
+ lclass
  = (
@@ -1483,32 +1483,24 @@
 ype).resolve
-Generics
 ();%0A    PsiC
@@ -1507,39 +1507,15 @@
 lass
-Type.ClassResolveResult rresult
+ rclass
  = (
@@ -1546,105 +1546,8 @@
 olve
-Generics();%0A%0A    PsiClass lclass = lresult.getElement();%0A    PsiClass rclass = rresult.getElement
 ();%0A
@@ -1737,32 +1737,207 @@
 ) return null;%0A%0A
+    PsiClassType.ClassResolveResult lresult = ((PsiClassType)ltype).resolveGenerics();%0A    PsiClassType.ClassResolveResult rresult = ((PsiClassType)rtype).resolveGenerics();%0A%0A
     if (typePara

optimize resolve. In some cases resolve() works faster than resolveGenerics (for example in LiteralClassType) and does not need to infer type parameters which can are needed only after some checks