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

Last change on this file since 7258 was 7205, checked in by Don-vip, 10 years ago

fix #10052 - Wrong server URL in HTTP 400 error message

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