blob: 075e4cbb80e03a1bb4d8da67ea73151b194d8ee0 [file] [log] [blame]
/*
* Copyright (c) 2014 Eike Stepper (Berlin, Germany) and others.
* Copyright (c) 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stefan Xenos - initial API and implementation
* Stefan Xenos - bug 174539 - add a 1-argument convert(...) method
* Stefan Xenos - bug 174040 - SubMonitor#convert doesn't always set task name
* Stefan Xenos - bug 206942 - updated javadoc to recommend better constants for infinite progress
* IBM Corporation - ongoing maintenance
* Eike Stepper - copied from org.eclipse.core.runtime.SubMonitor and enhanced as outlined in the JavaDoc below
*/
package org.eclipse.net4j.util.om.monitor;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
/**
* A {@link IProgressMonitorWithBlocking 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 {@link SubProgressMonitor}.
* <p>
* Progress monitoring is generally quite invasive to the code that is monitored.
* At the same time progress monitoring itself is typically very hard to implement correctly.
* This class aims at reducing the invasiveness as much as possible while offering all the functionality needed
* to do the job right.
* <p>
* The following aspects of this class help to keep the progress monitoring code short and nice and to avoid common monitoring mistakes:
* <ul>
* <li> It offers the full functionality of {@link org.eclipse.core.runtime.SubMonitor}, which already makes progress monitoring a lot easier.
* Refer to the {@link org.eclipse.core.runtime.SubMonitor} documentation or to this <a href="https://wiki.eclipse.org/Progress_Reporting">article</a> for details and examples.
*
* <li> In addition to the {@link SubMonitor#setWorkRemaining(int)} method it offers a {@link #skipped(int)} method, which redistributes the remaining work
* according to the last skipped {@link #worked(int)} or {@link #newChild(int)} call rather than on the sum of all subsequent calls.
*
* <li> It reduces the need to specify <code>work</code> arguments by using the default value {@link #DEFAULT_WORK 1} with the overloaded
* {@link #worked()}, {@link #skipped()} and {@link #newChild()} calls.
*
* <li> Basically all methods of this class can implicitely check for cancelation, thereby ensuring that the monitored code is always cancelable by the user
* without cluttering the code with repetitions of the following idiom:
* <pre>
if (monitor.isCanceled())
{
throw new OperationCanceledException();
}
* </pre>
* For details about automatic cancelation detection refer to {@link #detectCancelation()}.
*
* <li> It is normally very challenging to find out how much time a program really spends in the different parts of the monitored methods or how often these
* parts get executed. Stepping through the program with a debugger obviously leads to distortion that renders the observations meaningless and adding
* extra code to measure a runtime scenario realisticly is not nice from a maintenance point of view.
* <p>
* As a solution to this problem this class offers the possibility to transparently instrument {@link SubMonitor} instances such that they automatically
* collect and report all kinds of statistics that may help to enhance the user experience. Sometimes it would even indicate to remove some progress monitoring
* because it turns out that almost no time is being spent in a particular part of the program. Another typical result from the analysis is the understanding of
* <i>one time effects</i> that might need special consideration.
* <p>
* For details about this <i>probing</i> mode refer to {@link ProbingSubMonitor}.
* </ul>
* <p>
* The following example shows how to monitor progress while recursing through a tree of folders:
* <pre>
public void recurse(IContainer container, IProgressMonitor monitor) throws Exception
{
IResource[] members = container.members();
SubMonitor progress = SubMonitor.convert(monitor, members.length).detectCancelation();
progress.subTask(container.getFullPath().toString());
for (IResource member : members)
{
if (member instanceof IContainer)
{
Thread.sleep(5);
recurse((IContainer)member, progress.newChild());
}
else
{
progress.skipped();
}
}
}
* </pre>
*
* @author Eike Stepper
* @since 3.4
*/
public class SubMonitor implements IProgressMonitorWithBlocking
{
/**
* Minimum number of ticks to allocate when calling beginTask on an unknown IProgressMonitor.
* Pick a number that is big enough such that, no matter where progress is being displayed,
* the user would be unlikely to notice if progress were to be reported with higher accuracy.
*/
private static final int MINIMUM_RESOLUTION = 1000;
/**
* The RootInfo holds information about the root progress monitor. A SubMonitor and
* its active descendents share the same RootInfo.
*/
// Can't be private because ProbingSubMonitor uses it.
static final class RootInfo
{
private final IProgressMonitor root;
/**
* Remembers the last task name. Prevents us from setting the same task name multiple
* times in a row.
*/
private String taskName = null;
/**
* Remembers the last subtask name. Prevents the SubMonitor from setting the same
* subtask string more than once in a row.
*/
private String subTask = null;
/**
* Creates a RootInfo struct that delegates to the given progress
* monitor.
*
* @param root progress monitor to delegate to
*/
public RootInfo(IProgressMonitor root)
{
this.root = root;
}
public IProgressMonitor getRoot()
{
return root;
}
public boolean isCanceled()
{
return root.isCanceled();
}
public void setCanceled(boolean value)
{
root.setCanceled(value);
}
public void setTaskName(String taskName)
{
if (eq(taskName, this.taskName))
{
return;
}
this.taskName = taskName;
root.setTaskName(taskName);
}
public void subTask(String name)
{
if (eq(subTask, name))
{
return;
}
subTask = name;
root.subTask(name);
}
public void worked(int i)
{
root.worked(i);
}
public void clearBlocked()
{
if (root instanceof IProgressMonitorWithBlocking)
{
((IProgressMonitorWithBlocking)root).clearBlocked();
}
}
public void setBlocked(IStatus reason)
{
if (root instanceof IProgressMonitorWithBlocking)
{
((IProgressMonitorWithBlocking)root).setBlocked(reason);
}
}
}
/**
* Total number of ticks that this progress monitor is permitted to consume
* from the root.
*/
private int totalParent;
/**
* Number of ticks that this progress monitor has already reported in the root.
*/
private int usedForParent = 0;
/**
* Number of ticks that have been consumed by this instance's children.
*/
private double usedForChildren = 0.0;
/**
* Number of ticks allocated for this instance's children. This is the total number
* of ticks that may be passed into worked(int) or newChild(int).
*/
private int totalForChildren;
/**
* Children created by newChild will be completed automatically the next time
* the parent progress monitor is touched. This points to the last incomplete child
* created with newChild.
*/
private IProgressMonitor lastSubMonitor = null;
/**
* Used to communicate with the root of this progress monitor tree
*/
// Can't be private because ProbingSubMonitor reads from it.
final RootInfo root;
/**
* A bitwise combination of the SUPPRESS_* flags.
*/
// Can't be final because detectCancelation() writes to it.
// Can't be private because ProbingSubMonitor reads from it.
int flags;
public static final int DEFAULT_WORK = 1;
/**
* May be passed as a flag to newChild. Indicates that the calls
* to subTask on the child should be ignored. Without this flag,
* calling subTask on the child will result in a call to subTask
* on its parent.
*/
public static final int SUPPRESS_SUBTASK = 0x0001;
/**
* May be passed as a flag to newChild. Indicates that strings
* passed into beginTask should be ignored. If this flag is
* specified, then the progress monitor instance will accept null
* as the first argument to beginTask. Without this flag, any
* string passed to beginTask will result in a call to
* setTaskName on the parent.
*/
public static final int SUPPRESS_BEGINTASK = 0x0002;
/**
* May be passed as a flag to newChild. Indicates that strings
* passed into setTaskName should be ignored. If this string
* is omitted, then a call to setTaskName on the child will
* result in a call to setTaskName on the parent.
*/
public static final int SUPPRESS_SETTASKNAME = 0x0004;
/**
* May be passed as a flag to newChild. Indicates that strings
* passed to setTaskName, subTask, and beginTask should all be ignored.
*/
public static final int SUPPRESS_ALL_LABELS = SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK | SUPPRESS_SUBTASK;
/**
* May be passed as a flag to newChild. Indicates that strings
* passed to setTaskName, subTask, and beginTask should all be propagated
* to the parent.
*/
public static final int SUPPRESS_NONE = 0;
private static final int DETECT_CANCELATION = 0x0008;
/**
* Creates a new SubMonitor that will report its progress via
* the given RootInfo.
* @param rootInfo the root of this progress monitor tree
* @param totalWork total work to perform on the given progress monitor
* @param availableToChildren number of ticks allocated for this instance's children
* @param flags a bitwise combination of the SUPPRESS_* constants
*/
SubMonitor(RootInfo rootInfo, int totalWork, int availableToChildren, int flags)
{
root = rootInfo;
totalParent = totalWork > 0 ? totalWork : 0;
totalForChildren = availableToChildren;
this.flags = flags;
}
SubMonitor createSubMonitor(RootInfo rootInfo, int totalWork, int availableToChildren, int flags)
{
// ProbingSubMonitor overwrites to create special sub monitors for probing and reporting purposes.
return new SubMonitor(rootInfo, totalWork, availableToChildren, flags);
}
void adjustLocation()
{
// Do nothing here.
// ProbingSubMonitor overwrites for probing and reporting purposes.
}
/**
* <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor. It is
* not necessary to call done() on the result, but the caller is responsible for calling
* done() on the argument. Calls beginTask on the argument.</p>
*
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
*
* @param monitor monitor to convert to a SubMonitor instance or null. Treats null
* as a new instance of <code>NullProgressMonitor</code>.
* @return a SubMonitor instance that adapts the argument
*/
public static SubMonitor convert(IProgressMonitor monitor)
{
return convert(monitor, "", 0, ProbingMode.DEFAULT); //$NON-NLS-1$
}
/**
* <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor allocated
* with the given number of ticks. It is not necessary to call done() on the result,
* but the caller is responsible for calling done() on the argument. Calls beginTask
* on the argument.</p>
*
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
*
* @param monitor monitor to convert to a SubMonitor instance or null. Treats null
* as a new instance of <code>NullProgressMonitor</code>.
* @param work number of ticks that will be available in the resulting monitor
* @return a SubMonitor instance that adapts the argument
*/
public static SubMonitor convert(IProgressMonitor monitor, int work)
{
return convert(monitor, "", work, ProbingMode.DEFAULT); //$NON-NLS-1$
}
/**
* <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor allocated
* with the given number of ticks. It is not necessary to call done() on the result,
* but the caller is responsible for calling done() on the argument. Calls beginTask
* on the argument.</p>
*
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
*
* @param monitor to convert into a SubMonitor instance or null. If given a null argument,
* the resulting SubMonitor will not report its progress anywhere.
* @param taskName user readable name to pass to monitor.beginTask. Never null.
* @param work initial number of ticks to allocate for children of the SubMonitor
* @return a new SubMonitor instance that is a child of the given monitor
*/
public static SubMonitor convert(IProgressMonitor monitor, String taskName, int work)
{
return convert(monitor, taskName, work, ProbingMode.DEFAULT);
}
/**
* <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor. It is
* not necessary to call done() on the result, but the caller is responsible for calling
* done() on the argument. Calls beginTask on the argument.</p>
*
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
*
* @param monitor monitor to convert to a SubMonitor instance or null. Treats null
* as a new instance of <code>NullProgressMonitor</code>.
* @return a SubMonitor instance that adapts the argument
*/
public static SubMonitor convert(IProgressMonitor monitor, ProbingMode probingMode)
{
return convert(monitor, "", 0, probingMode); //$NON-NLS-1$
}
/**
* <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor allocated
* with the given number of ticks. It is not necessary to call done() on the result,
* but the caller is responsible for calling done() on the argument. Calls beginTask
* on the argument.</p>
*
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
*
* @param monitor monitor to convert to a SubMonitor instance or null. Treats null
* as a new instance of <code>NullProgressMonitor</code>.
* @param work number of ticks that will be available in the resulting monitor
* @return a SubMonitor instance that adapts the argument
*/
public static SubMonitor convert(IProgressMonitor monitor, int work, ProbingMode probingMode)
{
return convert(monitor, "", work, probingMode); //$NON-NLS-1$
}
/**
* <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor allocated
* with the given number of ticks. It is not necessary to call done() on the result,
* but the caller is responsible for calling done() on the argument. Calls beginTask
* on the argument.</p>
*
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
*
* @param monitor to convert into a SubMonitor instance or null. If given a null argument,
* the resulting SubMonitor will not report its progress anywhere.
* @param taskName user readable name to pass to monitor.beginTask. Never null.
* @param work initial number of ticks to allocate for children of the SubMonitor
* @return a new SubMonitor instance that is a child of the given monitor
*/
public static SubMonitor convert(IProgressMonitor monitor, String taskName, int work, ProbingMode probingMode)
{
if (monitor == null)
{
monitor = new NullProgressMonitor();
}
// Optimization: if the given monitor already a SubMonitor, no conversion is necessary
if (monitor instanceof SubMonitor)
{
SubMonitor subMonitor = (SubMonitor)monitor;
subMonitor.beginTask(taskName, work);
subMonitor.adjustLocation();
return subMonitor;
}
monitor.beginTask(taskName, MINIMUM_RESOLUTION);
if (probingMode == ProbingMode.OFF)
{
return new SubMonitor(new RootInfo(monitor), MINIMUM_RESOLUTION, work, SUPPRESS_NONE);
}
return createProbingSubMonitor(monitor, work, probingMode == ProbingMode.FULL);
}
/**
* Helps to avoid unnecessary loading of the {@link ProbingProgress} class, which forks a monitoring thread.
*/
private static SubMonitor createProbingSubMonitor(IProgressMonitor monitor, int availableToChildren, boolean full)
{
ProbingSubMonitor parent = monitor instanceof ProbingSubMonitor ? (ProbingSubMonitor)monitor : null;
return new ProbingSubMonitor(parent, new RootInfo(monitor), MINIMUM_RESOLUTION, availableToChildren, SUPPRESS_NONE,
full);
}
public final SubMonitor detectCancelation()
{
return detectCancelation(true);
}
public final SubMonitor detectCancelation(boolean on)
{
if (on)
{
flags |= DETECT_CANCELATION;
}
else
{
flags &= ~DETECT_CANCELATION;
}
return this;
}
private void checkCancelation()
{
if ((flags & DETECT_CANCELATION) != 0 && isCanceled())
{
throw new OperationCanceledException();
}
}
/**
* <p>Sets the work remaining for this SubMonitor instance. This is the total number
* of ticks that may be reported by all subsequent calls to worked(int), newChild(int), etc.
* This may be called many times for the same SubMonitor instance. When this method
* is called, the remaining space on the progress monitor is redistributed into the given
* number of ticks.</p>
*
* <p>It doesn't matter how much progress has already been reported with this SubMonitor
* instance. If you call setWorkRemaining(100), you will be able to report 100 more ticks of
* work before the progress meter reaches 100%.</p>
*
* @param workRemaining total number of remaining ticks
* @return the receiver
*/
public final SubMonitor setWorkRemaining(int workRemaining)
{
checkCancelation();
// Ensure we don't try to allocate negative ticks
workRemaining = Math.max(0, workRemaining);
// Ensure we don't cause division by zero
if (totalForChildren > 0 && totalParent > usedForParent)
{
// Note: We want the following value to remain invariant after this method returns
double remainForParent = totalParent * (1.0d - usedForChildren / totalForChildren);
usedForChildren = workRemaining * (1.0d - remainForParent / (totalParent - usedForParent));
}
else
{
usedForChildren = 0.0d;
}
totalParent = totalParent - usedForParent;
usedForParent = 0;
totalForChildren = workRemaining;
return this;
}
/**
* Consumes the given number of child ticks, given as a double. Must only
* be called if the monitor is in floating-point mode.
*
* @param ticks the number of ticks to consume
* @return ticks the number of ticks to be consumed from parent
*/
private int consume(double ticks)
{
if (totalParent == 0 || totalForChildren == 0)
{
return 0;
}
usedForChildren += ticks;
if (usedForChildren > totalForChildren)
{
usedForChildren = totalForChildren;
}
else if (usedForChildren < 0.0)
{
usedForChildren = 0.0;
}
int parentPosition = (int)(totalParent * usedForChildren / totalForChildren);
int delta = parentPosition - usedForParent;
usedForParent = parentPosition;
return delta;
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitor#isCanceled()
*/
public final boolean isCanceled()
{
return root.isCanceled();
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitor#setTaskName(java.lang.String)
*/
public final void setTaskName(String name)
{
checkCancelation();
if ((flags & SUPPRESS_SETTASKNAME) == 0)
{
root.setTaskName(name);
}
}
/**
* Starts a new main task. The string argument is ignored
* if and only if the SUPPRESS_BEGINTASK flag has been set on this SubMonitor
* instance.
*
* <p>This method is equivalent calling setWorkRemaining(...) on the receiver. Unless
* the SUPPRESS_BEGINTASK flag is set, this will also be equivalent to calling
* setTaskName(...) on the parent.</p>
*
* @param name new main task name
* @param totalWork number of ticks to allocate
*
* @see org.eclipse.core.runtime.IProgressMonitor#beginTask(java.lang.String, int)
*/
public final void beginTask(String name, int totalWork)
{
if ((flags & SUPPRESS_BEGINTASK) == 0 && name != null)
{
root.setTaskName(name);
}
setWorkRemaining(totalWork);
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitor#done()
*/
// Can't be final because ProbingSubMonitor overrides it.
public void done()
{
cleanupActiveChild();
int delta = totalParent - usedForParent;
if (delta > 0)
{
root.worked(delta);
}
totalParent = 0;
usedForParent = 0;
totalForChildren = 0;
usedForChildren = 0.0d;
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitor#internalWorked(double)
*/
public final void internalWorked(double work)
{
checkCancelation();
cleanupActiveChild();
int delta = consume(work > 0.0d ? work : 0.0d);
if (delta != 0)
{
root.worked(delta);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitor#subTask(java.lang.String)
*/
public final void subTask(String name)
{
checkCancelation();
if ((flags & SUPPRESS_SUBTASK) == 0)
{
root.subTask(name);
}
}
/**
* Same as {@link #worked(int) worked(1)}.
*/
public final void worked()
{
worked(DEFAULT_WORK);
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitor#worked(int)
*/
// Can't be final because ProbingSubMonitor overrides it.
public void worked(int work)
{
internalWorked(work);
}
/**
* Same as {@link #setWorkRemaining(int) setWorkRemaining(totalParent - usedForParent)}.
*/
public final void skipped(int ticks)
{
checkCancelation();
setWorkRemaining(totalParent - usedForParent);
}
/**
* Same as {@link #skipped(int) skipped(1)}.
*/
public final void skipped()
{
skipped(DEFAULT_WORK);
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitor#setCanceled(boolean)
*/
public final void setCanceled(boolean b)
{
root.setCanceled(b);
}
/**
* Same as {@link #newChild(int) newChild(1)}.
*/
public final SubMonitor newChild()
{
return newChild(DEFAULT_WORK, SUPPRESS_BEGINTASK);
}
/**
* <p>Creates a sub progress monitor that will consume the given number of ticks from the
* receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
* result. However, the resulting progress monitor will not report any work after the first
* call to done() or before ticks are allocated. Ticks may be allocated by calling beginTask
* or setWorkRemaining.</p>
*
* <p>Each SubMonitor only has one active child at a time. Each time newChild() is called, the
* result becomes the new active child and any unused progress from the previously-active child is
* consumed.</p>
*
* <p>This is property makes it unnecessary to call done() on a SubMonitor instance, since child
* monitors are automatically cleaned up the next time the parent is touched.</p>
*
* <code><pre>
* ////////////////////////////////////////////////////////////////////////////
* // Example 1: Typical usage of newChild
* void myMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
* doSomething(progress.newChild(50));
* doSomethingElse(progress.newChild(50));
* }
*
* ////////////////////////////////////////////////////////////////////////////
* // Example 2: Demonstrates the function of active children. Creating children
* // is sufficient to smoothly report progress, even if worked(...) and done()
* // are never called.
* void myMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
*
* for (int i = 0; i < 100; i++) {
* // Creating the next child monitor will clean up the previous one,
* // causing progress to be reported smoothly even if we don't do anything
* // with the monitors we create
* progress.newChild(1);
* }
* }
*
* ////////////////////////////////////////////////////////////////////////////
* // Example 3: Demonstrates a common anti-pattern
* void wrongMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
*
* // WRONG WAY: Won't have the intended effect, as only one of these progress
* // monitors may be active at a time and the other will report no progress.
* callMethod(progress.newChild(50), computeValue(progress.newChild(50)));
* }
*
* void rightMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
*
* // RIGHT WAY: Break up method calls so that only one SubMonitor is in use at a time.
* Object someValue = computeValue(progress.newChild(50));
* callMethod(progress.newChild(50), someValue);
* }
* </pre></code>
*
* @param totalWork number of ticks to consume from the receiver
* @return new sub progress monitor that may be used in place of a new SubMonitor
*/
public final SubMonitor newChild(int totalWork)
{
return newChild(totalWork, SUPPRESS_BEGINTASK);
}
/**
* <p>Creates a sub progress monitor that will consume the given number of ticks from the
* receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
* result. However, the resulting progress monitor will not report any work after the first
* call to done() or before ticks are allocated. Ticks may be allocated by calling beginTask
* or setWorkRemaining.</p>
*
* <p>Each SubMonitor only has one active child at a time. Each time newChild() is called, the
* result becomes the new active child and any unused progress from the previously-active child is
* consumed.</p>
*
* <p>This is property makes it unnecessary to call done() on a SubMonitor instance, since child
* monitors are automatically cleaned up the next time the parent is touched.</p>
*
* <code><pre>
* ////////////////////////////////////////////////////////////////////////////
* // Example 1: Typical usage of newChild
* void myMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
* doSomething(progress.newChild(50));
* doSomethingElse(progress.newChild(50));
* }
*
* ////////////////////////////////////////////////////////////////////////////
* // Example 2: Demonstrates the function of active children. Creating children
* // is sufficient to smoothly report progress, even if worked(...) and done()
* // are never called.
* void myMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
*
* for (int i = 0; i < 100; i++) {
* // Creating the next child monitor will clean up the previous one,
* // causing progress to be reported smoothly even if we don't do anything
* // with the monitors we create
* progress.newChild(1);
* }
* }
*
* ////////////////////////////////////////////////////////////////////////////
* // Example 3: Demonstrates a common anti-pattern
* void wrongMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
*
* // WRONG WAY: Won't have the intended effect, as only one of these progress
* // monitors may be active at a time and the other will report no progress.
* callMethod(progress.newChild(50), computeValue(progress.newChild(50)));
* }
*
* void rightMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
*
* // RIGHT WAY: Break up method calls so that only one SubMonitor is in use at a time.
* Object someValue = computeValue(progress.newChild(50));
* callMethod(progress.newChild(50), someValue);
* }
* </pre></code>
*
* @param totalWork number of ticks to consume from the receiver
* @return new sub progress monitor that may be used in place of a new SubMonitor
*/
public final SubMonitor newChild(int totalWork, int suppressFlags)
{
checkCancelation();
double totalWorkDouble = totalWork > 0 ? totalWork : 0.0d;
totalWorkDouble = Math.min(totalWorkDouble, totalForChildren - usedForChildren);
cleanupActiveChild();
// Compute the flags for the child. We want the net effect to be as though the child is
// delegating to its parent, even though it is actually talking directly to the root.
// This means that we need to compute the flags such that - even if a label isn't
// suppressed by the child - if that same label would have been suppressed when the
// child delegated to its parent, the child must explicitly suppress the label.
int childFlags = SUPPRESS_NONE;
if ((flags & SUPPRESS_SETTASKNAME) != 0)
{
// If the parent was ignoring labels passed to setTaskName, then the child will ignore
// labels passed to either beginTask or setTaskName - since both delegate to setTaskName
// on the parent
childFlags |= SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK;
}
if ((flags & SUPPRESS_SUBTASK) != 0)
{
// If the parent was suppressing labels passed to subTask, so will the child.
childFlags |= SUPPRESS_SUBTASK;
}
// Note: the SUPPRESS_BEGINTASK flag does not affect the child since there
// is no method on the child that would delegate to beginTask on the parent.
childFlags |= suppressFlags;
SubMonitor result = createSubMonitor(root, consume(totalWorkDouble), (int)totalWorkDouble, childFlags);
lastSubMonitor = result;
return result;
}
public void childDone()
{
// Do nothing
}
private void cleanupActiveChild()
{
if (lastSubMonitor == null)
{
return;
}
IProgressMonitor child = lastSubMonitor;
lastSubMonitor = null;
child.done();
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
*/
public final void clearBlocked()
{
checkCancelation();
root.clearBlocked();
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
*/
public final void setBlocked(IStatus reason)
{
checkCancelation();
root.setBlocked(reason);
}
protected static boolean eq(Object o1, Object o2)
{
if (o1 == null)
{
return o2 == null;
}
if (o2 == null)
{
return false;
}
return o1.equals(o2);
}
/**
* Enumerates the possible probing mode values {@link #OFF}, {@link #STANDARD} and {@link #FULL}.
*
* @author Eike Stepper
* @since 3.4
*/
public enum ProbingMode
{
OFF, STANDARD, FULL;
public static final ProbingMode DEFAULT = getDefault();
private static ProbingMode getDefault()
{
String mode = System.getProperty("submonitor.probing");
if (FULL.toString().equalsIgnoreCase(mode))
{
return FULL;
}
if (STANDARD.toString().equalsIgnoreCase(mode) || Boolean.TRUE.toString().equalsIgnoreCase(mode))
{
return STANDARD;
}
return OFF;
}
}
}