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

Last change on this file since 11553 was 11553, checked in by Don-vip, 7 years ago

refactor handling of null values - use Java 8 Optional where possible

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