// License: WTFPL. For details, see LICENSE file.
package iodb;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.gui.PleaseWaitRunnable;
/**
* A task to query the imagery offset server and process the response.
*
* @author Zverik
* @license WTFPL
*/
class SimpleOffsetQueryTask extends PleaseWaitRunnable {
private String query;
private String errorMessage;
private String title;
protected boolean cancelled;
private QuerySuccessListener listener;
/**
* Initialize the task.
* @param query A query string, usually starting with an action word and a question mark.
* @param title A title for the progress monitor.
*/
SimpleOffsetQueryTask(String query, String title) {
super(ImageryOffsetTools.DIALOG_TITLE);
this.query = query;
this.title = title;
cancelled = false;
}
/**
* In case a query was not specified when the object was constructed,
* it can be set with this method.
* @see #SimpleOffsetQueryTask(java.lang.String, java.lang.String)
*/
public void setQuery(String query) {
this.query = query;
}
/**
* Install a listener for successful responses. There can be only one.
*/
public void setListener(QuerySuccessListener listener) {
this.listener = listener;
}
/**
* Remove a listener for successful responses.
*/
public void removeListener() {
this.listener = null;
}
/**
* The main method: calls {@link #doQuery(java.lang.String)} and processes exceptions.
*/
@Override
protected void realRun() {
getProgressMonitor().indeterminateSubTask(title);
try {
errorMessage = null;
doQuery(query);
} catch (UploadException e) {
errorMessage = tr("Server has rejected the request") + ":\n" + e.getMessage();
} catch (IOException e) {
errorMessage = tr("Unable to connect to the server") + "\n" + e.getMessage();
}
}
/**
* Sends a request to the imagery offset server. Processes exceptions and
* return codes, calls {@link #processResponse(java.io.InputStream)} on success.
*/
private void doQuery(String query) throws UploadException, IOException {
try {
String serverURL = Main.pref.get("iodb.server.url", "http://offsets.textual.ru/");
URL url = new URL(serverURL + query);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != 200) {
throw new IOException("HTTP Response code " + connection.getResponseCode() + " (" + connection.getResponseMessage() + ")");
}
InputStream inp = connection.getInputStream();
if (inp == null)
throw new IOException("Empty response");
try {
if (!cancelled)
processResponse(inp);
} finally {
connection.disconnect();
}
} catch (MalformedURLException ex) {
throw new IOException("Malformed URL: " + ex.getMessage());
}
}
/**
* Doesn't actually cancel, just raises a flag.
*/
@Override
protected void cancel() {
cancelled = true;
}
/**
* Is called after {@link #realRun()}. Either displays an error message
* or notifies a listener of success.
*/
@Override
protected void finish() {
if (errorMessage != null) {
JOptionPane.showMessageDialog(Main.parent, errorMessage,
ImageryOffsetTools.DIALOG_TITLE, JOptionPane.ERROR_MESSAGE);
} else if (listener != null) {
listener.queryPassed();
}
}
/**
* Parse the response input stream and determine whether an operation
* was successful or not.
* @throws iodb.SimpleOffsetQueryTask.UploadException Thrown if an error message was found.
*/
protected void processResponse(InputStream inp) throws UploadException {
String response = "";
if (inp != null) {
Scanner sc = new Scanner(inp).useDelimiter("\\A");
response = sc.hasNext() ? sc.next() : "";
}
Pattern p = Pattern.compile("<(\\w+)>([^<]+)</\\1>");
Matcher m = p.matcher(response);
if (m.find()) {
if (m.group(1).equals("error")) {
throw new UploadException(m.group(2));
}
} else {
throw new UploadException("No response");
}
}
/**
* A placeholder exception for error messages.
*/
public static class UploadException extends Exception {
UploadException(String message) {
super(message);
}
}
}