001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.uml.util;
043:
044: import java.io.IOException;
045: import java.io.PrintWriter;
046: import java.util.HashMap;
047: import java.util.Vector;
048:
049: import org.openide.ErrorManager;
050: import org.openide.util.Cancellable;
051: import org.openide.util.NbBundle;
052: import org.openide.windows.IOProvider;
053: import org.openide.windows.InputOutput;
054: import org.openide.windows.TopComponent;
055: import org.openide.windows.WindowManager;
056:
057: import org.netbeans.api.progress.aggregate.AggregateProgressHandle;
058: import org.netbeans.api.progress.aggregate.AggregateProgressFactory;
059: import org.netbeans.api.progress.aggregate.ProgressContributor;
060:
061: import org.netbeans.modules.uml.core.metamodel.structure.IProject;
062:
063: /**
064: * author Craig Conover, craig.conover@sun.com
065: */
066: public abstract class AbstractNBTask extends Thread implements
067: Cancellable, ITaskSupervisor {
068: private AggregateProgressHandle progressHandle;
069: protected ProgressContributor[] progressContribs;
070:
071: protected boolean cancelled = false;
072: protected long start;
073: protected boolean success = true;
074: private InputOutput inputOutput;
075: private PrintWriter out;
076: private HashMap<String, Object> taskSettings = new HashMap<String, Object>();
077: private String timeMsg;
078: private int contribCount = -1;
079: private int counter = 0;
080: private int logLevel = SUMMARY;
081: private boolean logging = false;
082: private boolean finished = false;
083:
084: public final static String SETTING_KEY_TASK_NAME = "TASK_NAME"; // NOI18N
085: public final static String SETTING_KEY_TOTAL_ITEMS = "TOTAL_ITEMS"; // NOI18N
086: public final static String SETTING_KEY_DISPLAY_OUTPUT = "DISPLAY_OUTPUT"; // NOI18N
087:
088: public AbstractNBTask() {
089: initialize();
090: }
091:
092: public AbstractNBTask(ITaskFinishListener listener) {
093: initialize();
094: addListener(listener);
095: }
096:
097: public AbstractNBTask(HashMap settings) {
098: taskSettings = settings;
099: initialize();
100: }
101:
102: public AbstractNBTask(HashMap settings, ITaskFinishListener listener) {
103: taskSettings = settings;
104: initialize();
105: addListener(listener);
106: }
107:
108: public void run() {
109: if (progressContribs != null && progressContribs.length > 0) {
110: progressHandle = AggregateProgressFactory.createHandle(
111: getTaskName(), progressContribs, this , null);
112: }
113:
114: try {
115: beginTask();
116: }
117:
118: catch (Exception e) {
119: fail();
120: ErrorManager.getDefault().notify(e);
121: }
122:
123: finally {
124: finishTask();
125: progressHandle.finish();
126: }
127:
128: }
129:
130: public boolean cancel() {
131: cancelled = true;
132: return true;
133: }
134:
135: // methods that should be implemented or overriden by subclass
136: /////////////////////////////////////////////////////////////////////
137:
138: protected abstract void initTask();
139:
140: protected abstract void begin();
141:
142: protected abstract void finish();
143:
144: /**
145: * This method logs a "header" message. Override to replace this message
146: * with a custom header message and invoke super to prepend or append
147: * to this message.
148: */
149: protected void beginLog() {
150: String msg = getBundleMessage("MSG_Begin_Processing") // NOI18N
151: + " " + getTaskName(); // NOI18N
152:
153: if (getTotalItems() > 0)
154: msg += ": " + getTotalItems() + " " // NOI18N
155: + getBundleMessage("MSG_Items"); // NOI18N
156:
157: log(SUMMARY, msg);
158: log(SUMMARY);
159: }
160:
161: /**
162: * This method logs the finish status message (success, fail or cancel)
163: * and the run time stats. Override to replace this message
164: * with a custom header message and invoke super to prepend or append
165: * to this message.
166: */
167: protected void finishLog() {
168: // there are some cases where the task "finish" gets called more than
169: // once and we want to prevent the finish message from being displayed
170: // more than once.
171: if (finished)
172: return;
173:
174: finished = true;
175:
176: log(SUMMARY); // NOI18N
177: log(SUMMARY, "================================"); // NOI18N
178:
179: if (cancelled)
180: log(NbBundle.getMessage(AbstractNBTask.class,
181: "MSG_Report_Cancelled")
182: + " " + // NOI18N
183: NbBundle.getMessage(AbstractNBTask.class,
184: "MSG_TotalTime", timeMsg)); // NOI18N
185:
186: else if (success)
187: log(NbBundle.getMessage(AbstractNBTask.class,
188: "MSG_Report_Successful")
189: + " " + // NOI18N
190: NbBundle.getMessage(AbstractNBTask.class,
191: "MSG_TotalTime", timeMsg)); // NOI18N
192:
193: else
194: log(NbBundle.getMessage(AbstractNBTask.class,
195: "MSG_Report_Failed")
196: + " " + // NOI18N
197: NbBundle.getMessage(AbstractNBTask.class,
198: "MSG_TotalTime", timeMsg)); // NOI18N
199: }
200:
201: /**
202: * Provides tokenized defaults.
203: * Should be overriden by subclass or settings values
204: * passed in by calling class.
205: */
206: protected void initDefaultSettings() {
207: taskSettings.put(SETTING_KEY_TASK_NAME, "<"
208: + getBundleMessage("MSG_Default_Task_Name") + ">"); // NOI18N
209:
210: taskSettings.put(SETTING_KEY_TOTAL_ITEMS, new Integer(-1));
211: }
212:
213: // public methods that should be invoked by subclass
214: ////////////////////////////////////////////////////
215:
216: public boolean start(int totalItems) {
217: return start(++contribCount, totalItems);
218: }
219:
220: public boolean start(int contributor, int totalItems) {
221: if (contributor < 0
222: && contributor > progressContribs.length - 1) {
223: finishTask();
224: return false;
225: }
226:
227: if (contributor > 0)
228: progressContribs[contributor].finish();
229:
230: counter = 0;
231:
232: progressHandle.setDisplayName(getTaskName() + ": " + // NOI18N
233: progressContribs[contributor].getTrackingId());
234:
235: progressContribs[contributor].start(totalItems);
236:
237: return true;
238: }
239:
240: public int increment() {
241: return ++counter;
242: }
243:
244: public int increment(int step) {
245: counter += step;
246: return counter;
247: }
248:
249: /**
250: * Called by task subclass to check confirm that the task hasn't been
251: * cancelled or failed. If there is a cancellation or failure, finish()
252: * is called.
253: *
254: * @return true if the process hasn't failed or been canceled
255: */
256: public boolean proceed() {
257: return proceed(0);
258: }
259:
260: /**
261: * Called by task subclass to check confirm that the task hasn't been
262: * cancelled or failed. If there is a cancellation or failure, finish()
263: * is called.
264: *
265: * @param count the number of items processed so far; used for % done
266: * calculation for the NB progress bar
267: *
268: * @param step the amount to increment the counter by.
269: *
270: * @return true if the process hasn't failed or been canceled
271: */
272: public boolean proceed(int step) {
273: if (cancelled || !success) {
274: progressContribs[contribCount].finish();
275: finishTask();
276: return false;
277: }
278:
279: else if (counter > -1) {
280: if (step > 0)
281: increment(step);
282:
283: log(DEBUG, "count " + counter);
284: progressContribs[contribCount].progress(counter);
285: }
286:
287: return true;
288: }
289:
290: public void setLogLevel(int level) {
291: logLevel = level;
292: }
293:
294: public int getLogLevel() {
295: return logLevel;
296: }
297:
298: public boolean isLogging() {
299: return logging;
300: }
301:
302: public void setLogging(boolean val) {
303: logging = val;
304: }
305:
306: /**
307: * Outputs a blank line
308: */
309: public void log() {
310: log("", true);
311: }
312:
313: public void log(int level) {
314: if (logLevel < level)
315: return;
316:
317: log("", true);
318: }
319:
320: public void log(int level, String msg) {
321: if (logLevel < level)
322: return;
323:
324: log(msg, true);
325: }
326:
327: public void log(int level, String msg, boolean newline) {
328: if (logLevel < level)
329: return;
330:
331: log(msg, newline);
332: }
333:
334: /**
335: * Outputs a message with and appends newline by default
336: *
337: * @param msg the message to be output
338: */
339: public void log(String msg) {
340: log(msg, true);
341: }
342:
343: /**
344: * Outputs a message
345: *
346: * @param msg the message to be output
347: * @param newline if true, appends newline
348: */
349: public void log(String msg, boolean newline) {
350: if (newline)
351: out.println(msg);
352:
353: else
354: out.print(msg);
355:
356: out.flush();
357: }
358:
359: /**
360: * Call this method when a failure in your task is detected and it will
361: * set the success flag to false. The next time proceed() is called,
362: * it will invoke finish() return false.
363: */
364: public void fail() {
365: success = false;
366: }
367:
368: // settings methods
369:
370: public String getTaskName() {
371: return (String) getSetting(SETTING_KEY_TASK_NAME);
372: }
373:
374: public void setTaskName(String taskName) {
375: setSetting(SETTING_KEY_TASK_NAME, taskName);
376: }
377:
378: public int getTotalItems() {
379: Integer total = (Integer) getSetting(SETTING_KEY_TOTAL_ITEMS);
380:
381: if (total == null)
382: return -1;
383:
384: return total.intValue();
385: }
386:
387: public void setTotalItems(int total) {
388: setSetting(SETTING_KEY_TOTAL_ITEMS, new Integer(total));
389: }
390:
391: public boolean isDisplayOutput() {
392: Boolean show = (Boolean) getSetting(SETTING_KEY_DISPLAY_OUTPUT);
393:
394: if (show == null)
395: return true;
396:
397: return show.booleanValue();
398: }
399:
400: public void setDisplayOutput(boolean val) {
401: setSetting(SETTING_KEY_DISPLAY_OUTPUT, new Boolean(val));
402: }
403:
404: public Object getSetting(String key) {
405: return taskSettings.get(key);
406: }
407:
408: public void setSetting(String key, Object value) {
409: taskSettings.put(key, value);
410: }
411:
412: public void addListener(ITaskFinishListener listener) {
413: if (!listeners.contains(listener))
414: listeners.addElement(listener);
415: }
416:
417: public void removeListener(ITaskFinishListener listener) {
418: listeners.removeElement(listener);
419: }
420:
421: // private methods
422: //////////////////
423:
424: private void initialize() {
425: if (taskSettings == null)
426: initDefaultSettings();
427:
428: initTask();
429: initLog();
430: }
431:
432: private void initLog() {
433: TopComponent tc = WindowManager.getDefault().findTopComponent(
434: "output"); // NOI18N
435:
436: if (tc != null && isDisplayOutput()) // NOI18N
437: {
438: tc.open();
439: tc.requestActive();
440: tc.toFront();
441: }
442:
443: inputOutput = IOProvider.getDefault().getIO(
444: getTaskName() + " " + getBundleMessage("MSG_Log"),
445: false); // NOI18N
446:
447: try {
448: inputOutput.getOut().reset();
449: out = inputOutput.getOut();
450: }
451:
452: catch (IOException e) {
453: // TODO: ignore
454: }
455:
456: if (isDisplayOutput()) // NOI18N
457: {
458: inputOutput.select();
459: inputOutput.setOutputVisible(true);
460: }
461: }
462:
463: private void beginTask() {
464: if (getTotalItems() > -1)
465: progressHandle.start(getTotalItems());
466:
467: else
468: progressHandle.start();
469:
470: start = System.currentTimeMillis();
471:
472: beginLog();
473: begin();
474: }
475:
476: private void finishTask() {
477: progressContribs[progressContribs.length - 1].finish();
478: finish();
479:
480: long total = (System.currentTimeMillis() - start) / 1000;
481: int minutes = (int) total / 60;
482: int seconds = (int) (total - minutes * 60);
483:
484: timeMsg = minutes == 0 ? seconds + " "
485: + getBundleMessage("MSG_Seconds") // NOI18N
486: : minutes + " " + getBundleMessage("MSG_Minutes") + // NOI18N
487: " " + seconds + " " + getBundleMessage("MSG_Seconds"); // NOI18N
488:
489: finishLog();
490:
491: inputOutput.getOut().flush();
492: inputOutput.getOut().close();
493: out.close();
494: notifyTaskFinishListeners();
495: }
496:
497: // bundle message helper methods
498:
499: private String getBundleMessage(String key) {
500: return NbBundle.getMessage(AbstractNBTask.class, key);
501: }
502:
503: private String getBundleMessage(String key, Object[] params) {
504: return NbBundle.getMessage(AbstractNBTask.class, key, params);
505: }
506:
507: private String getBundleMessage(String key, Object param) {
508: return NbBundle.getMessage(AbstractNBTask.class, key, param);
509: }
510:
511: private String getBundleMessage(String key, Object param,
512: Object param0) {
513: return NbBundle.getMessage(AbstractNBTask.class, key, param,
514: param0);
515: }
516:
517: private String getBundleMessage(String key, Object param,
518: Object param0, Object param1) {
519: return NbBundle.getMessage(AbstractNBTask.class, key, param,
520: param0, param1);
521: }
522:
523: private Vector<ITaskFinishListener> listeners = new Vector<ITaskFinishListener>();
524:
525: private void notifyTaskFinishListeners() {
526: for (ITaskFinishListener listener : listeners)
527: listener.taskFinished();
528: }
529:
530: }
|