/*
* Copyright 2003-2015 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.errors.IRuleConflictWarningProducer;
import jetbrains.mps.lang.typesystem.runtime.CheckingRuleSet;
import jetbrains.mps.lang.typesystem.runtime.ComparisonRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.DoubleRuleSet;
import jetbrains.mps.lang.typesystem.runtime.IHelginsDescriptor;
import jetbrains.mps.lang.typesystem.runtime.InequationReplacementRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.InferenceRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.IsApplicable2Status;
import jetbrains.mps.lang.typesystem.runtime.IsApplicableStatus;
import jetbrains.mps.lang.typesystem.runtime.NonTypesystemRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.OverloadedOperationsManager;
import jetbrains.mps.lang.typesystem.runtime.RuleSet;
import jetbrains.mps.lang.typesystem.runtime.SubstituteType_Runtime;
import jetbrains.mps.lang.typesystem.runtime.SubtypingRule_Runtime;
import jetbrains.mps.smodel.language.LanguageRuntime;
import jetbrains.mps.util.Pair;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.mps.openapi.model.SNode;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class RulesManager {
private RuleSet<InferenceRule_Runtime> myInferenceRules = new CheckingRuleSet<InferenceRule_Runtime>();
private RuleSet<SubtypingRule_Runtime> mySubtypingRules = new RuleSet<SubtypingRule_Runtime>();
private RuleSet<SubstituteType_Runtime> mySubstituteTypeRules = new RuleSet<SubstituteType_Runtime>();
private DoubleRuleSet<ComparisonRule_Runtime> myComparisonRules = new DoubleRuleSet<ComparisonRule_Runtime>();
private DoubleRuleSet<InequationReplacementRule_Runtime> myReplacementRules = new DoubleRuleSet<InequationReplacementRule_Runtime>();
private RuleSet<NonTypesystemRule_Runtime> myNonTypeSystemRules = new CheckingRuleSet<NonTypesystemRule_Runtime>();
private Set<IVariableConverter_Runtime> myVariableConverters = new THashSet<IVariableConverter_Runtime>();
private OverloadedOperationsManager myOverloadedOperationsManager;
private static final Logger LOG = LogManager.getLogger(RulesManager.class);
private volatile boolean myNeedsLoading = false;
private Set<LanguageRuntime> myLoadedLanguages = new HashSet<LanguageRuntime>();
private Set<LanguageRuntime> myLanguagesToLoad = new HashSet<LanguageRuntime>();
public RulesManager(TypeChecker typeChecker) {
myOverloadedOperationsManager = new OverloadedOperationsManager(typeChecker);
}
public void loadLanguages(Iterable<LanguageRuntime> languages) {
for (LanguageRuntime language : languages) {
assert !myLoadedLanguages.contains(language);
myLanguagesToLoad.add(language);
myNeedsLoading = true;
}
}
private void ensureAllRulesLoaded() {
if (!myNeedsLoading) {
return;
}
synchronized (this) {
if (!myNeedsLoading) {
return;
}
for (LanguageRuntime language : myLanguagesToLoad) {
assert !myLoadedLanguages.contains(language);
myLoadedLanguages.add(language);
IHelginsDescriptor typesystem = null;
try {
typesystem = language.getAspect(IHelginsDescriptor.class);
} catch (LinkageError linkageError) {
LOG.warn("Problems with creating typesystem descriptor " + linkageError.getMessage());
} catch (Throwable t) {
LOG.error("Error while loading language: " + language.getNamespace(), t);
}
if (typesystem == null) {
continue;
}
try {
myInferenceRules.addRuleSetItem(typesystem.getInferenceRules());
mySubtypingRules.addRuleSetItem(typesystem.getSubtypingRules());
mySubstituteTypeRules.addRuleSetItem(typesystem.getSubstituteTypeRules());
Set<ComparisonRule_Runtime> comparisonRule_runtimes = typesystem.getComparisonRules();
myComparisonRules.addRuleSetItem(comparisonRule_runtimes);
myReplacementRules.addRuleSetItem(typesystem.getEliminationRules());
myVariableConverters.addAll(typesystem.getVariableConverters());
myNonTypeSystemRules.addRuleSetItem(typesystem.getNonTypesystemRules());
myOverloadedOperationsManager.addOverloadedOperationsTypeProviders(typesystem.getOverloadedOperationsTypesProviders());
} catch (RuntimeException t) {
}
}
myLanguagesToLoad = new HashSet<LanguageRuntime>();
myNeedsLoading = false;
}
}
public void unloadLanguages(Iterable<LanguageRuntime> languages) {
for (LanguageRuntime language : languages) {
if (myLoadedLanguages.contains(language)) {
unloadLoadedAllLoaded();
}
myLanguagesToLoad.remove(language);
myNeedsLoading = true;
}
}
private void unloadLoadedAllLoaded() {
myLanguagesToLoad.addAll(myLoadedLanguages);
myLoadedLanguages = new HashSet<LanguageRuntime>();
// TODO: cleanup
myInferenceRules.clear();
mySubtypingRules.clear();
mySubstituteTypeRules.clear();
myComparisonRules.clear();
myReplacementRules.clear();
myVariableConverters.clear();
myOverloadedOperationsManager.clear();
myNonTypeSystemRules.clear();
}
public IVariableConverter_Runtime getVariableConverter(SNode context, String role, SNode variable, boolean isAggregation) {
ensureAllRulesLoaded();
for (IVariableConverter_Runtime converter : myVariableConverters) {
if (converter.isApplicable(context, role, variable, isAggregation)) return converter;
}
return null;
}
public List<Pair<InferenceRule_Runtime, IsApplicableStatus>> getInferenceRules(final SNode node) {
ensureAllRulesLoaded();
List<Pair<InferenceRule_Runtime, IsApplicableStatus>> result = new LinkedList<Pair<InferenceRule_Runtime, IsApplicableStatus>>();
Set<InferenceRule_Runtime> ruleSet;
ruleSet = myInferenceRules.getRules(node);
for (InferenceRule_Runtime rule : ruleSet) {
IsApplicableStatus status = rule.isApplicableAndPattern(node);
if (status.isApplicable()) {
result.add(new Pair<InferenceRule_Runtime, IsApplicableStatus>(rule, status));
}
if (rule.overrides(node, status)) {
break;
}
}
return result;
}
public List<Pair<NonTypesystemRule_Runtime, IsApplicableStatus>> getNonTypesystemRules(SNode node) {
ensureAllRulesLoaded();
List<Pair<NonTypesystemRule_Runtime, IsApplicableStatus>> result = new LinkedList<Pair<NonTypesystemRule_Runtime, IsApplicableStatus>>();
Set<NonTypesystemRule_Runtime> ruleSet;
ruleSet = myNonTypeSystemRules.getRules(node);
for (NonTypesystemRule_Runtime rule : ruleSet) {
IsApplicableStatus status = rule.isApplicableAndPattern(node);
if (status.isApplicable()) {
result.add(new Pair<NonTypesystemRule_Runtime, IsApplicableStatus>(rule, status));
}
}
return result;
}
public List<Pair<SubtypingRule_Runtime, IsApplicableStatus>> getSubtypingRules(final SNode node, final boolean isWeak) {
ensureAllRulesLoaded();
List<Pair<SubtypingRule_Runtime, IsApplicableStatus>> result = new LinkedList<Pair<SubtypingRule_Runtime, IsApplicableStatus>>();
for (SubtypingRule_Runtime rule : mySubtypingRules.getRules(node)) {
if ((isWeak || !rule.isWeak())) {
IsApplicableStatus status = rule.isApplicableAndPattern(node);
if (status.isApplicable()) {
result.add(new Pair<SubtypingRule_Runtime, IsApplicableStatus>(rule, status));
}
}
}
return result;
}
public List<Pair<SubstituteType_Runtime, IsApplicableStatus>> getSubstituteTypeRules(final SNode node) {
ensureAllRulesLoaded();
List<Pair<SubstituteType_Runtime, IsApplicableStatus>> result = new LinkedList<Pair<SubstituteType_Runtime, IsApplicableStatus>>();
for (SubstituteType_Runtime rule : mySubstituteTypeRules.getRules(node)) {
IsApplicableStatus status = rule.isApplicableAndPattern(node);
if (status.isApplicable()) {
result.add(new Pair<SubstituteType_Runtime, IsApplicableStatus>(rule, status));
}
}
return result;
}
public List<Pair<ComparisonRule_Runtime, IsApplicable2Status>> getComparisonRules(final SNode node1, final SNode node2, final boolean isWeak) {
ensureAllRulesLoaded();
List<Pair<ComparisonRule_Runtime, IsApplicable2Status>> result = new LinkedList<Pair<ComparisonRule_Runtime, IsApplicable2Status>>();
Set<ComparisonRule_Runtime> ruleSet = myComparisonRules.getRules(node1, node2);
for (ComparisonRule_Runtime rule : ruleSet) {
if (isWeak || !rule.isWeak()) {
IsApplicable2Status status = rule.isApplicableAndPatterns(node1, node2);
if (status.isApplicable()) {
result.add(new Pair<ComparisonRule_Runtime, IsApplicable2Status>(rule, status));
}
}
}
return result;
}
public List<Pair<InequationReplacementRule_Runtime, IsApplicable2Status>> getReplacementRules(final SNode node1, final SNode node2) {
ensureAllRulesLoaded();
List<Pair<InequationReplacementRule_Runtime, IsApplicable2Status>> result = new LinkedList<Pair<InequationReplacementRule_Runtime, IsApplicable2Status>>();
Set<InequationReplacementRule_Runtime> ruleSet = myReplacementRules.getRules(node1, node2);
for (InequationReplacementRule_Runtime rule : ruleSet) {
IsApplicable2Status status = rule.isApplicableAndPatterns(node1, node2);
if (status.isApplicable()) {
result.add(new Pair<InequationReplacementRule_Runtime, IsApplicable2Status>(rule, status));
}
}
return result;
}
public SNode getOperationType(SNode operation, SNode leftOperandType, SNode rightOperandType) {
return getOperationType(operation, leftOperandType, rightOperandType, IRuleConflictWarningProducer.NULL);
}
public SNode getOperationType(SNode operation, SNode leftOperandType, SNode rightOperandType, IRuleConflictWarningProducer warningProducer) {
ensureAllRulesLoaded();
return myOverloadedOperationsManager.getOperationType(operation, leftOperandType, rightOperandType, warningProducer);
}
}