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

Last change on this file since 8997 was 8846, checked in by Don-vip, 9 years ago

sonar - fb-contrib - minor performance improvements:

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