| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| 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; |
| } |
| |
| PsiClass lclass = ((PsiClassType)ltype).resolve(); |
| PsiClass rclass = ((PsiClassType)rtype).resolve(); |
| |
| if (lclass == null || rclass == null) return null; |
| |
| if (lclass.getTypeParameters().length == 0) return null; |
| |
| if (!InheritanceUtil.isInheritorOrSelf(rclass, lclass, true)) return null; |
| |
| PsiClassType.ClassResolveResult lresult = ((PsiClassType)ltype).resolveGenerics(); |
| PsiClassType.ClassResolveResult rresult = ((PsiClassType)rtype).resolveGenerics(); |
| |
| 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; |
| } |
| } |