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}