KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > NotifyExcPanel


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 package org.netbeans.core;
21
22 import java.awt.BorderLayout JavaDoc;
23 import java.awt.Component JavaDoc;
24 import java.awt.Dialog JavaDoc;
25 import java.awt.Dimension JavaDoc;
26 import java.awt.Font JavaDoc;
27 import java.awt.GraphicsEnvironment JavaDoc;
28 import java.awt.Rectangle JavaDoc;
29 import java.awt.Window JavaDoc;
30 import java.awt.event.ActionListener JavaDoc;
31 import java.io.PrintWriter JavaDoc;
32 import java.io.StringWriter JavaDoc;
33 import java.lang.reflect.ParameterizedType JavaDoc;
34 import java.lang.reflect.Type JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.ResourceBundle JavaDoc;
37 import java.util.concurrent.Callable JavaDoc;
38 import java.util.logging.Handler JavaDoc;
39 import java.util.logging.Level JavaDoc;
40 import java.util.logging.Logger JavaDoc;
41 import javax.swing.Icon JavaDoc;
42 import javax.swing.ImageIcon JavaDoc;
43 import javax.swing.JButton JavaDoc;
44 import javax.swing.JDialog JavaDoc;
45 import javax.swing.JPanel JavaDoc;
46 import javax.swing.JScrollPane JavaDoc;
47 import javax.swing.JTextPane JavaDoc;
48 import javax.swing.SwingUtilities JavaDoc;
49 import javax.swing.UIManager JavaDoc;
50 import org.netbeans.core.startup.CLIOptions;
51 import org.openide.DialogDescriptor;
52 import org.openide.DialogDisplayer;
53 import org.openide.NotifyDescriptor;
54 import org.openide.awt.Mnemonics;
55 import org.openide.util.Exceptions;
56 import org.openide.util.NbBundle;
57 import org.openide.util.Utilities;
58 import org.openide.windows.WindowManager;
59
60 /**
61  * Notifies exceptions.
62  *
63  * This class is public only because the MainWindow needs get the flashing
64  * icon to its status bar from this class (method getNotificationVisualizer()).
65  *
66  * @author Jaroslav Tulach
67  */

