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

Last change on this file since 11340 was 10717, checked in by simon04, 8 years ago

see #11390, see #12890 - Lambda can be replaced with method reference

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