Source code: de/hunsicker/jalopy/plugin/AbstractPlugin.java
1 /*
2 * Copyright (c) 2001-2002, Marco Hunsicker. All rights reserved.
3 *
4 * This software is distributable under the BSD license. See the terms of the
5 * BSD license in the documentation provided with this software.
6 */
7 package de.hunsicker.jalopy.plugin;
8
9 import java.awt.AWTEvent;
10 import java.awt.BorderLayout;
11 import java.awt.Component;
12 import java.awt.Cursor;
13 import java.awt.EventQueue;
14 import java.awt.Frame;
15 import java.awt.Toolkit;
16 import java.awt.event.AWTEventListener;
17 import java.awt.event.KeyEvent;
18 import java.io.File;
19 import java.io.IOException;
20 import java.lang.reflect.InvocationTargetException;
21 import java.text.MessageFormat;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26
27 import javax.swing.JComponent;
28 import javax.swing.JDialog;
29 import javax.swing.JFrame;
30 import javax.swing.WindowConstants;
31
32 import de.hunsicker.io.FileFormat;
33 import de.hunsicker.jalopy.Jalopy;
34 import de.hunsicker.jalopy.language.Position;
35 import de.hunsicker.jalopy.storage.Convention;
36 import de.hunsicker.jalopy.storage.ConventionDefaults;
37 import de.hunsicker.jalopy.storage.ConventionKeys;
38 import de.hunsicker.jalopy.storage.History;
39 import de.hunsicker.jalopy.storage.Loggers;
40 import de.hunsicker.jalopy.swing.ProgressMonitor;
41 import de.hunsicker.jalopy.swing.ProgressPanel;
42 import de.hunsicker.swing.ErrorDialog;
43 import de.hunsicker.swing.util.SwingWorker;
44 import de.hunsicker.util.ChainingRuntimeException;
45 import de.hunsicker.util.ResourceBundleFactory;
46
47 import org.apache.log4j.ConsoleAppender;
48 import org.apache.log4j.Level;
49 import org.apache.log4j.PatternLayout;
50
51
52 //J- needed only as a workaround for a Javadoc bug
53 import java.lang.System;
54 import javax.swing.SwingUtilities;
55 //J+
56
57 /**
58 * Skeleton implementation of a Jalopy Plug-in for integrated development environments.
59 *
60 * @author <a href="http://jalopy.sf.net/contact.html">Marco Hunsicker</a>
61 * @version $Revision: 1.8 $
62 */
63 public abstract class AbstractPlugin
64 {
65 //~ Static variables/initializers ----------------------------------------------------
66
67 private static final String EMPTY_STRING = "" /* NOI18N */.intern();
68
69 /** The default status bar does nothing. */
70 private static final StatusBar DEFAULT_STATUS_BAR = new DummyStatusBar();
71
72 /** Cursor to display whilst long-running operations. */
73 private static final Cursor WAIT_CURSOR =
74 Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
75
76 /** The name for ResourceBundle lookup. */
77 private static final String BUNDLE_NAME =
78 "de.hunsicker.jalopy.plugin.Bundle" /* NOI18N */;
79
80 //~ Instance variables ---------------------------------------------------------------
81
82 /** The main Jalopy instance. */
83 protected Jalopy jalopy;
84
85 /** Appender to write messages to. */
86 protected SwingAppender appender;
87 int offset = 0;
88
89 /** The action that was last performed. */
90 private Action _lastAction;
91
92 /** Holds the currently running action worker. */
93 private ActionWorker _worker;
94
95 /** Stores the original glass pane. */
96 private Component _oldGlassPane;
97
98 /** Pane to display on top of all other components. */
99 private GlassPane _glassPane;
100
101 /** Used as a monitor to synchronize processes. */
102 private final Object _lock = new Object();
103
104 /** Progress monitor for long running operations. */
105 private ProgressMonitor _progressMonitor;
106 private final Object[] _args = new Object[3];
107
108 /** Number of running formatting threads. */
109 private int _threadCount;
110
111 /** When did the worker thread start? */
112 private long _start;
113
114 //~ Constructors ---------------------------------------------------------------------
115
116 /**
117 * Creates a new AbstractPlugin object. Uses a default appender which outputs all
118 * messages to <code>System.out</code>.
119 */
120 public AbstractPlugin()
121 {
122 this(new DefaultAppender());
123 }
124
125
126 /**
127 * Creates a new AbstractPlugin object.
128 *
129 * @param appender appender to use for logging; if <code>null</code> all logging
130 * output goes to <code>System.out</code>.
131 */
132 public AbstractPlugin(SwingAppender appender)
133 {
134 this.appender = appender;
135 initLogging();
136 }
137
138 //~ Methods --------------------------------------------------------------------------
139
140 /**
141 * Returns the currently active project.
142 *
143 * @return the active user project.
144 */
145 public abstract Project getActiveProject();
146
147
148 /**
149 * Returns the main window of the application.
150 *
151 * @return the main application window.
152 */
153 public abstract Frame getMainWindow();
154
155
156 /**
157 * Returns the elapsed execution time of the run.
158 *
159 * @return the elapsed execution time.
160 */
161 public long getElapsed()
162 {
163 return System.currentTimeMillis() - _start;
164 }
165
166
167 /**
168 * Returns the action that was performed last.
169 *
170 * @return Last performed action. Returns <code>null</code> if no action was ever
171 * performed.
172 */
173 public final Action getLastAction()
174 {
175 return _lastAction;
176 }
177
178
179 /**
180 * Returns the state info of Plug-in. Use this method to query the state after a run
181 * finished.
182 *
183 * @return The run state.
184 *
185 * @see de.hunsicker.jalopy.Jalopy#getState
186 */
187 public final synchronized Jalopy.State getState()
188 {
189 if (this.jalopy == null)
190 {
191 return Jalopy.State.UNDEFINED;
192 }
193
194 return this.jalopy.getState();
195 }
196
197
198 /**
199 * Determines whether the Plug-in currently processes a request.
200 *
201 * @return <code>true</code> if an action is currently being performed.
202 *
203 * @see #performAction
204 * @see #interrupt
205 * @since 1.0b8
206 */
207 public synchronized boolean isRunning()
208 {
209 return _worker != null;
210 }
211
212
213 /**
214 * Returns the active status bar. Override to provide access to the status bar of the
215 * used application.
216 *
217 * @return the active status bar. The default implementation returns a dummy.
218 */
219 public StatusBar getStatusBar()
220 {
221 return DEFAULT_STATUS_BAR;
222 }
223
224
225 /**
226 * Called on the event dispatching thread after an action was performed.
227 *
228 * <p>
229 * Override this method to perform any custom work after the formatting process
230 * finished.
231 * </p>
232 */
233 public void afterEnd()
234 {
235 }
236
237
238 /**
239 * Called on the event dispatching thread before an action will be started.
240 *
241 * <p>
242 * Override this method to perform any custom work before the formatting process
243 * starts.
244 * </p>
245 */
246 public void beforeStart()
247 {
248 }
249
250
251 /**
252 * Interrupts the currently performed action, if any.
253 *
254 * @since 1.0b8
255 */
256 public final synchronized void interrupt()
257 {
258 if (_worker != null)
259 {
260 _worker.interrupt();
261 }
262 }
263
264
265 /**
266 * Performs the given action.
267 *
268 * @param action action to perform.
269 */
270 public final synchronized void performAction(final AbstractPlugin.Action action)
271 {
272 // clear the message window
273 this.appender.clear();
274
275 _worker = new ActionWorker(action);
276 _worker.start();
277
278 _lastAction = action;
279 }
280
281
282 /**
283 * Returns the file format to use for writing Java source files.
284 *
285 * @return the file format to use.
286 */
287 protected abstract FileFormat getFileFormat();
288
289
290 /**
291 * Returns a Jalopy instance. The instance will be configured according to the
292 * current code convention.
293 *
294 * @return a Jalopy instance.
295 *
296 * @since 1.0b8
297 */
298 protected Jalopy getEngine()
299 {
300 if (this.jalopy == null)
301 {
302 if (_progressMonitor != null)
303 {
304 _progressMonitor.setText(
305 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
306 "MSG_INITIALIZATION" /* NOI18N */));
307 }
308
309 getStatusBar().setText(
310 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
311 "MSG_INITIALIZATION" /* NOI18N */));
312 this.jalopy = new Jalopy();
313 }
314
315 configureJalopy(this.jalopy);
316
317 return this.jalopy;
318 }
319
320
321 /**
322 * Creates a monitor to be used for long-running operations.
323 *
324 * @return the progress monitor to use for long-running operations.
325 */
326 protected ProgressMonitor createProgressMonitor()
327 {
328 return new ProgressMonitorImpl();
329 }
330
331
332 /**
333 * Displays an error dialog.
334 *
335 * @param error the throwable which caused the error.
336 * @param parent parent frame of the dialog (used to position the dialog).
337 */
338 protected void displayError(
339 Throwable error,
340 Frame parent)
341 {
342 ErrorDialog d = ErrorDialog.create(getMainWindow(), error);
343 d.setVisible(true);
344 d.dispose();
345 }
346
347
348 /**
349 * Executes the given runnable asynchronously on the AWT event dispatching thread.
350 *
351 * @param operation runnable to be invoked asynchronously on the AWT event
352 * dispatching thread.
353 *
354 * @see javax.swing.SwingUtilities#invokeLater
355 */
356 protected void executeAsynchron(Runnable operation)
357 {
358 EventQueue.invokeLater(operation);
359 }
360
361
362 /**
363 * Executes the given runnable synchronously on the AWT event dispatching thread.
364 *
365 * @param operation runnable to be invoked synchronously on the AWT event dispatching
366 * thread.
367 *
368 * @throws InterruptedException if another thread has interrupted this thread.
369 * @throws InvocationTargetException if an exception is thrown when running runnable.
370 *
371 * @see javax.swing.SwingUtilities#invokeAndWait
372 */
373 protected void executeSynchron(Runnable operation)
374 throws InterruptedException, InvocationTargetException
375 {
376 EventQueue.invokeAndWait(operation);
377 }
378
379
380 /**
381 * Hides the wait cursor.
382 */
383 protected void hideWaitCursor()
384 {
385 if ((_glassPane != null) && _glassPane.isVisible())
386 {
387 // hiding the glass pane restores the normal cursor too
388 _glassPane.setVisible(false);
389
390 Frame window = getMainWindow();
391
392 if ((window != null) && window instanceof JFrame)
393 {
394 JFrame w = (JFrame) window;
395
396 // restore the original glass pane
397 w.getRootPane().setGlassPane(_oldGlassPane);
398 }
399 }
400 }
401
402
403 /**
404 * Shows the wait cursor to indicate a long-running operation. Keyboard and mouse
405 * input will be blocked.
406 */
407 protected void showWaitCursor()
408 {
409 final Frame window = getMainWindow();
410
411 if ((window != null) && window instanceof JFrame)
412 {
413 if (_glassPane == null)
414 {
415 _glassPane = new GlassPane();
416 }
417
418 _glassPane.setThread(Thread.currentThread());
419
420 try
421 {
422 executeSynchron(
423 new Runnable()
424 {
425 public void run()
426 {
427 // make the glass pane visible so that all mouse and key
428 // actions will be blocked and a wait cursor displayed;
429 // the pane will be made hidden in the afterEnd() method that
430 // is always called after the run has finished
431 JFrame w = (JFrame) window;
432 _oldGlassPane = w.getRootPane().getGlassPane();
433 w.getRootPane().setGlassPane(_glassPane);
434 _glassPane.setCursor(WAIT_CURSOR);
435 _glassPane.setVisible(true);
436 }
437 });
438 }
439 catch (Throwable ignored)
440 {
441 hideWaitCursor();
442 }
443 }
444 }
445
446
447 /**
448 * Configures the given Jalopy instance to meet the current code convention.
449 *
450 * @param jalopy Jalopy instance to configure.
451 */
452 private void configureJalopy(Jalopy jalopy)
453 {
454 Convention settings = Convention.getInstance();
455 int backupLevel =
456 settings.getInt(ConventionKeys.BACKUP_LEVEL, ConventionDefaults.BACKUP_LEVEL);
457 jalopy.setBackup(backupLevel > 0);
458 jalopy.setBackupDirectory(
459 settings.get(
460 ConventionKeys.BACKUP_DIRECTORY,
461 Convention.getBackupDirectory().getAbsolutePath()));
462 jalopy.setHistoryPolicy(
463 History.Policy.valueOf(
464 settings.get(
465 ConventionKeys.HISTORY_POLICY, ConventionDefaults.HISTORY_POLICY)));
466 jalopy.setHistoryMethod(
467 History.Method.valueOf(
468 settings.get(
469 ConventionKeys.HISTORY_METHOD, ConventionDefaults.HISTORY_METHOD)));
470 jalopy.setInspect(
471 settings.getBoolean(ConventionKeys.INSPECTOR, ConventionDefaults.INSPECTOR));
472 jalopy.setBackupLevel(backupLevel);
473 jalopy.setFileFormat(getFileFormat());
474 jalopy.setForce(
475 settings.getBoolean(
476 ConventionKeys.FORCE_FORMATTING, ConventionDefaults.FORCE_FORMATTING));
477 }
478
479
480 /**
481 * Formats the given file. This method performs the actual work.
482 *
483 * @param file Java source file.
484 * @param jalopy Jalopy instance to use.
485 *
486 * @throws IOException if an I/O error occured.
487 * @throws InvocationTargetException if the updating of an editor window failed.
488 */
489 private void format(
490 ProjectFile file,
491 final Jalopy jalopy)
492 throws IOException, InvocationTargetException
493 {
494 jalopy.setEncoding(file.getEncoding());
495
496 if (_progressMonitor != null)
497 {
498 _args[0] = file.getName();
499
500 _progressMonitor.setText(
501 MessageFormat.format(
502 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
503 "MSG_FORMATTING_FILE" /* NOI18N */), _args));
504
505 if (_progressMonitor instanceof ProgressMonitorImpl)
506 {
507 ((ProgressMonitorImpl) _progressMonitor).progressPanel.increaseFiles();
508 }
509 }
510
511 // only update the file if available
512 if (!file.isReadOnly())
513 {
514 getStatusBar().setText(
515 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
516 "MSG_FORMATTING" /* NOI18N */));
517
518 if (file.isOpened()) // we're interacting with an editor view
519 {
520 final Editor editor = file.getEditor();
521
522 String content = editor.getText();
523
524 if ((content != null) && (content.length() > 0))
525 {
526 jalopy.setInput(content, file.getFile().getAbsolutePath());
527
528 List annotations = editor.detachAnnotations();
529 jalopy.getRecognizer().attachAnnotations(annotations);
530
531 try
532 {
533 executeSynchron(
534 new Runnable()
535 {
536 public void run()
537 {
538 jalopy.getRecognizer().setPosition(
539 editor.getLine(), editor.getColumn());
540 }
541 });
542 }
543 catch (InterruptedException ex)
544 {
545 ;
546 }
547
548 final StringBuffer textBuf = new StringBuffer(content.length());
549 jalopy.setOutput(textBuf);
550 jalopy.format();
551
552 if ((_progressMonitor != null) && _progressMonitor.isCanceled())
553 {
554 jalopy.getRecognizer().detachAnnotations();
555
556 return;
557 }
558
559 // only update the editor view if no errors showed up
560 if (getState() != Jalopy.State.ERROR)
561 {
562 try
563 {
564 executeSynchron(
565 new Runnable()
566 {
567 public void run()
568 {
569 editor.setText(textBuf.toString());
570 editor.attachAnnotations(
571 jalopy.getRecognizer().detachAnnotations());
572
573 Position position =
574 jalopy.getRecognizer().getPosition();
575 editor.setCaretPosition(
576 position.getLine(), position.getColumn());
577 }
578 });
579 }
580 catch (InterruptedException ex)
581 {
582 ;
583 }
584 }
585 }
586 }
587 else // update the physical file
588 {
589 File f = file.getFile();
590 jalopy.setInput(f);
591 jalopy.setOutput(f);
592 jalopy.format();
593 }
594 }
595 else
596 {
597 Object[] args = { file };
598 Loggers.IO.l7dlog(Level.INFO, "FILE_READ_ONLY" /* NOI18N */, args, null);
599 }
600 }
601
602
603 /**
604 * Formats the given files.
605 *
606 * @param jalopy the Jalopy instance to use for formatting.
607 * @param files list with the files to format.
608 *
609 * @throws IOException if an I/O error occured.
610 * @throws InvocationTargetException if the updating of an editor window failed.
611 */
612 private void formatSeveral(
613 Jalopy jalopy,
614 Collection files)
615 throws IOException, InvocationTargetException
616 {
617 formatSeveral(jalopy, files, true);
618 }
619
620
621 /**
622 * Formats the given files.
623 *
624 * @param jalopy the Jalopy instance to use for formatting.
625 * @param files list with the files to format.
626 * @param checkThreading if <code>true</code> checks whether the user enabled the
627 * multi-threaded execution and if so, uses multiple threads to perform the
628 * operation.
629 *
630 * @throws IOException if an I/O error occured.
631 * @throws InvocationTargetException if the updating of an editor window failed.
632 */
633 private void formatSeveral(
634 final Jalopy jalopy,
635 final Collection files,
636 final boolean checkThreading)
637 throws IOException, InvocationTargetException
638 {
639 final int size = files.size();
640
641 if (size > 0)
642 {
643 synchronized (this)
644 {
645 if (_progressMonitor == null)
646 {
647 _progressMonitor = createProgressMonitor();
648
649 _progressMonitor.begin(
650 ((jalopy == null)
651 ? ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
652 "MSG_INITIALIZATION" /* NOI18N */)
653 : EMPTY_STRING), files.size());
654 }
655 }
656
657 int numThreads =
658 Convention.getInstance().getInt(
659 ConventionKeys.THREAD_COUNT, ConventionDefaults.THREAD_COUNT);
660
661 if (!checkThreading || (numThreads == 1) || (size == 1))
662 {
663 for (Iterator i = files.iterator(); i.hasNext();)
664 {
665 // the user canceled the program execution
666 if (_progressMonitor.isCanceled())
667 {
668 return;
669 }
670
671 ProjectFile file = (ProjectFile) i.next();
672 format(file, jalopy);
673
674 synchronized (_progressMonitor)
675 {
676 _progressMonitor.setProgress(_progressMonitor.getProgress() + 1);
677 }
678 }
679 }
680 else
681 {
682 List workList =
683 (files instanceof List) ? (List) files
684 : new ArrayList(files);
685 int amount = 1;
686
687 if (numThreads < size)
688 {
689 amount = size / numThreads;
690 }
691 else
692 {
693 numThreads = size;
694 }
695
696 // the number of threads to span
697 _threadCount = numThreads - 1;
698
699 for (int i = 0, threshold = numThreads - 1; i < threshold; i++)
700 {
701 Jalopy j = new Jalopy();
702 configureJalopy(j);
703
704 Collection part = workList.subList(i * amount, (i + 1) * amount);
705 new FormatThread(j, part).start();
706 }
707
708 Collection rest = workList.subList((numThreads - 1) * amount, size);
709 formatSeveral(jalopy, rest, false);
710
711 try
712 {
713 synchronized (_lock)
714 {
715 while (_threadCount > 0)
716 {
717 _lock.wait();
718 }
719 }
720 }
721 catch (InterruptedException ignored)
722 {
723 ;
724 }
725 }
726 }
727 }
728
729
730 private synchronized void hideProgressMonitor()
731 {
732 if (_progressMonitor != null)
733 {
734 _progressMonitor.done();
735 _progressMonitor = null;
736 }
737 }
738
739
740 /**
741 * Initializes the logging system. This method is called upon the creation of the
742 * object.
743 */
744 private void initLogging()
745 {
746 // if no appender was specified, we output all messages to System.out
747 if (this.appender == null)
748 {
749 this.appender = new DefaultAppender();
750 }
751
752 Loggers.initialize(this.appender);
753 }
754
755 //~ Inner Classes --------------------------------------------------------------------
756
757 /**
758 * Represents an action that can be performed.
759 *
760 * @see AbstractPlugin#performAction
761 * @since 1.0b8
762 */
763 public static final class Action
764 {
765 /** Indicates that no action was ever performed. */
766 public static final Action UNDEFINED = new Action("undefined" /* NOI18N */);
767
768 /** Format the currently active (opened) file. */
769 public static final Action FORMAT_ACTIVE =
770 new Action("format_active" /* NOI18N */);
771
772 /** Format all Java Source files of the currently active project. */
773 public static final Action FORMAT_ALL = new Action("format_all" /* NOI18N */);
774
775 /** Format all currently opened Java source files. */
776 public static final Action FORMAT_OPEN = new Action("format_open" /* NOI18N */);
777
778 /** Format the selected Java source file(s). */
779 public static final Action FORMAT_SELECTED =
780 new Action("format_selected" /* NOI18N */);
781
782 /** Parse the currently active (opened) file. */
783 public static final Action PARSE_ACTIVE = new Action("parse_active" /* NOI18N */);
784
785 /** Parse all Java Source files of the currently active project. */
786 public static final Action PARSE_ALL = new Action("parse_all" /* NOI18N */);
787
788 /** Parse all currently opened Java source files. */
789 public static final Action PARSE_OPEN = new Action("parse_open" /* NOI18N */);
790
791 /** Parse the selected Java source file(s). */
792 public static final Action PARSE_SELECTED =
793 new Action("parse_selected" /* NOI18N */);
794
795 /** Inspect the currently active (opened) file. */
796 public static final Action INSPECT_ACTIVE =
797 new Action("inspect_active" /* NOI18N */);
798
799 /** Inspect all Java Source files of the currently active project. */
800 public static final Action INSPECT_ALL = new Action("inspect_all" /* NOI18N */);
801
802 /** Inspect all currently opened Java source files. */
803 public static final Action INSPECT_OPEN = new Action("inspect_open" /* NOI18N */);
804
805 /** Inspect the selected Java source file(s). */
806 public static final Action INSPECT_SELECTED =
807 new Action("inspect_selected" /* NOI18N */);
808 final String name;
809
810 private Action(String name)
811 {
812 this.name = name.intern();
813 }
814 }
815
816
817 /**
818 * Default appender to use if no custom appender was specified. All output goes to
819 * System.out.
820 */
821 private static class DefaultAppender
822 extends ConsoleAppender
823 implements SwingAppender
824 {
825 /**
826 * DOCUMENT ME!
827 *
828 * @todo overide format() to add stacktraces
829 */
830 public DefaultAppender()
831 {
832 super(new PatternLayout("[%p] %m\n" /* NOI18N */), "System.out" /* NOI18N */);
833 }
834
835 public void clear()
836 {
837 }
838
839
840 public void done()
841 {
842 }
843 }
844
845
846 private static final class DummyStatusBar
847 implements StatusBar
848 {
849 public void setText(String text)
850 {
851 }
852 }
853
854
855 /**
856 * Worker to perform our actions in a dedicated thread.
857 */
858 private class ActionWorker
859 extends SwingWorker
860 {
861 /** The action to perform. */
862 Action action;
863
864 /**
865 * Stores the initial position of the caret in case we're formatting an opened
866 * file.
867 */
868 int offset;
869
870 public ActionWorker(Action action)
871 {
872 this.action = action;
873 }
874
875 public Object construct()
876 {
877 _start = System.currentTimeMillis();
878
879 try
880 {
881 if (this.action == Action.FORMAT_ACTIVE)
882 {
883 // wait cursor indicates running operation
884 showWaitCursor();
885 beforeStart();
886
887 ProjectFile activeFile = getActiveProject().getActiveFile();
888
889 //final Editor editor = activeFile.getEditor();
890 // store the current offset to reposition the caret
891 // (synchronization needed to make Eclipse happy)
892
893 /*executeSynchron(
894 new Runnable()
895 {
896 public void run()
897 {
898 offset = editor.getCaretPosition();
899 }
900 });*/
901 Jalopy jalopy = getEngine();
902 format(activeFile, jalopy);
903
904 // only change if no errors showed up
905 /*if (getState() != Jalopy.State.ERROR)
906 {
907 // move the cursor
908
909 **
910 * @todo this could be improved. Determine the location prior
911 * formatting (relative to the next known node) and set
912 * the cursor to that position after formatting; quite
913 * involved, but possible (and only necessary if in
914 * sorting mode)
915 *
916 executeSynchron(
917 new Runnable()
918 {
919 public void run()
920 {
921 editor.requestFocus();
922
923 if (editor.getLength() > offset)
924 {
925 editor.setCaretPosition(offset);
926 }
927 }
928 });
929 }*/
930 jalopy.cleanupBackupDirectory();
931 }
932 else if (this.action == Action.FORMAT_ALL)
933 {
934 beforeStart();
935
936 Jalopy jalopy = getEngine();
937 formatSeveral(jalopy, getActiveProject().getAllFiles());
938 jalopy.cleanupBackupDirectory();
939 }
940 else if (this.action == Action.FORMAT_SELECTED)
941 {
942 beforeStart();
943
944 Jalopy jalopy = getEngine();
945 formatSeveral(jalopy, getActiveProject().getSelectedFiles());
946 jalopy.cleanupBackupDirectory();
947 }
948 else if (this.action == Action.FORMAT_OPEN)
949 {
950 beforeStart();
951
952 Jalopy jalopy = getEngine();
953 formatSeveral(jalopy, getActiveProject().getOpenedFiles());
954 jalopy.cleanupBackupDirectory();
955 }
956 }
957
958 /*catch (InterruptedException ex)
959 {
960 hideProgressMonitor();
961 notifyAll();
962 }*/
963 catch (Throwable ex)
964 {
965 hideProgressMonitor();
966 displayError(ex, getMainWindow());
967 notifyAll();
968 }
969
970 return null;
971 }
972
973
974 /**
975 * Updates the status bar after a run has finished.
976 *
977 * @see #getStatusBar
978 */
979 public void finished()
980 {
981 try
982 {
983 if (_lastAction == Action.FORMAT_ACTIVE)
984 {
985 hideWaitCursor();
986 }
987 else
988 {
989 hideProgressMonitor();
990 }
991
992 StatusBar statusBar = getStatusBar();
993
994 if (statusBar != null)
995 {
996 _args[0] =
997 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
998 (getState() == Jalopy.State.ERROR)
999 ? "MSG_FORMAT_FAILED" /* NOI18N */
1000 : "MSG_FORMAT_SUCCEEDED" /* NOI18N */);
1001
1002 long time = getElapsed();
1003
1004 if (time > 999)
1005 {
1006 time /= 1000;
1007 _args[2] =
1008 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
1009 (time == 1) ? "MSG_SECOND" /* NOI18N */
1010 : "MSG_SECONDS" /* NOI18N */);
1011 }
1012 else
1013 {
1014 _args[2] =
1015 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
1016 "MSG_MILLI_SECONDS" /* NOI18N */);
1017 }
1018
1019 _args[1] = String.valueOf(time);
1020
1021 statusBar.setText(
1022 MessageFormat.format(
1023 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
1024 "MSG_FORMAT_FINISHED" /* NOI18N */), _args));
1025 }
1026
1027 AbstractPlugin.this.appender.done();
1028
1029 // call the user hook
1030 AbstractPlugin.this.afterEnd();
1031 }
1032 finally
1033 {
1034 _worker = null;
1035 }
1036 }
1037 }
1038
1039
1040 /**
1041 * Helper to format several files in a dedicated thread.
1042 */
1043 private class FormatThread
1044 extends Thread
1045 {
1046 Collection files; // Collection of <ProjectFile>
1047 Jalopy jalopy;
1048
1049 public FormatThread(
1050 Jalopy jalopy,
1051 Collection files)
1052 {
1053 this.files = files;
1054 this.jalopy = jalopy;
1055 }
1056
1057 public void run()
1058 {
1059 try
1060 {
1061 formatSeveral(jalopy, files, false);
1062 }
1063 catch (Exception ex)
1064 {
1065 throw new ChainingRuntimeException(ex);
1066 }
1067 finally
1068 {
1069 synchronized (_lock)
1070 {
1071 _threadCount--;
1072 _lock.notify();
1073 }
1074 }
1075 }
1076 }
1077
1078
1079 /**
1080 * GlassPane used to block input whilst formatting the currently active file.
1081 */
1082 private class GlassPane
1083 extends JComponent
1084 implements AWTEventListener
1085 {
1086 Thread thread;
1087
1088 public void setThread(Thread thread)
1089 {
1090 this.thread = thread;
1091 }
1092
1093
1094 public void setVisible(boolean visible)
1095 {
1096 super.setVisible(visible);
1097
1098 if (visible)
1099 {
1100 Toolkit.getDefaultToolkit().addAWTEventListener(
1101 this,
1102 AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK
1103 | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.TEXT_EVENT_MASK
1104 | AWTEvent.INPUT_METHOD_EVENT_MASK);
1105 }
1106 else
1107 {
1108 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
1109 }
1110 }
1111
1112
1113 public void eventDispatched(AWTEvent ev)
1114 {
1115 if (ev instanceof KeyEvent)
1116 {
1117 KeyEvent e = (KeyEvent) ev;
1118
1119 switch (e.getKeyCode())
1120 {
1121 case KeyEvent.VK_ESCAPE :
1122 interrupt();
1123
1124 break;
1125
1126 case KeyEvent.VK_C :
1127
1128 if (e.isControlDown())
1129 {
1130 interrupt();
1131 }
1132
1133 break;
1134
1135 default :
1136 e.consume();
1137
1138 break;
1139 }
1140 }
1141 }
1142 }
1143
1144
1145 /**
1146 * A concrete progress monitor implemenation for Swing-based applications.
1147 */
1148 private final class ProgressMonitorImpl
1149 implements ProgressMonitor
1150 {
1151 JDialog dialog;
1152 ProgressPanel progressPanel;
1153 boolean running;
1154 int progress;
1155
1156 public ProgressMonitorImpl()
1157 {
1158 this.progressPanel = new ProgressPanel();
1159 this.progressPanel.setProgressBarVisible(true);
1160 }
1161
1162 public synchronized void setCanceled(boolean state)
1163 {
1164 }
1165
1166
1167 public synchronized boolean isCanceled()
1168 {
1169 return this.progressPanel.isCanceled();
1170 }
1171
1172
1173 public synchronized void setProgress(int units)
1174 {
1175 if (this.running)
1176 {
1177 this.progress = units;
1178 this.progressPanel.setValue(units);
1179 }
1180 }
1181
1182
1183 public synchronized int getProgress()
1184 {
1185 return this.progress;
1186 }
1187
1188
1189 public synchronized void setText(String text)
1190 {
1191 if (this.running)
1192 {
1193 this.progressPanel.setText(text);
1194 }
1195 }
1196
1197
1198 public synchronized void begin(
1199 String text,
1200 int units)
1201 {
1202 if (!this.running)
1203 {
1204 dialog =
1205 new JDialog(
1206 getMainWindow(),
1207 ResourceBundleFactory.getBundle(BUNDLE_NAME).getString(
1208 "TLE_FORMAT_PROGRESS" /* NOI18N */), true);
1209 dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
1210 dialog.getContentPane().add(this.progressPanel, BorderLayout.CENTER);
1211 dialog.pack();
1212 dialog.setLocationRelativeTo(getMainWindow());
1213
1214 this.progressPanel.setText(text);
1215 this.progressPanel.setMaximum(units);
1216
1217 executeAsynchron(
1218 new Runnable()
1219 {
1220 public void run()
1221 {
1222 running = true;
1223 dialog.setVisible(true);
1224 }
1225 });
1226 }
1227 }
1228
1229
1230 public synchronized void done()
1231 {
1232 if (this.running)
1233 {
1234 try
1235 {
1236 executeSynchron(
1237 new Runnable()
1238 {
1239 public void run()
1240 {
1241 progressPanel.setValue(progressPanel.getMaximum());
1242 }
1243 });
1244 }
1245 catch (Throwable ignored)
1246 {
1247 ;
1248 }
1249
1250 this.dialog.setVisible(false);
1251 this.running = false;
1252 this.progressPanel.dispose();
1253 this.dialog.dispose();
1254 this.progressPanel = null;
1255 }
1256 }
1257 }
1258}