001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003import static org.openstreetmap.josm.tools.I18n.tr;
004
005import org.openstreetmap.josm.tools.Logging;
006import org.openstreetmap.josm.tools.Utils;
007
008/**
009 * Exception thrown when a communication error occurs when accessing the <a href="http://wiki.openstreetmap.org/wiki/API_v0.6">OSM API</a>.
010 * @see OsmApi
011 */
012public class OsmApiException extends OsmTransferException {
013
014    private int responseCode;
015    private String contentType;
016    private String errorHeader;
017    private String errorBody;
018    private String accessedUrl;
019    private String login;
020
021    /**
022     * Constructs an {@code OsmApiException} with the specified response code, error header and error body
023     * @param responseCode The HTTP response code replied by the OSM server.
024     * See {@link java.net.HttpURLConnection HttpURLConnection} for predefined HTTP response code values
025     * @param errorHeader The error header, as transmitted in the {@code Error} field of the HTTP response header
026     * @param errorBody The error body, as transmitted in the HTTP response body
027     * @param accessedUrl The complete URL accessed when this error occurred
028     * @param login the login used to connect to OSM API (can be null)
029     * @param contentType the response content-type
030     * @since 13499
031     */
032    public OsmApiException(int responseCode, String errorHeader, String errorBody, String accessedUrl, String login, String contentType) {
033        this.responseCode = responseCode;
034        this.errorHeader = errorHeader;
035        this.errorBody = Utils.strip(errorBody);
036        this.accessedUrl = accessedUrl;
037        this.login = login;
038        this.contentType = contentType;
039        checkHtmlBody();
040    }
041
042    /**
043     * Constructs an {@code OsmApiException} with the specified response code, error header and error body
044     * @param responseCode The HTTP response code replied by the OSM server.
045     * See {@link java.net.HttpURLConnection HttpURLConnection} for predefined HTTP response code values
046     * @param errorHeader The error header, as transmitted in the {@code Error} field of the HTTP response header
047     * @param errorBody The error body, as transmitted in the HTTP response body
048     * @param accessedUrl The complete URL accessed when this error occurred
049     * @param login the login used to connect to OSM API (can be null)
050     * @since 12992
051     */
052    public OsmApiException(int responseCode, String errorHeader, String errorBody, String accessedUrl, String login) {
053        this(responseCode, errorHeader, errorBody, accessedUrl, login, null);
054    }
055
056    /**
057     * Constructs an {@code OsmApiException} with the specified response code, error header and error body
058     * @param responseCode The HTTP response code replied by the OSM server.
059     * See {@link java.net.HttpURLConnection HttpURLConnection} for predefined HTTP response code values
060     * @param errorHeader The error header, as transmitted in the {@code Error} field of the HTTP response header
061     * @param errorBody The error body, as transmitted in the HTTP response body
062     * @param accessedUrl The complete URL accessed when this error occurred
063     * @since 5584
064     */
065    public OsmApiException(int responseCode, String errorHeader, String errorBody, String accessedUrl) {
066        this(responseCode, errorHeader, errorBody, accessedUrl, null);
067    }
068
069    /**
070     * Constructs an {@code OsmApiException} with the specified response code, error header and error body
071     * @param responseCode The HTTP response code replied by the OSM server.
072     * See {@link java.net.HttpURLConnection HttpURLConnection} for predefined HTTP response code values
073     * @param errorHeader The error header, as transmitted in the {@code Error} field of the HTTP response header
074     * @param errorBody The error body, as transmitted in the HTTP response body
075     */
076    public OsmApiException(int responseCode, String errorHeader, String errorBody) {
077        this(responseCode, errorHeader, errorBody, null);
078    }
079
080    /**
081     * Constructs an {@code OsmApiException} with the specified detail message.
082     * The cause is not initialized, and may subsequently be initialized by a call to {@link #initCause}.
083     *
084     * @param message The detail message (which is saved for later retrieval by the {@link #getMessage} method)
085     */
086    public OsmApiException(String message) {
087        super(message);
088    }
089
090    /**
091     * Constructs an {@code OsmApiException} with the specified cause and a detail message of
092     * <code>(cause==null ? null : cause.toString())</code>
093     * (which typically contains the class and detail message of <code>cause</code>).
094     *
095     * @param cause the cause (which is saved for later retrieval by the {@link #getCause} method).
096     *              A <code>null</code> value is permitted, and indicates that the cause is nonexistent or unknown.
097     */
098    public OsmApiException(Throwable cause) {
099        super(cause);
100    }
101
102    /**
103     * Constructs an {@code OsmApiException} with the specified detail message and cause.
104     *
105     * <p> Note that the detail message associated with {@code cause} is <i>not</i> automatically incorporated
106     * into this exception's detail message.
107     *
108     * @param message The detail message (which is saved for later retrieval by the {@link #getMessage} method)
109     * @param cause   The cause (which is saved for later retrieval by the {@link #getCause} method).
110     *                A null value is permitted, and indicates that the cause is nonexistent or unknown.
111     *
112     */
113    public OsmApiException(String message, Throwable cause) {
114        super(message, cause);
115    }
116
117    private void checkHtmlBody() {
118        if (errorBody != null && errorBody.matches("^<.*>.*<.*>$")) {
119            setContentType("text/html");
120            if (!errorBody.contains("<html>")) {
121                errorBody = "<html>" + errorBody + "</html>";
122            }
123        }
124    }
125
126    /**
127     * Replies the HTTP response code.
128     * @return The HTTP response code replied by the OSM server. Refer to
129     * <a href="http://wiki.openstreetmap.org/wiki/API_v0.6">OSM API</a> to see the list of response codes returned by the API for each call.
130     */
131    public int getResponseCode() {
132        return responseCode;
133    }
134
135    /**
136     * Sets the HTTP response code.
137     * @param responseCode The HTTP response code replied by the OSM server.
138     * See {@link java.net.HttpURLConnection HttpURLConnection} for predefined HTTP response code values
139     */
140    public void setResponseCode(int responseCode) {
141        this.responseCode = responseCode;
142    }
143
144    /**
145     * Replies the error header.
146     * @return the error header, as transmitted in the {@code Error} field of the HTTP response header
147     */
148    public String getErrorHeader() {
149        return errorHeader;
150    }
151
152    /**
153     * Sets the error header.
154     * @param errorHeader the error header, as transmitted in the {@code Error} field of the HTTP response header
155     */
156    public void setErrorHeader(String errorHeader) {
157        this.errorHeader = errorHeader;
158    }
159
160    /**
161     * Replies the error body.
162     * @return The error body, as transmitted in the HTTP response body
163     */
164    public String getErrorBody() {
165        return errorBody;
166    }
167
168    /**
169     * Sets the error body.
170     * @param errorBody The error body, as transmitted in the HTTP response body
171     */
172    public void setErrorBody(String errorBody) {
173        this.errorBody = errorBody;
174    }
175
176    @Override
177    public String getMessage() {
178        StringBuilder sb = new StringBuilder();
179        sb.append("ResponseCode=")
180        .append(responseCode);
181        String eh = "";
182        try {
183            if (errorHeader != null)
184                eh = tr(errorHeader.trim());
185            if (!eh.isEmpty()) {
186                sb.append(", Error Header=<")
187                .append(eh)
188                .append('>');
189            }
190        } catch (IllegalArgumentException e) {
191            // Ignored
192            Logging.trace(e);
193        }
194        try {
195            String eb = errorBody != null ? tr(errorBody.trim()) : "";
196            if (!eb.isEmpty() && !eb.equals(eh)) {
197                sb.append(", Error Body=<")
198                .append(eb)
199                .append('>');
200            }
201        } catch (IllegalArgumentException e) {
202            // Ignored
203            Logging.trace(e);
204        }
205        return sb.toString();
206    }
207
208    /**
209     * Replies a message suitable to be displayed in a message dialog
210     *
211     * @return a message which is suitable to be displayed in a message dialog
212     */
213    public String getDisplayMessage() {
214        StringBuilder sb = new StringBuilder();
215        String header = Utils.strip(errorHeader);
216        String body = Utils.strip(errorBody);
217        if ((header == null || header.isEmpty()) && (body == null || body.isEmpty())) {
218            sb.append(tr("The server replied an error with code {0}.", responseCode));
219        } else {
220            if (header != null && !header.isEmpty()) {
221                sb.append(tr(header));
222            }
223            if (body != null && !body.isEmpty() && !body.equals(header)) {
224                if (sb.length() > 0) {
225                    sb.append(". ");
226                }
227                sb.append(tr(body));
228            }
229            sb.append(' ').append(tr("(Code={0})", responseCode));
230        }
231        return sb.toString();
232    }
233
234    /**
235     * Sets the complete URL accessed when this error occurred.
236     * This is distinct from the one set with {@link #setUrl}, which is generally only the base URL of the server.
237     * @param url the complete URL accessed when this error occurred.
238     */
239    public void setAccessedUrl(String url) {
240        this.accessedUrl = url;
241    }
242
243    /**
244     * Replies the complete URL accessed when this error occurred.
245     * This is distinct from the one returned by {@link #getUrl}, which is generally only the base URL of the server.
246     * @return the complete URL accessed when this error occurred.
247     */
248    public String getAccessedUrl() {
249        return accessedUrl;
250    }
251
252    /**
253     * Sets the login used to connect to OSM API.
254     * @param login the login used to connect to OSM API
255     * @since 12992
256     */
257    public void setLogin(String login) {
258        this.login = login;
259    }
260
261    /**
262     * Replies the login used to connect to OSM API.
263     * @return the login used to connect to OSM API, or {@code null}
264     * @since 12992
265     */
266    public String getLogin() {
267        return login;
268    }
269
270    /**
271     * Sets the response content-type.
272     * @param contentType the response content-type.
273     * @since 13499
274     */
275    public final void setContentType(String contentType) {
276        this.contentType = contentType;
277    }
278
279    /**
280     * Replies the response content-type.
281     * @return the response content-type
282     * @since 13499
283     */
284    public final String getContentType() {
285        return contentType;
286    }
287
288    /**
289     * Determines if the exception has {@code text/html} as content type.
290     * @return {@code true} if the exception has {@code text/html} as content type.
291     * @since 14810
292     */
293    public final boolean isHtml() {
294        return "text/html".equals(contentType);
295    }
296}