EMMA Coverage Report (generated Sun Apr 20 22:38:01 CEST 2008)
[all classes][net.sf.jomic.tools]

COVERAGE SUMMARY FOR SOURCE FILE [ErrorTools.java]

nameclass, %method, %block, %line, %
ErrorTools.java100% (3/3)80%  (20/25)80%  (485/603)82%  (112.6/137)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ErrorTools100% (1/1)76%  (16/21)78%  (410/527)81%  (101.7/126)
setCannotProcessActionCommand (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
setCannotProcessActionEvent (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
showError (ActionEvent, Throwable): void 0%   (0/1)0%   (0/35)0%   (0/11)
showError (PropertyChangeEvent, Throwable): void 0%   (0/1)0%   (0/27)0%   (0/3)
showErrorMessage (JFrame, String, Throwable): void 0%   (0/1)0%   (0/8)0%   (0/2)
assertValidMessageType (int): void 100% (1/1)40%  (8/20)68%  (1.4/2)
<static initializer> 100% (1/1)80%  (12/15)80%  (0.8/1)
getDetailedExceptionMessage (Throwable): String 100% (1/1)82%  (82/100)92%  (22/24)
titled (String): String 100% (1/1)92%  (23/25)88%  (7/8)
createDialog (JFrame, int, String, Throwable, boolean): JDialog 100% (1/1)97%  (137/141)98%  (28.5/29)
ErrorTools (): void 100% (1/1)100% (28/28)100% (9/9)
center (Component, int): void 100% (1/1)100% (24/24)100% (4/4)
centerUp (Component): void 100% (1/1)100% (5/5)100% (2/2)
getMessageCount (): int 100% (1/1)100% (3/3)100% (1/1)
getStackTrace (Throwable): String 100% (1/1)100% (18/18)100% (5/5)
getTitle (int): String 100% (1/1)100% (11/11)100% (2/2)
instance (): ErrorTools 100% (1/1)100% (8/8)100% (3/3)
setCannotChangePropertyText (String): void 100% (1/1)100% (4/4)100% (2/2)
setShowStackTraceText (String): void 100% (1/1)100% (4/4)100% (2/2)
setTitle (int, String): void 100% (1/1)100% (13/13)100% (3/3)
showMessage (JFrame, int, String, Throwable, boolean): void 100% (1/1)100% (30/30)100% (9/9)
     
class ErrorTools$1100% (1/1)100% (2/2)96%  (25/26)98%  (3.9/4)
itemStateChanged (ItemEvent): void 100% (1/1)93%  (13/14)98%  (2.9/3)
ErrorTools$1 (ErrorTools, JScrollPane, JDialog): void 100% (1/1)100% (12/12)100% (1/1)
     
class ErrorTools$2100% (1/1)100% (2/2)100% (50/50)100% (7/7)
ErrorTools$2 (ErrorTools, JDialog, JOptionPane, boolean, JCheckBox, ItemListe... 100% (1/1)100% (21/21)100% (1/1)
propertyChange (PropertyChangeEvent): void 100% (1/1)100% (29/29)100% (6/6)

1// Jomic - a viewer for comic book archives.
2// Copyright (C) 2004-2008 Thomas Aglassinger
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16package net.sf.jomic.tools;
17 
18import java.awt.Component;
19import java.awt.Dimension;
20import java.awt.event.ActionEvent;
21import java.awt.event.ItemEvent;
22import java.awt.event.ItemListener;
23import java.beans.PropertyChangeEvent;
24import java.beans.PropertyChangeListener;
25import java.io.PrintStream;
26import java.io.PrintWriter;
27import java.io.StringWriter;
28import java.text.MessageFormat;
29import java.util.LinkedList;
30import java.util.List;
31import java.util.Map;
32import java.util.TreeMap;
33 
34import javax.swing.JCheckBox;
35import javax.swing.JDialog;
36import javax.swing.JFrame;
37import javax.swing.JOptionPane;
38import javax.swing.JScrollPane;
39import javax.swing.JTextArea;
40import javax.swing.WindowConstants;
41 
42import net.sf.jomic.common.PropertyConstants;
43 
44/**
45 *  Tools for error handling and reporting. This class is self contained and does not require any
46 *  other classes from the tools package, therefor its methods can be used to handle errors right
47 *  from the beginning.
48 *
49 * @author    Thomas Aglassinger
50 */
51public final class ErrorTools
52{
53    private static final int UPPER_CENTER_HEIGHT_DIVISOR = 4;
54    private static /*@ spec_public nullable @*/ ErrorTools instance;
55 
56    private /*@ spec_public @*/ String cannotChangePropertyText;
57    private /*@ spec_public @*/ String cannotProcessActionCommand;
58    private /*@ spec_public @*/ String cannotProcessActionEvent;
59    private int messageCount;
60    private /*@ spec_public @*/ String showStackTraceText;
61    private Map typeToTitleMap;
62 
63    private ErrorTools() {
64        typeToTitleMap = new TreeMap();
65        showStackTraceText = "Show internal call stack";
66        cannotChangePropertyText = "cannot change property {0} from \"{1}\" to \"{2}\"";
67        cannotProcessActionCommand = "cannot process internal command \"{0}\"";
68        cannotProcessActionEvent = "cannot process ActionEvent:  {0}";
69        setTitle(JOptionPane.WARNING_MESSAGE, "Warning");
70        setTitle(JOptionPane.ERROR_MESSAGE, "Error");
71    }
72 
73    public void setCannotChangePropertyText(String newCannotChangePropertyText) {
74        cannotChangePropertyText = newCannotChangePropertyText;
75    }
76 
77    public void setCannotProcessActionCommand(String newCannotProcessActionCommand) {
78        cannotProcessActionCommand = newCannotProcessActionCommand;
79    }
80 
81    public void setCannotProcessActionEvent(String newCannotProcessActionEvent) {
82        cannotProcessActionEvent = newCannotProcessActionEvent;
83    }
84 
85    /**
86     *  Set text for "Show stack trace button" in dialogs.
87     *
88     * @see    #createDialog(JFrame, int, String, Throwable, boolean)
89     */
90    //@ assignable showStackTraceText;
91    public void setShowStackTraceText(String newText) {
92        showStackTraceText = newText;
93    }
94 
95    /**
96     *  Set title for dialog of a certain type.
97     *
98     * @see          JOptionPane
99     * @param  type  one of <code>JOptionPane.*_MESSAGE</code>
100     */
101    public void setTitle(int type, String title) {
102        assertValidMessageType(type);
103        typeToTitleMap.put(new Integer(type), title);
104    }
105 
106    /**
107     *  Get detailed exception message by concatenating <code>getMessage()</code> of <code>error</code>
108     *  and all nested exceptions.
109     *
110     * @see    Throwable#getCause()
111     */
112    public String getDetailedExceptionMessage(Throwable error) {
113        String result = "";
114        Throwable cause = error;
115 
116        while (cause != null) {
117            String message = cause.getMessage();
118            Class clazz = cause.getClass();
119            String clazzName = clazz.getName();
120            boolean includeClassName = (message == null);
121 
122            if ((cause instanceof NullPointerException) || (cause instanceof AssertionError) || (cause instanceof ClassCastException)) {
123                includeClassName = true;
124            }
125            if (message == null) {
126                message = "";
127            } else if (includeClassName) {
128                message = ": " + message;
129            }
130 
131            if (includeClassName) {
132                int lastDotIndex = clazzName.lastIndexOf('.');
133 
134                if (lastDotIndex >= 0) {
135                    clazzName = clazzName.substring(lastDotIndex + 1);
136                }
137                message = clazzName + message;
138            }
139            if (result.length() > 0) {
140                result += ":\n";
141            }
142            result += titled(message);
143            cause = cause.getCause();
144        }
145        return result;
146    }
147 
148    /**
149     *  Get number of message dialogs shown so far.
150     */
151    //@ ensures \result >= 0;
152    public int getMessageCount() {
153        return messageCount;
154    }
155 
156    /**
157     *  Stack trace as string.
158     */
159    public String getStackTrace(Throwable error) {
160        String result;
161        StringWriter stringWriter = new StringWriter();
162        PrintWriter printWriter = new PrintWriter(stringWriter);
163 
164        error.printStackTrace(printWriter);
165        result = stringWriter.getBuffer().toString();
166        return result;
167    }
168 
169    public String getTitle(int type) {
170        String result = (String) typeToTitleMap.get(new Integer(type));
171 
172        return result;
173    }
174 
175    //@ ensures instance != null;
176    public static synchronized ErrorTools instance() {
177        if (instance == null) {
178            instance = new ErrorTools();
179        }
180        return instance;
181    }
182 
183    /**
184     *  Center frame in upper half of the screen.
185     */
186    public void centerUp(Component frame) {
187        center(frame, UPPER_CENTER_HEIGHT_DIVISOR);
188    }
189 
190    /**
191     *  Create error dialog.
192     *
193     * @param  owner  Frame to which error belongs, or <code>null</code>
194     * @param  type   one of <code>JOptionPane.*_MESSAGE</code>
195     * @param  error  null or stack that caused error
196     */
197    public JDialog createDialog(/*@ nullable @*/ JFrame owner, int type, String message,
198            /*@ nullable @*/ Throwable error, boolean modal) {
199        assert message != null;
200        assertValidMessageType(type);
201 
202        final JDialog dialog = new JDialog(owner, getTitle(type), modal);
203        final ItemListener showStackListener;
204        final JCheckBox showStackCheckBox;
205        final boolean hasShowStackCheckBox = (error != null);
206        String errorMessage;
207 
208        if (error == null) {
209            errorMessage = "";
210        } else {
211            errorMessage = ":\n" + getDetailedExceptionMessage(error);
212        }
213 
214        List dialogItems = new LinkedList();
215 
216        dialogItems.add(titled(message) + errorMessage);
217        if (!hasShowStackCheckBox) {
218            showStackListener = null;
219            showStackCheckBox = null;
220        } else {
221            showStackCheckBox = new JCheckBox(showStackTraceText);
222 
223            String stackTrace = getStackTrace(error).trim();
224            JTextArea stackArea = new JTextArea(stackTrace);
225            final JScrollPane stackScrollPane = new JScrollPane(stackArea);
226 
227            dialogItems.add(showStackCheckBox);
228            dialogItems.add(stackScrollPane);
229            showStackListener =
230                new ItemListener()
231                {
232                    public void itemStateChanged(ItemEvent event) {
233                        stackScrollPane.setVisible(event.getStateChange() == ItemEvent.SELECTED);
234                        dialog.pack();
235                    }
236                };
237            showStackCheckBox.addItemListener(showStackListener);
238            stackScrollPane.setVisible(false);
239        }
240 
241        final JOptionPane optionPane = new JOptionPane(dialogItems.toArray(), type);
242        PropertyChangeListener propertyListener =
243            new PropertyChangeListener()
244            {
245                public void propertyChange(PropertyChangeEvent event) {
246                    String propertyName = event.getPropertyName();
247 
248                    if (dialog.isVisible()
249                            && (event.getSource() == optionPane)
250                            && (propertyName.equals(JOptionPane.VALUE_PROPERTY))) {
251                        if (hasShowStackCheckBox) {
252                            showStackCheckBox.removeItemListener(showStackListener);
253                        }
254                        dialog.setVisible(false);
255                    }
256                }
257            };
258 
259        optionPane.addPropertyChangeListener(propertyListener);
260        dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
261        dialog.setContentPane(optionPane);
262        dialog.pack();
263        centerUp(dialog);
264        return dialog;
265    }
266 
267    /**
268     *  Show error message for being unable to change property according to <code>event</code>.
269     */
270    public void showError(PropertyChangeEvent event, Throwable error) {
271        String message = MessageFormat.format(cannotChangePropertyText,
272                    new Object[]{event.getPropertyName(), event.getOldValue(), event.getNewValue()});
273 
274        showErrorMessage(null, message, error);
275    }
276 
277    /**
278     *  Show error message for being unable to process <code>event</code> (which may be null).
279     */
280    public void showError(/*@ nullable @*/ ActionEvent event, Throwable error) {
281        String command = null;
282        Object messageOption;
283        String messagePattern;
284 
285        if (event != null) {
286            command = event.getActionCommand();
287        }
288 
289        if (command == null) {
290            messagePattern = cannotProcessActionEvent;
291            messageOption = event;
292        } else {
293            messagePattern = cannotProcessActionCommand;
294            messageOption = command;
295        }
296 
297        String message = MessageFormat.format(messagePattern, new Object[]{messageOption});
298 
299        showErrorMessage(null, message, error);
300    }
301 
302    /**
303     *  Show error dialog.
304     *
305     * @see    #showErrorMessage(JFrame, String, Throwable)
306     */
307    public void showErrorMessage(/*@ nullable @*/ JFrame owner, String message,
308            /*@ nullable @*/ Throwable error) {
309        showMessage(owner, JOptionPane.ERROR_MESSAGE, message, error, true);
310    }
311 
312    /**
313     *  Show error dialog, optionally allowing user to inspect call stack.
314     *
315     * @see           JOptionPane
316     * @see           #createDialog(JFrame, int, String, Throwable, boolean)
317     * @param  owner  Frame to which error belongs, or <code>null</code>
318     * @param  type   one of <code>JOptionPane.*_MESSAGE</code>
319     * @param  error  <code>Throwable</code> that caused the error, or null
320     * @param  modal  <code>true</code> to wait for user to click dialog away
321     */
322    //@ requires (type == JOptionPane.INFORMATION_MESSAGE)
323    //@     || (type == JOptionPane.WARNING_MESSAGE)
324    //@     || (type == JOptionPane.ERROR_MESSAGE)
325    //@     || (type == JOptionPane.PLAIN_MESSAGE)
326    //@     || (type == JOptionPane.QUESTION_MESSAGE);
327    public void showMessage(/*@ nullable @*/ JFrame owner, int type, String message,
328            /*@ nullable @*/ Throwable error, boolean modal) {
329        assertValidMessageType(type);
330 
331        JDialog dialog = createDialog(owner, type, message, error, modal);
332 
333        if (Boolean.getBoolean(PropertyConstants.TEST_IGNORE_MESSAGE_DIALOGS)) {
334            PrintStream errorStream = System.err;
335 
336            errorStream.println(message);
337        } else {
338            dialog.setVisible(true);
339        }
340        messageCount += 1;
341    }
342 
343    /**
344     *  Some as <code>some</code>, but with the first character converted to title case.
345     *
346     * @see    Character#toTitleCase(char)
347     */
348    public /*@ pure @*/ String titled(String some) {
349        String result;
350 
351        if (some != null) {
352            StringBuffer buffer = new StringBuffer(some);
353 
354            if (some.length() != 0) {
355                buffer.setCharAt(0, Character.toTitleCase(some.charAt(0)));
356            }
357            result = buffer.toString();
358        } else {
359            result = "null";
360        }
361        return result;
362    }
363 
364    private void assertValidMessageType(int type) {
365        assert (type == JOptionPane.ERROR_MESSAGE) || (type == JOptionPane.WARNING_MESSAGE) : "type=" + type;
366    }
367 
368    //@ requires heightDivisor > 0;
369    private void center(Component frame, int heightDivisor) {
370        Dimension screenSize = frame.getToolkit().getScreenSize();
371        Dimension frameSize = frame.getSize();
372 
373        frame.setLocation((screenSize.width - frameSize.width) / 2,
374                (screenSize.height - frameSize.height) / heightDivisor);
375    }
376}

[all classes][net.sf.jomic.tools]
EMMA 2.0.4217 (C) Vladimir Roubtsov