All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.squirrel_sql.fw.datasetviewer.PopupEditableIOPanel Maven / Gradle / Ivy

package net.sourceforge.squirrel_sql.fw.datasetviewer;
/*
 * Copyright (C) 2001 Colin Bell
 * [email protected]
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.BinaryDisplayConverter;
import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.CellComponentFactory;
import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.RestorableJTextArea;
import net.sourceforge.squirrel_sql.fw.gui.TextPopupMenu;
import net.sourceforge.squirrel_sql.fw.gui.action.BaseAction;
import net.sourceforge.squirrel_sql.fw.util.IOUtilities;
import net.sourceforge.squirrel_sql.fw.util.IOUtilitiesImpl;
import net.sourceforge.squirrel_sql.fw.util.StringManager;
import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;

/**
 * @author gwg
 *
 * Class to handle IO between user and editable text, text and object,
 * and text/object to/from file.
 */
public class PopupEditableIOPanel extends JPanel implements ActionListener {

    private static final long serialVersionUID = 1L;

    private static final StringManager s_stringMgr =
		StringManagerFactory.getStringManager(PopupEditableIOPanel.class);

	// The text area displaying the object contents
	private final JTextArea _ta;

	// the scroll pane that holds the text area
	private final JScrollPane scrollPane;

	// Description needed to handle conversion of data to/from Object
	transient private final ColumnDisplayDefinition _colDef;

	transient private MouseAdapter _lis;

	private final TextPopupMenu _popupMenu;

	// name of file to do export/import/process on
	private JTextField fileNameField;

	// command to use when processing data with an external program
	private JComboBox externalCommandCombo;

	// save the original value for re-use by CLOB/BLOB types in conversion
	private Object originalValue;

	// Binary data viewing option: which radix to use
	// This object is only non-null when the data is binary data
	private JComboBox radixList = null;
	private String previousRadixListItem = null;
	// Binary data viewing option: view ascii as char rather than as numeric value
	private JCheckBox showAscii = null;
	private boolean previousShowAscii;

	private IOUtilities _iou = new IOUtilitiesImpl();	
	
	class BinaryOptionActionListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {

			// user asked to see binary data in a different format
			int base = 16;	// default to hex
			if (previousRadixListItem.equals("Decimal")) base = 10;
			else if (previousRadixListItem.equals("Octal")) base = 8;
			else if (previousRadixListItem.equals("Binary")) base = 2;

			Byte[] bytes = BinaryDisplayConverter.convertToBytes(_ta.getText(),
				base, previousShowAscii);

			// return the expected format for this data
			base = 16;	// default to hex
			if (radixList.getSelectedItem().equals("Decimal")) base = 10;
			else if (radixList.getSelectedItem().equals("Octal")) base = 8;
			else if (radixList.getSelectedItem().equals("Binary")) base = 2;

			((RestorableJTextArea)_ta).updateText(
				BinaryDisplayConverter.convertToString(bytes,
				base, showAscii.isSelected()));

			previousRadixListItem = (String)radixList.getSelectedItem();
			previousShowAscii = showAscii.isSelected();