68 public final class NotifyExcPanel extends JPanel JavaDoc implements ActionListener JavaDoc {
69     static final long serialVersionUID =3680397500573480127L;
70
71
72     /** the instance */
73     private static NotifyExcPanel INSTANCE = null;
74     /** preferred width of this component */
75     private static final int SIZE_PREFERRED_WIDTH=550;
76     /** preferred height of this component */
77     private static final int SIZE_PREFERRED_HEIGHT=250;
78
79     /** enumeration of NbExceptionManager.Exc to notify */
80     private static ArrayListPos exceptions;
81     /** current exception */
82     private NbErrorManager.Exc current;
83
84     /** dialog descriptor */
85     private DialogDescriptor descriptor;
86     /** dialog that displayes the exceptions */
87     java.awt.Dialog JavaDoc dialog;
88     /** button to show next exceptions */
89     private JButton JavaDoc next;
90     /** button to show previous exceptions */
91     private JButton JavaDoc previous;
92     /** details button */
93     private JButton JavaDoc details;
94     /** details window */
95     private JTextPane JavaDoc output;
96
97     /** boolean to show/hide details */
98     private static boolean showDetails;
99     
100     /** the last position of the exception dialog window */
101     private static Rectangle JavaDoc lastBounds;
102
103     /** Constructor.
104     */

105     private NotifyExcPanel () {
106         setPreferredSize(new Dimension JavaDoc(SIZE_PREFERRED_WIDTH,SIZE_PREFERRED_HEIGHT));
107
108         java.util.ResourceBundle JavaDoc bundle = org.openide.util.NbBundle.getBundle(NotifyExcPanel.class);
109         next = new JButton JavaDoc ();
110         Mnemonics.setLocalizedText(next, bundle.getString("CTL_NextException"));
111         // bugfix 25684, don't set Previous/Next as default capable
112
next.setDefaultCapable (false);
113         previous = new JButton JavaDoc ();
114         Mnemonics.setLocalizedText(previous, bundle.getString("CTL_PreviousException"));
115         previous.setDefaultCapable (false);
116         details = new JButton JavaDoc ();
117         details.setDefaultCapable (false);
118
119         output = new JTextPane JavaDoc() {
120             public boolean getScrollableTracksViewportWidth() {
121                 return false;
122             }
123         };
124         output.setEditable(false);
125         output.setFont(new Font JavaDoc("Monospaced", Font.PLAIN, output.getFont().getSize() + 1)); // NOI18N
126
output.setForeground(UIManager.getColor("Label.foreground")); // NOI18N
127
output.setBackground(UIManager.getColor("Label.background")); // NOI18N
128

129         setLayout( new BorderLayout JavaDoc() );
130         add(new JScrollPane JavaDoc(output));
131         setBorder( new javax.swing.border.BevelBorder JavaDoc(javax.swing.border.BevelBorder.LOWERED));
132             
133         next.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_NextException"));
134         previous.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_PreviousException"));
135         output.getAccessibleContext().setAccessibleName(bundle.getString("ACSN_ExceptionStackTrace"));
136         output.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_ExceptionStackTrace"));
137         getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_NotifyExceptionPanel"));
138
139         descriptor = new DialogDescriptor ("", ""); // NOI18N
140

141         descriptor.setMessageType (DialogDescriptor.ERROR_MESSAGE);
142         descriptor.setOptions (computeOptions(previous, next));
143         descriptor.setAdditionalOptions (new Object JavaDoc[] {
144                                              details
145                                          });
146         descriptor.setClosingOptions (new Object JavaDoc[0]);
147         descriptor.setButtonListener (this);
148
149         // bugfix #27176, create dialog in modal state if some other modal
150
// dialog is opened at the time
151
// #53328 do not let the error dialog to be created modal unless the main
152
// window is visible. otherwise the error message may be hidden behind
153
// the main window thus making the main window unusable
154
descriptor.setModal( isModalDialogPresent()
155                 && WindowManager.getDefault().getMainWindow().isVisible() );
156         
157         dialog = DialogDisplayer.getDefault().createDialog(descriptor);
158         if( null != lastBounds )
159             dialog.setBounds( lastBounds );
160         
161         dialog.getAccessibleContext().setAccessibleName(bundle.getString("ACN_NotifyExcPanel_Dialog")); // NOI18N
162
dialog.getAccessibleContext().setAccessibleDescription(bundle.getString("ACD_NotifyExcPanel_Dialog")); // NOI18N
163
}
164
165     static Object JavaDoc[] computeOptions(Object JavaDoc previous, Object JavaDoc next) {
166         ArrayList JavaDoc<Object JavaDoc> arr = new ArrayList JavaDoc<java.lang.Object JavaDoc>();
167         arr.add(previous);
168         arr.add(next);
169         
170         for (Handler JavaDoc h : Logger.getLogger("").getHandlers()) {
171             if (h instanceof Callable JavaDoc<?>) {
172                 boolean foundCallableForJButton = false;
173                 for (Type JavaDoc t : h.getClass().getGenericInterfaces()) {
174                     if (t instanceof ParameterizedType JavaDoc) {
175                         ParameterizedType JavaDoc p = (ParameterizedType JavaDoc)t;
176                         Type JavaDoc[] params = p.getActualTypeArguments();
177                         if (params.length == 1 && params[0] == JButton JavaDoc.class) {
178                             foundCallableForJButton = true;
179                             break;
180                         }
181                     }
182                 }
183                 if (!foundCallableForJButton) {
184                     continue;
185                 }
186                 
187                 
188                 try {
189                     Object JavaDoc o = ((Callable JavaDoc<?>)h).call();
190                     assert o instanceof JButton JavaDoc;
191                     arr.add(o);
192                 } catch (Exception JavaDoc ex) {
193                     Exceptions.printStackTrace(ex);
194                 }
195             }
196         }
197         
198         arr.add(DialogDescriptor.OK_OPTION);
199         return arr.toArray();
200     }
201     
202     private static boolean isModalDialogPresent() {
203         return hasModalDialog(WindowManager.getDefault().getMainWindow())
204             // XXX Trick to get the shared frame instance.
205
|| hasModalDialog(new JDialog JavaDoc().getOwner());
206     }
207     
208     private static boolean hasModalDialog(Window JavaDoc w) {
209         if (w == null) { // #63830
210
return false;
211         }
212         Window JavaDoc[] ws = w.getOwnedWindows();
213         for(int i = 0; i < ws.length; i++) {
214             if(ws[i] instanceof Dialog JavaDoc && ((Dialog JavaDoc)ws[i]).isModal() && ws[i].isVisible()) {
215                 return true;
216             } else if(hasModalDialog(ws[i])) {
217                 return true;
218             }
219         }
220         
221         return false;
222     }
223     
224     /**
225      * For unit-testing only
226      */

227     static void cleanInstance() {
228         INSTANCE = null;
229     }
230
231
232     /** Adds new exception into the queue.
233      */

234     public static void notify (
235         final NbErrorManager.Exc t
236     ) {
237         if (!shallNotify(t.getSeverity(), false)) {
238             return;
239         }
240         
241         // #50018 Don't try to show any notify dialog when reporting headless exception
242
if ("java.awt.HeadlessException".equals(t.getClassName()) && GraphicsEnvironment.isHeadless()) {
243             t.printStackTrace(System.err);
244             return;
245         }
246
247         SwingUtilities.invokeLater (new Runnable JavaDoc () {
248             public void run() {
249                 String JavaDoc glm = t.getLocalizedMessage();
250                 Level JavaDoc gs = t.getSeverity();
251                 boolean loc = t.isLocalized();
252
253                 if (loc) {
254                     if (gs == Level.WARNING) {
255                         DialogDisplayer.getDefault().notify(
256                             new NotifyDescriptor.Message(glm, NotifyDescriptor.WARNING_MESSAGE)
257                         );
258                         return;
259                     }
260
261                     if (gs.intValue() == 1973) {
262                         DialogDisplayer.getDefault().notify(
263                             new NotifyDescriptor.Message(glm, NotifyDescriptor.INFORMATION_MESSAGE)
264                         );
265                         return;
266                     }
267
268                     if (gs == Level.SEVERE) {
269                         DialogDisplayer.getDefault().notify(
270                             new NotifyDescriptor.Message(glm, NotifyDescriptor.ERROR_MESSAGE)
271                         );
272                         return;
273                     }
274                 }
275
276                 
277                 if( null == exceptions )
278                     exceptions = new ArrayListPos();
279                 exceptions.add(t);
280                 exceptions.position = exceptions.size()-1;
281
282                 if(shallNotify(t.getSeverity(), true)) {
283                     // Assertions are on, so show the exception window.
284
if( INSTANCE == null ) {
285                         INSTANCE = new NotifyExcPanel();
286                     }
287                     INSTANCE.updateState(t);
288                 } else {
289                     // No assertions, use the flashing icon.
290
if( null != flasher && null == INSTANCE ) {
291                         //exception window is not visible, start flashing the icon
292
flasher.setToolTipText( getExceptionSummary( t ) );
293                         flasher.startFlashing();
294                     } else {
295                         //exception window is already visible (or the flashing icon is not available)
296
//so we'll only update the exception window
297
if( INSTANCE == null ) {
298                             INSTANCE = new NotifyExcPanel();
299                         }
300                         INSTANCE.updateState(t);
301                     }
302                 }
303             }
304         });
305     }
306     
307     /**
308      * @return A brief exception summary for the flashing icon tooltip (either
309      * the exception message or exception class name).
310      */

311     private static String JavaDoc getExceptionSummary( final NbErrorManager.Exc t ) {
312         String JavaDoc plainmsg;
313         String JavaDoc glm = t.getLocalizedMessage();
314         if (glm != null) {
315             plainmsg = glm;
316         } else if (t.getMessage() != null) {
317             plainmsg = t.getMessage();
318         } else {
319             plainmsg = t.getClassName();
320         }
321         assert plainmsg != null;
322         return plainmsg;
323     }
324
325
326     /**
327      * updates the state of the dialog. called only in AWT thread.
328      */

329     private void updateState (NbErrorManager.Exc t) {
330         if (!exceptions.existsNextElement()) {
331             // it can be commented out while INSTANCE is not cached
332
// (see the comment in actionPerformed)
333
/*// be modal if some modal dialog is already opened, nonmodal otherwise
334             boolean isModalDialogOpened = NbPresenter.currentModalDialog != null;
335             if (descriptor.isModal() != isModalDialogOpened) {
336                 descriptor.setModal(isModalDialogOpened);
337                // bugfix #27176, old dialog is disposed before recreating
338                if (dialog != null) dialog.dispose ();
339                // so we can safely send it to gc and recreate dialog
340                // dialog = org.openide.DialogDisplayer.getDefault ().createDialog (descriptor);
341             }*/

342             // the dialog is not shown
343
current = t;
344             update ();
345         } else {
346             // add the exception to the queue
347
next.setVisible (true);
348         }
349         try {
350             //Dialog.show() will pump events for the AWT thread. If the
351
//exception happened because of a paint, it will trigger opening
352
//another dialog, which will trigger another exception, endlessly.
353
//Catch any exceptions and append them to the list instead.
354
ensurePreferredSize();
355             dialog.setVisible(true);
356             //throw new RuntimeException ("I am not so exceptional"); //uncomment to test
357
} catch (Exception JavaDoc e) {
358             exceptions.add(new NbErrorManager().createExc(
359                 e, Level.SEVERE, null));
360             next.setVisible(true);
361         }
362     }
363
364     private void ensurePreferredSize() {
365         if( null != lastBounds )
366             return; //we remember the last window position
367
Dimension JavaDoc sz = dialog.getSize();
368         Dimension JavaDoc pref = dialog.getPreferredSize();
369         if (pref.height == 0) pref.height = SIZE_PREFERRED_HEIGHT;
370         if (pref.width == 0) pref.width = SIZE_PREFERRED_WIDTH;
371         if (!sz.equals(pref)) {
372             dialog.setSize(pref.width, pref.height);
373             dialog.validate();
374             dialog.repaint();
375         }
376     }
377     
378
379     /** Updates the visual state of the dialog.
380     */

381     private void update () {
382         // JST: this can be improved in future...
383
boolean isLocalized = current.isLocalized();
384
385         next.setVisible (exceptions.existsNextElement());
386         previous.setVisible (exceptions.existsPreviousElement());
387
388         if (showDetails) {
389             Mnemonics.setLocalizedText(details, org.openide.util.NbBundle.getBundle(NotifyExcPanel.class).getString("CTL_Exception_Hide_Details"));
390             details.getAccessibleContext().setAccessibleDescription(
391                 org.openide.util.NbBundle.getBundle(NotifyExcPanel.class).getString("ACSD_Exception_Hide_Details"));
392         } else {
393             Mnemonics.setLocalizedText(details, org.openide.util.NbBundle.getBundle(NotifyExcPanel.class).getString("CTL_Exception_Show_Details"));
394             details.getAccessibleContext().setAccessibleDescription(
395                 org.openide.util.NbBundle.getBundle(NotifyExcPanel.class).getString("ACSD_Exception_Show_Details"));
396         }
397
398         // setText (current.getLocalizedMessage ());
399
String JavaDoc title = org.openide.util.NbBundle.getBundle(NotifyExcPanel.class).getString("CTL_Title_Exception");
400
401         if (showDetails) {
402             descriptor.setMessage (this);
403             
404             SwingUtilities.invokeLater(new Runnable JavaDoc() {
405                 public void run() {
406                     // XXX #28191: some other piece of code should underline these, etc.
407
StringWriter JavaDoc wr = new StringWriter JavaDoc();
408                         current.printStackTrace(new PrintWriter JavaDoc(wr, true));
409                         output.setText(wr.toString());
410                         output.getCaret().setDot(0);
411                         output.requestFocus ();
412                 }
413             });
414         } else {
415             if (isLocalized) {
416                 String JavaDoc msg = current.getLocalizedMessage ();
417                 if (msg != null) {
418                     descriptor.setMessage (msg);
419                 }
420             } else {
421                 ResourceBundle JavaDoc curBundle = NbBundle.getBundle (NotifyExcPanel.class);
422                 if (current.getSeverity() == Level.WARNING) {
423                     // less scary message for warning level
424
descriptor.setMessage (
425                         java.text.MessageFormat.format(
426                             curBundle.getString("NTF_ExceptionWarning"),
427                             new Object JavaDoc[] {
428                                 current.getClassName ()
429                             }
430                         )
431                     );
432                     title = curBundle.getString("NTF_ExceptionWarningTitle"); // NOI18N
433
} else {
434                     // emphasize user-non-friendly exceptions
435
// if (this.getMessage() == null || "".equals(this.getMessage())) { // NOI18N
436
descriptor.setMessage (
437                         java.text.MessageFormat.format(
438                             curBundle.getString("NTF_ExceptionalException"),
439                             new Object JavaDoc[] {
440                                 current.getClassName (),
441                                 CLIOptions.getLogDir ()
442                             }
443                         )
444                     );
445
446                     title = curBundle.getString("NTF_ExceptionalExceptionTitle"); // NOI18N
447
}
448             }
449         }
450
451         descriptor.setTitle (title);
452        
453     }
454
455     //
456
// Handlers
457
//
458

459     public void actionPerformed(final java.awt.event.ActionEvent JavaDoc ev) {
460         if (ev.getSource () == next && exceptions.setNextElement() || ev.getSource () == previous && exceptions.setPreviousElement()) {
461             current = exceptions.get();
462             update ();
463             // bugfix #27266, don't change the dialog's size when jumping Next<->Previous
464
//ensurePreferredSize();
465
return;
466         }
467
468         if (ev.getSource () == details) {
469             showDetails = !showDetails;
470             lastBounds = null;
471             try {
472                 update ();
473                 ensurePreferredSize();
474                 //throw new RuntimeException ("I am reallly exceptional!"); //uncomment to test
475
} catch (Exception JavaDoc e) {
476                 //Do not allow an exception thrown here to trigger an endless
477
//loop
478
exceptions.add(new NbErrorManager().createExc(e, //ugly but works
479
Level.SEVERE, null));
480                 next.setVisible(true);
481             }
482             return;
483         }
484
485         // bugfix #40834, remove all exceptions to notify when close a dialog
486
if (ev.getSource () == DialogDescriptor.OK_OPTION || ev.getSource () == DialogDescriptor.CLOSED_OPTION) {
487             try {
488                 exceptions.removeAll();
489             //Fixed bug #9435, call of setVisible(false) replaced by call of dispose()
490
//It did not work on Linux when JDialog is reused.
491
//dialog.setVisible (false);
492
// XXX(-ttran) no, it still doesn't work, getPreferredSize() on the
493
// reused dialog returns (0,0). We stop caching the dialog
494
// completely by setting INSTANCE to null here.
495
lastBounds = dialog.getBounds();
496                 dialog.dispose();
497                 exceptions = null;
498                 INSTANCE = null;
499                 //throw new RuntimeException ("You must be exceptional"); //uncomment to test
500
} catch (RuntimeException JavaDoc e) {
501                 //Do not allow window of opportunity when dialog in a possibly
502
//inconsistent state may be reuse
503
exceptions = null;
504                 INSTANCE = null;
505                 throw e;
506             } finally {
507                 exceptions = null;
508                 INSTANCE = null;
509             }
510         }
511     }
512
513
514     /** Method that checks whether the level is high enough to be notified
515      * at all.
516      * @param dialog shall we check for dialog or just a blinking icon (false)
517      */

518     private static boolean shallNotify(Level JavaDoc level, boolean dialog) {
519         int minAlert = Integer.getInteger("netbeans.exception.alert.min.level", 900); // NOI18N
520
boolean assertionsOn = false;
521         assert assertionsOn = true;
522         int defReport = assertionsOn ? 900 : 1001;
523         int minReport = Integer.getInteger("netbeans.exception.report.min.level", defReport); // NOI18N
524

525         if (dialog) {
526             return level.intValue() >= minReport;
527         } else {
528             return level.intValue() >= minAlert || level.intValue() >= minReport;
529         }
530     }
531     
532
533     /**
534      * The icon shown in the main status bar that is flashing when an exception
535      * is encountered.
536      */

537     static FlashingIcon flasher = null;
538     
539     /**
540      * Return an icon that is flashing when a new internal exception occurs.
541      * Clicking the icon opens the regular exception dialog box. The icon
542      * disappears (is hidden) after a short period of time and the exception
543      * list is cleared.
544      *
545      * @return A flashing icon component or null if console logging is switched on.
546      */

547     public static Component JavaDoc getNotificationVisualizer() {
548         //do not create flashing icon if not allowed in system properties
549
if( null == flasher ) {
550             ImageIcon JavaDoc img1 = new ImageIcon JavaDoc( Utilities.loadImage("org/netbeans/core/resources/exception.gif", true) );
551             flasher = new ExceptionFlasher( img1 );
552         }
553         return flasher;
554     }
555
556     private static class ExceptionFlasher extends FlashingIcon {
557         public ExceptionFlasher( Icon JavaDoc img1 ) {
558             super( img1 );
559         }
560
561         /**
562          * User clicked the flashing icon, display the exception window.
563          */

564         protected void onMouseClick() {
565             if (null != exceptions && exceptions.size() > 0) {
566                 if (INSTANCE == null) {
567                     INSTANCE = new NotifyExcPanel();
568                 }
569                 INSTANCE.updateState(exceptions.get(exceptions.size() - 1));
570             }
571         }
572         
573         /**
574          * The flashing icon disappeared (timed-out), clear the current
575          * exception list.
576          */

577         protected void timeout() {
578             SwingUtilities.invokeLater( new Runnable JavaDoc() {
579                 public void run() {
580                     if( null != INSTANCE )
581                         return;
582                     if( null != exceptions )
583                         exceptions.clear();
584                     exceptions = null;
585                 }
586             });
587         }
588     }
589
590     protected static class ArrayListPos extends ArrayList JavaDoc<NbErrorManager.Exc> {
591         protected int position;
592
593         protected ArrayListPos () {
594             super();
595             position=0;
596         }
597
598         protected boolean existsElement () {
599             return size()>0;
600         }
601
602         protected boolean existsNextElement () {
603             return position+1<size();
604         }
605
606         protected boolean existsPreviousElement () {
607             return position>0&&size()>0;
608         }
609
610         protected boolean setNextElement () {
611             if(!existsNextElement())
612                 return false;
613             position++;
614             return true;
615         }
616
617         protected boolean setPreviousElement () {
618             if(!existsPreviousElement())
619                 return false;
620             position--;
621             return true;
622         }
623
624         protected NbErrorManager.Exc get () {
625             return existsElement()?get(position):null;
626         }
627
628         protected void removeAll () {
629             clear();
630             position=0;
631         }
632     }
633 }
634
Popular Tags