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 fileList = new ArrayList(); 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 commandList = new ArrayList(); // 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(); }