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        if (errorHeader != null) {
216            sb.append(tr(errorHeader));
217            sb.append(tr("(Code={0})", responseCode));
218        } else if (errorBody != null && !errorBody.trim().isEmpty()) {
219            errorBody = errorBody.trim();
220            sb.append(tr(errorBody));
221            sb.append(tr("(Code={0})", responseCode));
222        } else {
223            sb.append(tr("The server replied an error with code {0}.", responseCode));
224        }
225        return sb.toString();
226    }
227
228    /**
229     * Sets the complete URL accessed when this error occurred.
230     * This is distinct from the one set with {@link #setUrl}, which is generally only the base URL of the server.
231     * @param url the complete URL accessed when this error occurred.
232     */
233    public void setAccessedUrl(String url) {
234        this.accessedUrl = url;
235    }
236
237    /**
238     * Replies the complete URL accessed when this error occurred.
239     * This is distinct from the one returned by {@link #getUrl}, which is generally only the base URL of the server.
240     * @return the complete URL accessed when this error occurred.
241     */
242    public String getAccessedUrl() {
243        return accessedUrl;
244    }
245
246    /**
247     * Sets the login used to connect to OSM API.
248     * @param login the login used to connect to OSM API
249     * @since 12992
250     */
251    public void setLogin(String login) {
252        this.login = login;
253    }
254
255    /**
256     * Replies the login used to connect to OSM API.
257     * @return the login used to connect to OSM API, or {@code null}
258     * @since 12992
259     */
260    public String getLogin() {
261        return login;
262    }
263
264    /**
265     * Sets the response content-type.
266     * @param contentType the response content-type.
267     * @since 13499
268     */
269    public final void setContentType(String contentType) {
270        this.contentType = contentType;
271    }
272
273    /**
274     * Replies the response content-type.
275     * @return the response content-type
276     * @since 13499
277     */
278    public final String getContentType() {
279        return contentType;
280    }
281
282    /**
283     * Determines if the exception has {@code text/html} as content type.
284     * @return {@code true} if the exception has {@code text/html} as content type.
285     * @since xxx
286     */
287    public final boolean isHtml() {
288        return "text/html".equals(contentType);
289    }
290}