KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > progress > spi > InternalHandle


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20
21 package org.netbeans.progress.spi;
22
23 import java.awt.event.ActionEvent JavaDoc;
24 import java.util.logging.Level JavaDoc;
25 import java.util.logging.Logger JavaDoc;
26 import javax.swing.Action JavaDoc;
27 import javax.swing.JComponent JavaDoc;
28 import javax.swing.JLabel JavaDoc;
29 import org.netbeans.progress.module.*;
30 import org.openide.util.Cancellable;
31 import org.openide.util.Lookup;
32
33 /**
34  * Instances provided by the ProgressHandleFactory allow the users of the API to
35  * notify the progress bar UI about changes in the state of the running task.
36  * @author Milos Kleint (mkleint@netbeans.org)
37  */

38 public final class InternalHandle {
39
40     private static final Logger JavaDoc LOG = Logger.getLogger(InternalHandle.class.getName());
41     
42     private String JavaDoc displayName;
43     private boolean customPlaced1 = false;
44     private boolean customPlaced2 = false;
45     private boolean customPlaced3 = false;
46     private int state;
47     private int totalUnits;
48     private int currentUnit;
49     private long initialEstimate;
50     private long timeStarted;
51     private long timeLastProgress;
52     private long timeSleepy = 0;
53     private String JavaDoc lastMessage;
54     private final Cancellable cancelable;
55     private final Action JavaDoc viewAction;
56     private final boolean userInitiated;
57     private int initialDelay = Controller.INITIAL_DELAY;
58     private Controller controller;
59     private ExtractedProgressUIWorker component;
60     
61     public static final int STATE_INITIALIZED = 0;
62     public static final int STATE_RUNNING = 1;
63     public static final int STATE_FINISHED = 2;
64     public static final int STATE_REQUEST_STOP = 3;
65
66     public static final int NO_INCREASE = -2;
67     
68
69     
70     /** Creates a new instance of ProgressHandle */
71     public InternalHandle(String JavaDoc displayName,
72                    Cancellable cancel,
73                    boolean userInitiated,
74                    Action JavaDoc view) {
75         this.displayName = displayName;
76         this.userInitiated = userInitiated;
77         state = STATE_INITIALIZED;
78         totalUnits = 0;
79         lastMessage = null;
80         cancelable = cancel;
81         viewAction = view;
82     }
83
84     public String JavaDoc getDisplayName() {
85         return displayName;
86     }
87
88     /**
89      * XXX - called from UI, threading
90      */

91     public synchronized int getState() {
92         return state;
93     }
94     
95     public boolean isAllowCancel() {
96         return cancelable != null && !isCustomPlaced();
97     }
98     
99     public boolean isAllowView() {
100         return viewAction != null && !isCustomPlaced();
101     }
102     
103     
104     public boolean isCustomPlaced() {
105         return component != null;
106     }
107     
108     public boolean isUserInitialized() {
109         return userInitiated;
110     }
111     
112     private int getCurrentUnit() {
113         return currentUnit;
114     }
115     
116     public int getTotalUnits() {
117         return totalUnits;
118     }
119     
120     public void setInitialDelay(int millis) {
121         if (state != STATE_INITIALIZED) {
122             LOG.warning("Setting ProgressHandle.setInitialDelay() after the task is started has no effect"); //NOI18N
123
return;
124         }
125         initialDelay = millis;
126     }
127     
128     public int getInitialDelay() {
129         return initialDelay;
130     }
131     
132     public synchronized void toSilent(String JavaDoc message) {
133         if (state != STATE_RUNNING && state != STATE_REQUEST_STOP) {
134             assert false : "cannot switch to silent mode when not running";
135         }
136         timeLastProgress = System.currentTimeMillis();
137         timeSleepy = timeLastProgress;
138         if (message != null) {
139             lastMessage = message;
140         }
141         controller.toSilent(this, message);
142     }
143     
144     public boolean isInSleepMode() {
145         return timeSleepy == timeLastProgress;
146     }
147     
148     public synchronized void toIndeterminate() {
149         if (state != STATE_RUNNING && state != STATE_REQUEST_STOP) {
150             assert false : "cannot switch to indeterminate mode when not running: " + state;
151         }
152         totalUnits = 0;
153         currentUnit = 0;
154         initialEstimate = -1;
155         timeLastProgress = System.currentTimeMillis();
156         controller.toIndeterminate(this);
157     }
158     
159     public synchronized void toDeterminate(int workunits, long estimate) {
160         if (state != STATE_RUNNING && state != STATE_REQUEST_STOP) {
161             assert false : "cannot switch to determinate mode when not running";
162         }
163         if (workunits < 0) {
164             throw new IllegalArgumentException JavaDoc("number of workunits cannot be negative");
165         }
166         totalUnits = workunits;
167         currentUnit = 0;
168         initialEstimate = estimate;
169         timeLastProgress = System.currentTimeMillis();
170         controller.toDeterminate(this);
171     }
172     
173     /**
174      * start the progress indication for a task with known number of steps and known
175      * time estimate for completing the task.
176      *
177      * @param message
178      * @param workunits
179      * @param estimate estimated time to process the task in seconds
180      */

181     public synchronized void start(String JavaDoc message, int workunits, long estimate) {
182         if (state != STATE_INITIALIZED) {
183             throw new IllegalStateException JavaDoc("Cannot call start twice on a handle");
184         }
185         if (workunits < 0) {
186             throw new IllegalArgumentException JavaDoc("number of workunits cannot be negative");
187         }
188         totalUnits = workunits;
189         currentUnit = 0;
190         if (message != null) {
191             lastMessage = message;
192         }
193         if (controller == null) {
194             controller = Controller.getDefault();
195         }
196         state = STATE_RUNNING;
197         initialEstimate = estimate;
198         timeStarted = System.currentTimeMillis();
199         timeLastProgress = timeStarted;
200
201         
202         controller.start(this);
203     }
204
205     /**
206      * finish the task, remove the task's component from the progress bar UI.
207      */

208     public synchronized void finish() {
209         if (state == STATE_INITIALIZED) {
210             throw new IllegalStateException JavaDoc("Cannot finish not a started task");
211         }
212         if (state == STATE_FINISHED) {
213             return;
214         }
215         state = STATE_FINISHED;
216         currentUnit = totalUnits;
217         
218         controller.finish(this);
219     }
220     
221     
222     /**
223      *
224      * @param message
225      * @param workunit
226      */

227     public synchronized void progress(String JavaDoc message, int workunit) {
228         if (state != STATE_RUNNING && state != STATE_REQUEST_STOP) {
229             return;
230         }
231
232         if (workunit != NO_INCREASE) {
233             if (workunit < currentUnit) {
234                 throw new IllegalArgumentException JavaDoc("Cannot decrease processed workunit count (" + workunit + ") to lower value than before (" + currentUnit + ")");
235             }
236             if (workunit > totalUnits) {
237                 // seems to be the by far most frequently abused contract. Record it to log file and safely handle the case
238
//#96921 - WARNING -> INFO to prevent users reporting the problem automatically.
239
LOG.log(Level.INFO,
240                     "Cannot process more work than scheduled. " +
241                     "Progress handle with name \"" + getDisplayName() + "\" has requested progress to workunit no." + workunit +
242                     " but the total number of workunits is " + totalUnits + ". That means the progress bar UI will not display real progress and will stay at 100%.",
243                     new IllegalArgumentException JavaDoc()
244                 );
245                 workunit = totalUnits;
246             }
247             currentUnit = workunit;
248         }
249         if (message != null) {
250             lastMessage = message;
251         }
252         timeLastProgress = System.currentTimeMillis();
253         
254         controller.progress(this, message, currentUnit,
255                             totalUnits > 0 ? getPercentageDone() : -1,
256                             (initialEstimate == -1 ? -1 : calculateFinishEstimate()));
257     }
258     
259     
260   // XXX - called from UI, threading
261

262     public void requestCancel() {
263         if (!isAllowCancel()) {
264             return;
265         }
266         synchronized (this) {
267             state = STATE_REQUEST_STOP;
268         }
269         // do not call in synchronized block because it can take a long time to process,
270
/// and it could slow down UI.
271
//TODO - call in some other thread, not AWT? what is the cancel() contract?
272
cancelable.cancel();
273         synchronized (this) {
274             requestStateSnapshot();
275         }
276     }
277     
278    ///XXX - called from UI, threading
279
public void requestView() {
280         if (!isAllowView()) {
281             return;
282         }
283         viewAction.actionPerformed(new ActionEvent JavaDoc(viewAction, ActionEvent.ACTION_PERFORMED, "performView"));
284     }
285     
286    // XXX - called from UI, threading
287
public synchronized void requestExplicitSelection() {
288         if (!isInSleepMode()) {
289             timeLastProgress = System.currentTimeMillis();
290         }
291         controller.explicitSelection(this);
292     }
293     
294     public synchronized void requestDisplayNameChange(String JavaDoc newDisplayName) {
295         displayName = newDisplayName;
296         if (state == STATE_INITIALIZED) {
297             return;
298         }
299         timeLastProgress = System.currentTimeMillis();
300         controller.displayNameChange(this, currentUnit,
301                             totalUnits > 0 ? getPercentageDone() : -1,
302                             (initialEstimate == -1 ? -1 : calculateFinishEstimate()), newDisplayName);
303     }
304     
305 // XXX - called from UI, threading
306
public synchronized ProgressEvent requestStateSnapshot() {
307         if (!isInSleepMode()) {
308             timeLastProgress = System.currentTimeMillis();
309         }
310         return controller.snapshot(this, lastMessage, currentUnit,
311                             totalUnits > 0 ? getPercentageDone() : -1,
312                             (initialEstimate == -1 ? -1 : calculateFinishEstimate()));
313     }
314     
315     private void createExtractedWorker() {
316         if (component == null) {
317             ProgressUIWorkerProvider prov = Lookup.getDefault().lookup(ProgressUIWorkerProvider.class);
318             if (prov == null) {
319                 LOG.log(Level.CONFIG, "Using fallback trivial progress implementation");
320                 prov = new TrivialProgressUIWorkerProvider();
321             }
322             component = prov.getExtractedComponentWorker();
323             controller = new Controller(component);
324         }
325     }
326     /**
327      * have the component in custom location, don't include in the status bar.
328      */

329     public synchronized JComponent JavaDoc extractComponent() {
330         if (customPlaced1) {
331             throw new IllegalStateException JavaDoc("Cannot retrieve progress component multiple times");
332         }
333         if (state != STATE_INITIALIZED) {
334             throw new IllegalStateException JavaDoc("You can request custom placement of progress component only before starting the task");
335         }
336         customPlaced1 = true;
337         createExtractedWorker();
338         return component.getProgressComponent();
339     }
340     
341     public synchronized JLabel JavaDoc extractDetailLabel() {
342         if (customPlaced2) {
343             throw new IllegalStateException JavaDoc("Cannot retrieve progress detail label component multiple times");
344         }
345         if (state != STATE_INITIALIZED) {
346             throw new IllegalStateException JavaDoc("You can request custom placement of progress component only before starting the task");
347         }
348         customPlaced2 = true;
349         createExtractedWorker();
350         return component.getDetailLabelComponent();
351     }
352
353     public synchronized JLabel JavaDoc extractMainLabel() {
354         if (customPlaced3) {
355             throw new IllegalStateException JavaDoc("Cannot retrieve progress main label component multiple times");
356         }
357         if (state != STATE_INITIALIZED) {
358             throw new IllegalStateException JavaDoc("You can request custom placement of progress component only before starting the task");
359         }
360         customPlaced3 = true;
361         createExtractedWorker();
362         return component.getMainLabelComponent();
363     }
364
365     long calculateFinishEstimate() {
366         
367         // we are interested in seconds only
368
double durationSoFar = ((double)(System.currentTimeMillis() - timeStarted)) / 1000;
369         if (initialEstimate == -1) {
370             // we don't have an initial estimate, calculate by real-life data only
371
return (long)(durationSoFar * (totalUnits - currentUnit) / totalUnits);
372         } else {
373             // in the begining give the initial estimate more weight than in the end.
374
// should give us more smooth estimates
375
long remainingUnits = (totalUnits - currentUnit);
376             double remainingPortion = (double)remainingUnits / (double)totalUnits;
377             double currentEstimate = durationSoFar / (double)currentUnit * totalUnits;
378             long retValue = (long)(((initialEstimate * remainingUnits * remainingPortion)
379                          + (currentEstimate * remainingUnits * (1 - remainingPortion)))
380                        / totalUnits);
381             return retValue;
382         }
383     }
384     /**
385      *public because of tests.
386      */

387     public int getPercentageDone() {
388         return (int)((long)currentUnit * 100 / (long)totalUnits);
389     }
390
391     public long getTimeStampStarted() {
392         return timeStarted;
393     }
394
395
396
397 }
398
Popular Tags