package tim.prune.load;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import tim.prune.App;
import tim.prune.ExternalTools;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.config.Config;
import tim.prune.data.SourceInfo;
import tim.prune.load.babel.BabelFilterPanel;
import tim.prune.load.xml.XmlFileLoader;
import tim.prune.load.xml.XmlHandler;
import tim.prune.save.GpxExporter;
/**
* Superclass to manage the loading of data using GpsBabel
* Subclasses handle either from GPS or from file
*/
public abstract class BabelLoader extends GenericFunction implements Runnable
{
private boolean _gpsBabelChecked = false;
protected JDialog _dialog = null;
// Checkboxes for which kinds of points to load
protected JCheckBox _waypointCheckbox = null, _trackCheckbox = null;
// Checkbox to save to file or not
protected JCheckBox _saveCheckbox = null;
protected JButton _okButton = null;
protected JProgressBar _progressBar = null;
protected File _saveFile = null;
protected boolean _cancelled = false;
protected BabelFilterPanel _filterPanel = null;
/**
* Constructor
* @param inApp Application object to inform of data load
*/
public BabelLoader(App inApp)
{
super(inApp);
}
/**
* Open the GUI to select options and start the load
*/
public void begin()
{
// Check if gpsbabel looks like it's installed
if (_gpsBabelChecked || ExternalTools.isToolInstalled(ExternalTools.TOOL_GPSBABEL)
|| JOptionPane.showConfirmDialog(_dialog,
I18nManager.getText("dialog.gpsload.nogpsbabel"),
I18nManager.getText(getNameKey()),
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION)
{
_gpsBabelChecked = true;
// Make dialog window
if (_dialog == null)
{
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
_dialog.getContentPane().add(makeDialogComponents());
_dialog.pack();
}
// Initialise progress bars, buttons
enableOkButton();
setupProgressBar(true);
initDialog(); // do any subclass-specific init here
_dialog.setVisible(true);
}
}
/**
* @return a panel containing the main dialog components
*/
protected abstract JPanel makeDialogComponents();
/** Do any subclass-specific dialog initialisation necessary */
protected void initDialog()
{
// GPSBabel filter, if any
_filterPanel.setFilterString(Config.getConfigString(Config.KEY_GPSBABEL_FILTER));
}
/**
* @param inStart true if the dialog is restarting
*/
private void setupProgressBar(boolean inStart)
{
// set visibility
_progressBar.setVisible(!inStart);
// set indeterminate flags, initial value
_progressBar.setIndeterminate(false);
_progressBar.setValue(0);
}
/**
* Enable or disable the ok button
*/
protected void enableOkButton()
{
_okButton.setEnabled(isInputOk());
}
/**
* @return true if input fields of dialog are valid
*/
protected abstract boolean isInputOk();
/**
* Run method for performing tasks in separate thread
*/
public void run()
{
_okButton.setEnabled(false);
setupProgressBar(false);
if (isInputOk())
{
_progressBar.setIndeterminate(true);
_saveFile = null;
try
{
callGpsBabel();
}
catch (Exception e)
{
_app.showErrorMessageNoLookup(getNameKey(), e.getMessage());
_cancelled = true;
}
}
setupProgressBar(true);
enableOkButton();
// Close dialog
if (!_cancelled) {
_dialog.dispose();
}
}
/**
* Execute the call to gpsbabel and pass the results back to the app
*/
private void callGpsBabel() throws Exception
{
// Set up command to call gpsbabel
String[] commands = getCommandArray();
// Save GPS settings in config
saveConfigValues();
String errorMessage = "", errorMessage2 = "";
XmlHandler handler = null;
Process process = Runtime.getRuntime().exec(commands);
String line = null;
if (_saveFile != null)
{
// data is being saved to file, so need to wait for it to finish
process.waitFor();
// try to read error message, if any
try {
BufferedReader r = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((line = r.readLine()) != null) {
errorMessage += line + "\n";
}
// Close error stream
try {
r.close();
} catch (Exception e) {}
}
catch (Exception e) {} // couldn't get error message
// Trigger it to be loaded by app
if (process.exitValue() == 0)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ArrayList<File> fileList = new ArrayList<File>();
fileList.add(_saveFile);
_app.loadDataFiles(fileList);
}
});
}
else if (errorMessage.length() > 0) {
throw new Exception(errorMessage);
}
else throw new Exception(I18nManager.getText("error.gpsload.unknown"));
}
else
{
// Pass input stream to try to parse the xml
try
{
XmlFileLoader xmlLoader = new XmlFileLoader(_app);
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
saxParser.parse(process.getInputStream(), xmlLoader);
handler = xmlLoader.getHandler();
if (handler == null) {
errorMessage = "Null handler";
}
}
catch (Exception e) {
errorMessage = e.getMessage();
}
// Read the error stream to see if there's a better error message there
BufferedReader r = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((line = r.readLine()) != null) {
errorMessage2 += line + "\n";
}
// Close error stream
try {
r.close();
} catch (Exception e) {}
if (errorMessage2.length() > 0) {errorMessage = errorMessage2;}
if (errorMessage.length() > 0) {throw new Exception(errorMessage);}
// Send data back to app
_app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(), null,
getSourceInfo(), handler.getTrackNameList());
}
}
/**
* Get the commands to call
* @return String array containing commands
*/
private String[] getCommandArray()
{
ArrayList<String> commandList = new ArrayList<String>();
// Firstly the command for gpsbabel itself
final String command = Config.getConfigString(Config.KEY_GPSBABEL_PATH);
commandList.add(command);
// Then whether to load waypoints or track points
final boolean loadWaypoints = _waypointCheckbox.isSelected();
final boolean loadTrack = _trackCheckbox.isSelected();
if (loadWaypoints) {
commandList.add("-w");
}
if (loadTrack) {
commandList.add("-t");
}
// Input format
commandList.add("-i");
commandList.add(getInputFormat());
// File path
commandList.add("-f");
commandList.add(getFilePath());
// Filters, if any
final String filter = _filterPanel.getFilterString();
if (filter != null && !filter.equals(""))
{
for (String arg : filter.split(" "))
{
if (arg.length() > 0) {
commandList.add(arg);
}
}
}
// Output format
commandList.add("-o");
commandList.add("gpx");
// Where to
commandList.add("-F");
String whereTo = "-";
// Do we want to save the gpx straight to file?
if (_saveCheckbox.isSelected())
{
// Select file to save to
_saveFile = GpxExporter.chooseGpxFile(_parentFrame);
if (_saveFile != null) {
whereTo = _saveFile.getAbsolutePath();
}
}
commandList.add(whereTo);
// Convert to string array
String[] args = new String[] {};
return commandList.toArray(args);
}
/**
* @return SourceInfo object corresponding to the load
*/
protected abstract SourceInfo getSourceInfo();
/**
* @return complete file path or device path for gpsbabel call
*/
protected abstract String getFilePath();
/**
* @return file name or device name
*/
protected abstract String getInputFormat();
/**
* Save any config values necessary
*/
protected abstract void saveConfigValues();
}