/*
* 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.nodeEditor.highlighter;
import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.smodel.ModelAccess;
import jetbrains.mps.util.Cancellable;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SNode;
/**
* Checks for certain events in {@link #isCancelled()}, cancels itself if any such event occurs, and stays cancelled forever afterwards.
* <p>The following events cause cancellation:</p>
* <ul>
* <li>the editor component is disposed</li>
* <li>the editor component highlighting update is disabled</li>
* <li>the edited node changes</li>
* <li>the highlighter is paused</li>
* <li>a write action is scheduled</li>
* </ul>
*/
class HighlighterUpdateSessionCancellable implements Cancellable {
private static final Logger LOG = Logger.getLogger(HighlighterUpdateSessionCancellable.class);
private static final long MAX_CHECK_INTERVAL_MS = 200L;
@NotNull
private final IHighlighter myHighlighter;
private final String myCheckerName;
@NotNull
private final EditorComponent myEditorComponent;
private final SNode myNode;
private volatile boolean myCancelRequested = false;
private long myLastCheckTime;
HighlighterUpdateSessionCancellable(@NotNull IHighlighter highlighter, String checkerName, @NotNull EditorComponent editorComponent) {
myHighlighter = highlighter;
myCheckerName = checkerName;
myEditorComponent = editorComponent;
myNode = myEditorComponent.getEditedNode();
myLastCheckTime = System.currentTimeMillis();
}
@Override
public boolean isCancelled() {
long timeSinceLastCheck = System.currentTimeMillis() - myLastCheckTime;
if (timeSinceLastCheck > MAX_CHECK_INTERVAL_MS && LOG.isDebugEnabled()) {
Throwable stackTrace = new Throwable();
stackTrace.fillInStackTrace();
LOG.debug(String.format("Checker %s: long time since last cancellation check (%d ms > threshold %d ms). Stack trace:",
myCheckerName, timeSinceLastCheck, MAX_CHECK_INTERVAL_MS), stackTrace);
}
myLastCheckTime += timeSinceLastCheck;
if (myCancelRequested) {
return true;
}
if (shouldCancel()) {
myCancelRequested = true;
}
return myCancelRequested;
}
private boolean shouldCancel() {
String reason = getCancellationReason();
if (reason != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Cancelling highlighter update run: " + reason);
}
return true;
}
return false;
}
@Nullable
private String getCancellationReason() {
if (!myEditorComponent.getHighlighter().mayHighlight()) {
return "editor component highlighting disabled";
}
if (myEditorComponent.getEditedNode() != myNode) {
return "edited node has changed";
}
if (myHighlighter.isPausedOrStopping()) {
return "highlighter is paused";
}
if (ModelAccess.instance().hasScheduledWrites()) {
return "writes are scheduled";
}
return null;
}
}