EMMA Coverage Report (generated Sat Oct 08 11:41:37 CEST 2011)
[all classes][net.sf.jomic.tools]

COVERAGE SUMMARY FOR SOURCE FILE [ErrorTools.java]

nameclass, %method, %block, %line, %
ErrorTools.java100% (3/3)76%  (19/25)78%  (472/603)80%  (109.7/137)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ErrorTools$1100% (1/1)50%  (1/2)46%  (12/26)25%  (1/4)
itemStateChanged (ItemEvent): void 0%   (0/1)0%   (0/14)0%   (0/3)
ErrorTools$1 (ErrorTools, JScrollPane, JDialog): void 100% (1/1)100% (12/12)100% (1/1)
     
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$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-2011 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)
123                    || (cause instanceof ClassCastException)) {
124                includeClassName = true;
125            }
126            if (message == null) {
127                message = "";
128            } else if (includeClassName) {
129                message = ": " + message;
130            }
131 
132            if (includeClassName) {
133                int lastDotIndex = clazzName.lastIndexOf('.');
134 
135                if (lastDotIndex >= 0) {
136                    clazzName = clazzName.substring(lastDotIndex + 1);
137                }
138                message = clazzName + message;
139            }
140            if (result.length() > 0) {
141                result += ":\n";
142            }
143            result += titled(message);
144            cause = cause.getCause();
145        }
146        return result;
147    }
148 
149    /**
150     *  Get number of message dialogs shown so far.
151     */
152    //@ ensures \result >= 0;
153    public int getMessageCount() {
154        return messageCount;
155    }
156 
157    /**
158     *  Stack trace as string.
159     */
160    public String getStackTrace(Throwable error) {
161        String result;
162        StringWriter stringWriter = new StringWriter();
163        PrintWriter printWriter = new PrintWriter(stringWriter);
164 
165        error.printStackTrace(printWriter);
166        result = stringWriter.getBuffer().toString();
167        return result;
168    }
169 
170    public String getTitle(int type) {
171        String result = (String) typeToTitleMap.get(new Integer(type));
172 
173        return result;
174    }
175 
176    //@ ensures instance != null;
177    public static synchronized ErrorTools instance() {
178        if (instance == null) {
179            instance = new ErrorTools();
180        }
181        return instance;
182    }
183 
184    /**
185     *  Center frame in upper half of the screen.
186     */
187    public void centerUp(Component frame) {
188        center(frame, UPPER_CENTER_HEIGHT_DIVISOR);
189    }
190 
191    /**
192     *  Create error dialog.
193     *
194     * @param  owner  Frame to which error belongs, or <code>null</code>
195     * @param  type   one of <code>JOptionPane.*_MESSAGE</code>
196     * @param  error  null or stack that caused error
197     */
198    public JDialog createDialog(/*@ nullable @*/ JFrame owner, int type, String message,
199            /*@ nullable @*/ Throwable error, boolean modal) {
200        assert message != null;
201        assertValidMessageType(type);
202 
203        final JDialog dialog = new JDialog(owner, getTitle(type), modal);
204        final ItemListener showStackListener;
205        final JCheckBox showStackCheckBox;
206        final boolean hasShowStackCheckBox = (error != null);
207        String errorMessage;
208 
209        if (error == null) {
210            errorMessage = "";
211        } else {
212            errorMessage = ":\n" + getDetailedExceptionMessage(error);
213        }
214 
215        List dialogItems = new LinkedList();
216 
217        dialogItems.add(titled(message) + errorMessage);
218        if (!hasShowStackCheckBox) {
219            showStackListener = null;
220            showStackCheckBox = null;
221        } else {
222            showStackCheckBox = new JCheckBox(showStackTraceText);
223 
224            String stackTrace = getStackTrace(error).trim();
225            JTextArea stackArea = new JTextArea(stackTrace);
226            final JScrollPane stackScrollPane = new JScrollPane(stackArea);
227 
228            dialogItems.add(showStackCheckBox);
229            dialogItems.add(stackScrollPane);
230            showStackListener =
231                new ItemListener()
232                {
233                    public void itemStateChanged(ItemEvent event) {
234                        stackScrollPane.setVisible(event.getStateChange() == ItemEvent.SELECTED);
235                        dialog.pack();
236                    }
237                };
238            showStackCheckBox.addItemListener(showStackListener);
239            stackScrollPane.setVisible(false);
240        }
241 
242        final JOptionPane optionPane = new JOptionPane(dialogItems.toArray(), type);
243        PropertyChangeListener propertyListener =
244            new PropertyChangeListener()
245            {
246                public void propertyChange(PropertyChangeEvent event) {
247                    String propertyName = event.getPropertyName();
248 
249                    if (dialog.isVisible()
250                            && (event.getSource() == optionPane)
251                            && (propertyName.equals(JOptionPane.VALUE_PROPERTY))) {
252                        if (hasShowStackCheckBox) {
253                            showStackCheckBox.removeItemListener(showStackListener);
254                        }
255                        dialog.setVisible(false);
256                    }
257                }
258            };
259 
260        optionPane.addPropertyChangeListener(propertyListener);
261        dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
262        dialog.setContentPane(optionPane);
263        dialog.pack();
264        centerUp(dialog);
265        return dialog;
266    }
267 
268    /**
269     *  Show error message for being unable to change property according to <code>event</code>.
270     */
271    public void showError(PropertyChangeEvent event, Throwable error) {
272        String message = MessageFormat.format(cannotChangePropertyText,
273                    new Object[]{event.getPropertyName(), event.getOldValue(), event.getNewValue()});
274 
275        showErrorMessage(null, message, error);
276    }
277 
278    /**
279     *  Show error message for being unable to process <code>event</code> (which may be null).
280     */
281    public void showError(/*@ nullable @*/ ActionEvent event, Throwable error) {
282        String command = null;
283        Object messageOption;
284        String messagePattern;
285 
286        if (event != null) {
287            command = event.getActionCommand();
288        }
289 
290        if (command == null) {
291            messagePattern = cannotProcessActionEvent;
292            messageOption = event;
293        } else {
294            messagePattern = cannotProcessActionCommand;
295            messageOption = command;
296        }
297 
298        String message = MessageFormat.format(messagePattern, new Object[]{messageOption});
299 
300        showErrorMessage(null, message, error);
301    }
302 
303    /**
304     *  Show error dialog.
305     *
306     * @see    #showErrorMessage(JFrame, String, Throwable)
307     */
308    public void showErrorMessage(/*@ nullable @*/ JFrame owner, String message,
309            /*@ nullable @*/ Throwable error) {
310        showMessage(owner, JOptionPane.ERROR_MESSAGE, message, error, true);
311    }
312 
313    /**
314     *  Show error dialog, optionally allowing user to inspect call stack.
315     *
316     * @see           JOptionPane
317     * @see           #createDialog(JFrame, int, String, Throwable, boolean)
318     * @param  owner  Frame to which error belongs, or <code>null</code>
319     * @param  type   one of <code>JOptionPane.*_MESSAGE</code>
320     * @param  error  <code>Throwable</code> that caused the error, or null
321     * @param  modal  <code>true</code> to wait for user to click dialog away
322     */
323    //@ requires (type == JOptionPane.INFORMATION_MESSAGE)
324    //@     || (type == JOptionPane.WARNING_MESSAGE)
325    //@     || (type == JOptionPane.ERROR_MESSAGE)
326    //@     || (type == JOptionPane.PLAIN_MESSAGE)
327    //@     || (type == JOptionPane.QUESTION_MESSAGE);
328    public void showMessage(/*@ nullable @*/ JFrame owner, int type, String message,
329            /*@ nullable @*/ Throwable error, boolean modal) {
330        assertValidMessageType(type);
331 
332        JDialog dialog = createDialog(owner, type, message, error, modal);
333 
334        if (Boolean.getBoolean(PropertyConstants.TEST_IGNORE_MESSAGE_DIALOGS)) {
335            PrintStream errorStream = System.err;
336 
337            errorStream.println(message);
338        } else {
339            dialog.setVisible(true);
340        }
341        messageCount += 1;
342    }
343 
344    /**
345     *  Some as <code>some</code>, but with the first character converted to title case.
346     *
347     * @see    Character#toTitleCase(char)
348     */
349    public /*@ pure @*/ String titled(String some) {
350        String result;
351 
352        if (some != null) {
353            StringBuffer buffer = new StringBuffer(some);
354 
355            if (some.length() != 0) {
356                buffer.setCharAt(0, Character.toTitleCase(some.charAt(0)));
357            }
358            result = buffer.toString();
359        } else {
360            result = "null";
361        }
362        return result;
363    }
364 
365    private void assertValidMessageType(int type) {
366        assert (type == JOptionPane.ERROR_MESSAGE) || (type == JOptionPane.WARNING_MESSAGE) : "type=" + type;
367    }
368 
369    //@ requires heightDivisor > 0;
370    private void center(Component frame, int heightDivisor) {
371        Dimension screenSize = frame.getToolkit().getScreenSize();
372        Dimension frameSize = frame.getSize();
373 
374        frame.setLocation((screenSize.width - frameSize.width) / 2,
375                (screenSize.height - frameSize.height) / heightDivisor);
376    }
377}

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