/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.pepsoft.util;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
*
* @author pepijn
*/
public class SubProgressReceiver implements ProgressReceiver {
public SubProgressReceiver(ProgressReceiver progressReceiver, float offset, float extent) throws OperationCancelled {
if ((offset < 0.0f) || (offset > 1.0f) || (extent <= 0.0f)) {
throw new IllegalArgumentException();
}
this.progressReceiver = progressReceiver;
this.offset = offset;
this.extent = extent;
creationTrace = new Throwable("Creation");
lastMessage = creationTrace.getStackTrace()[1].getClassName() + '.' + creationTrace.getStackTrace()[1].getMethodName() + '#' + creationTrace.getStackTrace()[1].getLineNumber();
}
/**
* Adds an additional progress receiver, to which the
* {@link #setProgress(float)}, {@link #setMessage(java.lang.String)},
* {@link #done()} and {@link #exceptionThrown(java.lang.Throwable)} methods
* will be forwarded (without remapping the progress). Note that recursive
* invocations of <code>setMessage()</code>, </code><code>exceptionThrown()</code>
* and <code>done()</code> are not reported.
*
* @param listener
*/
public synchronized void addListener(ProgressReceiver listener) {
if (listeners == null) {
listeners = new ArrayList<>();
}
listeners.add(listener);
}
public synchronized void removeListener(ProgressReceiver listener) {
listeners.remove(listener);
}
public ProgressReceiver getParent() {
return progressReceiver;
}
public synchronized String getLastMessage() {
return lastMessage;
}
public Throwable getCreationTrace() {
return creationTrace;
}
// ProgressReceiver
@Override
public void setProgress(float progress) throws OperationCancelled {
if (! reportedToParent) {
progressReceiver.subProgressStarted(this);
reportedToParent = true;
}
float parentProgress = offset + progress * extent;
if (parentProgress < 0.0f) {
progressReceiver.setProgress(0.0f);
} else if (parentProgress > 1.0f) {
progressReceiver.setProgress(1.0f);
} else {
progressReceiver.setProgress(parentProgress);
}
synchronized (this) {
if (listeners != null) {
for (ProgressReceiver listener: listeners) {
listener.setProgress(progress);
}
}
}
}
@Override
public void exceptionThrown(Throwable exception) {
if (! recursiveCall.get().get()) {
recursiveCall.get().set(true);
try {
progressReceiver.exceptionThrown(exception);
synchronized (this) {
if (listeners != null) {
for (ProgressReceiver listener: listeners) {
listener.exceptionThrown(exception);
}
}
}
} finally {
recursiveCall.get().set(false);
}
} else {
progressReceiver.exceptionThrown(exception);
}
}
@Override
public synchronized void done() {
if (listeners != null) {
for (ProgressReceiver listener: listeners) {
listener.done();
}
}
}
@Override
public void setMessage(String message) throws OperationCancelled {
if (! recursiveCall.get().get()) {
recursiveCall.get().set(true);
try {
if (! reportedToParent) {
progressReceiver.subProgressStarted(this);
reportedToParent = true;
}
progressReceiver.setMessage(message);
synchronized (this) {
if (listeners != null) {
for (ProgressReceiver listener: listeners) {
listener.setMessage(message);
}
}
}
synchronized (this) {
lastMessage = message;
}
} finally {
recursiveCall.get().set(false);
}
} else {
progressReceiver.setMessage(message);
}
}
@Override
public void checkForCancellation() throws OperationCancelled {
progressReceiver.checkForCancellation();
}
@Override
public void reset() {
throw new UnsupportedOperationException("Not supported");
}
@Override
public void subProgressStarted(SubProgressReceiver subProgressReceiver) throws OperationCancelled {
progressReceiver.subProgressStarted(subProgressReceiver);
}
private final ProgressReceiver progressReceiver;
private final float offset, extent;
private final Throwable creationTrace;
private List<ProgressReceiver> listeners;
private String lastMessage;
private boolean reportedToParent;
private static final ThreadLocal<AtomicBoolean> recursiveCall = new ThreadLocal<AtomicBoolean>() {
@Override
protected AtomicBoolean initialValue() {
return new AtomicBoolean(false);
}
};
}