// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.io; import static org.openstreetmap.josm.tools.I18n.tr; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.openstreetmap.josm.Main; /** * A ChangesetClosedException is thrown if the server replies with a HTTP * return code 409 (Conflict) with the error header {@link #ERROR_HEADER_PATTERN}. * * Depending on the context the exception is thrown in we have to react differently. * */ public class ChangesetClosedException extends OsmTransferException { /** the error header pattern for in case of HTTP response 409 indicating * that a changeset was closed */ public static final String ERROR_HEADER_PATTERN = "The changeset (\\d+) was closed at (.*)"; public static enum Source { /** * The exception was thrown when a changeset was updated. This most likely means * that the changeset was closed before. */ UPDATE_CHANGESET, /** * The exception was thrown when data was uploaded to the changeset. This most * likely means that the servers capability limits for a changeset have been * exceeded. */ UPLOAD_DATA, /** * Unspecified source */ UNSPECIFIED } /** * Replies true if errorHeader matches with {@link #ERROR_HEADER_PATTERN} * * @param errorHeader the error header * @return true if errorHeader matches with {@link #ERROR_HEADER_PATTERN} */ public static boolean errorHeaderMatchesPattern(String errorHeader) { if (errorHeader == null) return false; Pattern p = Pattern.compile(ERROR_HEADER_PATTERN); Matcher m = p.matcher(errorHeader); return m.matches(); } /** the changeset id */ private long changesetId; /** the date on which the changeset was closed */ private Date closedOn; /** the source */ private Source source; protected void parseErrorHeader(String errorHeader) { Pattern p = Pattern.compile(ERROR_HEADER_PATTERN); Matcher m = p.matcher(errorHeader); if (m.matches()) { changesetId = Long.parseLong(m.group(1)); // Example: "2010-09-07 14:39:41 UTC". Always parsed with US locale regardless // of the current locale in JOSM DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.US); try { closedOn = formatter.parse(m.group(2)); } catch(ParseException ex) { Main.error(tr("Failed to parse date ''{0}'' replied by server.", m.group(2))); Main.error(ex); } } else { Main.error(tr("Unexpected format of error header for conflict in changeset update. Got ''{0}''", errorHeader)); } } /** * Creates the exception with the given errorHeader * * @param errorHeader the error header */ public ChangesetClosedException(String errorHeader) { super(errorHeader); parseErrorHeader(errorHeader); this.source = Source.UNSPECIFIED; } /** * Creates the exception with the given error header and the given * source. * * @param errorHeader the error header * @param source the source for the exception */ public ChangesetClosedException(String errorHeader, Source source) { super(errorHeader); parseErrorHeader(errorHeader); this.source = source == null ? Source.UNSPECIFIED : source; } /** * Creates the exception * * @param changesetId the id if the closed changeset * @param closedOn the date the changeset was closed on * @param source the source for the exception */ public ChangesetClosedException(long changesetId, Date closedOn, Source source) { super(""); this.source = source == null ? Source.UNSPECIFIED : source; this.changesetId = changesetId; this.closedOn = closedOn; } /** * Replies the id of the changeset which was closed * * @return the id of the changeset which was closed */ public long getChangesetId() { return changesetId; } /** * Replies the date the changeset was closed * * @return the date the changeset was closed. May be null if the date isn't known. */ public Date getClosedOn() { return closedOn; } /** * Replies the source where the exception was thrown * * @return the source */ public Source getSource() { return source; } public void setSource(Source source) { this.source = source == null ? Source.UNSPECIFIED : source; } }