package jetbrains.mps.baseLanguage.doubleDispatch.typesystem;
/*Generated by MPS */
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import jetbrains.mps.internal.collections.runtime.Sequence;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import jetbrains.mps.internal.collections.runtime.ISelector;
import jetbrains.mps.internal.collections.runtime.IMapping;
public class DispatchGroup {
private DispatchGroupDescriptor myDescriptor;
private List<DispatchGroup.ClassMethodGroup> myGroupsByClass = ListSequence.fromList(new ArrayList<DispatchGroup.ClassMethodGroup>());
public DispatchGroup(DispatchGroupDescriptor descriptor, SNode cls) {
myDescriptor = descriptor;
startNewClass(cls);
}
public void startNewClass(SNode cls) {
ListSequence.fromList(myGroupsByClass).addElement(new DispatchGroup.ClassMethodGroup(cls));
}
public void addMethod(SNode method) {
ListSequence.fromList(myGroupsByClass).last().addMethod(method);
}
@Override
public void finalize() {
List<DispatchGroup.ClassMethodGroup> filtered = ListSequence.fromList(myGroupsByClass).where(new IWhereFilter<DispatchGroup.ClassMethodGroup>() {
public boolean accept(DispatchGroup.ClassMethodGroup it) {
return MapSequence.fromMap(it.methods).isNotEmpty();
}
}).toListSequence();
myGroupsByClass = filtered;
}
public DispatchGroup.Error check() {
DispatchGroup.ClassMethodGroup thisClassGroup = ListSequence.fromList(myGroupsByClass).first();
Iterable<DispatchGroup.ClassMethodGroup> superClassesGroups = ListSequence.fromList(myGroupsByClass).skip(1);
Set<SNode> roots = thisClassGroup.getRoots();
if (ListSequence.fromList(myGroupsByClass).count() == 1) {
// this group is local to our class, doesn't span to superclasses
if (SetSequence.fromSet(roots).count() == 1) {
return null;
}
// more than one root
Iterable<SNode> methodsForRoots = thisClassGroup.methodsByDispatchTypes(roots);
return new DispatchGroup.Error("Dispatch parameter type hierarchy must have a single root", methodsForRoots);
}
// The group spans to super-classes.
// dispatch param classes that are not handled in superclasses
Set<SNode> badRoots = SetSequence.fromSet(new HashSet<SNode>());
for (final SNode root : SetSequence.fromSet(roots)) {
if (!(Sequence.fromIterable(superClassesGroups).any(new IWhereFilter<DispatchGroup.ClassMethodGroup>() {
public boolean accept(DispatchGroup.ClassMethodGroup it) {
return MapSequence.fromMap(it.methods).containsKey(root);
}
}))) {
SetSequence.fromSet(badRoots).addElement(root);
}
}
if (SetSequence.fromSet(badRoots).isEmpty()) {
return null;
}
Iterable<SNode> methodsForBadRoots = thisClassGroup.methodsByDispatchTypes(badRoots);
if (SetSequence.fromSet(badRoots).count() == 1) {
// check if the class is the superclass for any other dispatch param classes in group
final SNode cls = SetSequence.fromSet(badRoots).first();
boolean isGlobalRoot = Sequence.fromIterable(superClassesGroups).all(new IWhereFilter<DispatchGroup.ClassMethodGroup>() {
public boolean accept(DispatchGroup.ClassMethodGroup it) {
return SetSequence.fromSet(MapSequence.fromMap(it.methods).keySet()).all(new IWhereFilter<SNode>() {
public boolean accept(SNode it) {
return DispatchUtil.isParent(cls, it);
}
});
}
});
if (!(isGlobalRoot)) {
return new DispatchGroup.Error("Dispatch type not present in super classes and is not a supertype for other param types", methodsForBadRoots);
}
} else {
// there are bad roots
return new DispatchGroup.Error("Dispatch type not present in super classes", methodsForBadRoots);
}
// no errors
return null;
}
public class ClassMethodGroup {
private SNode classifier;
private Map<SNode, SNode> methods;
public ClassMethodGroup(SNode cls) {
classifier = cls;
methods = MapSequence.fromMap(new HashMap<SNode, SNode>());
}
public void addMethod(SNode method) {
SNode paramClass = DispatchUtil.getParamClass(method);
MapSequence.fromMap(methods).put(paramClass, method);
}
public Set<SNode> getRoots() {
Set<SNode> roots = SetSequence.fromSet(new HashSet<SNode>());
Iterable<Iterator<SNode>> paths = MapSequence.fromMap(methods).select(new ISelector<IMapping<SNode, SNode>, Iterator<SNode>>() {
public Iterator<SNode> select(IMapping<SNode, SNode> it) {
return Sequence.fromIterable(DispatchUtil.ancestors(it.key(), false)).iterator();
}
});
while (Sequence.fromIterable(paths).isNotEmpty()) {
List<Iterator<SNode>> unendedPaths = ListSequence.fromList(new ArrayList<Iterator<SNode>>());
for (Iterator<SNode> p : Sequence.fromIterable(paths)) {
SNode c = p.next();
if (MapSequence.fromMap(methods).containsKey(c)) {
SetSequence.fromSet(roots).addElement(c);
} else {
ListSequence.fromList(unendedPaths).addElement(p);
}
}
paths = unendedPaths;
}
return roots;
}
public Iterable<SNode> methodsByDispatchTypes(final Set<SNode> classes) {
return MapSequence.fromMap(methods).where(new IWhereFilter<IMapping<SNode, SNode>>() {
public boolean accept(IMapping<SNode, SNode> it) {
return SetSequence.fromSet(classes).contains(it.key());
}
}).select(new ISelector<IMapping<SNode, SNode>, SNode>() {
public SNode select(IMapping<SNode, SNode> it) {
return it.value();
}
});
}
}
public class Error {
private String msg;
private Iterable<SNode> errMethods;
public Error(String msg, Iterable<SNode> ms) {
this.msg = msg;
errMethods = ms;
}
public String getMessage() {
return msg;
}
public Iterable<SNode> getMethods() {
return errMethods;
}
}
}