source: josm/trunk/src/org/openstreetmap/josm/tools/ExceptionUtil.java @ 5241

Revision 4821, 31.1 KB checked in by simon04, 4 months ago (diff)

fix #7257 - NumberFormatException

  • Property svn:eol-style set to native
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.io.IOException;
8import java.net.HttpURLConnection;
9import java.net.MalformedURLException;
10import java.net.SocketException;
11import java.net.URL;
12import java.net.UnknownHostException;
13import java.text.DateFormat;
14import java.text.ParseException;
15import java.text.SimpleDateFormat;
16import java.util.Collection;
17import java.util.Date;
18import java.util.Locale;
19import java.util.Set;
20import java.util.TreeSet;
21import java.util.regex.Matcher;
22import java.util.regex.Pattern;
23
24import org.openstreetmap.josm.Main;
25import org.openstreetmap.josm.data.osm.Node;
26import org.openstreetmap.josm.data.osm.OsmPrimitive;
27import org.openstreetmap.josm.data.osm.Relation;
28import org.openstreetmap.josm.data.osm.Way;
29import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
30import org.openstreetmap.josm.io.ChangesetClosedException;
31import org.openstreetmap.josm.io.IllegalDataException;
32import org.openstreetmap.josm.io.MissingOAuthAccessTokenException;
33import org.openstreetmap.josm.io.OsmApi;
34import org.openstreetmap.josm.io.OsmApiException;
35import org.openstreetmap.josm.io.OsmApiInitializationException;
36import org.openstreetmap.josm.io.OsmTransferException;
37import org.openstreetmap.josm.io.auth.CredentialsManager;
38
39@SuppressWarnings("CallToThreadDumpStack")
40public class ExceptionUtil {
41    private ExceptionUtil() {
42    }
43
44    /**
45     * handles an exception caught during OSM API initialization
46     *
47     * @param e the exception
48     */
49    public static String explainOsmApiInitializationException(OsmApiInitializationException e) {
50        e.printStackTrace();
51        String msg = tr(
52                "<html>Failed to initialize communication with the OSM server {0}.<br>"
53                + "Check the server URL in your preferences and your internet connection.", Main.pref.get(
54                        "osm-server.url", "http://api.openstreetmap.org/api"));
55        return msg;
56    }
57
58
59    /**
60     *  Creates the error message
61     *
62     * @param e the exception
63     */
64    public static String explainMissingOAuthAccessTokenException(MissingOAuthAccessTokenException e) {
65        e.printStackTrace();
66        String msg = tr(
67                "<html>Failed to authenticate at the OSM server ''{0}''.<br>"
68                + "You are using OAuth to authenticate but currently there is no<br>"
69                + "OAuth Access Token configured.<br>"
70                + "Please open the Preferences Dialog and generate or enter an Access Token."
71                + "</html>",
72                Main.pref.get("osm-server.url", "http://api.openstreetmap.org/api")
73        );
74        return msg;
75    }
76
77    public static Pair<OsmPrimitive, Collection<OsmPrimitive>> parsePreconditionFailed(String msg) {
78        final String ids = "(\\d+(?:,\\d+)*)";
79        final Collection<OsmPrimitive> refs = new TreeSet<OsmPrimitive>(); // error message can contain several times the same way
80        Matcher m;
81        m = Pattern.compile(".*Node (\\d+) is still used by relations " + ids + ".*").matcher(msg);
82        if (m.matches()) {
83            OsmPrimitive n = new Node(Long.parseLong(m.group(1)));
84            for (String s : m.group(2).split(",")) {
85                refs.add(new Relation(Long.parseLong(s)));
86            }
87            return Pair.create(n, refs);
88        }
89        m = Pattern.compile(".*Node (\\d+) is still used by ways " + ids + ".*").matcher(msg);
90        if (m.matches()) {
91            OsmPrimitive n = new Node(Long.parseLong(m.group(1)));
92            for (String s : m.group(2).split(",")) {
93                refs.add(new Way(Long.parseLong(s)));
94            }
95            return Pair.create(n, refs);
96        }
97        m = Pattern.compile(".*The relation (\\d+) is used in relations? " + ids + ".*").matcher(msg);
98        if (m.matches()) {
99            OsmPrimitive n = new Relation(Long.parseLong(m.group(1)));
100            for (String s : m.group(2).split(",")) {
101                refs.add(new Relation(Long.parseLong(s)));
102            }
103            return Pair.create(n, refs);
104        }
105        m = Pattern.compile(".*Way (\\d+) is still used by relations " + ids + ".*").matcher(msg);
106        if (m.matches()) {
107            OsmPrimitive n = new Way(Long.parseLong(m.group(1)));
108            for (String s : m.group(2).split(",")) {
109                refs.add(new Relation(Long.parseLong(s)));
110            }
111            return Pair.create(n, refs);
112        }
113        m = Pattern.compile(".*Way (\\d+) requires the nodes with id in " + ids + ".*").matcher(msg); // ... ", which either do not exist, or are not visible"
114        if (m.matches()) {
115            OsmPrimitive n = new Way(Long.parseLong(m.group(1)));
116            for (String s : m.group(2).split(",")) {
117                refs.add(new Node(Long.parseLong(s)));
118            }
119            return Pair.create(n, refs);
120        }
121        return null;
122    }
123
124    /**
125     * Explains an upload error due to a violated precondition, i.e. a HTTP return code 412
126     *
127     * @param e the exception
128     */
129    public static String explainPreconditionFailed(OsmApiException e) {
130        e.printStackTrace();
131        String msg = e.getErrorHeader();
132        Pair<OsmPrimitive, Collection<OsmPrimitive>> conflict = parsePreconditionFailed(e.getErrorHeader());
133        if (conflict != null) {
134            OsmPrimitive firstRefs = conflict.b.iterator().next();
135            Long objId = conflict.a.getId();
136            Collection<Long> refIds= Utils.transform(conflict.b, new Utils.Function<OsmPrimitive, Long>() {
137
138                @Override
139                public Long apply(OsmPrimitive x) {
140                    return x.getId();
141                }
142            });
143            String refIdsString = refIds.size() == 1 ? refIds.iterator().next().toString() : refIds.toString();
144            if (conflict.a instanceof Node) {
145                if (firstRefs instanceof Node) {
146                    return "<html>" + trn(
147                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
148                            + " It is still referred to by node {1}.<br>"
149                            + "Please load the node, remove the reference to the node, and upload again.",
150                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
151                            + " It is still referred to by nodes {1}.<br>"
152                            + "Please load the nodes, remove the reference to the node, and upload again.",
153                            conflict.b.size(), objId, refIdsString) + "</html>";
154                } else if (firstRefs instanceof Way) {
155                    return "<html>" + trn(
156                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
157                            + " It is still referred to by way {1}.<br>"
158                            + "Please load the way, remove the reference to the node, and upload again.",
159                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
160                            + " It is still referred to by ways {1}.<br>"
161                            + "Please load the ways, remove the reference to the node, and upload again.",
162                            conflict.b.size(), objId, refIdsString) + "</html>";
163                } else if (firstRefs instanceof Relation) {
164                    return "<html>" + trn(
165                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
166                            + " It is still referred to by relation {1}.<br>"
167                            + "Please load the relation, remove the reference to the node, and upload again.",
168                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
169                            + " It is still referred to by relations {1}.<br>"
170                            + "Please load the relations, remove the reference to the node, and upload again.",
171                            conflict.b.size(), objId, refIdsString) + "</html>";
172                } else {
173                    throw new IllegalStateException();
174                }
175            } else if (conflict.a instanceof Way) {
176                if (firstRefs instanceof Node) {
177                    return "<html>" + trn(
178                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
179                            + " It is still referred to by node {1}.<br>"
180                            + "Please load the node, remove the reference to the way, and upload again.",
181                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
182                            + " It is still referred to by nodes {1}.<br>"
183                            + "Please load the nodes, remove the reference to the way, and upload again.",
184                            conflict.b.size(), objId, refIdsString) + "</html>";
185                } else if (firstRefs instanceof Way) {
186                    return "<html>" + trn(
187                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
188                            + " It is still referred to by way {1}.<br>"
189                            + "Please load the way, remove the reference to the way, and upload again.",
190                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
191                            + " It is still referred to by ways {1}.<br>"
192                            + "Please load the ways, remove the reference to the way, and upload again.",
193                            conflict.b.size(), objId, refIdsString) + "</html>";
194                } else if (firstRefs instanceof Relation) {
195                    return "<html>" + trn(
196                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
197                            + " It is still referred to by relation {1}.<br>"
198                            + "Please load the relation, remove the reference to the way, and upload again.",
199                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
200                            + " It is still referred to by relations {1}.<br>"
201                            + "Please load the relations, remove the reference to the way, and upload again.",
202                            conflict.b.size(), objId, refIdsString) + "</html>";
203                } else {
204                    throw new IllegalStateException();
205                }
206            } else if (conflict.a instanceof Relation) {
207                if (firstRefs instanceof Node) {
208                    return "<html>" + trn(
209                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
210                            + " It is still referred to by node {1}.<br>"
211                            + "Please load the node, remove the reference to the relation, and upload again.",
212                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
213                            + " It is still referred to by nodes {1}.<br>"
214                            + "Please load the nodes, remove the reference to the relation, and upload again.",
215                            conflict.b.size(), objId, refIdsString) + "</html>";
216                } else if (firstRefs instanceof Way) {
217                    return "<html>" + trn(
218                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
219                            + " It is still referred to by way {1}.<br>"
220                            + "Please load the way, remove the reference to the relation, and upload again.",
221                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
222                            + " It is still referred to by ways {1}.<br>"
223                            + "Please load the ways, remove the reference to the relation, and upload again.",
224                            conflict.b.size(), objId, refIdsString) + "</html>";
225                } else if (firstRefs instanceof Relation) {
226                    return "<html>" + trn(
227                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
228                            + " It is still referred to by relation {1}.<br>"
229                            + "Please load the relation, remove the reference to the relation, and upload again.",
230                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
231                            + " It is still referred to by relations {1}.<br>"
232                            + "Please load the relations, remove the reference to the relation, and upload again.",
233                            conflict.b.size(), objId, refIdsString) + "</html>";
234                } else {
235                    throw new IllegalStateException();
236                }
237            } else {
238                throw new IllegalStateException();
239            }
240        } else {
241            return tr(
242                    "<html>Uploading to the server <strong>failed</strong> because your current<br>"
243                    + "dataset violates a precondition.<br>" + "The error message is:<br>" + "{0}" + "</html>",
244                    escapeReservedCharactersHTML(e.getMessage()));
245        }
246    }
247
248    public static String explainFailedBasicAuthentication(OsmApiException e) {
249        e.printStackTrace();
250        return tr("<html>"
251                + "Authentication at the OSM server with the username ''{0}'' failed.<br>"
252                + "Please check the username and the password in the JOSM preferences."
253                + "</html>",
254                CredentialsManager.getInstance().getUsername()
255        );
256    }
257
258    public static String explainFailedOAuthAuthentication(OsmApiException e) {
259        e.printStackTrace();
260        return tr("<html>"
261                + "Authentication at the OSM server with the OAuth token ''{0}'' failed.<br>"
262                + "Please launch the preferences dialog and retrieve another OAuth token."
263                + "</html>",
264                OAuthAccessTokenHolder.getInstance().getAccessTokenKey()
265        );
266    }
267
268    public static String explainFailedAuthorisation(OsmApiException e) {
269        e.printStackTrace();
270        String header = e.getErrorHeader();
271        String body = e.getErrorBody();
272        if (body.equals("Your access to the API is temporarily suspended. Please log-in to the web interface to view the Contributor Terms. You do not need to agree, but you must view them.")) {
273            return tr("<html>"
274                    +"Your access to the API is temporarily suspended.<br>"
275                    + "Please log-in to the web interface to view the Contributor Terms.<br>"
276                    + "You do not need to agree, but you must view them."
277                    + "</html>");
278        }
279        String msg = null;
280        if (header != null) {
281            if (body != null && !header.equals(body)) {
282                msg = header + " (" + body + ")";
283            } else {
284                msg = header;
285            }
286        } else {
287            msg = body;
288        }
289       
290        return tr("<html>"
291                + "Authorisation at the OSM server failed.<br>"
292                + "The server reported the following error:<br>"
293                + "''{0}''"
294                + "</html>",
295                msg
296        );
297    }
298
299    public static String explainFailedOAuthAuthorisation(OsmApiException e) {
300        e.printStackTrace();
301        return tr("<html>"
302                + "Authorisation at the OSM server with the OAuth token ''{0}'' failed.<br>"
303                + "The token is not authorised to access the protected resource<br>"
304                + "''{1}''.<br>"
305                + "Please launch the preferences dialog and retrieve another OAuth token."
306                + "</html>",
307                OAuthAccessTokenHolder.getInstance().getAccessTokenKey(),
308                e.getAccessedUrl() == null ? tr("unknown") : e.getAccessedUrl()
309        );
310    }
311
312    /**
313     * Explains an OSM API exception because of a client timeout (HTTP 408).
314     *
315     * @param e the exception
316     * @return the message
317     */
318    public static String explainClientTimeout(OsmApiException e) {
319        e.printStackTrace();
320        return tr("<html>"
321                + "Communication with the OSM server ''{0}'' timed out. Please retry later."
322                + "</html>",
323                OsmApi.getOsmApi().getBaseUrl()
324        );
325    }
326
327    /**
328     * Replies a generic error message for an OSM API exception
329     *
330     * @param e the exception
331     * @return the message
332     */
333    public static String explainGenericOsmApiException(OsmApiException e) {
334        e.printStackTrace();
335        String errMsg = e.getErrorHeader();
336        if (errMsg == null) {
337            errMsg = e.getErrorBody();
338        }
339        if (errMsg == null) {
340            errMsg = tr("no error message available");
341        }
342        return tr("<html>"
343                + "Communication with the OSM server ''{0}''failed. The server replied<br>"
344                + "the following error code and the following error message:<br>"
345                + "<strong>Error code:<strong> {1}<br>"
346                + "<strong>Error message (untranslated)</strong>: {2}"
347                + "</html>",
348                OsmApi.getOsmApi().getBaseUrl(),
349                e.getResponseCode(),
350                errMsg
351        );
352    }
353
354    /**
355     * Explains an error due to a 409 conflict
356     *
357     * @param e the exception
358     */
359    public static String explainConflict(OsmApiException e) {
360        e.printStackTrace();
361        String msg = e.getErrorHeader();
362        if (msg != null) {
363            String pattern = "The changeset (\\d+) was closed at (.*)";
364            Pattern p = Pattern.compile(pattern);
365            Matcher m = p.matcher(msg);
366            if (m.matches()) {
367                long changesetId = Long.parseLong(m.group(1));
368                // Example: "2010-09-07 14:39:41 UTC". Always parsed with US locale, regardless
369                // of the current locale in JOSM
370                DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.US);
371                Date closeDate = null;
372                try {
373                    closeDate = formatter.parse(m.group(2));
374                } catch(ParseException ex) {
375                    System.err.println(tr("Failed to parse date ''{0}'' replied by server.", m.group(2)));
376                    ex.printStackTrace();
377                }
378                if (closeDate == null) {
379                    msg = tr(
380                            "<html>Closing of changeset <strong>{0}</strong> failed <br>because it has already been closed.",
381                            changesetId
382                    );
383                } else {
384                    SimpleDateFormat dateFormat = new SimpleDateFormat();
385                    msg = tr(
386                            "<html>Closing of changeset <strong>{0}</strong> failed<br>"
387                            +" because it has already been closed on {1}.",
388                            changesetId,
389                            dateFormat.format(closeDate)
390                    );
391                }
392                return msg;
393            }
394            msg = tr(
395                    "<html>The server reported that it has detected a conflict.<br>" +
396                    "Error message (untranslated):<br>{0}</html>",
397                    msg
398            );
399        } else {
400            msg = tr(
401                    "<html>The server reported that it has detected a conflict.");
402        }
403        return msg;
404    }
405
406    /**
407     * Explains an exception thrown during upload because the changeset which data is
408     * uploaded to is already closed.
409     *
410     * @param e the exception
411     */
412    public static String explainChangesetClosedException(ChangesetClosedException e) {
413        String msg;
414        SimpleDateFormat dateFormat = new SimpleDateFormat();
415        msg = tr(
416                "<html>Failed to upload to changeset <strong>{0}</strong><br>"
417                +"because it has already been closed on {1}.",
418                e.getChangesetId(),
419                e.getClosedOn() == null ? "?" : dateFormat.format(e.getClosedOn())
420        );
421        e.printStackTrace();
422        return msg;
423    }
424
425    /**
426     * Explains an exception with a generic message dialog
427     *
428     * @param e the exception
429     */
430    public static String explainGeneric(Exception e) {
431        String msg = e.getMessage();
432        if (msg == null || msg.trim().equals("")) {
433            msg = e.toString();
434        }
435        e.printStackTrace();
436        return escapeReservedCharactersHTML(msg);
437    }
438
439    /**
440     * Explains a {@see SecurityException} which has caused an {@see OsmTransferException}.
441     * This is most likely happening when user tries to access the OSM API from within an
442     * applet which wasn't loaded from the API server.
443     *
444     * @param e the exception
445     */
446
447    public static String explainSecurityException(OsmTransferException e) {
448        String apiUrl = e.getUrl();
449        String host = tr("unknown");
450        try {
451            host = new URL(apiUrl).getHost();
452        } catch (MalformedURLException ex) {
453            // shouldn't happen
454        }
455
456        String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''<br>"
457                + "for security reasons. This is most likely because you are running<br>"
458                + "in an applet and because you did not load your applet from ''{1}''.", apiUrl, host);
459        return message;
460    }
461
462    /**
463     * Explains a {@see SocketException} which has caused an {@see OsmTransferException}.
464     * This is most likely because there's not connection to the Internet or because
465     * the remote server is not reachable.
466     *
467     * @param e the exception
468     */
469
470    public static String explainNestedSocketException(OsmTransferException e) {
471        String apiUrl = e.getUrl();
472        String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''.<br>"
473                + "Please check your internet connection.", apiUrl);
474        e.printStackTrace();
475        return message;
476    }
477
478    /**
479     * Explains a {@see IOException} which has caused an {@see OsmTransferException}.
480     * This is most likely happening when the communication with the remote server is
481     * interrupted for any reason.
482     *
483     * @param e the exception
484     */
485
486    public static String explainNestedIOException(OsmTransferException e) {
487        IOException ioe = getNestedException(e, IOException.class);
488        String apiUrl = e.getUrl();
489        String message = tr("<html>Failed to upload data to or download data from<br>" + "''{0}''<br>"
490                + "due to a problem with transferring data.<br>" + "Details(untranslated): {1}</html>", apiUrl, ioe
491                .getMessage());
492        e.printStackTrace();
493        return message;
494    }
495
496    /**
497     * Explains a {@see IllegalDataException} which has caused an {@see OsmTransferException}.
498     * This is most likely happening when JOSM tries to load data in in an unsupported format.
499     *
500     * @param e the exception
501     */
502    public static String explainNestedIllegalDataException(OsmTransferException e) {
503        IllegalDataException ide = getNestedException(e, IllegalDataException.class);
504        String message = tr("<html>Failed to download data. "
505                + "Its format is either unsupported, ill-formed, and/or inconsistent.<br>"
506                + "<br>Details (untranslated): {0}</html>", ide.getMessage());
507        e.printStackTrace();
508        return message;
509    }
510
511    /**
512     * Explains a {@see OsmApiException} which was thrown because of an internal server
513     * error in the OSM API server..
514     *
515     * @param e the exception
516     */
517
518    public static String explainInternalServerError(OsmTransferException e) {
519        String apiUrl = e.getUrl();
520        String message = tr("<html>The OSM server<br>" + "''{0}''<br>" + "reported an internal server error.<br>"
521                + "This is most likely a temporary problem. Please try again later.", apiUrl);
522        e.printStackTrace();
523        return message;
524    }
525
526    /**
527     * Explains a {@see OsmApiException} which was thrown because of a bad
528     * request
529     *
530     * @param e the exception
531     */
532    public static String explainBadRequest(OsmApiException e) {
533        String apiUrl = OsmApi.getOsmApi().getBaseUrl();
534        String message = tr("The OSM server ''{0}'' reported a bad request.<br>", apiUrl);
535        if (e.getErrorHeader() != null &&
536                (e.getErrorHeader().startsWith("The maximum bbox") ||
537                        e.getErrorHeader().startsWith("You requested too many nodes"))) {
538            message += "<br>"
539                + tr("The area you tried to download is too big or your request was too large."
540                        + "<br>Either request a smaller area or use an export file provided by the OSM community.");
541        } else if (e.getErrorHeader() != null) {
542            message += tr("<br>Error message(untranslated): {0}", e.getErrorHeader());
543        }
544        message = "<html>" + message + "</html>";
545        e.printStackTrace();
546        return message;
547    }
548   
549    /**
550     * Explains a {@see OsmApiException} which was thrown because of
551     * bandwidth limit exceeded (HTTP error 509)
552     *
553     * @param e the exception
554     */
555    public static String explainBandwidthLimitExceeded(OsmApiException e) {
556        // TODO: Write a proper error message
557        String message = explainGenericOsmApiException(e);
558        e.printStackTrace();
559        return message;
560    }
561   
562
563    /**
564     * Explains a {@see OsmApiException} which was thrown because a resource wasn't found.
565     *
566     * @param e the exception
567     */
568    public static String explainNotFound(OsmApiException e) {
569        String apiUrl = OsmApi.getOsmApi().getBaseUrl();
570        String message = tr("The OSM server ''{0}'' does not know about an object<br>"
571                + "you tried to read, update, or delete. Either the respective object<br>"
572                + "does not exist on the server or you are using an invalid URL to access<br>"
573                + "it. Please carefully check the server''s address ''{0}'' for typos."
574                , apiUrl);
575        message = "<html>" + message + "</html>";
576        e.printStackTrace();
577        return message;
578    }
579
580    /**
581     * Explains a {@see UnknownHostException} which has caused an {@see OsmTransferException}.
582     * This is most likely happening when there is an error in the API URL or when
583     * local DNS services are not working.
584     *
585     * @param e the exception
586     */
587
588    public static String explainNestedUnknownHostException(OsmTransferException e) {
589        String apiUrl = e.getUrl();
590        String host = tr("unknown");
591        try {
592            host = new URL(apiUrl).getHost();
593        } catch (MalformedURLException ex) {
594            // shouldn't happen
595        }
596
597        String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''.<br>"
598                + "Host name ''{1}'' could not be resolved. <br>"
599                + "Please check the API URL in your preferences and your internet connection.", apiUrl, host);
600        e.printStackTrace();
601        return message;
602    }
603
604    /**
605     * Replies the first nested exception of type <code>nestedClass</code> (including
606     * the root exception <code>e</code>) or null, if no such exception is found.
607     *
608     * @param <T>
609     * @param e the root exception
610     * @param nestedClass the type of the nested exception
611     * @return the first nested exception of type <code>nestedClass</code> (including
612     * the root exception <code>e</code>) or null, if no such exception is found.
613     */
614    protected static <T> T getNestedException(Exception e, Class<T> nestedClass) {
615        Throwable t = e;
616        while (t != null && !(nestedClass.isInstance(t))) {
617            t = t.getCause();
618        }
619        if (t == null)
620            return null;
621        else if (nestedClass.isInstance(t))
622            return nestedClass.cast(t);
623        return null;
624    }
625
626    /**
627     * Explains an {@see OsmTransferException} to the user.
628     *
629     * @param e the {@see OsmTransferException}
630     */
631    public static String explainOsmTransferException(OsmTransferException e) {
632        if (getNestedException(e, SecurityException.class) != null)
633            return explainSecurityException(e);
634        if (getNestedException(e, SocketException.class) != null)
635            return explainNestedSocketException(e);
636        if (getNestedException(e, UnknownHostException.class) != null)
637            return explainNestedUnknownHostException(e);
638        if (getNestedException(e, IOException.class) != null)
639            return explainNestedIOException(e);
640        if (e instanceof OsmApiInitializationException)
641            return explainOsmApiInitializationException((OsmApiInitializationException) e);
642
643        if (e instanceof ChangesetClosedException)
644            return explainChangesetClosedException((ChangesetClosedException)e);
645
646        if (e instanceof OsmApiException) {
647            OsmApiException oae = (OsmApiException) e;
648            if (oae.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED)
649                return explainPreconditionFailed(oae);
650            if (oae.getResponseCode() == HttpURLConnection.HTTP_GONE)
651                return explainGoneForUnknownPrimitive(oae);
652            if (oae.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR)
653                return explainInternalServerError(oae);
654            if (oae.getResponseCode() == HttpURLConnection.HTTP_BAD_REQUEST)
655                return explainBadRequest(oae);
656            if (oae.getResponseCode() == 509)
657                return explainBandwidthLimitExceeded(oae);
658        }
659        return explainGeneric(e);
660    }
661
662    /**
663     * explains the case of an error due to a delete request on an already deleted
664     * {@see OsmPrimitive}, i.e. a HTTP response code 410, where we don't know which
665     * {@see OsmPrimitive} is causing the error.
666     *
667     * @param e the exception
668     */
669    public static String explainGoneForUnknownPrimitive(OsmApiException e) {
670        String msg = tr(
671                "<html>The server reports that an object is deleted.<br>"
672                + "<strong>Uploading failed</strong> if you tried to update or delete this object.<br> "
673                + "<strong>Downloading failed</strong> if you tried to download this object.<br>"
674                + "<br>"
675                + "The error message is:<br>" + "{0}"
676                + "</html>", escapeReservedCharactersHTML(e.getMessage()));
677        return msg;
678
679    }
680
681    /**
682     * Explains an {@see Exception} to the user.
683     *
684     * @param e the {@see Exception}
685     */
686    public static String explainException(Exception e) {
687        String msg = "";
688        if (e instanceof OsmTransferException) {
689            msg = explainOsmTransferException((OsmTransferException) e);
690        } else {
691            msg = explainGeneric(e);
692        }
693        e.printStackTrace();
694        return msg;
695    }
696   
697    /**
698     * Replaces some HTML reserved characters (<, > and &) by their equivalent entity (&lt;, &gt; and &amp;);
699     * @param s The unescaped string
700     * @return The escaped string
701     */
702    public static String escapeReservedCharactersHTML(String s) {
703        return s == null ? "" : s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
704    }
705}
Note: See TracBrowser for help on using the repository browser.