001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.ui;
038:
039: import javax.swing.*;
040: import javax.swing.event.*;
041: import javax.swing.text.*;
042: import javax.swing.border.*;
043: import java.awt.event.*;
044: import java.awt.*;
045: import java.io.ByteArrayOutputStream;
046:
047: import edu.rice.cs.drjava.DrJava;
048: import edu.rice.cs.util.UnexpectedException;
049: import edu.rice.cs.util.StringOps;
050: import edu.rice.cs.util.swing.BorderlessScrollPane;
051: import edu.rice.cs.drjava.platform.PlatformFactory;
052:
053: /** Displays uncaught exceptions and logged conditions.
054: * This window is not automatically updated when new errors occur. In the case of errors, we want to
055: * minimize the effects on the GUI. If we want to see an updated dialog, we can click on the "DrJava Errors"
056: * button again.
057: * @version $Id: DrJavaErrorWindow.java 4255 2007-08-28 19:17:37Z mgricken $
058: */
059: public class DrJavaErrorWindow extends JDialog {
060: /** Sourceforge add bug URL */
061: public static final String SF_ADD_BUG_URL = "http://sourceforge.net/tracker/?func=add&group_id=44253&atid=438935/";
062:
063: /** Sourceforge URL */
064: public static final String SF_LINK_NAME = "http://sourceforge.net/projects/drjava";
065:
066: /** information about the error */
067: private volatile JEditorPane _errorInfo;
068: /** contains the stack trace */
069: private final JTextArea _stackTrace;
070: /** label with index */
071: private final JLabel _indexLabel;
072: /** scroll pane for _stackTrace */
073: private final JScrollPane _stackTraceScroll;
074: /** compresses the buttonPanel into the east */
075: private final JPanel _bottomPanel;
076: /** contains the butons */
077: private final JPanel _buttonPanel;
078: /** the button that copies the stack trace to the clipboard */
079: private final JButton _copyButton;
080: /** the button that closes this window */
081: private final JButton _okButton;
082: /** the button that moves to the next error */
083: private final JButton _nextButton;
084: /** the button that moves to the previous error */
085: private final JButton _prevButton;
086: /** the button that clears all errors and closes the window */
087: private final JButton _dismissButton;
088: /** the number of errors that had occurred */
089: private volatile int _errorCount;
090: /** the currently selected error */
091: private volatile Throwable _error;
092: /** the currently selected error index */
093: private volatile int _errorIndex;
094: /** the parent frame */
095: private static volatile JFrame _parentFrame = new JFrame();
096: /** true if parent changed since last singleton() call */
097: private static volatile boolean _parentChanged = true;
098:
099: /** Sets the parent frame. */
100: public static void setFrame(JFrame f) {
101: _parentFrame = f;
102: _parentChanged = true;
103: }
104:
105: /** Gets the parent frame. */
106: public static JFrame getFrame() {
107: return _parentFrame;
108: }
109:
110: /** The singleton instance of this dialog. */
111: private static volatile DrJavaErrorWindow _singletonInstance;
112:
113: /** Returns the singleton instance. Recreates it if necessary. */
114: public static DrJavaErrorWindow singleton() {
115: if (_parentChanged) {
116: synchronized (DrJavaErrorWindow.class) {
117: if (_parentChanged) {
118: _singletonInstance = new DrJavaErrorWindow();
119: _parentChanged = false;
120: }
121: }
122: }
123: return _singletonInstance;
124: }
125:
126: /** Creates a window to graphically display the errors that have occurred in the code of DrJava. */
127: private DrJavaErrorWindow() {
128: super (_parentFrame, "DrJava Errors");
129:
130: this .setSize(600, 400);
131:
132: // If we set this pane to be of type text/rtf, it wraps based on words
133: // as opposed to based on characters.
134: _stackTrace = new JTextArea();
135: _stackTrace.setEditable(false);
136:
137: _prevButton = new JButton(_prevAction);
138: _nextButton = new JButton(_nextAction);
139: _copyButton = new JButton(_copyAction);
140: _dismissButton = new JButton(_dismissAction);
141: _okButton = new JButton(_okAction);
142:
143: _bottomPanel = new JPanel(new BorderLayout());
144: _buttonPanel = new JPanel();
145: _buttonPanel.add(_prevButton);
146: _buttonPanel.add(_nextButton);
147: _buttonPanel.add(_copyButton);
148: _buttonPanel.add(_dismissButton);
149: _buttonPanel.add(_okButton);
150: _indexLabel = new JLabel();
151: _bottomPanel.add(_indexLabel, BorderLayout.CENTER);
152: _bottomPanel.add(_buttonPanel, BorderLayout.EAST);
153:
154: _stackTraceScroll = new BorderlessScrollPane(_stackTrace,
155: JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
156: JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
157: _errorInfo = _errorInfo = new JEditorPane("text/html",
158: HEADER_HTML + NO_ERRORS_HTML);
159: _errorInfo.setEditable(false);
160: _errorInfo.setBackground(getContentPane().getBackground());
161: final JPanel cp = new JPanel(new BorderLayout(5, 5));
162: cp.setBorder(new EmptyBorder(5, 5, 5, 5));
163: setContentPane(cp);
164: cp.add(_errorInfo, BorderLayout.NORTH);
165: cp.add(_stackTraceScroll, BorderLayout.CENTER);
166: cp.add(_bottomPanel, BorderLayout.SOUTH);
167: getRootPane().setDefaultButton(_okButton);
168: init();
169: }
170:
171: /** Initialize the dialog when setting to visible. */
172: public void setVisible(boolean b) {
173: if (b) {
174: init();
175: }
176: super .setVisible(b);
177: }
178:
179: /** Initialize the dialog. */
180: private void init() {
181: _errorCount = DrJavaErrorHandler.getErrorCount();
182: if (_errorCount > 0) {
183: _error = DrJavaErrorHandler.getError(0);
184: _errorIndex = 0;
185: } else {
186: _error = null;
187: _errorIndex = -1;
188: }
189: _prevAction.setEnabled(false);
190: _nextAction.setEnabled(_errorCount > 1);
191: _dismissAction.setEnabled(_errorCount > 0);
192: _copyAction.setEnabled(_errorCount > 0);
193: updateErrorInfo();
194: }
195:
196: /** Update the buttons and text area after next or previous. */
197: private void updateErrorInfo() {
198: getContentPane().remove(_errorInfo);
199: if (_error != null) {
200: final StringBuilder b = new StringBuilder();
201: if (_error instanceof DrJavaErrorHandler.LoggedCondition) {
202: b.append("Logged condition: ");
203: b.append(_error.getMessage());
204: b.append('\n');
205: boolean first = true;
206: for (StackTraceElement ste : _error.getStackTrace()) {
207: if (first) {
208: first = false;
209: continue; /* skip first frame, that's the log method itself */
210: }
211: b.append("\tat ");
212: b.append(ste);
213: b.append('\n');
214: }
215: } else {
216: b.append(StringOps.getStackTrace(_error));
217: if (_error instanceof UnexpectedException) {
218: Throwable t = ((UnexpectedException) _error)
219: .getCause();
220: b.append("\nCaused by:\n");
221: b.append(StringOps.getStackTrace(t));
222: }
223: }
224:
225: b.append("\n\n");
226: b.append(getSystemAndDrJavaInfo());
227:
228: _stackTrace.setText(b.toString());
229: _stackTrace.setCaretPosition(0);
230:
231: final StringBuilder b2 = new StringBuilder();
232: b2.append(HEADER_HTML);
233: b2.append(_errorCount);
234: b2.append(" error");
235: b2.append(((_errorCount > 1) ? "s" : ""));
236: b2.append(" occured!<br>");
237: b2.append(ERRORS_FOOTER_HTML);
238: _errorInfo = new JEditorPane("text/html", b2.toString());
239: _errorInfo.addHyperlinkListener(new HyperlinkListener() {
240: public void hyperlinkUpdate(HyperlinkEvent e) {
241: if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
242: try {
243: PlatformFactory.ONLY.openURL(e.getURL());
244: } catch (Exception ex) { /* ignore, just not open web page */
245: }
246: }
247: }
248: });
249: _errorInfo.setEditable(false);
250: _errorInfo.setBackground(getContentPane().getBackground());
251: _indexLabel.setText("Error " + (_errorIndex + 1) + " of "
252: + (_errorCount));
253: } else {
254: _errorInfo = new JEditorPane("text/html", HEADER_HTML
255: + NO_ERRORS_HTML);
256: _errorInfo.addHyperlinkListener(new HyperlinkListener() {
257: public void hyperlinkUpdate(HyperlinkEvent e) {
258: if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
259: try {
260: PlatformFactory.ONLY.openURL(e.getURL());
261: } catch (Exception ex) { /* ignore, just not open web page */
262: }
263: }
264: }
265: });
266: _errorInfo.setEditable(false);
267: _errorInfo.setBackground(getContentPane().getBackground());
268: _stackTrace.setText("");
269: _indexLabel.setText("");
270: }
271: getContentPane().add(_errorInfo, BorderLayout.NORTH);
272: validate();
273: }
274:
275: /** Return a string with the system properties, the DrJava configuration file contents, and
276: * information about memory. The data is anonymized.
277: * @return information string */
278: public static String getSystemAndDrJavaInfo() {
279: final StringBuilder b = new StringBuilder();
280: b.append("System Properties:\n");
281: b.append("DrJava Version ");
282: b.append(edu.rice.cs.drjava.Version.getBuildTimeString());
283: b.append('\n');
284: java.util.Properties props = System.getProperties();
285: int size = props.size();
286: java.util.Iterator entries = props.entrySet().iterator();
287: while (entries.hasNext()) {
288: java.util.Map.Entry entry = (java.util.Map.Entry) entries
289: .next();
290: b.append(entry.getKey());
291: b.append(" = ");
292: if (entry.getKey().equals("line.separator")) {
293: b.append("\"");
294: String ls = (String) entry.getValue();
295: for (int i = 0; i < ls.length(); ++i) {
296: int ch = ls.charAt(i);
297: b.append("\\u");
298: String hexString = "0000" + Integer.toHexString(ch);
299: b.append(hexString
300: .substring(hexString.length() - 4));
301: }
302: b.append("\"");
303: } else {
304: b.append(entry.getValue());
305: }
306: b.append('\n');
307: }
308: b.append('\n');
309: ByteArrayOutputStream baos = new ByteArrayOutputStream();
310: try {
311: DrJava.getConfig().saveConfiguration(baos,
312: "DrJava configuration file");
313: b.append(baos.toString());
314: } catch (java.io.IOException ioe) {
315: b
316: .append("IOException when trying to print DrJava configuration file");
317: }
318:
319: b.append("\n\nUsed memory: about ");
320: b.append(StringOps.memSizeToString(Runtime.getRuntime()
321: .totalMemory()
322: - Runtime.getRuntime().freeMemory()));
323: b.append("\nFree memory: about ");
324: b.append(StringOps.memSizeToString(Runtime.getRuntime()
325: .freeMemory()));
326: b.append("\nTotal memory: about ");
327: b.append(StringOps.memSizeToString(Runtime.getRuntime()
328: .totalMemory()));
329: b.append("\nTotal memory can expand to: about ");
330: b.append(StringOps.memSizeToString(Runtime.getRuntime()
331: .maxMemory()));
332: b.append("\n\n");
333:
334: // filter out user.dir, user.home and user.name
335: String infoText = b.toString();
336:
337: String userHome = System.getProperty("user.home");
338: String anonUserHome = "<anonymized user.home>";
339: infoText = replaceString(infoText, userHome, anonUserHome);
340:
341: String userDir = System.getProperty("user.dir");
342: String anonUserDir = "<anonymized user.dir>";
343: infoText = replaceString(infoText, userDir, anonUserDir);
344:
345: String userName = System.getProperty("user.name");
346: String anonUserName = "<anonymized user.name>";
347: infoText = replaceString(infoText, userName, anonUserName);
348:
349: return infoText;
350: }
351:
352: /* Close the window. */
353: private final Action _okAction = new AbstractAction("OK") {
354: public void actionPerformed(ActionEvent e) {
355: DrJavaErrorWindow.this .dispose();
356: }
357: };
358:
359: /* Go to the previous error. */
360: private final Action _prevAction = new AbstractAction("Previous") {
361: public void actionPerformed(ActionEvent e) {
362: if (_errorIndex > 0) {
363: --_errorIndex;
364: _error = DrJavaErrorHandler.getError(_errorIndex);
365: if (_errorIndex == 0) {
366: setEnabled(false);
367: }
368: if (_errorCount > 1) {
369: _nextAction.setEnabled(true);
370: }
371: updateErrorInfo();
372: }
373: }
374: };
375:
376: /** Replaces all occurrences of orig in text with repl. */
377: private static String replaceString(String text, String orig,
378: String repl) {
379: int pos = 0;
380: while ((pos = text.indexOf(orig, pos)) >= 0) {
381: // found occurrence at pos
382: text = text.substring(0, pos)
383: + repl
384: + text
385: .substring(pos + orig.length(), text
386: .length());
387: }
388: return text;
389: }
390:
391: /** Go to the next error. */
392: private final Action _nextAction = new AbstractAction("Next") {
393: public void actionPerformed(ActionEvent e) {
394: if (_errorIndex < _errorCount - 1) {
395: ++_errorIndex;
396: _error = DrJavaErrorHandler.getError(_errorIndex);
397: if (_errorIndex == _errorCount - 1) {
398: setEnabled(false);
399: }
400: if (_errorCount > 1) {
401: _prevAction.setEnabled(true);
402: }
403: updateErrorInfo();
404: }
405: }
406: };
407:
408: /** Dismiss all errors and close the window. */
409: private Action _dismissAction = new AbstractAction("Dismiss") {
410: public void actionPerformed(ActionEvent e) {
411: DrJavaErrorHandler.clearErrors();
412: _errorCount = 0;
413: _error = null;
414: _errorIndex = -1;
415: setEnabled(false);
416: _prevAction.setEnabled(false);
417: _nextAction.setEnabled(false);
418: _copyAction.setEnabled(false);
419: updateErrorInfo();
420: JButton errorsButton = DrJavaErrorHandler.getButton();
421: if (errorsButton != null) {
422: errorsButton.setVisible(false);
423: }
424: _okAction.actionPerformed(e);
425: }
426: };
427:
428: /** Copy currently selected error to clip board. */
429: private Action _copyAction = new AbstractAction("Copy This Error") {
430: public void actionPerformed(ActionEvent e) {
431: _stackTrace.grabFocus();
432: _stackTrace.getActionMap().get(
433: DefaultEditorKit.selectAllAction)
434: .actionPerformed(e);
435: _stackTrace.getActionMap().get(DefaultEditorKit.copyAction)
436: .actionPerformed(e);
437: }
438: };
439:
440: /**
441: * Canned message for the user.
442: */
443: private final String HEADER_HTML = "<html><font size=\"-1\" face=\"sans-serif, Arial, Helvetica, Geneva\"><b>";
444: private final String ERRORS_FOOTER_HTML = "Please submit a bug report containing the information below "
445: + "and an account of the actions that caused the bug (if known) to "
446: + "<a href=\""
447: + SF_ADD_BUG_URL
448: + "\"><b>"
449: + SF_LINK_NAME
450: + "</b></a>.<br>"
451: + "You may wish to save all your work and restart DrJava.<br>"
452: + "Thanks for your help in making DrJava better!</b></font></p></html>";
453: private final String NO_ERRORS_HTML = "No errors occurred!<br>"
454: + "Thanks for using DrJava!</b></font></p></html>";
455: }
|