/*
* Copyright 2003-2011 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 jetbrains.mps.typesystem.inference;
import gnu.trove.THashSet;
import jetbrains.mps.lang.pattern.util.MatchingUtil;
import jetbrains.mps.lang.typesystem.runtime.HUtil;
import jetbrains.mps.newTypesystem.SubTypingManagerNew;
import jetbrains.mps.newTypesystem.SubtypingUtil;
import jetbrains.mps.newTypesystem.state.Equations;
import jetbrains.mps.newTypesystem.state.State;
import jetbrains.mps.smodel.StaticReference;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SReference;
import java.util.*;
public class InequalitySystem {
private SNode myHoleType;
private State myState;
public InequalitySystem(SNode holeType, State state) {
myHoleType = holeType;
myState = state;
}
public SNode getHoleType() {
return myHoleType;
}
private Set<SNode> myEquals = new THashSet<SNode>();
private Set<SNode> mySubTypes = new THashSet<SNode>();
private Set<SNode> myStrongSubTypes = new THashSet<SNode>();
private Set<SNode> mySuperTypes = new THashSet<SNode>();
private Set<SNode> myStrongSuperTypes = new THashSet<SNode>();
private Set<SNode> myComparableTypes = new THashSet<SNode>();
private Set<SNode> myStrongComparableTypes = new THashSet<SNode>();
public void addEquation(SNode equalWrapper) {
myEquals.add(equalWrapper);
}
public void addSupertype(SNode supertype, boolean isWeak) {
if (isWeak) {
mySuperTypes.add(supertype);
} else {
myStrongSuperTypes.add(supertype);
}
}
public void addSubtype(SNode subtype, boolean isWeak) {
if (isWeak) {
mySubTypes.add(subtype);
} else {
myStrongSubTypes.add(subtype);
}
}
public void addComparable(SNode comparable, boolean isWeak) {
if (isWeak) {
myComparableTypes.add(comparable);
} else {
myStrongComparableTypes.add(comparable);
}
}
public boolean satisfies(SNode type) {
SubtypingManager subtypingManager = TypeChecker.getInstance().getSubtypingManager();
for (SNode w : myEquals) {
if (!HUtil.isRuntimeHoleType(w) && !MatchingUtil.matchNodes(w, type)) {
return false;
}
}
for (SNode supertype : mySuperTypes) {
if (!subtypingManager.isSubtype(type, supertype, true)) return false;
}
for (SNode supertype : myStrongSuperTypes) {
if (!subtypingManager.isSubtype(type, supertype, false)) return false;
}
for (SNode subtype : mySubTypes) {
if (!subtypingManager.isSubtype(subtype, type, true)) return false;
}
for (SNode subtype : myStrongSubTypes) {
if (!subtypingManager.isSubtype(subtype, type, false)) return false;
}
for (SNode comparable : myComparableTypes) {
if (!subtypingManager.isComparable(comparable, type, true)) return false;
}
for (SNode comparable : myStrongComparableTypes) {
if (!subtypingManager.isComparable(comparable, type, false)) return false;
}
return true;
}
private Set<SNode> expandSet(Set<SNode> set, Equations equations) {
if (set.isEmpty()) return set;
Set<SNode> result = new HashSet<SNode>();
for (SNode node : set) {
SNode expanded = equations.expandNode(node, true);
//if (!TypesUtil.isVariable(expanded)) {
result.add(expanded);
//}
}
return result;
}
public void expandAll(Equations equations) {
myEquals = expandSet(myEquals, equations);
mySubTypes = expandSet(mySubTypes, equations);
myComparableTypes = expandSet(myComparableTypes, equations);
mySuperTypes = expandSet(mySuperTypes, equations);
myStrongSubTypes = expandSet(myStrongSubTypes, equations);
myStrongSuperTypes = expandSet(myStrongSuperTypes, equations);
myStrongComparableTypes = expandSet(myStrongComparableTypes, equations);
}
public boolean isEmpty() {
return myEquals.isEmpty() && mySubTypes.isEmpty() && mySuperTypes.isEmpty()
&& myStrongSubTypes.isEmpty() && myStrongSuperTypes.isEmpty() && myComparableTypes.isEmpty() && myStrongComparableTypes.isEmpty();
}
public String[] getPresentation() {
if (isEmpty()) {
return new String[]{"empty"};
}
String[] result = new String[myEquals.size() + mySubTypes.size() + mySuperTypes.size() + myStrongSubTypes.size() + myStrongSuperTypes.size()];
int i = 0;
for (SNode wrapper : myEquals) {
result[i++] = "* == " + wrapper.toString();
}
for (SNode wrapper : mySubTypes) {
result[i++] = wrapper.toString() + " < *";
}
for (SNode wrapper : myStrongSubTypes) {
result[i++] = wrapper.toString() + " << *";
}
for (SNode wrapper : mySuperTypes) {
result[i++] = "* < " + wrapper.toString();
}
for (SNode wrapper : myStrongSuperTypes) {
result[i++] = "* << " + wrapper.toString();
}
for (SNode wrapper : myComparableTypes) {
result[i++] = " ~ " + wrapper.toString();
}
for (SNode wrapper : myStrongComparableTypes) {
result[i++] = " *~ " + wrapper.toString();
}
return result;
}
public SNode getExpectedType() {
if (isEmpty()) return null;
SubTypingManagerNew subtypingManager = (SubTypingManagerNew) TypeChecker.getInstance().getSubtypingManager();
List<SNode> superTypes = new LinkedList<SNode>();
expandAll(myState.getEquations());
superTypes.addAll(mySuperTypes);
superTypes.addAll(myStrongSuperTypes);
if (superTypes.isEmpty()) {
superTypes.addAll(myComparableTypes);
superTypes.addAll(myStrongComparableTypes);
}
return SubtypingUtil.createLeastCommonSupertype(superTypes, myState.getTypeCheckingContext());
}
public void replaceRefs(Map<SNode, SNode> mapping) {
Map<SNode, SNode> back = new HashMap<SNode, SNode>();
for (SNode key : mapping.keySet()) {
back.put(mapping.get(key), key);
}
replaceRefs(mySuperTypes, back);
replaceRefs(myStrongSuperTypes, back);
replaceRefs(mySubTypes, back);
replaceRefs(myStrongSubTypes, back);
replaceRefs(myComparableTypes, back);
replaceRefs(myStrongComparableTypes, back);
replaceRefs(myEquals, back);
}
private void replaceRefs(Set<SNode> nodes, Map<SNode, SNode> mapping) {
for (SNode node : nodes) {
for (SReference ref : node.getReferences()) {
SNode target = jetbrains.mps.util.SNodeOperations.getTargetNodeSilently(ref);
SNode restored = mapping.get(target);
if (restored != null) {
node.setReference(ref.getRole(), new StaticReference(ref.getRole(), ref.getSourceNode(), restored));
}
}
}
}
}