/*
* 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.newTypesystem.context;
import jetbrains.mps.errors.IErrorReporter;
import jetbrains.mps.errors.MessageStatus;
import jetbrains.mps.errors.QuickFixProvider;
import jetbrains.mps.errors.SimpleErrorReporter;
import jetbrains.mps.errors.messageTargets.MessageTarget;
import jetbrains.mps.util.Computable;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import jetbrains.mps.newTypesystem.context.typechecking.IncrementalTypechecking;
import jetbrains.mps.newTypesystem.SubTypingManagerNew;
import jetbrains.mps.newTypesystem.operation.TraceWarningOperation;
import jetbrains.mps.newTypesystem.state.State;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.typesystem.inference.TypeChecker;
import jetbrains.mps.util.SNodeOperations;
import java.util.HashMap;
import java.util.Map;
public class IncrementalTypecheckingContext extends SimpleTypecheckingContext<State, IncrementalTypechecking> {
private static Logger LOG = LogManager.getLogger(IncrementalTypecheckingContext.class);
private boolean myIsNonTypesystemComputation = false;
// private boolean myIsInferenceMode = false;
private Map<Object, Integer> myRequesting = new HashMap<Object, Integer>();
private Integer myOldHash = 0;
public IncrementalTypecheckingContext(SNode node, TypeChecker typeChecker) {
super(node, typeChecker);
}
@Override
protected IncrementalTypechecking createTypechecking() {
return new IncrementalTypechecking(getNode(), getState());
}
@Override
public boolean isSingleTypeComputation() {
return false;
}
public TypeChecker getTypeChecker() {
return myTypeChecker;
}
public SubTypingManagerNew getSubTyping() {
return (SubTypingManagerNew) myTypeChecker.getSubtypingManager();
}
@Override
public void clear() {
synchronized (TYPECHECKING_LOCK) {
getTypechecking().clear();
}
}
@Override
public final IncrementalTypechecking getBaseNodeTypesComponent() {
return getTypechecking();
}
@Override
public void addDependencyForCurrent(SNode node) {
getTypechecking().addDependencyForCurrent(node);
}
@Override
public SNode getTypeOf(SNode node, TypeChecker typeChecker) {
if (node == null) return null;
synchronized (TYPECHECKING_LOCK) {
return getTypeOf_normalMode(node);
}
}
@Override
public SNode getTypeOf_generationMode(final SNode node) {
throw new IllegalStateException("Invalid usage of IncrementalTypecheckingContext");
}
@Override
public SNode getTypeOf_resolveMode(SNode node, TypeChecker typeChecker) {
throw new IllegalStateException("Invalid usage of IncrementalTypecheckingContext");
}
@Override
public SNode getTypeOf_normalMode(SNode node) {
if (!checkIfNotChecked(node, false)) return null;
return getTypeDontCheck(node);
}
@Override
public void dispose() {
getTypechecking().dispose();
myRequesting.clear();
super.dispose();
}
@Override
public boolean messagesChanged(Object requesting) {
int hash = getTypechecking().getNodesWithErrors(true).hashCode();
if (hash != myOldHash) {
myRequesting.clear();
myOldHash = hash;
}
Integer oldHash = myRequesting.get(requesting);
if (oldHash == null || oldHash != hash) {
myRequesting.put(requesting, hash);
return true;
}
return false;
}
//--------
@Override
public boolean isIncrementalMode() {
return true; /*!myTypeChecker.isGenerationMode() && getState().getInequalitySystem() == null;*/
}
public void runTypeCheckingAction(Runnable r) {
synchronized (TYPECHECKING_LOCK) {
r.run();
}
}
public <T> T runTypeCheckingAction(Computable<T> c) {
synchronized (TYPECHECKING_LOCK) {
return c.compute();
}
}
@Override
public void setIsNonTypesystemComputation() {
myIsNonTypesystemComputation = true;
}
@Override
public void resetIsNonTypesystemComputation() {
myIsNonTypesystemComputation = false;
}
@Override
public boolean isNonTypesystemComputation() {
return myIsNonTypesystemComputation;
}
@Override
public IErrorReporter reportTypeError(SNode nodeWithError, String errorString, String ruleModel, String ruleId, QuickFixProvider intentionProvider, MessageTarget errorTarget) {
SimpleErrorReporter reporter = new SimpleErrorReporter(nodeWithError, errorString, ruleModel, ruleId, MessageStatus.ERROR, errorTarget);
reporter.setIntentionProvider(intentionProvider);
if (nodeWithError == null) {
LOG.warn("Node used to report an error is null. Reported from model "+ruleModel+" by rule "+ruleId + ".", new Throwable());
return reporter;
}
else if (nodeWithError.getModel() == null) {
LOG.warn("Node used to report an error is not in a model. Node=" + SNodeOperations.getDebugText(nodeWithError) + ". Reported from model "+ruleModel+" by rule "+ruleId + ".", new Throwable());
return reporter;
}
reportMessage(nodeWithError, reporter);
return reporter;
}
@Override
public IErrorReporter reportWarning(SNode nodeWithError, String errorString, String ruleModel, String ruleId, QuickFixProvider intentionProvider, MessageTarget errorTarget) {
SimpleErrorReporter reporter = new SimpleErrorReporter(nodeWithError, errorString, ruleModel, ruleId, MessageStatus.WARNING, errorTarget);
reporter.setIntentionProvider(intentionProvider);
if (nodeWithError.getModel() == null) {
LOG.error("Node to report error for must be in a model. Node=" + SNodeOperations.getDebugText(nodeWithError), new Throwable());
return reporter;
}
reportMessage(nodeWithError, reporter);
return reporter;
}
@Override
public IErrorReporter reportInfo(SNode nodeWithInfo, String message, String ruleModel, String ruleId, QuickFixProvider intentionProvider, MessageTarget errorTarget) {
SimpleErrorReporter reporter = new SimpleErrorReporter(nodeWithInfo, message, ruleModel, ruleId, MessageStatus.OK, errorTarget);
reporter.setIntentionProvider(intentionProvider);
if (nodeWithInfo.getModel() == null) {
LOG.error("Node to report error for must be in a model. Node=" + SNodeOperations.getDebugText(nodeWithInfo), new Throwable());
return reporter;
}
reportMessage(nodeWithInfo, reporter);
return reporter;
}
@Override
public void reportMessage(SNode nodeWithError, IErrorReporter errorReporter) {
if (nodeWithError == null) {
getState().executeOperation(new TraceWarningOperation("Error was not added: " + errorReporter.reportError()));
return;//todo
}
getTypechecking().reportTypeError(nodeWithError, errorReporter);
// the following line messes up the typechecking even if the error is caused by a non-typechecking rule
// this further complicates incremental types calculation and produces unwanted results MPS-21481
// TODO: rethink the way errors affect the typechecking
// getTypechecking().addDependencyOnCurrent(nodeWithError, false);
}
@Override
protected void processDependency(SNode node, String ruleModel, String ruleId, boolean addDependency) {
IncrementalTypechecking currentTypesComponent = getTypechecking();
currentTypesComponent.typeOfNodeCalled(node);
if (addDependency) {
currentTypesComponent.addDependencyOnCurrent(node);
}
if (ruleModel != null && ruleId != null) {
currentTypesComponent.markNodeAsAffectedByRule(node, ruleModel, ruleId);
//todo wrap into "if (addDependency) {}" when sure that typeof works fine
}
}
@Override
protected void applyNonTypesystemRules() {
getTypechecking().applyNonTypesystemRulesToRoot(this);
}
}