/* * Copyright 2003-2016 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.newTypesystem.state; import gnu.trove.THashMap; import jetbrains.mps.errors.IErrorReporter; import jetbrains.mps.errors.QuickFixProvider; import jetbrains.mps.errors.SimpleErrorReporter; import jetbrains.mps.errors.messageTargets.NodeMessageTarget; import jetbrains.mps.newTypesystem.EquationErrorReporterNew; import jetbrains.mps.newTypesystem.TypesUtil; import jetbrains.mps.newTypesystem.operation.AddErrorOperation; import jetbrains.mps.newTypesystem.operation.AssignTypeOperation; import jetbrains.mps.newTypesystem.operation.ExpandTypeOperation; import jetbrains.mps.typesystem.inference.EquationInfo; import jetbrains.mps.util.Pair; import jetbrains.mps.validation.IModelValidationSettings; import jetbrains.mps.validation.ValidationSettings; import org.jetbrains.mps.openapi.model.SNode; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; public class NodeMaps { @StateObject private final Map<SNode, SNode> myNodesToTypes = new THashMap<SNode, SNode>(); @StateObject private final Map<SNode, SNode> myTypesToNodes = new THashMap<SNode, SNode>(); @StateObject private final Map<SNode, List<IErrorReporter>> myNodesToErrors = new THashMap<SNode, List<IErrorReporter>>(); private final State myState; public NodeMaps(State state) { myState = state; } public void addNodeToType(SNode node, SNode type, EquationInfo info) { myState.executeOperation(new AssignTypeOperation(node, type, info)); } public void updateNodeToType(SNode node, SNode type, EquationInfo info) { SNode oldType = myNodesToTypes.get(node); if (oldType != null) { myState.executeOperation(new ExpandTypeOperation(node, type, info, oldType)); } } @StateMethod public void assignNodeType(SNode node, SNode type) { myState.assertIsInStateChangeAction(); myTypesToNodes.put(type, node); myNodesToTypes.put(node, type); } @StateMethod public void removeNodeType(SNode node) { myState.assertIsInStateChangeAction(); SNode type = myNodesToTypes.remove(node); myTypesToNodes.remove(type); } @StateMethod public void removeNodeErrors(SNode node) { myState.assertIsInStateChangeAction(); myNodesToErrors.remove(node); } @StateMethod public void addNodeErrors(SNode node, List<IErrorReporter> errors) { myState.assertIsInStateChangeAction(); myNodesToErrors.put(node, errors); } @StateMethod public void assignNodeTypeDontChangeSource(SNode node, SNode type) { myState.assertIsInStateChangeAction(); myNodesToTypes.put(node, type); } @StateMethod public void addError(SNode node, IErrorReporter errorReporter) { myState.assertIsInStateChangeAction(); List<IErrorReporter> errors = myNodesToErrors.get(node); if (errors == null) { errors = new LinkedList<IErrorReporter>(); myNodesToErrors.put(node, errors); } errors.add(errorReporter); } @StateMethod public void removeError(SNode node, IErrorReporter errorReporter) { myState.assertIsInStateChangeAction(); List<IErrorReporter> errors = myNodesToErrors.get(node); errors.remove(errorReporter); if (errors.isEmpty()) { myNodesToErrors.remove(node); } } public SNode typeOf(SNode node, EquationInfo info) { SNode type = myNodesToTypes.get(node); if (type == null) { type = myState.createNewRuntimeTypesVariable(); addNodeToType(node, type, info); } return myState.getRepresentative(type); } public void addNodeToError(SNode node, IErrorReporter error, EquationInfo info) { myState.executeOperation(new AddErrorOperation(node, error, info)); } public List<IErrorReporter> getNodeErrors(SNode node) { List<IErrorReporter> result = myNodesToErrors.get(node); if (result == null) { result = new LinkedList<IErrorReporter>(); } return result; } public void clear() { myNodesToErrors.clear(); myNodesToTypes.clear(); myTypesToNodes.clear(); } public SNode getType(SNode node) { if (node == null) return null; SNode type = myNodesToTypes.get(node); return myState.getRepresentative(type); } public SNode getInitialType(SNode node) { return myNodesToTypes.get(node); } public List<String> getErrorListPresentation() { List<String> result = new LinkedList<String>(); for (Map.Entry<SNode, List<IErrorReporter>> entry : myNodesToErrors.entrySet()) { for (IErrorReporter error : entry.getValue()) { result.add(entry.getKey() + " " + error.reportError()); } } return result; } private boolean showTypeWasNotCalculated() { ValidationSettings instance = ValidationSettings.getInstance(); if (instance == null) return false; IModelValidationSettings modelValidationSettings = instance.getModelValidationSettings(); return modelValidationSettings != null && !modelValidationSettings.isDisableTypeWasNotCalculated(); } public void expandNode(SNode node, boolean finalExpansion) { SNode var = myNodesToTypes.get(node); SNode type = myState.getEquations().expandNode(var, finalExpansion); updateNodeToType(node, type, null); if (finalExpansion && showTypeWasNotCalculated() && (type == null || TypesUtil.isVariable(type))) { myState.getTypeCheckingContext().reportWarning(node, "Type "+ type+ " was not calculated", null, null, null, new NodeMessageTarget()); } } public void expandAll(Set<SNode> nodes, boolean finalExpansion) { Set<SNode> keySet = myNodesToTypes.keySet(); for (SNode node : nodes) { if (!keySet.contains(node)) continue; expandNode(node, finalExpansion); } } public SNode getNode(SNode type) { return myTypesToNodes.get(type); } public void clearTypesToNodes() { myTypesToNodes.clear(); } public Set<SNode> getTypeKeySet() { return myNodesToTypes.keySet(); } public void reportEquationBroken(EquationInfo info, SNode left, SNode right) { IErrorReporter errorReporter; SNode nodeWithError = null; List<QuickFixProvider> intentionProviders = new ArrayList<QuickFixProvider>(); String errorString = null; String ruleModel = null; String ruleId = null; if (info != null) { nodeWithError = info.getNodeWithError(); intentionProviders = info.getIntentionProviders(); errorString = info.getErrorString(); ruleModel = info.getRuleModel(); ruleId = info.getRuleId(); } if (errorString != null) { errorReporter = new SimpleErrorReporter(nodeWithError, errorString, ruleModel, ruleId); } else { errorReporter = new EquationErrorReporterNew(nodeWithError, myState, "incompatible types: ", right, " and ", left, "", ruleModel, ruleId); } for (QuickFixProvider quickFixProvider : intentionProviders) { errorReporter.setIntentionProvider(quickFixProvider); } setAdditionalRulesIds(info, errorReporter); // addNodeToError(nodeWithError, errorReporter, info); myState.getTypeCheckingContext().reportMessage(nodeWithError, errorReporter); } public void reportSubTypeError(SNode subType, SNode superType, EquationInfo equationInfo, boolean isWeak) { IErrorReporter errorReporter; String errorString = equationInfo.getErrorString(); String ruleModel = equationInfo.getRuleModel(); String ruleId = equationInfo.getRuleId(); SNode nodeWithError = equationInfo.getNodeWithError(); if (errorString == null) { String strongString = isWeak ? "" : " strong"; errorReporter = new EquationErrorReporterNew(nodeWithError, myState, "type ", subType, " is not a" + strongString + " subtype of ", superType, "", ruleModel, ruleId); } else { errorReporter = new SimpleErrorReporter(nodeWithError, errorString, ruleModel, ruleId); } for (QuickFixProvider quickFixProvider : equationInfo.getIntentionProviders()) { errorReporter.setIntentionProvider(quickFixProvider); } setAdditionalRulesIds(equationInfo, errorReporter); myState.getTypeCheckingContext().reportMessage(nodeWithError, errorReporter); } public void reportComparableError(SNode subType, SNode superType, EquationInfo equationInfo, boolean isWeak) { IErrorReporter errorReporter; String errorString = equationInfo.getErrorString(); String ruleModel = equationInfo.getRuleModel(); String ruleId = equationInfo.getRuleId(); SNode nodeWithError = equationInfo.getNodeWithError(); if (errorString == null) { String strongString = isWeak ? "" : " strongly"; errorReporter = new EquationErrorReporterNew(nodeWithError, myState, "type ", subType, " is not" + strongString + " comparable with ", superType, "", ruleModel, ruleId); } else { errorReporter = new SimpleErrorReporter(nodeWithError, errorString, ruleModel, ruleId); } for (QuickFixProvider provider : equationInfo.getIntentionProviders()) { errorReporter.addIntentionProvider(provider); } setAdditionalRulesIds(equationInfo, errorReporter); myState.getTypeCheckingContext().reportMessage(nodeWithError, errorReporter); } public Map<SNode, SNode> getNodesToTypes() { return Collections.unmodifiableMap(myNodesToTypes); } public Map<SNode, List<IErrorReporter>> getNodesToErrors() { return Collections.unmodifiableMap(myNodesToErrors); } private static void setAdditionalRulesIds(EquationInfo from, IErrorReporter to) { if (from == null) { return; } for (Pair<String, String> p : from.getAdditionalRulesIds()) { to.addAdditionalRuleId(p.o1, p.o2); } } }