001/* Handle a MoML Parsing Error.
002
003 Copyright (c) 2000-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.vergil;
029
030import java.awt.Component;
031import java.awt.Dimension;
032
033import javax.swing.JOptionPane;
034import javax.swing.JScrollPane;
035import javax.swing.JTextArea;
036
037import ptolemy.gui.UndeferredGraphicalMessageHandler;
038import ptolemy.kernel.util.KernelException;
039import ptolemy.kernel.util.NamedObj;
040import ptolemy.moml.ErrorHandler;
041import ptolemy.util.StringUtilities;
042
043///////////////////////////////////////////////////////////////////
044//// VergilErrorHandler
045
046/**
047 This error handler attempts to replace any failed MoML elements with
048 generic versions so that the parsing of the MoML can continue. The
049 generic versions, where appropriate, have icons that indicate failure.
050
051 @author Edward A. Lee
052 @version $Id$
053 @since Ptolemy II 2.0
054 @Pt.ProposedRating Red (eal)
055 @Pt.AcceptedRating Red (reviewmoderator)
056 */
057public class VergilErrorHandler implements ErrorHandler {
058    ///////////////////////////////////////////////////////////////////
059    ////                         public methods                    ////
060
061    /** Enable or disable skipping of errors.
062     *  If this method is called with a true argument, then
063     *  do not report subsequent errors when handleError() is called,
064     *  and instead return CONTINUE. If it is called with a false
065     *  argument, then report all subsequent errors.
066     *  <p>
067     *  This method is intended to be used when an operation may trigger
068     *  a large number of errors, and the user interface wishes to offer
069     *  the user the option of ignoring them.  This method should be
070     *  called with a true argument before the operation begins, and
071     *  then called with a false argument after the operation ends.
072     *  @param enable True to enable skipping, false to disable.
073     */
074    @Override
075    public void enableErrorSkipping(boolean enable) {
076        _skippingEnabled = enable;
077
078        if (!enable) {
079            _skipping = false;
080        }
081    }
082
083    /** Handle an error.
084     *  @param element The XML element that triggered the error.
085     *  @param context The container object for the element.
086     *  @param exception The exception that was thrown.
087     *  @return CONTINUE to skip this element, CANCEL to abort processing
088     *   of the XML, IGNORE to continue to process the XML as if nothing
089     *   had happened, or RETHROW to request that the exception be rethrown.
090     */
091    @Override
092    public int handleError(String element, NamedObj context,
093            Throwable exception) {
094        if (_skipping) {
095            return CONTINUE;
096        }
097
098        // Get the context w.r.t. which the dialog should be iconified.
099        // FIXME: This pattern window actually is never set.
100        Component parentWindow = UndeferredGraphicalMessageHandler.getContext();
101
102        // If the element is long, we truncate it.
103        String message = "Error encountered in XML element:\n"
104                + StringUtilities.truncateString(element, 80, 1) + "\n"
105                + exception.getMessage();
106
107        Object[] messageArray = new Object[1];
108        messageArray[0] = StringUtilities.ellipsis(message,
109                StringUtilities.ELLIPSIS_LENGTH_LONG);
110
111        if (context == null) {
112            // Top-level object, so continuing is not an option.
113            messageArray[0] = messageArray[0]
114                    + "\nThis is a top-level element, so cannot continue.";
115
116            Object[] options = { "Display stack trace", "Cancel" };
117
118            // Show the MODAL dialog
119            int selected = JOptionPane.showOptionDialog(parentWindow,
120                    messageArray, "Error", JOptionPane.YES_NO_OPTION,
121                    JOptionPane.ERROR_MESSAGE, null, options, options[0]);
122
123            if (selected == 0) {
124                return _showStackTrace(parentWindow, false, false, exception,
125                        message);
126            }
127
128            return CANCEL;
129        } else {
130            if (_skippingEnabled) {
131                Object[] options = { "Skip element", "Skip remaining errors",
132                        "Display stack trace", "Cancel" };
133
134                // Show a MODAL dialog
135                int selected = JOptionPane.showOptionDialog(parentWindow,
136                        messageArray, "Error", JOptionPane.YES_NO_OPTION,
137                        JOptionPane.ERROR_MESSAGE, null, options, options[0]);
138
139                if (selected == 3) {
140                    return CANCEL;
141                } else if (selected == 2) {
142                    return _showStackTrace(parentWindow, true, _skippingEnabled,
143                            exception, message);
144                } else if (selected == 1) {
145                    _skipping = true;
146                }
147
148                return CONTINUE;
149            } else {
150                // Skipping is not enabled.
151                Object[] options = { "Skip element", "Display stack trace",
152                        "Cancel" };
153
154                // Show the MODAL dialog
155                int selected = JOptionPane.showOptionDialog(parentWindow,
156                        messageArray, "Error", JOptionPane.YES_NO_OPTION,
157                        JOptionPane.ERROR_MESSAGE, null, options, options[0]);
158
159                if (selected == 1) {
160                    return _showStackTrace(parentWindow, false,
161                            _skippingEnabled, exception, message);
162                } else if (selected == 2) {
163                    return CANCEL;
164                }
165
166                return CONTINUE;
167            }
168        }
169    }
170
171    ///////////////////////////////////////////////////////////////////
172    ////                         private methods                   ////
173
174    /** Display a stack trace dialog. The "info" argument is a
175     *  string printed at the top of the dialog instead of the Exception
176     *  message.
177     *  @param context The context.
178     *  @param skipElement True if one of the buttons should be
179     *  'Skip element'.    If skippingEnabled is true,
180     *  then the value of skipElement is ignored.
181     *  @param skippingEnabled True if one of the buttons should be
182     *  'Skip remaining messages'.
183     *  @param exception The exception.
184     *  @param info A message.
185     *  @return CONTINUE to skip this element, CANCEL to abort processing
186     *   of the XML.
187     */
188    private int _showStackTrace(Component context, boolean skipElement,
189            boolean skippingEnabled, Throwable exception, String info) {
190        // FIXME: Eventually, the dialog should
191        // be able to email us a bug report.
192        // FIXME: The user should be able to click on the links and
193        // jump to the line in the offending text.
194        // Show the stack trace in a scrollable text area.
195        JTextArea text = new JTextArea(
196                KernelException.stackTraceToString(exception), 60, 80);
197        JScrollPane scrollPane = new JScrollPane(text);
198        scrollPane.setPreferredSize(new Dimension(600, 300));
199        text.setCaretPosition(0);
200        text.setEditable(false);
201
202        // We want to stack the text area with another message
203        Object[] message = new Object[2];
204        String string;
205
206        if (info != null) {
207            string = info + "\n" + exception.getMessage();
208        } else {
209            string = exception.getMessage();
210        }
211
212        message[0] = StringUtilities.ellipsis(string,
213                StringUtilities.ELLIPSIS_LENGTH_SHORT);
214
215        message[1] = scrollPane;
216
217        Object[] options = null;
218
219        if (skippingEnabled) {
220            options = new Object[] { "Skip element", "Skip remaining errors",
221                    "Cancel" };
222        } else {
223            if (skipElement) {
224                options = new Object[] { "Skip element", "Cancel" };
225            } else {
226                options = new Object[] { "Cancel" };
227            }
228        }
229
230        // Show the MODAL dialog
231        int selected = JOptionPane.showOptionDialog(context, message,
232                "Stack trace", JOptionPane.YES_NO_OPTION,
233                JOptionPane.ERROR_MESSAGE, null, options, options[0]);
234
235        if (selected == options.length - 1) {
236            // The last button is the Cancel button.
237            return CANCEL;
238        }
239
240        if (skippingEnabled && selected == 1) {
241            _skipping = true;
242        }
243
244        return CONTINUE;
245    }
246
247    ///////////////////////////////////////////////////////////////////
248    ////                         private variables                 ////
249    // Enable skipping.
250    private boolean _skippingEnabled = false;
251
252    // Activate skipping.
253    private boolean _skipping = false;
254}