A progress monitor that uses a given amount of work ticks from a parent monitor. This is intended as a
safer, easier-to-use alternative to SubProgressMonitor. The main benefits of SubMonitor over
SubProgressMonitor are:
- It is not necessary to call beginTask() or done() on an instance of SubMonitor.
- SubMonitor has a simpler syntax for creating nested monitors.
- SubMonitor is more efficient for deep recursion chains.
- SubMonitor has a setWorkRemining method that allows the remaining space on the monitor to be
redistributed without reporting any work.
- SubMonitor protects the caller from common progress reporting bugs in a called method. For example,
if a called method fails to call done() on the given monitor or fails to consume all the ticks on
the given monitor, the parent will correct the problem after the method returns.
USAGE:
When implementing a method that accepts an IProgressMonitor:
- At the start of your method, use
SubMonitor.convert(...).
to convert the IProgressMonitor
into a SubMonitor.
- Use
SubMonitor.newChild(...)
whenever you need to call another method that
accepts an IProgressMonitor.
DEFAULT BEHAVIOR:
When writing JavaDoc for a method that accepts an IProgressMonitor, you should assume the
following default behavior unless the method's JavaDoc says otherwise:
- It WILL call beginTask on the IProgressMonitor.
- It WILL NOT accept a null argument.
- It WILL call done on the IProgressMonitor.
BEST PRACTISES:
We recommend that newly-written methods follow the given contract:
- It WILL call beginTask on the IProgressMonitor.
- It WILL accept a null argument, indicating that no progress should be reported and the operation cannot be cancelled.
- It WILL NOT call done on the IProgressMonitor, leaving this responsibility up to the caller.
If you wish to follow these conventions, you may copy and paste the following text into your method's JavaDoc:
@param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
to call done() on the given monitor. Accepts null
, indicating that no progress should be
reported and that the operation cannot be cancelled.
Example: Recommended usage
This example demonstrates how the recommended usage of SubMonitor
makes it unnecessary to call
IProgressMonitor.done() in most situations.
It is never necessary to call done() on a monitor obtained from convert
or progress.newChild()
.
In this example, there is no guarantee that monitor
is an instance of SubMonitor
, making it
necessary to call monitor.done()
. The JavaDoc contract makes this the responsibility of the caller.
// param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
// to call done() on the given monitor. Accepts null
, indicating that no progress should be
// reported and that the operation cannot be cancelled.
//
void doSomething(IProgressMonitor monitor) {
// Convert the given monitor into a progress instance
SubMonitor progress = SubMonitor.convert(monitor, 100);
// Use 30% of the progress to do some work
doSomeWork(progress.newChild(30));
// Advance the monitor by another 30%
progress.worked(30);
// Use the remaining 40% of the progress to do some more work
doSomeWork(progress.newChild(40));
}
Example: Default usage
You will often need to implement a method that does not explicitly stipulate that calling done() is the responsibility
of the caller. In this case, you should use the following pattern:
// param monitor the progress monitor to use for reporting progress to the user, or null
indicating
// that no progress should be reported and the operation cannot be cancelled.
//
void doSomething(IProgressMonitor monitor) {
// Convert the given monitor into a progress instance
SubMonitor progress = SubMonitor.convert(monitor, 100);
try {
// Use 30% of the progress to do some work
doSomeWork(progress.newChild(30));
// Advance the monitor by another 30%
progress.worked(30);
// Use the remaining 40% of the progress to do some more work
doSomeWork(progress.newChild(40));
} finally {
if (monitor != null) {
monitor.done();
}
}
}
Example: Branches
This example demonstrates how to smoothly report progress in situations where some of the work is optional.
void doSomething(IProgressMonitor monitor) {
SubMonitor progress = SubMonitor.convert(monitor, 100);
if (condition) {
// Use 50% of the progress to do some work
doSomeWork(progress.newChild(50));
}
// Don't report any work, but ensure that we have 50 ticks remaining on the progress monitor.
// If we already consumed 50 ticks in the above branch, this is a no-op. Otherwise, the remaining
// space in the monitor is redistributed into 50 ticks.
progress.setWorkRemaining(50);
// Use the remainder of the progress monitor to do the rest of the work
doSomeWork(progress.newChild(50));
}
Please beware of the following anti-pattern:
if (condition) {
// Use 50% of the progress to do some work
doSomeWork(progress.newChild(50));
} else {
// Bad: Causes the progress monitor to appear to start at 50%, wasting half of the
// space in the monitor.
progress.worked(50);
}
Example: Loops
This example demonstrates how to report progress in a loop.
void doSomething(IProgressMonitor monitor, Collection someCollection) {
SubMonitor progress = SubMonitor.convert(monitor, 100);
// Create a new progress monitor that uses 70% of the total progress and will allocate one tick
// for each element of the given collection.
SubMonitor loopProgress = progress.newChild(70).setWorkRemaining(someCollection.size());
for (Iterator iter = someCollection.iterator(); iter.hasNext();) {
Object next = iter.next();
doWorkOnElement(next, loopProgress.newChild(1));
}
// Use the remaining 30% of the progress monitor to do some work outside the loop
doSomeWork(progress.newChild(30));
}
Example: Infinite progress
This example demonstrates how to report logarithmic progress in situations where the number of ticks
cannot be easily computed in advance.
void doSomething(IProgressMonitor monitor, LinkedListNode node) {
SubMonitor progress = SubMonitor.convert(monitor);
while (node != null) {
// Regardless of the amount of progress reported so far,
// use 0.01% of the space remaining in the monitor to process the next node.
progress.setWorkRemaining(10000);
doWorkOnElement(node, progress.newChild(1));
node = node.next;
}
}
This class can be used without OSGi running.