blob: 4b31cdcf03e39cbd62ce5bffbfc64c76b6d50eca [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>Using Progress Monitors</title>
<link rel="stylesheet" href="../default_style.css">
</head>
<body link="#0000ff" vlink="#800080">
<div align="right">&nbsp; <font face="Times New Roman, Times, serif" size="2">Copyright
&copy; 2016 Stefan Xenos and others</font>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
<tbody>
<tr>
<td colspan="2" align="left" bgcolor="#0080c0" valign="top"><b><font face="Arial,Helvetica"><font color="#ffffff">&nbsp;Eclipse
Corner Article</font></font></b></td>
</tr>
</tbody>
</table>
</div>
<div align="left">
<h1><img src="images/Idea.jpg" align="middle" height="86" width="120"></h1>
</div>
<p>&nbsp;</p>
<h1 align="center">Using Progress Monitors</h1>
<blockquote>
<b>Summary</b>
<br>
In this article I'll explain how to report progress in Eclipse. I'll
discuss the contract on IProgressMonitor, demonstrate some common patterns
using SubMonitor, and explain how to migrate legacy code to take advantage
of the API introduced in Eclipse 4.6.
<br>
<p><b> By Stefan Xenos, Google</b> <br>
April 19, 2016<font size="-1"></font> </p>
</blockquote>
<hr width="100%">
<h2>1.0 Introduction</h2>
So you're writing a long-running method. It takes an IProgressMonitor as a parameter. You want it to
report smooth progress, be cancellable from the UI thread and accomplish this with minimal boilerplate.
Unfortunately, most of the examples you've seen either omit progress reporting entirely or contain
more boilerplate than code.
<p>
Is it possible to get the smooth progress reporting without all that boilerplate? If so, what are the
responsibilities of your method and what are the responsibilities of its caller? This article should help.
<h2>2.0 Using SubMonitor</h2>
Use SubMonitor. If you remember nothing else from this article, remember that.
<p>
SubMonitor is an implementation of IProgressMonitor that simplifies everything related to progress
reporting. Whenever you write a method that accepts an IProgressMonitor, the first thing you should
do is convert it to a SubMonitor using <code>SubMonitor.convert(...)</code>.
<h3>2.1 Allocating Ticks</h3>
<p>
There are several overloads for <code>convert</code> and most of them accept a number of ticks as an
argument. These are said to "allocate ticks". A tick is a unit of work to be performed. Allocating
ticks distributes those ticks across the remaining space of the progress monitor but doesn't
actually report any progress. It basically sets the denominator that will be used for subsequent
progress reporting.
<p>
How many ticks do you need? Take a look at all the slow things your method does and assign them
a number of ticks based on how long you think they will take. Any method you call that takes a
progress monitor should be considered a "slow thing". If you think one thing will take longer
than another, give it more ticks. If you think it will take twice as long, give it twice as many.
When you're done, add up all the ticks. That's how many you should allocate. Ticks only have
meaning relative to other ticks on the same monitor instance: their absolute value doesn't
mean anything.
<p>
There are several methods which allocate ticks. Normally you'll allocate them at construction-time
using <code>SubMonitor.convert(...)</code> but this only works if you're creating a new
SubMonitor instance.
<p>
Sometimes you'll want to allocate (or reallocate) the ticks on an existing
monitor in which case you'll want <code>SubMonitor.setWorkRemaining</code>. You can
call this as often as you like on a SubMonitor instance. When you do, any remaining unconsumed
space on the monitor is divided into the given number of ticks and any previously-allocated
ticks are forgotten.
<p>
The last method that allocates ticks is called <code>beginTask</code>. It's used as part of the
progress reporting framework and you rarely need to call it directly. You'll see this used
a lot in older code and we'll get more into it later. For now, it's best to avoid it in new
code unless you're implementing your own IProgressMonitor.
<h3>2.2 Consuming Ticks</h3>
Once you've allocated the ticks you can consume them. Consuming ticks is what reports progress.
Let's say you allocate 50 ticks and then consume 3 of them. That means your progress bar will
be 3/50 of the way across, or 6%. Note that you must allocate ticks before you can consume them
on any given progress monitor instance. Attempting to consume ticks without allocating them is an error.
<p>
There are several methods on SubMonitor which consume ticks. Namely, <code>split(...)</code>,
<code>newChild(...)</code>, and <code>worked(...)</code>. Practically speaking the only one you
need is <code>split(...)</code>.
<p>
I'll be using <code>split(...)</code> for most of the code examples in this article, but it is new
in Eclipse 4.6. If your code is meant to work on earlier versions of Eclipse you should use
<code>newChild(...)</code> instead of <code>split(...)</code>. The two do pretty much the same
thing except that <code>split(...)</code> also performs cancellation checks.
<p>
<h3>2.3 Split</h3>
<code>split(...)</code> doesn't immediately consume the ticks. It uses the ticks to create a new
child monitor but first it fully consumes any leftover ticks in any previous children
of the same parent.
<p>
I'll demonstrate with an example:
<pre>
void myMethod(IProgressMonitor monitor) {
// No ticks have been allocated yet, so we can't consume them.
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
// monitor is now being managed by subMonitor. We shouldn't use it directly again.
// subMonitor has 100 ticks allocated for it and we can start consuming them.
SubMonitor child1 = subMonitor.split(10);
// subMonitor now has 90 ticks allocated. 10 of the original 100 were used to build child1.
// child1 has no ticks allocated for it yet so we can't consume ticks from it yet.
child1.setWorkRemaining(100);
// child1 now has 100 ticks allocated for it. Consuming 1 tick from child1 now would
// advance the root monitor by 0.1%.
SubMonitor grandchild1 = child1.split(50);
// child1 now has 50 ticks allocated.
SubMonitor grandchild2 = child1.split(50);
// Allocating a new grandchild from child1 has caused grandchild1 to be consumed.
// Our root progress monitor now shows 5% progress.
SubMonitor child2 = subMonitor.split(40);
// Allocating a new child from subMonitor has caused child1 and grandchild2 to be
// consumed. Our root progress monitor now shows 10% progress.
SubMonitor child3 = subMonitor.split(10);
// Child2 was consumed. The root progress monitor now shows 50% progress.
SubMonitor child4 = subMonitor.split(40);
// Child3 was consumed. The root progress monitor now shows 60% progress.
}
</pre>
<h3>2.4 Cancellation</h3>
Long-running methods should check the value of <code>IProgressMonitor.isCanceled()</code>
periodically. If it returns true, they should terminate cleanly and throw
<code>OperationCanceledException</code>. In Eclipse 4.5 and earlier, this was done with
explicit cancellation checks like this:
<pre>
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
</pre>
Unfortunately, these sorts of cancellation checks are cumbersome and can become a
performance bottleneck if performed too frequently.
<p>
In Eclipse 4.6 and on, cancellation checks are be performed implicitly by
<code>SubMonitor.split(...)</code>. Code should be migrated to use <code>split</code>
wherever possible and explicit cancellation checks should be deleted.
<p>
So how does <code>split</code> work and how does it replace explicit cancellation checks?
It's basically just a helper that does the same thing as <code>newChild</code> but
additionally includes a cancellation check. Internally, split does something like this
(pseudocode):
<pre>
SubMonitor split(int ticks) {
if (checking_cancelation_now_wouldnt_cause_a_performance_problem()) {
if (isCanceled()) {
throw new OperationCanceledException();
}
}
return newChild(ticks);
}
</pre>
<h2>3.0 Examples</h2>
Lets look at some examples of common progress reporting patterns.
<h3>3.1 Calling child methods</h3>
Most long-running operation will need to call out to other long-running operations.
<pre>
void doSomething(IProgressMonitor monitor) {
// Convert the given monitor into a SubMonitor instance. We shouldn't use the original
// monitor object again since subMonitor will consume the entire monitor.
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
// Use 30% of the progress to do some work
someChildTask(subMonitor.split(30));
// Use the remaining 70% of the progress to do some more work
someChildTask(subMonitor.split(70));
}
</pre>
<h3>3.2 Branches</h3>
Sometimes some long-running piece of code is optional. If the optional piece is skipped, you still want the
total work to add up to 100%, but you also don't want to waste a portion of the progress monitor just to
make the ticks line up. One approach is to do the optional part first and then use setWorkRemaining to
redistribute the remaining ticks.
<pre>
void doSomething(IProgressMonitor monitor) {
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
if (condition1) {
doSomeWork(subMonitor.split(20));
}
// Don't report any work, but ensure that we have 80 ticks remaining on the progress monitor.
// If we already consumed ticks in the above branch, this is a no-op. Otherwise, the remaining
// space in the monitor is redistributed.
subMonitor.setWorkRemaining(80);
if (condition2) {
doMoreWork(subMonitor.split(40));
}
subMonitor.setWorkRemaining(40)
doSomeMoreWork(subMonitor.split(40));
}
</pre>
This approach works well enough in most cases and requires minimal boilerplate but the progress it reports
can sometimes be uneven if the method ends with a bunch of conditionals that are often skipped. Another
approach is to count the number of ticks in advance:
<pre>
void doSomething(IProgressMonitor monitor) {
int totalTicks = 0;
if (condition1) {
totalTicks += OPERATION_ONE_TICKS;
}
if (condition2) {
totalTicks += OPERATION_TWO_TICKS;
}
if (condition3) {
totalTicks += OPERATION_THREE_TICKS;
}
// Allocate a different number of ticks based on which branches we expect to execute.
SubMonitor subMonitor = SubMonitor.convert(monitor, totalTicks);
if (condition1) {
doSomeWork(subMonitor.split(OPERATION_ONE_TICKS));
}
if (condition2) {
doSomeWork(subMonitor.split(OPERATION_TWO_TICKS));
}
if (condition3) {
doSomeWork(subMonitor.split(OPERATION_THREE_TICKS));
}
}
</pre>
This will usually report smoother progress, but due to the extra complexity it's usually best to only
use this pattern if otherwise jerkiness of reported progress would be annoying to the user.
<h3>3.3 Loops</h3>
This example demonstrates how to report progress in a loop where every iteration takes roughly
the same amount of time.</p>
<pre>
void doSomething(IProgressMonitor monitor, Collection someCollection) {
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
// Create a new progress monitor for the loop.
SubMonitor loopMonitor = subMonitor.split(70).setWorkRemaining(someCollection.size());
for (Object next: someCollection) {
// Create a progress monitor for each loop iteration.
SubMonitor iterationMonitor = loopMonitor.split(1);
doWorkOnElement(next, iterationMonitor);
}
// The original progress monitor can be used for further work after the loop terminates.
doSomeWork(subMonitor.split(30));
}
</pre>
<h3>3.4 Skipping elements in a loop</h3>
Sometimes some elements from a loop will be skipped. In that case, it's better to use the space
in the progress monitor to report the work done for the long-running elements rather than the
fast elements.
<pre>
void doSomething(IProgressMonitor monitor, Collection someCollection) {
SubMonitor loopMonitor = SubMonitor.convert(monitor, someCollection.size());
int remaining = someCollection.size();
for (Object next : someCollection) {
loopMonitor.setWorkRemaining(remaining--);
if (shouldSkip(next)) {
continue;
}
// Create a progress monitor for each loop iteration.
SubMonitor iterationMonitor = loopMonitor.split(1);
doWorkOnElement(next, iterationMonitor);
}
}
</pre>
This works well enough if skipped elements are rare but if most of the elements are skipped, this pattern
will tend to make poor use of the end of the monitor. If many elements might be skipped, it's better to
pre-filter the list like this:
<pre>
void doSomething(IProgressMonitor monitor, Collection someCollection) {
List filteredElements = someCollection
.stream()
.filter(next -> !shouldSkip(next))
.collect(Collectors.toList());
SubMonitor loopMonitor = SubMonitor.convert(monitor, filteredElements.size());
for (Object next : filteredElements) {
doWorkOnElement(next, loopMonitor.split(1));
}
}
</pre>
<h3>3.5 Queues</h3>
What if you're performing a depth-first search or some other algorithm where more work is
continually discovered as you proceed? Try putting the work-discovered-so-far in a queue
and using the size of the queue to allocate ticks on your monitor. When using this pattern,
always make sure you never allocate less than some minimum number of ticks or you'll consume
the entire progress monitor on the first few iterations of your algorithm.
<pre>
void depthFirstSearch(IProgressMonitor monitor, Object root) {
SubMonitor subMonitor = SubMonitor.convert(monitor);
ArrayList queue = new ArrayList();
queue.add(root);
while (!queue.isEmpty()) {
// Allocate a number of ticks equal to the size of the queue or some constant,
// whatever is larger. This constant prevents the entire monitor from being consumed
// at the start when the queue is very small.
subMonitor.setWorkRemaining(Math.max(queue.size(), 20));
Object next = queue.remove(queue.size() - 1);
processElement(next, subMonitor.split(1));
queue.addAll(getChildrenFor(next));
}
}
</pre>
<h3>3.6 Unknown progress</h3>
What about those situations where you really have no idea how much work to report or how
long it will take? Try allocating a small percentage of the remaining space on each iteration.
<pre>
void unknownProgress(IProgressMonitor monitor) {
SubMonitor subMonitor = SubMonitor.convert(monitor);
while (hasMore()) {
// Use 1% of the remaining space for each iteration
processNext(subMonitor.setWorkRemaining(100).split(1));
}
}
</pre>
Notice the idiom <code>setWorkRemaining(denominator).split(numerator)</code>. This can be used
at any point to consume numerator/denominator of the remaining space in a monitor.
<h3>3.7 Naming conventions</h3>
In these examples we've used the same naming convention that has been used within
the Eclipse platform. You may wish to use the same convention to help convey the purpose of your
progress monitors:
<ul>
<li><b>subMonitor</b> - a SubMonitor that tracks the entire progress of the method.</li>
<li><b>loopMonitor</b> - a SubMonitor that tracks the progress of a single loop. If a method contains
multiple loops, this is used as a suffix.</li>
<li><b>iterationMonitor</b> - a SubMonitor that tracks the progress of a single iteration of a loop.
If there are multiple nested loops, this may be used as a suffix.</li>
<li><b>monitor</b> - an IProgressMonitor that is passed as a method argument.</li>
</ul>
</ul>
<h2>4.0 Responsibilities of callers and callees</h2>
Imagine you need to invoke another method that accepts an IProgressMonitor.
What are the responsibilities of the caller and what are the responsibilities
of the callee?
<p>
The caller:
<ul>
<li><b>Will</b> pass an IProgressMonitor instance which has not had <code>beginTask</code> invoked on it yet
(or an implementation such as SubMonitor which permits beginTask to be invoked multiple times).</li>
<li><b>Will not</b> expect that the callee invokes <code>done()</code> on the monitor. The
caller must either use an SubMonitor (or a similar implementation which does not require <code>done()</code>
to be invoked), or it must take responsibility for calling <code>done()</code> on the monitor after the
callee has finished.</li>
<li><b>Will not</b> pass in a null monitor unless the JavaDoc for the callee says that it accepts null.</li>
<li><b>Will</b> pass in a monitor which ignores the String argument to <code>beginTask</code> unless the JavaDoc
for the callee says that it requires otherwise.</li>
</ul>
The callee:
<ul>
<li><b>Will</b> call <code>beginTask</code> 0 or 1 times on the monitor, at its option.</li>
<li><b>Will not</b> promise to call <code>done()</code> on the monitor, although it is allowed to do so.</li>
<li><b>Will not</b> call <code>setCanceled</code> on the monitor.</li>
<li><b>Will not</b> accept a null monitor unless its JavaDoc says otherwise.</li>
<li><b>Will not</b> expect the caller to do anything with the string passed to <code>beginTask</code> unless
its JavaDoc says otherwise.</li>
</ul>
In practice, callers will be using SubMonitor wherever possible and method
implementations will not be calling <code>done()</code>. This means that the only
calls to <code>done()</code> will occur in root-level methods (methods which obtain
their own IProgressMonitor via some mechanism other than having it passed it as a method parameter).
<h2>5.0 Different versions of Eclipse</h2>
In Eclipse 4.5 (Mars) and earlier, it was standard practice for method implementations
to invoke <code>done()</code> on any monitor passed in as an argument and for the caller
to rely upon this fact.
<p>
In Eclipse 4.6 (Neon), method implementations should still invoke <code>done()</code> if
they did so previously. Callers are also required to either invoke <code>done()</code>
or select a monitor implementation like SubMonitor which doesn't require the use of
<code>done()</code>.
<p>
In Eclipse 4.7 (Oxygen) and higher, method implementations are not required to invoke
<code>done()</code>. Callers must either invoke <code>done()</code> or select a monitor
implementation like SubMonitor which doesn't require the use of <code>done()</code>.
<h3>5.1 Changes in Eclipse 4.6</h3>
The following changes were made in Eclipse 4.6:
<ul>
<li>SubMonitor now has a static <code>done(IProgressMonitor)</code> method that can be used to
call <code>done()</code> on a possibly-null IProgressMonitor instance.</li>
<li>The recommended policy for checking cancellation has changed. Rather than
using explicit cancellation checks, clients should rely on the implicit
cancellation checks done by SubMonitor.split.</li>
<li>SubProgressMonitor is now deprecated.</li>
</ul>
As of Eclipse 4.6 (Neon), any client code that obtains a root monitor (any monitor
that isn't passed in as a method argument) is responsible for invoking <code>done()</code>
on that monitor. It must not rely on the methods it calls to invoke
<code>done()</code>. Please see the
<a href="https://www.eclipse.org/eclipse/development/porting/eclipse_4_6_porting_guide.html">Migration guide</a>
for more information on how to locate such code.
<p>
Method implementations that previously invoked <code>done()</code> should
continue to do so, since the root monitors need to be updated first.
<h3>5.2 Migrating from SubProgressMonitor to SubMonitor</h3>
Eclipse 3.2 and earlier used SubProgressMonitor for nesting progress monitors. This class is
deprecated as of Eclipse 4.6 (Neon). It has been replaced by SubMonitor.
<p>
The process for converting code which used SubProgressMonitor into SubMonitor is:
<ul>
<li>Calls to <code>IProgressMonitor.beginTask</code> on the root monitor should be replaced by a call
to <code>SubMonitor.convert</code>. Keep the returned SubMonitor around as a local variable and refer
to it instead of the root monitor for the remainder of the method.</li>
<li>All calls to <code>new SubProgressMonitor(IProgressMonitor, int)</code> should be replaced by calls to
<code>SubMonitor.split(int)</code>.</li>
<li>If a SubProgressMonitor is constructed using the SUPPRESS_SUBTASK_LABEL flag, replace it with the
two-argument version of <code>SubMonitor.split(int, int)</code> using <code>SubMonitor.SUPPRESS_SUBTASK</code>
as the second argument.</li>
<li>It is not necessary to call done on an instance of <code>SubMonitor</code>.</li>
<li>There is no drop-in replacement for PREPEND_MAIN_LABEL_TO_SUBTASK. PREPEND_MAIN_LABEL_TO_SUBTASK
made use of string arithmetic that didn't handle translation well. Clients that were using this
previously should switch to using fully-formatted task labels instead.
</ul>
Note that <code>SubMonitor.convert(monitor, ticks)</code> is <b>not</b> a direct replacement for
<code>new SubProgressMonitor(monitor, ticks)</code>. The former fully consumes a
monitor which hasn't had ticks allocated on it yet and creates a new monitor with the given
number of ticks allocated. The latter consumes only the given number of ticks from an input
monitor which has already had ticks allocated and produces a monitor with no ticks allocated.
If you attempt to do a search-and-replace of one to the other, your progress reporting won't work.
<h3>5.3 Changes in Eclipse 4.7</h3>
In Eclipse 4.7 (Oxygen) and higher, method implementations that receive a progress monitor are
not required to invoke <code>done()</code> on it. Such calls may be removed if they were present
in earlier versions.
<p>
Methods in plugins that are also intended for use with earlier Eclipse versions should continue calling
<code>done()</code> as long as those earlier Eclipse versions are still being supported by the plugin.
<h2>6.0 The protocol of <i>IProgressMonitor</i></h2>
This section is for those of you doing something advanced. What if you want to implement your own
IProgressMonitor or can't use the SubMonitor helper class? In that case you'll need
to work with the IProgressMonitor contracts directly.
<p>
An IProgressMonitor instance can be in one of three states. Any given
implementation may or may not track state changes and may or may
not do anything about them.
<p>
<b>UNALLOCATED</b>
<p>
This is the initial state of a newly created <code>IProgressMonitor</code> instance,
before <code>beginTask()</code> has been called to allocate ticks. Attempting to
call <code>worked()</code> or otherwise consume ticks from a monitor in this state is an error.
From here the monitor can enter the ALLOCATED state as a result of a <code>beginTask()</code>
call or the FINISHED <code>done()</code> state as a result of a call to <code>done()</code>.
<p>
<b>ALLOCATED</b>
<p>
The monitor enters this state after the first and only call to <code>beginTask()</code>
has allocated ticks for the monitor. Attempting to call <code>beginTask()</code> in this
state is an error. From here the monitor can enter the FINISHED state as a result of a
<code>done()</code> call.
<p>
<b>FINISHED</b>
<p>
The monitor enters this state after the first call to <code>done()</code>.
Unlike <code>beginTask()</code>, <code>done()</code> may be called any number of times.
However, only the first call to <code>done()</code> has any effect.
Reporting work or calling <code>beginTask()</code> on a FINISHED monitor is a programming
error and will have no effect. Unless the implementation says otherwise, <code>done()</code> must always
be called on a monitor once <code>beginTask()</code> has been called.
It is not necessary to call <code>done()</code> on a monitor in the UNALLOCATED
state.
</ul>
<h3>6.1 Cancellation</h3>
A monitor can become cancelled at any time due to activity on another thread.
The monitor indicates that it has been cancelled by returning
true from <code>isCanceled()</code>. When a long-running operation detects that
it has been cancelled, it should abort its operation cleanly.
<p>
It is technically possible to cancel a monitor by invoking
<code>setCanceled()</code> but you should
never do this. A long-running method that wishes to cancel itself it should
throw OperationCanceledException rather than invoking any particular method
on its monitor.
<h3>6.2 Threading</h3>
All implementations of IProgressMonitor are single-threaded by default. Unless the
JavaDoc for the specific monitor implementation says otherwise, all methods
must be invoked on the same thread that instantiated the monitor. If the concrete
type of the IProgressMonitor is unknown, it must be assumed to be single-threaded.
<h3>6.3 Allocating ticks</h3>
When using <code>beginTask</code> to allocate ticks on an IProgressMonitor, the caller should
always allocate at least 1000 ticks. Many root monitors use this value to set
the resolution for all subsequent progress reporting, even if the ticks are
later subdivided using a SubMonitor.
<p>
SubMonitor users don't need to worry about this since <code>SubMonitor.convert</code>
does this internally when converting an unknown monitor.
<p>
<h3>6.4 Passing strings to beginTask</h3>
At the lowest level, any method that needs to allocate ticks ends up calling
<code>beginTask</code>. Usually this is done indirectly by a call to
<code>SubMonitor.convert(...)</code>. Unfortunately, <code>beginTask</code>
also takes a string argument which root monitors use to set the task name.
<p>
Every long-running operation needs to allocate ticks but most don't want to
modify the task name. For this reason, most callers of <code>beginTask</code>
call it with the empty string or a temporary string that isn't intended
to be seen by the end user. Similarly, most implementations of
<code>beginTask</code> are expected to ignore the string argument.
<p>
The exception is root monitors. Many root monitors display the string argument
somewhere. For this reason, any code that obtains a root monitor is expected
to convert it to a form that will filter out the string argument
before passing it to another method.
<p>
The default expectation is that progress monitors passed as parameters will
do this filtering. Any method that receives a progress monitor and does not
want the <code>beginTask(...)</code> argument filtered must say so clearly in
its JavaDoc.
<h3>6.5 IProgressMonitor.UNKNOWN</h3>
IProgressMonitor.UNKNOWN is only supported by root monitors and should never
be used by method implementations which receive a progress monitor as a parameter.
<h3>6.6 Task names and performance concerns</h3>
As of the time of this writing (Eclipse 4.6), several of the root monitors
in Eclipse have expensive implementations of <code>setTaskName(...)</code>
and <code>subTask(...)</code>. Plugin authors are advised not to call
these methods more than 3 times per second. Doing so may introduce
performance problems. See
<a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=445802">Eclipse bug 445802</a>
for more information.
<p>
</body>
</html>