/*
* 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.progress;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.util.ProgressMonitor;
import org.jetbrains.mps.openapi.util.SubProgressKind;
/**
* Evgeny Gryaznov, 10/3/11
*/
public abstract class ProgressMonitorBase implements ProgressMonitor {
private static final Logger LOG = LogManager.getLogger(ProgressMonitorBase.class);
// -1 means "not started", 0 is possible not to check collection.size()>0 when calling start()
// if totalWork==0, step() can be called, advance(>0) can't
private int myTotal = -1;
private int myDone = 0;
private SubProgressMonitor myActiveChild;
private int myAfterActiveChild;
private String myName;
private String myStepName;
protected ProgressMonitorBase() {
}
@Override
public final void start(@NotNull String taskName, int totalWork) {
if (myTotal >= 0) {
throw new IllegalStateException("start() is called twice");
}
myActiveChild = null;
myDone = 0;
assert totalWork >= 0 : "totalWork=" + totalWork;
myTotal = totalWork;
myName = taskName;
setTitleInternal(taskName);
setStepInternal("");
startInternal(taskName);
update();
}
protected String getTaskName() {
return myName;
}
@Override
public final void step(String title) {
check();
if (myTotal < 0) {
throw new IllegalStateException("call start() first");
}
myStepName = title;
setStepInternal(title);
}
@Override
public void advance(int work) {
check();
if (myTotal < 0) {
throw new IllegalStateException("call start() first");
}
assert work >= 0;
//todo replace with exception and remove overflow check when MPS-24455 is fixed
if (myTotal < myDone + work || myDone + work < 0) {
LOG.warn("advance(work): work is too big: total=" + myTotal + "; done=" + myDone + "; work=" + work);
myDone = myTotal;
} else {
myDone += work;
}
update();
}
@Override
public void done() {
check();
myDone = myTotal;
doneInternal(getTaskName());
update();
}
protected abstract void update(double fraction);
protected abstract void startInternal(String text);
protected abstract void doneInternal(String text);
protected abstract void setTitleInternal(String name);
protected abstract void setStepInternal(String comment);
private void update() {
if (myTotal > 0 && myDone >= 0 && myDone <= myTotal) {
update((double) myDone / myTotal);
} else {
update(1.0d);
}
}
protected final void check() {
if (myActiveChild != null) {
myDone = myAfterActiveChild;
myActiveChild = null;
setTitleInternal(myName);
setStepInternal(myStepName);
}
}
@Override
public final ProgressMonitor subTask(int work) {
return subTask(work, SubProgressKind.DEFAULT);
}
@Override
public final ProgressMonitor subTask(int work, SubProgressKind kind) {
check();
if (work < 0) {
throw new IllegalArgumentException("illegal amount of work");
}
//todo replace with exception and remove overflow check when MPS-24455 is fixed
if (myTotal < myDone + work || myDone + work < 0) {
LOG.warn("subTask(work): work is too big: total=" + myTotal + "; done=" + myDone + "; work=" + work);
myAfterActiveChild = myTotal;
} else {
myAfterActiveChild = myDone + work;
}
return (myActiveChild = subTaskInternal(work, kind));
}
protected SubProgressMonitor subTaskInternal(int work, SubProgressKind kind) {
return new SubProgressMonitor(this, work, kind);
}
public static class SubProgressMonitor extends ProgressMonitorBase {
private final ProgressMonitorBase parent;
private final int parentTotalWork;
private final SubProgressKind kind;
public SubProgressMonitor(ProgressMonitorBase parent, int work, SubProgressKind kind) {
this.parent = parent;
this.parentTotalWork = work;
this.kind = kind;
}
@Override
protected void setTitleInternal(String name) {
if (kind == SubProgressKind.DEFAULT) {
parent.setTitleInternal(combineTasks(parent.getTaskName(), name));
} else if (kind == SubProgressKind.REPLACING) {
parent.setTitleInternal(name);
} else if (kind == SubProgressKind.AS_COMMENT) {
parent.setStepInternal(name);
}
}
@Override
protected void setStepInternal(String comment) {
if (kind == SubProgressKind.DEFAULT || kind == SubProgressKind.REPLACING) {
parent.setStepInternal(comment);
}
}
@Override
protected void doneInternal(String text) {
}
@Override
protected void startInternal(String text) {
}
public ProgressMonitorBase getParent() {
return parent;
}
@Override
public boolean isCanceled() {
return parent.isCanceled();
}
@Override
public void cancel() {
parent.cancel();
}
@Override
protected void update(double fraction) {
if (parent.myActiveChild == this) {
int startTicks = parent.myAfterActiveChild - parentTotalWork;
double parentFraction = (startTicks + fraction * parentTotalWork) / parent.myTotal;
if (parentFraction < 0d) {
parentFraction = 0d;
}
if (parentFraction > 1d) {
parentFraction = 1d;
}
parent.update(parentFraction);
}
}
}
private static String combineTasks(String taskName, String subTask) {
if (taskName == null || taskName.isEmpty()) {
return subTask;
}
if (subTask == null || subTask.isEmpty()) {
return taskName;
}
return taskName.trim() + " :: " + subTask;
}
}