/* * Copyright 2000-2013 JetBrains s.r.o. * Copyright 2014-2015 AS3Boyan * Copyright 2014-2014 Elias Ku * * 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 com.intellij.plugins.haxe.model.type; import com.intellij.plugins.haxe.lang.psi.HaxeClass; import com.intellij.plugins.haxe.lang.psi.HaxeType; import com.intellij.plugins.haxe.lang.psi.HaxeTypeOrAnonymous; import com.intellij.plugins.haxe.model.HaxeClassModel; import com.intellij.plugins.haxe.util.HaxeResolveUtil; import com.intellij.psi.impl.source.resolve.ResolveClassUtil; import org.apache.xmlbeans.impl.common.ResolverUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; public class HaxeTypeCompatible { static public boolean canApplyBinaryOperator(SpecificTypeReference left, SpecificTypeReference right, String operator) { // @TODO: Stub. Implement. return true; } static public boolean canAssignToFrom(SpecificTypeReference to, ResultHolder from) { return canAssignToFrom(to, from.getType()); } static public boolean canAssignToFrom(ResultHolder to, ResultHolder from) { if (to.isUnknown()) { to.setType(from.getType().withoutConstantValue()); } else if (from.isUnknown()) { from.setType(to.getType().withoutConstantValue()); } return canAssignToFrom(to.getType(), from.getType()); } static public boolean canAssignToFrom( @Nullable SpecificTypeReference to, @Nullable SpecificTypeReference from ) { if (to == null || from == null) return false; if (to.isDynamic() || from.isDynamic()) return true; if (to instanceof SpecificFunctionReference && from instanceof SpecificFunctionReference) { return canAssignToFromFunction((SpecificFunctionReference)to, (SpecificFunctionReference)from); } if (to instanceof SpecificHaxeClassReference && from instanceof SpecificHaxeClassReference) { return canAssignToFromType((SpecificHaxeClassReference)to, (SpecificHaxeClassReference)from); } return false; } static private boolean canAssignToFromFunction( @NotNull SpecificFunctionReference to, @NotNull SpecificFunctionReference from ) { if (to.params.size() != from.params.size()) return false; for (int n = 0; n < to.params.size(); n++) { if (!to.params.get(n).canAssign(from.params.get(n))) return false; } return to.retval.canAssign(from.retval); } static private boolean canAssignToFromType( @NotNull SpecificHaxeClassReference to, @NotNull SpecificHaxeClassReference from ) { if (to.isDynamic() || from.isDynamic()) { return true; } if (from.isUnknown()) { return true; } // @TODO: A first tdd-dummy approach if (to.clazz.equals(from.clazz)) { if (to.specifics.length == from.specifics.length) { int specificsLength = to.specifics.length; for (int n = 0; n < specificsLength; n++) { if (!canAssignToFrom(to.specifics[n], from.specifics[n])) { return false; } } return true; } // issue #388: allow `public var m:Map<String, String> = new Map();` else if(from.specifics.length == 0) { return true; } } if (to.toStringWithoutConstant().equals(from.toStringWithoutConstant())) { return true; } // Check from abstracts HaxeClass thisClassPsi = to.clazz.getHaxeClass(); if (thisClassPsi != null) { HaxeClassModel thisClass = thisClassPsi.getModel(); for (HaxeType type : thisClass.getAbstractFromList()) { if (HaxeTypeResolver.getTypeFromType(type).toStringWithoutConstant().equals(from.toStringWithoutConstant())) { return true; } } } // Check to abstracts HaxeClass thatClassPsi = from.clazz.getHaxeClass(); if (thatClassPsi != null) { HaxeClassModel thatClass = thatClassPsi.getModel(); if (thatClass.isAbstract()) { // Check if this is required! HaxeTypeOrAnonymous underlyingAbstractType = thatClass.getAbstractUnderlyingType(); if (underlyingAbstractType != null) { ResultHolder thatUnderlying = HaxeTypeResolver.getTypeFromTypeOrAnonymous(underlyingAbstractType); if (to.toStringWithoutConstant().equals(thatUnderlying.toStringWithoutConstant())) { return true; } } for (HaxeType type : thatClass.getAbstractToList()) { if (to.canAssign(HaxeTypeResolver.getTypeFromType(type))) { return true; } } } } // check interface/class type overrides HaxeClass baseClass = to.clazz.getHaxeClass(); HaxeClass derivedClass = from.clazz.getHaxeClass(); if (baseClass != null && derivedClass != null) { final HashSet<HaxeClass> set = HaxeResolveUtil.getBaseClassesSet(derivedClass, new HashSet<HaxeClass>()); if(set.contains(baseClass)) { return true; } } return to.toStringWithoutConstant().equals(from.toStringWithoutConstant()); } }