001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui;
003    
004    import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005    import static org.openstreetmap.josm.tools.I18n.tr;
006    
007    import java.io.IOException;
008    import java.lang.reflect.InvocationTargetException;
009    import java.net.HttpURLConnection;
010    import java.net.SocketException;
011    import java.net.UnknownHostException;
012    
013    import javax.swing.JOptionPane;
014    
015    import org.openstreetmap.josm.Main;
016    import org.openstreetmap.josm.data.osm.OsmPrimitive;
017    import org.openstreetmap.josm.io.ChangesetClosedException;
018    import org.openstreetmap.josm.io.IllegalDataException;
019    import org.openstreetmap.josm.io.MissingOAuthAccessTokenException;
020    import org.openstreetmap.josm.io.OsmApiException;
021    import org.openstreetmap.josm.io.OsmApiInitializationException;
022    import org.openstreetmap.josm.io.OsmTransferException;
023    import org.openstreetmap.josm.tools.BugReportExceptionHandler;
024    import org.openstreetmap.josm.tools.ExceptionUtil;
025    
026    /**
027     * This utility class provides static methods which explain various exceptions to the user.
028     *
029     */
030    public class ExceptionDialogUtil {
031    
032        /**
033         * just static utility functions. no constructor
034         */
035        private ExceptionDialogUtil() {
036        }
037    
038        /**
039         * handles an exception caught during OSM API initialization
040         *
041         * @param e the exception
042         */
043        public static void explainOsmApiInitializationException(OsmApiInitializationException e) {
044            HelpAwareOptionPane.showOptionDialog(
045                    Main.parent,
046                    ExceptionUtil.explainOsmApiInitializationException(e),
047                    tr("Error"),
048                    JOptionPane.ERROR_MESSAGE,
049                    ht("/ErrorMessages#OsmApiInitializationException")
050            );
051        }
052    
053        /**
054         * handles a ChangesetClosedException
055         *
056         * @param e the exception
057         */
058        public static void explainChangesetClosedException(ChangesetClosedException e) {
059            HelpAwareOptionPane.showOptionDialog(
060                    Main.parent,
061                    ExceptionUtil.explainChangesetClosedException(e),
062                    tr("Error"),
063                    JOptionPane.ERROR_MESSAGE,
064                    ht("/Action/Upload#ChangesetClosed")
065            );
066        }
067    
068        /**
069         * Explains an upload error due to a violated precondition, i.e. a HTTP return code 412
070         *
071         * @param e the exception
072         */
073        public static void explainPreconditionFailed(OsmApiException e) {
074            HelpAwareOptionPane.showOptionDialog(
075                    Main.parent,
076                    ExceptionUtil.explainPreconditionFailed(e),
077                    tr("Precondition violation"),
078                    JOptionPane.ERROR_MESSAGE,
079                    ht("/ErrorMessages#OsmApiException")
080            );
081        }
082    
083        /**
084         * Explains an exception with a generic message dialog
085         *
086         * @param e the exception
087         */
088        public static void explainGeneric(Exception e) {
089            e.printStackTrace();
090            BugReportExceptionHandler.handleException(e);
091        }
092    
093        /**
094         * Explains a {@link SecurityException} which has caused an {@link OsmTransferException}.
095         * This is most likely happening when user tries to access the OSM API from within an
096         * applet which wasn't loaded from the API server.
097         *
098         * @param e the exception
099         */
100    
101        public static void explainSecurityException(OsmTransferException e) {
102            HelpAwareOptionPane.showOptionDialog(
103                    Main.parent,
104                    ExceptionUtil.explainSecurityException(e),
105                    tr("Security exception"),
106                    JOptionPane.ERROR_MESSAGE,
107                    ht("/ErrorMessages#SecurityException")
108            );
109        }
110    
111        /**
112         * Explains a {@link SocketException} which has caused an {@link OsmTransferException}.
113         * This is most likely because there's not connection to the Internet or because
114         * the remote server is not reachable.
115         *
116         * @param e the exception
117         */
118    
119        public static void explainNestedSocketException(OsmTransferException e) {
120            HelpAwareOptionPane.showOptionDialog(
121                    Main.parent,
122                    ExceptionUtil.explainNestedSocketException(e),
123                    tr("Network exception"),
124                    JOptionPane.ERROR_MESSAGE,
125                    ht("/ErrorMessages#NestedSocketException")
126            );
127        }
128    
129        /**
130         * Explains a {@link IOException} which has caused an {@link OsmTransferException}.
131         * This is most likely happening when the communication with the remote server is
132         * interrupted for any reason.
133         *
134         * @param e the exception
135         */
136    
137        public static void explainNestedIOException(OsmTransferException e) {
138            HelpAwareOptionPane.showOptionDialog(
139                    Main.parent,
140                    ExceptionUtil.explainNestedIOException(e),
141                    tr("IO Exception"),
142                    JOptionPane.ERROR_MESSAGE,
143                    ht("/ErrorMessages#NestedIOException")
144            );
145        }
146    
147        /**
148         * Explains a {@link IllegalDataException} which has caused an {@link OsmTransferException}.
149         * This is most likely happening when JOSM tries to load data in in an unsupported format.
150         *
151         * @param e the exception
152         */
153    
154        public static void explainNestedIllegalDataException(OsmTransferException e) {
155            HelpAwareOptionPane.showOptionDialog(
156                    Main.parent,
157                    ExceptionUtil.explainNestedIllegalDataException(e),
158                    tr("Illegal Data"),
159                    JOptionPane.ERROR_MESSAGE,
160                    ht("/ErrorMessages#IllegalDataException")
161            );
162        }
163    
164        /**
165         * Explains a {@link InvocationTargetException }
166         *
167         * @param e the exception
168         */
169    
170        public static void explainNestedInvocationTargetException(Exception e) {
171            InvocationTargetException ex = getNestedException(e, InvocationTargetException.class);
172            if (ex != null) {
173                // Users should be able to submit a bug report for an invocation target exception
174                //
175                BugReportExceptionHandler.handleException(ex);
176                return;
177            }
178        }
179    
180        /**
181         * Explains a {@link OsmApiException} which was thrown because of an internal server
182         * error in the OSM API server.
183         *
184         * @param e the exception
185         */
186    
187        public static void explainInternalServerError(OsmTransferException e) {
188            HelpAwareOptionPane.showOptionDialog(
189                    Main.parent,
190                    ExceptionUtil.explainInternalServerError(e),
191                    tr("Internal Server Error"),
192                    JOptionPane.ERROR_MESSAGE,
193                    ht("/ErrorMessages#InternalServerError")
194            );
195        }
196    
197        /**
198         * Explains a {@link OsmApiException} which was thrown because of a bad
199         * request
200         *
201         * @param e the exception
202         */
203        public static void explainBadRequest(OsmApiException e) {
204            HelpAwareOptionPane.showOptionDialog(
205                    Main.parent,
206                    ExceptionUtil.explainBadRequest(e),
207                    tr("Bad Request"),
208                    JOptionPane.ERROR_MESSAGE,
209                    ht("/ErrorMessages#BadRequest")
210            );
211        }
212    
213        /**
214         * Explains a {@link OsmApiException} which was thrown because a resource wasn't found
215         * on the server
216         *
217         * @param e the exception
218         */
219        public static void explainNotFound(OsmApiException e) {
220            HelpAwareOptionPane.showOptionDialog(
221                    Main.parent,
222                    ExceptionUtil.explainNotFound(e),
223                    tr("Not Found"),
224                    JOptionPane.ERROR_MESSAGE,
225                    ht("/ErrorMessages#NotFound")
226            );
227        }
228    
229        /**
230         * Explains a {@link OsmApiException} which was thrown because of a conflict
231         *
232         * @param e the exception
233         */
234        public static void explainConflict(OsmApiException e) {
235            HelpAwareOptionPane.showOptionDialog(
236                    Main.parent,
237                    ExceptionUtil.explainConflict(e),
238                    tr("Conflict"),
239                    JOptionPane.ERROR_MESSAGE,
240                    ht("/ErrorMessages#Conflict")
241            );
242        }
243    
244        /**
245         * Explains a {@link OsmApiException} which was thrown because the authentication at
246         * the OSM server failed
247         *
248         * @param e the exception
249         */
250        public static void explainAuthenticationFailed(OsmApiException e) {
251            String authMethod = Main.pref.get("osm-server.auth-method", "basic");
252            String msg;
253            if (authMethod.equals("oauth")) {
254                msg = ExceptionUtil.explainFailedOAuthAuthentication(e);
255            } else {
256                msg = ExceptionUtil.explainFailedBasicAuthentication(e);
257            }
258    
259            HelpAwareOptionPane.showOptionDialog(
260                    Main.parent,
261                    msg,
262                    tr("Authentication Failed"),
263                    JOptionPane.ERROR_MESSAGE,
264                    ht("/ErrorMessages#AuthenticationFailed")
265            );
266        }
267    
268        /**
269         * Explains a {@link OsmApiException} which was thrown because accessing a protected
270         * resource was forbidden.
271         *
272         * @param e the exception
273         */
274        public static void explainAuthorizationFailed(OsmApiException e) {
275            // Fixme: add special handling that calls ExceptionUtil.explainFailedOAuthAuthorisation(e)
276            HelpAwareOptionPane.showOptionDialog(
277                    Main.parent,
278                    ExceptionUtil.explainFailedAuthorisation(e),
279                    tr("Authorisation Failed"),
280                    JOptionPane.ERROR_MESSAGE,
281                    ht("/ErrorMessages#AuthenticationFailed")
282            );
283        }
284    
285        /**
286         * Explains a {@link OsmApiException} which was thrown because of a
287         * client timeout (HTTP 408)
288         *
289         * @param e the exception
290         */
291        public static void explainClientTimeout(OsmApiException e) {
292            HelpAwareOptionPane.showOptionDialog(
293                    Main.parent,
294                    ExceptionUtil.explainClientTimeout(e),
295                    tr("Client Time Out"),
296                    JOptionPane.ERROR_MESSAGE,
297                    ht("/ErrorMessages#ClientTimeOut")
298            );
299        }
300    
301        /**
302         * Explains a {@link OsmApiException} which was thrown because of a
303         * bandwidth limit (HTTP 509)
304         *
305         * @param e the exception
306         */
307        public static void explainBandwidthLimitExceeded(OsmApiException e) {
308            HelpAwareOptionPane.showOptionDialog(
309                    Main.parent,
310                    ExceptionUtil.explainBandwidthLimitExceeded(e),
311                    tr("Bandwidth Limit Exceeded"),
312                    JOptionPane.ERROR_MESSAGE,
313                    ht("/ErrorMessages#BandwidthLimit")
314            );
315        }
316    
317        /**
318         * Explains a {@link OsmApiException} with a generic error
319         * message.
320         *
321         * @param e the exception
322         */
323        public static void explainGenericHttpException(OsmApiException e) {
324            HelpAwareOptionPane.showOptionDialog(
325                    Main.parent,
326                    ExceptionUtil.explainClientTimeout(e),
327                    tr("Communication with OSM server failed"),
328                    JOptionPane.ERROR_MESSAGE,
329                    ht("/ErrorMessages#GenericCommunicationError")
330            );
331        }
332    
333        /**
334         * Explains a {@link OsmApiException} which was thrown because accessing a protected
335         * resource was forbidden.
336         *
337         * @param e the exception
338         */
339        public static void explainMissingOAuthAccessTokenException(MissingOAuthAccessTokenException e) {
340            HelpAwareOptionPane.showOptionDialog(
341                    Main.parent,
342                    ExceptionUtil.explainMissingOAuthAccessTokenException(e),
343                    tr("Authentication failed"),
344                    JOptionPane.ERROR_MESSAGE,
345                    ht("/ErrorMessages#MissingOAuthAccessToken")
346            );
347        }
348    
349        /**
350         * Explains a {@link UnknownHostException} which has caused an {@link OsmTransferException}.
351         * This is most likely happening when there is an error in the API URL or when
352         * local DNS services are not working.
353         *
354         * @param e the exception
355         */
356    
357        public static void explainNestedUnkonwnHostException(OsmTransferException e) {
358            HelpAwareOptionPane.showOptionDialog(
359                    Main.parent,
360                    ExceptionUtil.explainNestedUnknownHostException(e),
361                    tr("Unknown host"),
362                    JOptionPane.ERROR_MESSAGE,
363                    ht("/ErrorMessages#UnknownHost")
364            );
365        }
366    
367        /**
368         * Replies the first nested exception of type <code>nestedClass</code> (including
369         * the root exception <code>e</code>) or null, if no such exception is found.
370         *
371         * @param <T>
372         * @param e the root exception
373         * @param nestedClass the type of the nested exception
374         * @return the first nested exception of type <code>nestedClass</code> (including
375         * the root exception <code>e</code>) or null, if no such exception is found.
376         */
377        protected static <T> T getNestedException(Exception e, Class<T> nestedClass) {
378            Throwable t = e;
379            while (t != null && !(nestedClass.isInstance(t))) {
380                t = t.getCause();
381            }
382            if (t == null)
383                return null;
384            else if (nestedClass.isInstance(t))
385                return nestedClass.cast(t);
386            return null;
387        }
388    
389        /**
390         * Explains an {@link OsmTransferException} to the user.
391         *
392         * @param e the {@link OsmTransferException}
393         */
394        public static void explainOsmTransferException(OsmTransferException e) {
395            if (getNestedException(e, SecurityException.class) != null) {
396                explainSecurityException(e);
397                return;
398            }
399            if (getNestedException(e, SocketException.class) != null) {
400                explainNestedSocketException(e);
401                return;
402            }
403            if (getNestedException(e, UnknownHostException.class) != null) {
404                explainNestedUnkonwnHostException(e);
405                return;
406            }
407            if (getNestedException(e, IOException.class) != null) {
408                explainNestedIOException(e);
409                return;
410            }
411            if (getNestedException(e, IllegalDataException.class) != null) {
412                explainNestedIllegalDataException(e);
413                return;
414            }
415            if (e instanceof OsmApiInitializationException) {
416                explainOsmApiInitializationException((OsmApiInitializationException) e);
417                return;
418            }
419    
420            if (e instanceof ChangesetClosedException) {
421                explainChangesetClosedException((ChangesetClosedException)e);
422                return;
423            }
424    
425            if (e instanceof MissingOAuthAccessTokenException) {
426                explainMissingOAuthAccessTokenException((MissingOAuthAccessTokenException)e);
427                return;
428            }
429    
430            if (e instanceof OsmApiException) {
431                OsmApiException oae = (OsmApiException) e;
432                switch(oae.getResponseCode()) {
433                case HttpURLConnection.HTTP_PRECON_FAILED:
434                    explainPreconditionFailed(oae);
435                    return;
436                case HttpURLConnection.HTTP_GONE:
437                    explainGoneForUnknownPrimitive(oae);
438                    return;
439                case HttpURLConnection.HTTP_INTERNAL_ERROR:
440                    explainInternalServerError(oae);
441                    return;
442                case HttpURLConnection.HTTP_BAD_REQUEST:
443                    explainBadRequest(oae);
444                    return;
445                case HttpURLConnection.HTTP_NOT_FOUND:
446                    explainNotFound(oae);
447                    return;
448                case HttpURLConnection.HTTP_CONFLICT:
449                    explainConflict(oae);
450                    return;
451                case HttpURLConnection.HTTP_UNAUTHORIZED:
452                    explainAuthenticationFailed(oae);
453                    return;
454                case HttpURLConnection.HTTP_FORBIDDEN:
455                    explainAuthorizationFailed(oae);
456                    return;
457                case HttpURLConnection.HTTP_CLIENT_TIMEOUT:
458                    explainClientTimeout(oae);
459                    return;
460                case 509:
461                    explainBandwidthLimitExceeded(oae);
462                    return;
463                default:
464                    explainGenericHttpException(oae);
465                    return;
466                }
467            }
468            explainGeneric(e);
469        }
470    
471        /**
472         * explains the case of an error due to a delete request on an already deleted
473         * {@link OsmPrimitive}, i.e. a HTTP response code 410, where we don't know which
474         * {@link OsmPrimitive} is causing the error.
475         *
476         * @param e the exception
477         */
478        public static void explainGoneForUnknownPrimitive(OsmApiException e) {
479            HelpAwareOptionPane.showOptionDialog(
480                    Main.parent,
481                    ExceptionUtil.explainGoneForUnknownPrimitive(e),
482                    tr("Object deleted"),
483                    JOptionPane.ERROR_MESSAGE,
484                    ht("/ErrorMessages#GoneForUnknownPrimitive")
485            );
486        }
487    
488        /**
489         * Explains an {@link Exception} to the user.
490         *
491         * @param e the {@link Exception}
492         */
493        public static void explainException(Exception e) {
494            if (getNestedException(e, InvocationTargetException.class) != null) {
495                explainNestedInvocationTargetException(e);
496                return;
497            }
498            if (e instanceof OsmTransferException) {
499                explainOsmTransferException((OsmTransferException) e);
500                return;
501            }
502            explainGeneric(e);
503        }
504    }