			return;
		}
	}
	transient private BinaryOptionActionListener optionActionListener =
		new BinaryOptionActionListener();

	// text put in file name field to indicate that we should
	// create a temp file for export
	private final String TEMP_FILE_FLAG = "";

	// Symbol used by user in Command field to indicate
	// "Put the file name here" when the command is executed.
	private final String FILE_REPLACE_FLAG = "%f";

	/**
	 * Constructor
	 */
	public PopupEditableIOPanel(ColumnDisplayDefinition colDef,
										 Object value, boolean isEditable) {

		originalValue = value;	// save for possible future use

		_popupMenu = new TextPopupMenu();

		_colDef = colDef;
		_ta = CellComponentFactory.getJTextArea(colDef, value);

		if (isEditable) {
			_ta.setEditable(true);
			_ta.setBackground(Color.yellow);	// tell user it is editable
		}
		else {
			_ta.setEditable(false);
		}


		_ta.setLineWrap(true);
		_ta.setWrapStyleWord(true);

		setLayout(new BorderLayout());

		// add a panel containing binary data editing options, if needed
		JPanel displayPanel = new JPanel();
		displayPanel.setLayout(new BorderLayout());
		scrollPane = new JScrollPane(_ta);
		/*
		 * TODO: When 1.4 is the earliest version supported, include
		 * the following line here:
		 * 	scrollPane.setWheelScrollingEnabled(true);
		 * The scroll-wheel function is important for ease of use, but the
		 * setWheelScrollingEnabled function is not available in java 1.3.
		 */

		displayPanel.add(scrollPane, BorderLayout.CENTER);
		if (CellComponentFactory.useBinaryEditingPanel(colDef)) {
			// this is a binary field, so allow for multiple viewing options

			String[] radixListData = { "Hex", "Decimal", "Octal", "Binary" };
			radixList = new JComboBox(radixListData);
			radixList.addActionListener(optionActionListener);
			previousRadixListItem = "Hex";

			showAscii = new JCheckBox();
			previousShowAscii = false;
			showAscii.addActionListener(optionActionListener);

			JPanel displayControlsPanel = new JPanel();
			// use default sequential layout

			// i18n[popupeditableIoPanel.numberBase=Number Base:]
			displayControlsPanel.add(new JLabel(s_stringMgr.getString("popupeditableIoPanel.numberBase")));
			displayControlsPanel.add(radixList);
			displayControlsPanel.add(new JLabel("    "));	// add some space
			displayControlsPanel.add(showAscii);
			// i18n[popupeditableIoPanel.showAscii=Show ASCII as chars]
			displayControlsPanel.add(new JLabel(s_stringMgr.getString("popupeditableIoPanel.showAscii")));
			displayPanel.add(displayControlsPanel, BorderLayout.SOUTH);
		}
		add(displayPanel, BorderLayout.CENTER);

		// add controls for file handling, but only if DataType
		// can do File operations
		if (CellComponentFactory.canDoFileIO(colDef)) {
			// yes it can, so add controls
			add(exportImportPanel(isEditable), BorderLayout.SOUTH);
		}

		_popupMenu.add(new LineWrapAction());
		_popupMenu.add(new WordWrapAction());
		_popupMenu.add(new XMLReformatAction());
		_popupMenu.setTextComponent(_ta);
	}

	/**
	 * build the user interface for export/import operations
	 */
	private JPanel exportImportPanel(boolean isEditable) {
		JPanel eiPanel = new JPanel();
		eiPanel.setLayout(new GridBagLayout());

		final GridBagConstraints gbc = new GridBagConstraints();

		gbc.insets = new Insets(4,4,4,4);
		gbc.gridx = 0;
		gbc.gridy = 0;

		// File handling controls
		// i18n[popupeditableIoPanel.useFile=Use File: ]
		eiPanel.add(new JLabel(s_stringMgr.getString("popupeditableIoPanel.useFile")), gbc);


		fileNameField = new JTextField(TEMP_FILE_FLAG, 19);
		gbc.gridx++;
		eiPanel.add(fileNameField, gbc);


		// add button for Brows
		// i18n[popupeditableIoPanel.browse=Browse]
		JButton browseButton = new JButton(s_stringMgr.getString("popupeditableIoPanel.browse"));
		browseButton.setActionCommand("browse");
		browseButton.addActionListener(this);


		gbc.gridx++;
		eiPanel.add(browseButton, gbc);


		// i18n[popupeditableIoPanel.export44=Export]
		JButton exportButton = new JButton(s_stringMgr.getString("popupeditableIoPanel.export44"));
		exportButton.setActionCommand("export");
		exportButton.addActionListener(this);

		gbc.gridx++;
		eiPanel.add(exportButton, gbc);

		// import and external processing can only be done if
		// panel is editable
		if ( isEditable == false)
			return eiPanel;

		// Add import control
		// i18n[popupeditableIoPanel.import44=Import]
		JButton importButton = new JButton(s_stringMgr.getString("popupeditableIoPanel.import44"));
		importButton.setActionCommand("import");
		importButton.addActionListener(this);

		gbc.gridx++;
		eiPanel.add(importButton, gbc);

		// add external processing command field and button
		gbc.gridy++;
		gbc.gridx = 0;
		// i18n[popupeditableIoPanel.withCommand=With command:]
		eiPanel.add(new JLabel(s_stringMgr.getString("popupeditableIoPanel.withCommand")), gbc);

		// add combo box for command to execute
		gbc.gridx++;
		externalCommandCombo = new JComboBox(
			CellImportExportInfoSaver.getInstance().getCmdList());
		externalCommandCombo.setSelectedIndex(-1);	// no entry selected
		externalCommandCombo.setEditable(true);

		// make this the same size as the fileNameField
		externalCommandCombo.setPreferredSize(fileNameField.getPreferredSize());

		eiPanel.add(externalCommandCombo, gbc);

		// add button to execute external command
		// i18n[popupeditableIoPanel.execute34=Execute]
		JButton externalCommandButton = new JButton(s_stringMgr.getString("popupeditableIoPanel.execute34"));
		externalCommandButton.setActionCommand("execute");
		externalCommandButton.addActionListener(this);

		gbc.gridx++;
		eiPanel.add(externalCommandButton, gbc);

		// add button for applying file & cmd info without doing anything else
		// i18n[popupeditableIoPanel.applyFile=Apply File & Cmd]
		JButton applyButton = new JButton(s_stringMgr.getString("popupeditableIoPanel.applyFile"));
		applyButton.setActionCommand("apply");
		applyButton.addActionListener(this);

		gbc.gridx++;
		gbc.gridwidth = 2;
		eiPanel.add(applyButton, gbc);
		gbc.gridwidth = 1;	// reset width to normal	

		// add note to user about including file name in command
		gbc.gridy++;
		gbc.gridx = 0;
		gbc.gridwidth = GridBagConstraints.REMAINDER;


		// i18n[popupeditableIoPanel.replaceFile=(In command, the string {0} is replaced by the file name when Executed.)]
		eiPanel.add(new JLabel(s_stringMgr.getString("popupeditableIoPanel.replaceFile", FILE_REPLACE_FLAG)), gbc);

		// load filename and command with previously entered info
		// if not the default
		CellImportExportInfo info =
			CellImportExportInfoSaver.getInstance().get(_colDef.getFullTableColumnName());
		if (info != null) {
			// load the info into the text fields
			fileNameField.setText(info.getFileName());
			externalCommandCombo.getEditor().setItem(info.getCommand());
		}

		return eiPanel;
	}


	/**
	 * Return the contents of the editable text area as an Object
	 * converted by the correct DataType function.  Errors in converting
	 * from the text form into the Object are reported
	 * through the messageBuffer.
	 */
	public Object getObject(StringBuffer messageBuffer) {
		String text = null;
		try {
			text = getTextAreaCannonicalForm();
		}
		catch (Exception e) {
			messageBuffer.append(
				"Failed to convert binary text; error was:\n"+e.getMessage());
			return null;
		}
		return CellComponentFactory.validateAndConvertInPopup(_colDef,
					originalValue, text, messageBuffer);
	}

	/**
	 * When focus is passed to this panel, automatically pass it
	 * on to the text area.
	 */
	public void requestFocus() {
		_ta.requestFocus();
	}

	/**
	 * Handle actions on the buttons in the file operations panel
	 */
	public void actionPerformed(ActionEvent e) {

		//File object for doing IO
		File file;

		if (e.getActionCommand().equals("browse")) {
			JFileChooser chooser = new JFileChooser();
			String filename = fileNameField.getText();
            if (filename != null && !"".equals(filename)) {
                File f = new File(filename);
                String path = f.getAbsolutePath();
                if (path != null && !"".equals(path)) {
                    chooser.setCurrentDirectory(new File(path));
                }
            }
			int returnVal = chooser.showOpenDialog(this);
			if(returnVal == JFileChooser.APPROVE_OPTION) {
				//	System.out.println("You chose to open this file: " +
				//		chooser.getSelectedFile().getName());
				try {
					fileNameField.setText(chooser.getSelectedFile().getCanonicalPath());
				}
				catch (Exception ex) {
					// should not happen since the file that was selected was
					// just being shown in the Chooser dialog, but just to be safe...
					JOptionPane.showMessageDialog(this,
						// i18n[popupeditableIoPanel.errorGettingPath=Error getting full path name for selected file]
						s_stringMgr.getString("popupeditableIoPanel.errorGettingPath"),
						// i18n[popupeditableIoPanel.fileChooserError=File Chooser Error]
						s_stringMgr.getString("popupeditableIoPanel.fileChooserError"),JOptionPane.ERROR_MESSAGE);
				}
			}
		}

		else if (e.getActionCommand().equals("apply")) {
			// If file name default and cmd is null or empty,
			// make sure this entry is not being held in CellImportExportInfoSaver
			if ( (fileNameField.getText() != null &&
					fileNameField.getText().equals(TEMP_FILE_FLAG)) &&
				(externalCommandCombo.getEditor().getItem() == null ||
					((String)externalCommandCombo.getEditor().getItem()).length() == 0)) {
				// user has not entered anything or has reset to defaults,
				// so make sure there is no entry for this column in the
				// saved info
				CellImportExportInfoSaver.remove(_colDef.getFullTableColumnName());
			}
			else {
				// user has entered some non-default info, so save it
				CellImportExportInfoSaver.getInstance().save(
					_colDef.getFullTableColumnName(),fileNameField.getText(),
					((String)externalCommandCombo.getEditor().getItem()));
			}

		}

		 else if (e.getActionCommand().equals("import")) {

			 // IMPORT OBJECT FROM OSX_FILE

			 if (fileNameField.getText() == null ||
				 fileNameField.getText().equals(TEMP_FILE_FLAG)) {
				 // not allowed - must have existing file for import
				 JOptionPane.showMessageDialog(this,
					 // i18n[popupeditableIoPanel.selectImportDataFile=You must select an existing file to import data from.]
						s_stringMgr.getString("popupeditableIoPanel.selectImportDataFile"),
					 // i18n[popupeditableIoPanel.noFile=No File Selected]
						s_stringMgr.getString("popupeditableIoPanel.noFile"),JOptionPane.ERROR_MESSAGE);
				return;	// do not do import
			 }

			 // get name of file, which must exist
			if (fileNameField.getText() == null)
				fileNameField.setText("");	// guard against something really stupid
			file = new File(fileNameField.getText());
			if ( ! file.exists() || ! file.isFile() || ! file.canRead()) {
				// not something we can read

				// i18n[popupeditableIoPanel.fileDoesNotExist=File {0} does not exist,\nor is not a readable, normal file.\nPlease enter a valid file name or use Browse to select a file.]
				String msg = s_stringMgr.getString("popupeditableIoPanel.fileDoesNotExist", fileNameField.getText());

			   JOptionPane.showMessageDialog(this, msg,

						// i18n[popupeditableIoPanel.fileError=File Name Error]
						s_stringMgr.getString("popupeditableIoPanel.fileError"),JOptionPane.ERROR_MESSAGE);
				return;	// do not do import
			}

			// Now that we have the file, do the import.
			//
			// Note: the sequence of operations is divided into two sections
			// at this point.  The preceeding code ensures that we have
			// a readable file, and the code in the following method call
			// does the import.  The reason for splitting at this point is
			// that the Execute operation needs to do an import, and it will
			// already have the file to do the import from (which is the same
			// as the file it exported into).
			importData(file);

			// save the user options - we know that it is not the default
			// because we do not allow importing from "temp file"
			CellImportExportInfoSaver.getInstance().save(
				_colDef.getFullTableColumnName(), fileNameField.getText(),
				((String)externalCommandCombo.getEditor().getItem()));

		 }

		else {

			// GET OSX_FILE FOR EXPORT & EXTERNAL PROCESSING

			String canonicalFilePathName = fileNameField.getText();

			// file name verification operations are the same for both
			// export and execute, so do that work here for both.
			//
			// If file name is null or empty, do not proceed
			if (fileNameField.getText() == null ||
				fileNameField.getText().equals("")) {

				JOptionPane.showMessageDialog(this,

					// i18n[popupeditableIoPanel.noExportFile=No file name given for export.\nPlease enter a file name  or use Browse before clicking Export.]
					s_stringMgr.getString("popupeditableIoPanel.noExportFile"),
					// i18n[popupeditableIoPanel.exportError=Export Error]
					s_stringMgr.getString("popupeditableIoPanel.exportError"),JOptionPane.ERROR_MESSAGE);
				return;
			}

			// create the file to open
			if (fileNameField.getText().equals(TEMP_FILE_FLAG)) {
				// user wants us to create a temp file
				try {
					file = File.createTempFile("squirrel", ".tmp");

					// get the real name for use later
					canonicalFilePathName = file.getCanonicalPath();
				}
				catch (Exception ex) {
					JOptionPane.showMessageDialog(this,
						// i18n[popupeditableIoPanel.cannotCreateTempFile=Cannot create temp file..\nError was:\n{0}]
						s_stringMgr.getString("popupeditableIoPanel.cannotCreateTempFile", ex.getMessage()),
						// i18n[popupeditableIoPanel.exportError2=Export Error]
						s_stringMgr.getString("popupeditableIoPanel.exportError2"),
						JOptionPane.ERROR_MESSAGE);
					return;
				}
			}
			else {
				//user must have supplied a file name.
				file = new File(fileNameField.getText());

				try {
					canonicalFilePathName = file.getCanonicalPath();
				}
				catch (Exception ex) {
					// an error here may mean that the file cannot be
					// reached or has moved or some such.  In any case,
					// the file cannot be used for export.
					JOptionPane.showMessageDialog(this,
						// i18n[popupeditableIoPanel.cannotAccessFile=Cannot access file name {0}\nAborting export.]
						s_stringMgr.getString("popupeditableIoPanel.cannotAccessFile", fileNameField.getText()),

						// i18n[popupeditableIoPanel.exportError3=Export Error]
						s_stringMgr.getString("popupeditableIoPanel.exportError3"),JOptionPane.ERROR_MESSAGE);
						return;
				}

				// see if file exists
				if (file.exists()) {
					if ( ! file.isFile()) {
						JOptionPane.showMessageDialog(this,
							// i18n[popupeditableIoPanel.notANormalFile=File is not a normal file.\n Cannot do export to a directory or system file.]
							s_stringMgr.getString("popupeditableIoPanel.notANormalFile"),
							// i18n[popupeditableIoPanel.exportError4=Export Error]
							s_stringMgr.getString("popupeditableIoPanel.exportError4"),JOptionPane.ERROR_MESSAGE);
						return;
					}
					if ( ! file.canWrite()) {
						JOptionPane.showMessageDialog(this,
							// i18n[popupeditableIoPanel.notWriteable=File is not writeable.\nChange file permissions or select a differnt file for export.]
							s_stringMgr.getString("popupeditableIoPanel.notWriteable"),
							// i18n[popupeditableIoPanel.exportError5=Export Error]
							s_stringMgr.getString("popupeditableIoPanel.exportError5"),JOptionPane.ERROR_MESSAGE);
						return;
					}
					// file exists, is normal and is writable, so see if user
					// wants to overwrite contents of file
					int option = JOptionPane.showConfirmDialog(this,
						// i18n[popupeditableIoPanel.fileOverwrite=File {0} already exists.\n\nDo you wish to overwrite this file?]
						s_stringMgr.getString("popupeditableIoPanel.fileOverwrite", canonicalFilePathName),
						// i18n[popupeditableIoPanel.overwriteWarning=File Overwrite Warning]
							s_stringMgr.getString("popupeditableIoPanel.overwriteWarning"), JOptionPane.YES_NO_OPTION);
					if (option != JOptionPane.YES_OPTION) {
						// user does not want to overwrite the file

						// we could tell user here that export was canceled,
						// but I don't think its necessary, and that avoids
						// forcing user to do yet another annoying mouse click.
						return;
					}
					// user is ok with overwriting file
					// We do not need to do anything special to overwrite
					// (as opposed to appending) since the OutputString
					// starts at the beginning of the file by default.
				}
				else {
					// file does not already exist, so try to create it
					try {
						if ( ! file.createNewFile()) {
							JOptionPane.showMessageDialog(this,
								// i18n[popupeditableIoPanel.createFileError=Failed to create file {0}.\nChange file name or select a differnt file for export.]
								s_stringMgr.getString("popupeditableIoPanel.createFileError", canonicalFilePathName),
								// i18n[popupeditableIoPanel.exportError6=Export Error]
								s_stringMgr.getString("popupeditableIoPanel.exportError6"),JOptionPane.ERROR_MESSAGE);
							return;
						}
					}
					catch (Exception ex) {

						Object[] args = new Object[]{canonicalFilePathName, ex.getMessage()};
						JOptionPane.showMessageDialog(this,
							// i18n[popupeditableIoPanel.cannotOpenFile=Cannot open file {0}.\nError was:{1}]
							s_stringMgr.getString("popupeditableIoPanel.cannotOpenFile", args),
							// i18n[popupeditableIoPanel.exportError7=Export Error]
							s_stringMgr.getString("popupeditableIoPanel.exportError7"),JOptionPane.ERROR_MESSAGE);
						return;
					}
				}
			}

			// at this point we have an actual file that we can output to,
			// so create the output stream
			// (so that data type objects do not have to).

			// We have done everything we can prior to this point
			// to ensure that the the file is accessible, but it is
			// still possible that an existing file was removed
			// at a bad moment.  Also, the compiler insists that we
			// put this in a try statement
			FileOutputStream outStream;
			try {
				outStream = new FileOutputStream(file);
			}
			catch (Exception ex) {
				JOptionPane.showMessageDialog(this,
					// i18n[popupeditableIoPanel.cannotFindFile=Cannot find file {0}\nCheck file name and re-try export.]
					s_stringMgr.getString("popupeditableIoPanel.cannotFindFile", canonicalFilePathName),
					// i18n[popupeditableIoPanel.exportError8=Export Error]
					s_stringMgr.getString("popupeditableIoPanel.exportError8"),JOptionPane.ERROR_MESSAGE);
				return;
			}

            String extCmdComboItemStr = null;
            if (externalCommandCombo != null
                    && externalCommandCombo.getEditor() != null) 
            {
                extCmdComboItemStr = 
                    (String)externalCommandCombo.getEditor().getItem();
            }
            
			// if user did anything other than default, then save
			// their options
			if ( ! TEMP_FILE_FLAG.equals(fileNameField.getText()) 
                    || (extCmdComboItemStr != null 
                            && extCmdComboItemStr.length() > 0)) {

				// This may be called either when the table is editable or when it is
				// read-only.  When it is read-only, there is no command to be saved,
				// but when it is editable, there may be a command.
				String commandString = extCmdComboItemStr;

				CellImportExportInfoSaver.getInstance().save(
					_colDef.getFullTableColumnName(), fileNameField.getText(),
					commandString);
			}

			if (e.getActionCommand().equals("export")) {

				// EXPORT OBJECT TO OSX_FILE

				if (exportData(outStream, canonicalFilePathName) == true) {

					// if we get here, then everything worked correctly, so
					// tell user that data was put into file.
					// This is different from the Import strategy
					// because the user may not know the name of the file
					// that was used if they selected the automatic temp file
					// operation, or they may not know what directory the file
					// was actually put into, so this tells them the full file path.
					JOptionPane.showMessageDialog(this,
						// i18n[popupeditableIoPanel.exportedToFile=Data Successfully exported to file {0}]
						s_stringMgr.getString("popupeditableIoPanel.exportedToFile", canonicalFilePathName),
						// i18n[popupeditableIoPanel.exportSuccess=Export Success]
						s_stringMgr.getString("popupeditableIoPanel.exportSuccess"),JOptionPane.INFORMATION_MESSAGE);
				}
			}

			else if (e.getActionCommand().equals("execute")) {

				// EXPORT OBJECT TO OSX_FILE, EXECUTE PROGRAM ON IT, IMPORT IT BACK

				if (((String)externalCommandCombo.getEditor().getItem()) == null ||
					((String)externalCommandCombo.getEditor().getItem()).length() == 0) {
					// cannot execute a null command
					JOptionPane.showMessageDialog(this,
						// i18n[popupeditableIoPanel.cannotExec=Cannot execute a null command.\nPlease enter a command in the Command field before clicking on Execute.]
						s_stringMgr.getString("popupeditableIoPanel.cannotExec"),
						// i18n[popupeditableIoPanel.executeError=Execute Error]
						s_stringMgr.getString("popupeditableIoPanel.executeError"),JOptionPane.ERROR_MESSAGE);
					return;
				}

				// replace any instance of flag in command with file name
				String command = ((String)externalCommandCombo.getEditor().getItem());

				int index;
				while ((index = command.indexOf(FILE_REPLACE_FLAG)) >= 0) {
					command = command.substring(0, index) +
						canonicalFilePathName +
						command.substring(index + FILE_REPLACE_FLAG.length());
				}

				// export data to file
				if (exportData(outStream, canonicalFilePathName) == false) {
					// bad export - do not proceed with command
					// The exportData() method has already put up a message
					// to the user saying the export failed.
					return;
				}

				int commandResult;
				BufferedReader err = null;
				try {
					// execute command
					Process cmdProcess = Runtime.getRuntime().exec(command);

					// wait for command to complete
					commandResult = cmdProcess.waitFor();

					// check the error stream for a problem
					//
					// This is a bit questionable since it is possible
					// for processes to output something on stderr
					// but continue processing.  But without this, some
					// problems are not seen (e.g. "bad argument" type
					// messages from the process).
					err = new BufferedReader(
						new InputStreamReader(cmdProcess.getErrorStream()));

					String errMsg = err.readLine();
					if (errMsg != null)
						throw new IOException(
							"text on error stream from command starting with:\n"+errMsg);
				}
				catch (Exception ex) {

					Object[] args = new Object[]{command, ex.getMessage()};
					JOptionPane.showMessageDialog(this,
						// i18n[popupeditableIoPanel.errWhileExecutin=Error while executing command.\nThe command was:\n {0}\nThe error was:\n{1}]
						s_stringMgr.getString("popupeditableIoPanel.errWhileExecutin", args),
						// i18n[popupeditableIoPanel.executeError2=Execute Error]
						s_stringMgr.getString("popupeditableIoPanel.executeError2"),JOptionPane.ERROR_MESSAGE);
					return;
				} finally {
				    _iou.closeReader(err);
				}

				// check for possibly bad return from child
				if (commandResult != 0) {
					// command returned non-standard value.
					// ask user before proceeding.
					int option = JOptionPane.showConfirmDialog(this,
							// i18n[popupeditableIoPanel.commandReturnNot0=The convention for command returns is that 0 means success, but this command returned {0}.\nDo you wish to import the file contents anyway?]
							s_stringMgr.getString("popupeditableIoPanel.commandReturnNot0", Integer.valueOf(commandResult)),

							// i18n[popupeditableIoPanel.importWarning=Import Warning]
							s_stringMgr.getString("popupeditableIoPanel.importWarning"), JOptionPane.YES_NO_OPTION);
					if (option != JOptionPane.YES_OPTION) {
						return;
					}
				}

				//import the data back from the same file
				importData(file);

				// If the file was a temp file, delete it now.
				// We assume that Export-only operations want to leave the
				// file in place, but Execute operations just want a temp
				// space to work with and do not want it lying around afterwards.
				file.delete();
			}
		} // end of combined export and execute operations
	}

	/**
	 * Function to import data from a file.
	 * This is a separate function because it is called from two places.
	 * One is when the user asks to do an import, and the other is when they
	 * ask to run an external command, which involves importing from the file
	 * after the command is completed.
	 */
	private void importData(File file) {
		// create the imput stream
		// (so that DataType objects don't have to)
		FileInputStream inStream;
		String canonicalFilePathName = fileNameField.getText();
		try {
			inStream = new FileInputStream(file);

			// it is handy to have the cannonical path name
			// to show user in error messages.  Since getting
			// that name might involve an IOException, we need
			// to put it inside a try statement.  However,
			// since the file does exist there is no good reason
			// for getting an IOException at this point, but
			// if we get one there is something seriously wrong
			// and we want to abort.  Therefore it make sense
			// to get that name here and save it for later use.
			canonicalFilePathName = file.getCanonicalPath();
		}
		catch (Exception ex) {

			Object[] args = new Object[]{canonicalFilePathName, ex.getMessage()};

			JOptionPane.showMessageDialog(this,
				// i18n[popupeditableIoPanel.fileOpenError=There was an error opening file {0}.\nThe error was:\n{1}]
				s_stringMgr.getString("popupeditableIoPanel.fileOpenError", args),
				// i18n[popupeditableIoPanel.fileOpenErrorHeader=File Open Error]
				s_stringMgr.getString("popupeditableIoPanel.fileOpenErrorHeader"),JOptionPane.ERROR_MESSAGE);
			return;
		}

		// hand file input stream to DataType object for import
		// Also, handle File IO errors here so that DataType objects
		// do not have to.
		try {

			// The DataObject returns a string to put into the
			// popup which can later be converted to the appropriate
			// object type.
			String replacementText =
				CellComponentFactory.importObject(_colDef, inStream);

			// since the above did not throw an exception,
			// we now have a good new data object, so
			// change the text area to reflect that new object.
			//

			// If the user has selected a non-cannonical Binary format, we need
			// to convert the text appropriately
			if (radixList != null &&
				! (radixList.getSelectedItem().equals("Hex") &&
					showAscii.isSelected() == false) ) {
				// we need to convert to a different format
				int base = 16;	// default to hex
				if (radixList.getSelectedItem().equals("Decimal")) base = 10;
				else if (radixList.getSelectedItem().equals("Octal")) base = 8;
				else if (radixList.getSelectedItem().equals("Binary")) base = 2;

				Byte[] bytes = BinaryDisplayConverter.convertToBytes(replacementText,
					16, false);

				// return the expected format for this data
				replacementText = BinaryDisplayConverter.convertToString(bytes,
					base, showAscii.isSelected());
			}

			((RestorableJTextArea)_ta).updateText(replacementText);
		}
		catch (Exception ex) {

			Object[] args = new Object[]{canonicalFilePathName, ex.getMessage()};
			JOptionPane.showMessageDialog(this,
				// i18n[popupeditableIoPanel.errorReadingFile=There was an error while reading file {0}.\nThe error was:\n{1}]
				s_stringMgr.getString("popupeditableIoPanel.errorReadingFile", args),
				// i18n[popupeditableIoPanel.importError2=Import Error]
				s_stringMgr.getString("popupeditableIoPanel.importError2"),JOptionPane.ERROR_MESSAGE);
			return;
		}

		// cleanup resources used
		try {
			inStream.close();
		}
		catch (Exception ex) {
			// I cannot think of any reason for doing anything
			// at all here
		}

		/*
				 * Issue: After importing the data, we could tell the user that
				 * it has been successfully imported.  This would give good
				 * positive feedback.  However, it would mean that they need
				 * to click on yet another button to dismiss that message.
				 * On the other hand, since the operation is synchronous,
				 * the user cannot proceed to do anything until it is done.
				 * If we assume that import usually works correctly unless
				 * they are shown an error message, then we do not need to
				 * display anything here.
				 */
	}


	/**
	 * Function to export data to a file.
	 * This is a separate function because it is called from two places.
	 * One is when the user asks to export, and the other is when they
	 * as to run an external command, which involves exporting to file first.
	 */
	private boolean exportData(FileOutputStream outStream,
	                           String canonicalFilePathName){

		// hand file output stream to DataType object for export
		// Also, handle File IO errors here so that DataType objects
		// do not have to.
		try {

			// Hand the current text to the DataType object.
			// DataType object is responsible for validating
			// that the text makes sense for this type of object
			// and converting it to the proper form for output.
			// All errors are handled as IOExceptions
			CellComponentFactory.exportObject(_colDef, outStream,
				getTextAreaCannonicalForm());

		}
		catch (Exception ex) {

			Object[] args = new Object[]{canonicalFilePathName, ex.getMessage()};

			JOptionPane.showMessageDialog(this,
				// i18n[popupeditableIoPanel.errorWritingFile=There was an error while writing file {0}.\nThe error was:\n{1}]
				s_stringMgr.getString("popupeditableIoPanel.errorWritingFile", args),
				// i18n[popupeditableIoPanel.exportError100=Export Error]
				s_stringMgr.getString("popupeditableIoPanel.exportError100"),JOptionPane.ERROR_MESSAGE);
			return false;
		}

		return true;
	}

	/**
	 * catch and handle mouse events to put up a menu
	 */
	public void addNotify()
	{
		super.addNotify();
		if (_lis == null)
		{
			_lis = new MouseAdapter()
			{
				public void mousePressed(MouseEvent evt)
				{
					if (evt.isPopupTrigger())
					{
						_popupMenu.show(evt);
					}
				}
				public void mouseReleased(MouseEvent evt)
				{
						if (evt.isPopupTrigger())
					{
						_popupMenu.show(evt);
					}
				}
			};
			_ta.addMouseListener(_lis);
		}
	}

	public void removeNotify()
	{
		super.removeNotify();
		if (_lis != null)
		{
			_ta.removeMouseListener(_lis);
			_lis = null;
		}
	}

	@SuppressWarnings("serial")
	private class LineWrapAction extends BaseAction
	{
		LineWrapAction()
		{
			// i18n[popupEditableIoPanel.wrapLines=Wrap Lines on/off]
			super(s_stringMgr.getString("popupEditableIoPanel.wrapLines"));
		}

		public void actionPerformed(ActionEvent evt)
		{
			if (_ta != null)
			{
				_ta.setLineWrap(!_ta.getLineWrap());
			}
		}
	}

	private class WordWrapAction extends BaseAction
	{
        private static final long serialVersionUID = 1L;

        WordWrapAction()
		{
			// i18n[popupEditableIoPanel.wrapWord=Wrap on Word on/off]
			super(s_stringMgr.getString("popupEditableIoPanel.wrapWord"));
		}

		public void actionPerformed(ActionEvent evt)
		{
			if (_ta != null)
			{
				_ta.setWrapStyleWord(!_ta.getWrapStyleWord());
			}
		}
	}

	private class XMLReformatAction extends BaseAction
	{
        private static final long serialVersionUID = 1L;

        XMLReformatAction()
		{
			// i18n[popupEditableIoPanel.reformatXml=Reformat XML]
			super(s_stringMgr.getString("popupEditableIoPanel.reformatXml"));
		}

		public void actionPerformed(ActionEvent evt)
		{
			if (_ta != null)
			{
				_ta.setText(XmlRefomatter.reformatXml(_ta.getText()));
			}
		}
	}

	/**
	 * Helper function that ensures that the data is acceptable to the DataType
	 * object. The issue addressed here is that Binary data can be represented
	 * in multiple formats (Hex, Octal, etc), and to keep the DataTypes simple
	 * we assume that they get only Hex data with ASCII chars shown as their
	 * numeric value. This makes sense from the point of view that the different
	 * formats are temporary views handled by this class rather than permanent
	 * settings applied to either the column or the DataType. Therefore, when we
	 * pass the data from the TextArea into the DataType, we may need to do a
	 * conversion on the way.
	 */
	private String getTextAreaCannonicalForm() {
		// handle null
		if (_ta.getText() == null ||
			_ta.getText().equals("") ||
			_ta.getText().length() == 0)
			return _ta.getText();

		// if the data is not binary, then there is no need for conversion.
		// if the data is Hex with ASCII not shown as chars, then no conversion needed.
		if (radixList == null ||
			(radixList.getSelectedItem().equals("Hex") && ! showAscii.isSelected()) ) {
			// no need for conversion
			return _ta.getText();
		}

		// The field is binary and not in the format expected by the DataType
		int base = 16;	// default to hex
		if (radixList.getSelectedItem().equals("Decimal")) base = 10;
		else if (radixList.getSelectedItem().equals("Octal")) base = 8;
		else if (radixList.getSelectedItem().equals("Binary")) base = 2;

		// the following can cause and exception if the text is not formatted correctly
		Byte[] bytes = BinaryDisplayConverter.convertToBytes(_ta.getText(),
			base, showAscii.isSelected());

		// return the expected format for this data
		return BinaryDisplayConverter.convertToString(bytes, 16, false);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy