/*
* 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.model.HaxeClassModel;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class HaxeTypeUnifier {
@NotNull
static public ResultHolder unify(ResultHolder a, ResultHolder b) {
return unify(a.getType(), b.getType(), a.getType().context).createHolder();
}
@NotNull
static public SpecificTypeReference unify(SpecificTypeReference a, SpecificTypeReference b, @NotNull PsiElement context) {
if (a == null && b == null) return SpecificTypeReference.getUnknown(context);
if (a == null) return b;
if (b == null) return a;
// Completely equals!
if (a.toStringWithoutConstant().equals(b.toStringWithoutConstant())) {
return a.withoutConstantValue();
}
if (a instanceof SpecificHaxeClassReference && b instanceof SpecificHaxeClassReference) {
return unifyTypes((SpecificHaxeClassReference)a, (SpecificHaxeClassReference)b, context);
}
if (a instanceof SpecificFunctionReference && b instanceof SpecificFunctionReference) {
return unifyFunctions((SpecificFunctionReference)a, (SpecificFunctionReference)b, context);
}
return SpecificTypeReference.getUnknown(a.getElementContext());
}
@NotNull
static public SpecificTypeReference unifyFunctions(SpecificFunctionReference a, SpecificFunctionReference b, @NotNull PsiElement context) {
final List<ResultHolder> pa = a.getParameters();
final List<ResultHolder> pb = b.getParameters();
//if (pa.size() != pb.size()) throw new HaxeCannotUnifyException();
if (pa.size() != pb.size()) return SpecificTypeReference.getInvalid(a.getElementContext());
int size = pa.size();
final ArrayList<ResultHolder> params = new ArrayList<ResultHolder>();
for (int n = 0; n < size; n++) {
final ResultHolder param = unify(pa.get(n), pb.get(n));
if (param.getType().isInvalid()) return SpecificTypeReference.getInvalid(a.getElementContext());
params.add(param);
}
final ResultHolder retval = unify(a.getReturnType(), b.getReturnType());
return new SpecificFunctionReference(params, retval, null, context);
}
@NotNull
static public SpecificTypeReference unifyTypes(SpecificHaxeClassReference a, SpecificHaxeClassReference b, @NotNull PsiElement context) {
if (a.isDynamic()) return a.withoutConstantValue();
if (b.isDynamic()) return b.withoutConstantValue();
if (a.getHaxeClassModel() == null) return SpecificTypeReference.getDynamic(context);
if (b.getHaxeClassModel() == null) return SpecificTypeReference.getDynamic(context);
final Set<HaxeClassModel> atypes = a.getHaxeClassModel().getCompatibleTypes();
final Set<HaxeClassModel> btypes = b.getHaxeClassModel().getCompatibleTypes();
// @TODO: this could be really slow, hotspot for optimizing
for (HaxeClassModel type : atypes) {
if (btypes.contains(type)) {
// @TODO: generics
return SpecificHaxeClassReference.withoutGenerics(
new HaxeClassReference(type, context)
);
}
}
// @TODO: Do a proper unification
return SpecificTypeReference.getDynamic(a.getElementContext());
}
@NotNull
static public SpecificTypeReference unify(SpecificTypeReference[] types, @NotNull PsiElement context) {
return unify(Arrays.asList(types), context);
}
@NotNull
static public SpecificTypeReference unify(List<SpecificTypeReference> types, @NotNull PsiElement context) {
if (types.size() == 0) {
return SpecificTypeReference.getUnknown(context);
}
SpecificTypeReference type = types.get(0);
for (int n = 1; n < types.size(); n++) {
type = unify(type, types.get(n), context);
}
return type;
}
@NotNull
static public ResultHolder unifyHolders(List<ResultHolder> typeHolders, @NotNull PsiElement context) {
// @TODO: This should mutate unknown holders?
return unify(ResultHolder.types(typeHolders), context).createHolder();
}
}