0001: package net.sourceforge.squirrel_sql.fw.datasetviewer;
0002:
0003: /*
0004: * Copyright (C) 2001 Colin Bell
0005: * colbell@users.sourceforge.net
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2.1 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this library; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0020: */
0021: import java.awt.BorderLayout;
0022: import java.awt.Color;
0023: import java.awt.GridBagConstraints;
0024: import java.awt.GridBagLayout;
0025: import java.awt.Insets;
0026: import java.awt.event.ActionEvent;
0027: import java.awt.event.ActionListener;
0028: import java.awt.event.MouseAdapter;
0029: import java.awt.event.MouseEvent;
0030: import java.io.BufferedReader;
0031: import java.io.File;
0032: import java.io.FileInputStream;
0033: import java.io.FileOutputStream;
0034: import java.io.IOException;
0035: import java.io.InputStreamReader;
0036:
0037: import javax.swing.JButton;
0038: import javax.swing.JCheckBox;
0039: import javax.swing.JComboBox;
0040: import javax.swing.JFileChooser;
0041: import javax.swing.JLabel;
0042: import javax.swing.JOptionPane;
0043: import javax.swing.JPanel;
0044: import javax.swing.JScrollPane;
0045: import javax.swing.JTextArea;
0046: import javax.swing.JTextField;
0047:
0048: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.BinaryDisplayConverter;
0049: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.CellComponentFactory;
0050: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.RestorableJTextArea;
0051: import net.sourceforge.squirrel_sql.fw.gui.TextPopupMenu;
0052: import net.sourceforge.squirrel_sql.fw.gui.action.BaseAction;
0053: import net.sourceforge.squirrel_sql.fw.util.StringManager;
0054: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
0055: import net.sourceforge.squirrel_sql.fw.util.Utilities;
0056:
0057: /**
0058: * @author gwg
0059: *
0060: * Class to handle IO between user and editable text, text and object,
0061: * and text/object to/from file.
0062: */
0063: public class PopupEditableIOPanel extends JPanel implements
0064: ActionListener {
0065:
0066: private static final long serialVersionUID = 1L;
0067:
0068: private static final StringManager s_stringMgr = StringManagerFactory
0069: .getStringManager(PopupEditableIOPanel.class);
0070:
0071: // The text area displaying the object contents
0072: private final JTextArea _ta;
0073:
0074: // the scroll pane that holds the text area
0075: private final JScrollPane scrollPane;
0076:
0077: // Description needed to handle conversion of data to/from Object
0078: transient private final ColumnDisplayDefinition _colDef;
0079:
0080: transient private MouseAdapter _lis;
0081:
0082: private final TextPopupMenu _popupMenu;
0083:
0084: // name of file to do export/import/process on
0085: private JTextField fileNameField;
0086:
0087: // command to use when processing data with an external program
0088: private JComboBox externalCommandCombo;
0089:
0090: // save the original value for re-use by CLOB/BLOB types in conversion
0091: private Object originalValue;
0092:
0093: // Binary data viewing option: which radix to use
0094: // This object is only non-null when the data is binary data
0095: private JComboBox radixList = null;
0096: private String previousRadixListItem = null;
0097: // Binary data viewing option: view ascii as char rather than as numeric value
0098: private JCheckBox showAscii = null;
0099: private boolean previousShowAscii;
0100:
0101: class BinaryOptionActionListener implements ActionListener {
0102: public void actionPerformed(ActionEvent e) {
0103:
0104: // user asked to see binary data in a different format
0105: int base = 16; // default to hex
0106: if (previousRadixListItem.equals("Decimal"))
0107: base = 10;
0108: else if (previousRadixListItem.equals("Octal"))
0109: base = 8;
0110: else if (previousRadixListItem.equals("Binary"))
0111: base = 2;
0112:
0113: Byte[] bytes = BinaryDisplayConverter.convertToBytes(_ta
0114: .getText(), base, previousShowAscii);
0115:
0116: // return the expected format for this data
0117: base = 16; // default to hex
0118: if (radixList.getSelectedItem().equals("Decimal"))
0119: base = 10;
0120: else if (radixList.getSelectedItem().equals("Octal"))
0121: base = 8;
0122: else if (radixList.getSelectedItem().equals("Binary"))
0123: base = 2;
0124:
0125: ((RestorableJTextArea) _ta)
0126: .updateText(BinaryDisplayConverter.convertToString(
0127: bytes, base, showAscii.isSelected()));
0128:
0129: previousRadixListItem = (String) radixList
0130: .getSelectedItem();
0131: previousShowAscii = showAscii.isSelected();
0132:
0133: return;
0134: }
0135: }
0136:
0137: transient private BinaryOptionActionListener optionActionListener = new BinaryOptionActionListener();
0138:
0139: // text put in file name field to indicate that we should
0140: // create a temp file for export
0141: private final String TEMP_FILE_FLAG = "<temp file>";
0142:
0143: // Symbol used by user in Command field to indicate
0144: // "Put the file name here" when the command is executed.
0145: private final String FILE_REPLACE_FLAG = "%f";
0146:
0147: /**
0148: * Constructor
0149: */
0150: public PopupEditableIOPanel(ColumnDisplayDefinition colDef,
0151: Object value, boolean isEditable) {
0152:
0153: originalValue = value; // save for possible future use
0154:
0155: _popupMenu = new TextPopupMenu();
0156:
0157: _colDef = colDef;
0158: _ta = CellComponentFactory.getJTextArea(colDef, value);
0159:
0160: if (isEditable) {
0161: _ta.setEditable(true);
0162: _ta.setBackground(Color.yellow); // tell user it is editable
0163: } else {
0164: _ta.setEditable(false);
0165: }
0166:
0167: _ta.setLineWrap(true);
0168: _ta.setWrapStyleWord(true);
0169:
0170: setLayout(new BorderLayout());
0171:
0172: // add a panel containing binary data editing options, if needed
0173: JPanel displayPanel = new JPanel();
0174: displayPanel.setLayout(new BorderLayout());
0175: scrollPane = new JScrollPane(_ta);
0176: /*
0177: * TODO: When 1.4 is the earliest version supported, include
0178: * the following line here:
0179: * scrollPane.setWheelScrollingEnabled(true);
0180: * The scroll-wheel function is important for ease of use, but the
0181: * setWheelScrollingEnabled function is not available in java 1.3.
0182: */
0183:
0184: displayPanel.add(scrollPane, BorderLayout.CENTER);
0185: if (CellComponentFactory.useBinaryEditingPanel(colDef)) {
0186: // this is a binary field, so allow for multiple viewing options
0187:
0188: String[] radixListData = { "Hex", "Decimal", "Octal",
0189: "Binary" };
0190: radixList = new JComboBox(radixListData);
0191: radixList.addActionListener(optionActionListener);
0192: previousRadixListItem = "Hex";
0193:
0194: showAscii = new JCheckBox();
0195: previousShowAscii = false;
0196: showAscii.addActionListener(optionActionListener);
0197:
0198: JPanel displayControlsPanel = new JPanel();
0199: // use default sequential layout
0200:
0201: // i18n[popupeditableIoPanel.numberBase=Number Base:]
0202: displayControlsPanel.add(new JLabel(s_stringMgr
0203: .getString("popupeditableIoPanel.numberBase")));
0204: displayControlsPanel.add(radixList);
0205: displayControlsPanel.add(new JLabel(" ")); // add some space
0206: displayControlsPanel.add(showAscii);
0207: // i18n[popupeditableIoPanel.showAscii=Show ASCII as chars]
0208: displayControlsPanel.add(new JLabel(s_stringMgr
0209: .getString("popupeditableIoPanel.showAscii")));
0210: displayPanel.add(displayControlsPanel, BorderLayout.SOUTH);
0211: }
0212: add(displayPanel, BorderLayout.CENTER);
0213:
0214: // add controls for file handling, but only if DataType
0215: // can do File operations
0216: if (CellComponentFactory.canDoFileIO(colDef)) {
0217: // yes it can, so add controls
0218: add(exportImportPanel(isEditable), BorderLayout.SOUTH);
0219: }
0220:
0221: _popupMenu.add(new LineWrapAction());
0222: _popupMenu.add(new WordWrapAction());
0223: _popupMenu.add(new XMLReformatAction());
0224: _popupMenu.setTextComponent(_ta);
0225: }
0226:
0227: /**
0228: * build the user interface for export/import operations
0229: */
0230: private JPanel exportImportPanel(boolean isEditable) {
0231: JPanel eiPanel = new JPanel();
0232: eiPanel.setLayout(new GridBagLayout());
0233:
0234: final GridBagConstraints gbc = new GridBagConstraints();
0235:
0236: gbc.insets = new Insets(4, 4, 4, 4);
0237: gbc.gridx = 0;
0238: gbc.gridy = 0;
0239:
0240: // File handling controls
0241: // i18n[popupeditableIoPanel.useFile=Use File: ]
0242: eiPanel.add(new JLabel(s_stringMgr
0243: .getString("popupeditableIoPanel.useFile")), gbc);
0244:
0245: fileNameField = new JTextField(TEMP_FILE_FLAG, 19);
0246: gbc.gridx++;
0247: eiPanel.add(fileNameField, gbc);
0248:
0249: // add button for Brows
0250: // i18n[popupeditableIoPanel.browse=Browse]
0251: JButton browseButton = new JButton(s_stringMgr
0252: .getString("popupeditableIoPanel.browse"));
0253: browseButton.setActionCommand("browse");
0254: browseButton.addActionListener(this );
0255:
0256: gbc.gridx++;
0257: eiPanel.add(browseButton, gbc);
0258:
0259: // i18n[popupeditableIoPanel.export44=Export]
0260: JButton exportButton = new JButton(s_stringMgr
0261: .getString("popupeditableIoPanel.export44"));
0262: exportButton.setActionCommand("export");
0263: exportButton.addActionListener(this );
0264:
0265: gbc.gridx++;
0266: eiPanel.add(exportButton, gbc);
0267:
0268: // import and external processing can only be done if
0269: // panel is editable
0270: if (isEditable == false)
0271: return eiPanel;
0272:
0273: // Add import control
0274: // i18n[popupeditableIoPanel.import44=Import]
0275: JButton importButton = new JButton(s_stringMgr
0276: .getString("popupeditableIoPanel.import44"));
0277: importButton.setActionCommand("import");
0278: importButton.addActionListener(this );
0279:
0280: gbc.gridx++;
0281: eiPanel.add(importButton, gbc);
0282:
0283: // add external processing command field and button
0284: gbc.gridy++;
0285: gbc.gridx = 0;
0286: // i18n[popupeditableIoPanel.withCommand=With command:]
0287: eiPanel.add(new JLabel(s_stringMgr
0288: .getString("popupeditableIoPanel.withCommand")), gbc);
0289:
0290: // add combo box for command to execute
0291: gbc.gridx++;
0292: externalCommandCombo = new JComboBox(CellImportExportInfoSaver
0293: .getInstance().getCmdList());
0294: externalCommandCombo.setSelectedIndex(-1); // no entry selected
0295: externalCommandCombo.setEditable(true);
0296:
0297: // make this the same size as the fileNameField
0298: externalCommandCombo.setPreferredSize(fileNameField
0299: .getPreferredSize());
0300:
0301: eiPanel.add(externalCommandCombo, gbc);
0302:
0303: // add button to execute external command
0304: // i18n[popupeditableIoPanel.execute34=Execute]
0305: JButton externalCommandButton = new JButton(s_stringMgr
0306: .getString("popupeditableIoPanel.execute34"));
0307: externalCommandButton.setActionCommand("execute");
0308: externalCommandButton.addActionListener(this );
0309:
0310: gbc.gridx++;
0311: eiPanel.add(externalCommandButton, gbc);
0312:
0313: // add button for applying file & cmd info without doing anything else
0314: // i18n[popupeditableIoPanel.applyFile=Apply File & Cmd]
0315: JButton applyButton = new JButton(s_stringMgr
0316: .getString("popupeditableIoPanel.applyFile"));
0317: applyButton.setActionCommand("apply");
0318: applyButton.addActionListener(this );
0319:
0320: gbc.gridx++;
0321: gbc.gridwidth = 2;
0322: eiPanel.add(applyButton, gbc);
0323: gbc.gridwidth = 1; // reset width to normal
0324:
0325: // add note to user about including file name in command
0326: gbc.gridy++;
0327: gbc.gridx = 0;
0328: gbc.gridwidth = GridBagConstraints.REMAINDER;
0329:
0330: // i18n[popupeditableIoPanel.replaceFile=(In command, the string {0} is replaced by the file name when Executed.)]
0331: eiPanel.add(
0332: new JLabel(s_stringMgr.getString(
0333: "popupeditableIoPanel.replaceFile",
0334: FILE_REPLACE_FLAG)), gbc);
0335:
0336: // load filename and command with previously entered info
0337: // if not the default
0338: CellImportExportInfo info = CellImportExportInfoSaver
0339: .getInstance().get(_colDef.getFullTableColumnName());
0340: if (info != null) {
0341: // load the info into the text fields
0342: fileNameField.setText(info.getFileName());
0343: externalCommandCombo.getEditor().setItem(info.getCommand());
0344: }
0345:
0346: return eiPanel;
0347: }
0348:
0349: /**
0350: * Return the contents of the editable text area as an Object
0351: * converted by the correct DataType function. Errors in converting
0352: * from the text form into the Object are reported
0353: * through the messageBuffer.
0354: */
0355: public Object getObject(StringBuffer messageBuffer) {
0356: String text = null;
0357: try {
0358: text = getTextAreaCannonicalForm();
0359: } catch (Exception e) {
0360: messageBuffer
0361: .append("Failed to convert binary text; error was:\n"
0362: + e.getMessage());
0363: return null;
0364: }
0365: return CellComponentFactory.validateAndConvertInPopup(_colDef,
0366: originalValue, text, messageBuffer);
0367: }
0368:
0369: /**
0370: * When focus is passed to this panel, automatically pass it
0371: * on to the text area.
0372: */
0373: public void requestFocus() {
0374: _ta.requestFocus();
0375: }
0376:
0377: /**
0378: * Handle actions on the buttons in the file operations panel
0379: */
0380: public void actionPerformed(ActionEvent e) {
0381:
0382: //File object for doing IO
0383: File file;
0384:
0385: if (e.getActionCommand().equals("browse")) {
0386: JFileChooser chooser = new JFileChooser();
0387: String filename = fileNameField.getText();
0388: if (filename != null && !"".equals(filename)) {
0389: File f = new File(filename);
0390: String path = f.getAbsolutePath();
0391: if (path != null && !"".equals(path)) {
0392: chooser.setCurrentDirectory(new File(path));
0393: }
0394: }
0395: int returnVal = chooser.showOpenDialog(this );
0396: if (returnVal == JFileChooser.APPROVE_OPTION) {
0397: // System.out.println("You chose to open this file: " +
0398: // chooser.getSelectedFile().getName());
0399: try {
0400: fileNameField.setText(chooser.getSelectedFile()
0401: .getCanonicalPath());
0402: } catch (Exception ex) {
0403: // should not happen since the file that was selected was
0404: // just being shown in the Chooser dialog, but just to be safe...
0405: JOptionPane
0406: .showMessageDialog(
0407: this ,
0408: // i18n[popupeditableIoPanel.errorGettingPath=Error getting full path name for selected file]
0409: s_stringMgr
0410: .getString("popupeditableIoPanel.errorGettingPath"),
0411: // i18n[popupeditableIoPanel.fileChooserError=File Chooser Error]
0412: s_stringMgr
0413: .getString("popupeditableIoPanel.fileChooserError"),
0414: JOptionPane.ERROR_MESSAGE);
0415: }
0416: }
0417: }
0418:
0419: else if (e.getActionCommand().equals("apply")) {
0420: // If file name default and cmd is null or empty,
0421: // make sure this entry is not being held in CellImportExportInfoSaver
0422: if ((fileNameField.getText() != null && fileNameField
0423: .getText().equals(TEMP_FILE_FLAG))
0424: && (externalCommandCombo.getEditor().getItem() == null || ((String) externalCommandCombo
0425: .getEditor().getItem()).length() == 0)) {
0426: // user has not entered anything or has reset to defaults,
0427: // so make sure there is no entry for this column in the
0428: // saved info
0429: CellImportExportInfoSaver.remove(_colDef
0430: .getFullTableColumnName());
0431: } else {
0432: // user has entered some non-default info, so save it
0433: CellImportExportInfoSaver.getInstance().save(
0434: _colDef.getFullTableColumnName(),
0435: fileNameField.getText(),
0436: ((String) externalCommandCombo.getEditor()
0437: .getItem()));
0438: }
0439:
0440: }
0441:
0442: else if (e.getActionCommand().equals("import")) {
0443:
0444: // IMPORT OBJECT FROM OSX_FILE
0445:
0446: if (fileNameField.getText() == null
0447: || fileNameField.getText().equals(TEMP_FILE_FLAG)) {
0448: // not allowed - must have existing file for import
0449: JOptionPane
0450: .showMessageDialog(
0451: this ,
0452: // i18n[popupeditableIoPanel.selectImportDataFile=You must select an existing file to import data from.]
0453: s_stringMgr
0454: .getString("popupeditableIoPanel.selectImportDataFile"),
0455: // i18n[popupeditableIoPanel.noFile=No File Selected]
0456: s_stringMgr
0457: .getString("popupeditableIoPanel.noFile"),
0458: JOptionPane.ERROR_MESSAGE);
0459: return; // do not do import
0460: }
0461:
0462: // get name of file, which must exist
0463: if (fileNameField.getText() == null)
0464: fileNameField.setText(""); // guard against something really stupid
0465: file = new File(fileNameField.getText());
0466: if (!file.exists() || !file.isFile() || !file.canRead()) {
0467: // not something we can read
0468:
0469: // 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.]
0470: String msg = s_stringMgr.getString(
0471: "popupeditableIoPanel.fileDoesNotExist",
0472: fileNameField.getText());
0473:
0474: JOptionPane
0475: .showMessageDialog(
0476: this ,
0477: msg,
0478:
0479: // i18n[popupeditableIoPanel.fileError=File Name Error]
0480: s_stringMgr
0481: .getString("popupeditableIoPanel.fileError"),
0482: JOptionPane.ERROR_MESSAGE);
0483: return; // do not do import
0484: }
0485:
0486: // Now that we have the file, do the import.
0487: //
0488: // Note: the sequence of operations is divided into two sections
0489: // at this point. The preceeding code ensures that we have
0490: // a readable file, and the code in the following method call
0491: // does the import. The reason for splitting at this point is
0492: // that the Execute operation needs to do an import, and it will
0493: // already have the file to do the import from (which is the same
0494: // as the file it exported into).
0495: importData(file);
0496:
0497: // save the user options - we know that it is not the default
0498: // because we do not allow importing from "temp file"
0499: CellImportExportInfoSaver.getInstance().save(
0500: _colDef.getFullTableColumnName(),
0501: fileNameField.getText(),
0502: ((String) externalCommandCombo.getEditor()
0503: .getItem()));
0504:
0505: }
0506:
0507: else {
0508:
0509: // GET OSX_FILE FOR EXPORT & EXTERNAL PROCESSING
0510:
0511: String canonicalFilePathName = fileNameField.getText();
0512:
0513: // file name verification operations are the same for both
0514: // export and execute, so do that work here for both.
0515: //
0516: // If file name is null or empty, do not proceed
0517: if (fileNameField.getText() == null
0518: || fileNameField.getText().equals("")) {
0519:
0520: JOptionPane
0521: .showMessageDialog(
0522: this ,
0523:
0524: // i18n[popupeditableIoPanel.noExportFile=No file name given for export.\nPlease enter a file name or use Browse before clicking Export.]
0525: s_stringMgr
0526: .getString("popupeditableIoPanel.noExportFile"),
0527: // i18n[popupeditableIoPanel.exportError=Export Error]
0528: s_stringMgr
0529: .getString("popupeditableIoPanel.exportError"),
0530: JOptionPane.ERROR_MESSAGE);
0531: return;
0532: }
0533:
0534: // create the file to open
0535: if (fileNameField.getText().equals(TEMP_FILE_FLAG)) {
0536: // user wants us to create a temp file
0537: try {
0538: file = File.createTempFile("squirrel", ".tmp");
0539:
0540: // get the real name for use later
0541: canonicalFilePathName = file.getCanonicalPath();
0542: } catch (Exception ex) {
0543: JOptionPane
0544: .showMessageDialog(
0545: this ,
0546: // i18n[popupeditableIoPanel.cannotCreateTempFile=Cannot create temp file..\nError was:\n{0}]
0547: s_stringMgr
0548: .getString(
0549: "popupeditableIoPanel.cannotCreateTempFile",
0550: ex.getMessage()),
0551: // i18n[popupeditableIoPanel.exportError2=Export Error]
0552: s_stringMgr
0553: .getString("popupeditableIoPanel.exportError2"),
0554: JOptionPane.ERROR_MESSAGE);
0555: return;
0556: }
0557: } else {
0558: //user must have supplied a file name.
0559: file = new File(fileNameField.getText());
0560:
0561: try {
0562: canonicalFilePathName = file.getCanonicalPath();
0563: } catch (Exception ex) {
0564: // an error here may mean that the file cannot be
0565: // reached or has moved or some such. In any case,
0566: // the file cannot be used for export.
0567: JOptionPane
0568: .showMessageDialog(
0569: this ,
0570: // i18n[popupeditableIoPanel.cannotAccessFile=Cannot access file name {0}\nAborting export.]
0571: s_stringMgr
0572: .getString(
0573: "popupeditableIoPanel.cannotAccessFile",
0574: fileNameField
0575: .getText()),
0576:
0577: // i18n[popupeditableIoPanel.exportError3=Export Error]
0578: s_stringMgr
0579: .getString("popupeditableIoPanel.exportError3"),
0580: JOptionPane.ERROR_MESSAGE);
0581: return;
0582: }
0583:
0584: // see if file exists
0585: if (file.exists()) {
0586: if (!file.isFile()) {
0587: JOptionPane
0588: .showMessageDialog(
0589: this ,
0590: // i18n[popupeditableIoPanel.notANormalFile=File is not a normal file.\n Cannot do export to a directory or system file.]
0591: s_stringMgr
0592: .getString("popupeditableIoPanel.notANormalFile"),
0593: // i18n[popupeditableIoPanel.exportError4=Export Error]
0594: s_stringMgr
0595: .getString("popupeditableIoPanel.exportError4"),
0596: JOptionPane.ERROR_MESSAGE);
0597: return;
0598: }
0599: if (!file.canWrite()) {
0600: JOptionPane
0601: .showMessageDialog(
0602: this ,
0603: // i18n[popupeditableIoPanel.notWriteable=File is not writeable.\nChange file permissions or select a differnt file for export.]
0604: s_stringMgr
0605: .getString("popupeditableIoPanel.notWriteable"),
0606: // i18n[popupeditableIoPanel.exportError5=Export Error]
0607: s_stringMgr
0608: .getString("popupeditableIoPanel.exportError5"),
0609: JOptionPane.ERROR_MESSAGE);
0610: return;
0611: }
0612: // file exists, is normal and is writable, so see if user
0613: // wants to overwrite contents of file
0614: int option = JOptionPane
0615: .showConfirmDialog(
0616: this ,
0617: // i18n[popupeditableIoPanel.fileOverwrite=File {0} already exists.\n\nDo you wish to overwrite this file?]
0618: s_stringMgr
0619: .getString(
0620: "popupeditableIoPanel.fileOverwrite",
0621: canonicalFilePathName),
0622: // i18n[popupeditableIoPanel.overwriteWarning=File Overwrite Warning]
0623: s_stringMgr
0624: .getString("popupeditableIoPanel.overwriteWarning"),
0625: JOptionPane.YES_NO_OPTION);
0626: if (option != JOptionPane.YES_OPTION) {
0627: // user does not want to overwrite the file
0628:
0629: // we could tell user here that export was canceled,
0630: // but I don't think its necessary, and that avoids
0631: // forcing user to do yet another annoying mouse click.
0632: return;
0633: }
0634: // user is ok with overwriting file
0635: // We do not need to do anything special to overwrite
0636: // (as opposed to appending) since the OutputString
0637: // starts at the beginning of the file by default.
0638: } else {
0639: // file does not already exist, so try to create it
0640: try {
0641: if (!file.createNewFile()) {
0642: JOptionPane
0643: .showMessageDialog(
0644: this ,
0645: // i18n[popupeditableIoPanel.createFileError=Failed to create file {0}.\nChange file name or select a differnt file for export.]
0646: s_stringMgr
0647: .getString(
0648: "popupeditableIoPanel.createFileError",
0649: canonicalFilePathName),
0650: // i18n[popupeditableIoPanel.exportError6=Export Error]
0651: s_stringMgr
0652: .getString("popupeditableIoPanel.exportError6"),
0653: JOptionPane.ERROR_MESSAGE);
0654: return;
0655: }
0656: } catch (Exception ex) {
0657:
0658: Object[] args = new Object[] {
0659: canonicalFilePathName, ex.getMessage() };
0660: JOptionPane
0661: .showMessageDialog(
0662: this ,
0663: // i18n[popupeditableIoPanel.cannotOpenFile=Cannot open file {0}.\nError was:{1}]
0664: s_stringMgr
0665: .getString(
0666: "popupeditableIoPanel.cannotOpenFile",
0667: args),
0668: // i18n[popupeditableIoPanel.exportError7=Export Error]
0669: s_stringMgr
0670: .getString("popupeditableIoPanel.exportError7"),
0671: JOptionPane.ERROR_MESSAGE);
0672: return;
0673: }
0674: }
0675: }
0676:
0677: // at this point we have an actual file that we can output to,
0678: // so create the output stream
0679: // (so that data type objects do not have to).
0680:
0681: // We have done everything we can prior to this point
0682: // to ensure that the the file is accessible, but it is
0683: // still possible that an existing file was removed
0684: // at a bad moment. Also, the compiler insists that we
0685: // put this in a try statement
0686: FileOutputStream outStream;
0687: try {
0688: outStream = new FileOutputStream(file);
0689: } catch (Exception ex) {
0690: JOptionPane
0691: .showMessageDialog(
0692: this ,
0693: // i18n[popupeditableIoPanel.cannotFindFile=Cannot find file {0}\nCheck file name and re-try export.]
0694: s_stringMgr
0695: .getString(
0696: "popupeditableIoPanel.cannotFindFile",
0697: canonicalFilePathName),
0698: // i18n[popupeditableIoPanel.exportError8=Export Error]
0699: s_stringMgr
0700: .getString("popupeditableIoPanel.exportError8"),
0701: JOptionPane.ERROR_MESSAGE);
0702: return;
0703: }
0704:
0705: String extCmdComboItemStr = null;
0706: if (externalCommandCombo != null
0707: && externalCommandCombo.getEditor() != null) {
0708: extCmdComboItemStr = (String) externalCommandCombo
0709: .getEditor().getItem();
0710: }
0711:
0712: // if user did anything other than default, then save
0713: // their options
0714: if (!TEMP_FILE_FLAG.equals(fileNameField.getText())
0715: || (extCmdComboItemStr != null && extCmdComboItemStr
0716: .length() > 0)) {
0717:
0718: // This may be called either when the table is editable or when it is
0719: // read-only. When it is read-only, there is no command to be saved,
0720: // but when it is editable, there may be a command.
0721: String commandString = extCmdComboItemStr;
0722:
0723: CellImportExportInfoSaver.getInstance().save(
0724: _colDef.getFullTableColumnName(),
0725: fileNameField.getText(), commandString);
0726: }
0727:
0728: if (e.getActionCommand().equals("export")) {
0729:
0730: // EXPORT OBJECT TO OSX_FILE
0731:
0732: if (exportData(outStream, canonicalFilePathName) == true) {
0733:
0734: // if we get here, then everything worked correctly, so
0735: // tell user that data was put into file.
0736: // This is different from the Import strategy
0737: // because the user may not know the name of the file
0738: // that was used if they selected the automatic temp file
0739: // operation, or they may not know what directory the file
0740: // was actually put into, so this tells them the full file path.
0741: JOptionPane
0742: .showMessageDialog(
0743: this ,
0744: // i18n[popupeditableIoPanel.exportedToFile=Data Successfully exported to file {0}]
0745: s_stringMgr
0746: .getString(
0747: "popupeditableIoPanel.exportedToFile",
0748: canonicalFilePathName),
0749: // i18n[popupeditableIoPanel.exportSuccess=Export Success]
0750: s_stringMgr
0751: .getString("popupeditableIoPanel.exportSuccess"),
0752: JOptionPane.INFORMATION_MESSAGE);
0753: }
0754: }
0755:
0756: else if (e.getActionCommand().equals("execute")) {
0757:
0758: // EXPORT OBJECT TO OSX_FILE, EXECUTE PROGRAM ON IT, IMPORT IT BACK
0759:
0760: if (((String) externalCommandCombo.getEditor()
0761: .getItem()) == null
0762: || ((String) externalCommandCombo.getEditor()
0763: .getItem()).length() == 0) {
0764: // cannot execute a null command
0765: JOptionPane
0766: .showMessageDialog(
0767: this ,
0768: // i18n[popupeditableIoPanel.cannotExec=Cannot execute a null command.\nPlease enter a command in the Command field before clicking on Execute.]
0769: s_stringMgr
0770: .getString("popupeditableIoPanel.cannotExec"),
0771: // i18n[popupeditableIoPanel.executeError=Execute Error]
0772: s_stringMgr
0773: .getString("popupeditableIoPanel.executeError"),
0774: JOptionPane.ERROR_MESSAGE);
0775: return;
0776: }
0777:
0778: // replace any instance of flag in command with file name
0779: String command = ((String) externalCommandCombo
0780: .getEditor().getItem());
0781:
0782: int index;
0783: while ((index = command.indexOf(FILE_REPLACE_FLAG)) >= 0) {
0784: command = command.substring(0, index)
0785: + canonicalFilePathName
0786: + command.substring(index
0787: + FILE_REPLACE_FLAG.length());
0788: }
0789:
0790: // export data to file
0791: if (exportData(outStream, canonicalFilePathName) == false) {
0792: // bad export - do not proceed with command
0793: // The exportData() method has already put up a message
0794: // to the user saying the export failed.
0795: return;
0796: }
0797:
0798: int commandResult;
0799: BufferedReader err = null;
0800: try {
0801: // execute command
0802: Process cmdProcess = Runtime.getRuntime().exec(
0803: command);
0804:
0805: // wait for command to complete
0806: commandResult = cmdProcess.waitFor();
0807:
0808: // check the error stream for a problem
0809: //
0810: // This is a bit questionable since it is possible
0811: // for processes to output something on stderr
0812: // but continue processing. But without this, some
0813: // problems are not seen (e.g. "bad argument" type
0814: // messages from the process).
0815: err = new BufferedReader(new InputStreamReader(
0816: cmdProcess.getErrorStream()));
0817:
0818: String errMsg = err.readLine();
0819: if (errMsg != null)
0820: throw new IOException(
0821: "text on error stream from command starting with:\n"
0822: + errMsg);
0823: } catch (Exception ex) {
0824:
0825: Object[] args = new Object[] { command,
0826: ex.getMessage() };
0827: JOptionPane
0828: .showMessageDialog(
0829: this ,
0830: // i18n[popupeditableIoPanel.errWhileExecutin=Error while executing command.\nThe command was:\n {0}\nThe error was:\n{1}]
0831: s_stringMgr
0832: .getString(
0833: "popupeditableIoPanel.errWhileExecutin",
0834: args),
0835: // i18n[popupeditableIoPanel.executeError2=Execute Error]
0836: s_stringMgr
0837: .getString("popupeditableIoPanel.executeError2"),
0838: JOptionPane.ERROR_MESSAGE);
0839: return;
0840: } finally {
0841: Utilities.closeReader(err);
0842: }
0843:
0844: // check for possibly bad return from child
0845: if (commandResult != 0) {
0846: // command returned non-standard value.
0847: // ask user before proceeding.
0848: int option = JOptionPane
0849: .showConfirmDialog(
0850: this ,
0851: // 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?]
0852: s_stringMgr
0853: .getString(
0854: "popupeditableIoPanel.commandReturnNot0",
0855: Integer
0856: .valueOf(commandResult)),
0857:
0858: // i18n[popupeditableIoPanel.importWarning=Import Warning]
0859: s_stringMgr
0860: .getString("popupeditableIoPanel.importWarning"),
0861: JOptionPane.YES_NO_OPTION);
0862: if (option != JOptionPane.YES_OPTION) {
0863: return;
0864: }
0865: }
0866:
0867: //import the data back from the same file
0868: importData(file);
0869:
0870: // If the file was a temp file, delete it now.
0871: // We assume that Export-only operations want to leave the
0872: // file in place, but Execute operations just want a temp
0873: // space to work with and do not want it lying around afterwards.
0874: file.delete();
0875: }
0876: } // end of combined export and execute operations
0877: }
0878:
0879: /**
0880: * Function to import data from a file.
0881: * This is a separate function because it is called from two places.
0882: * One is when the user asks to do an import, and the other is when they
0883: * ask to run an external command, which involves importing from the file
0884: * after the command is completed.
0885: */
0886: private void importData(File file) {
0887: // create the imput stream
0888: // (so that DataType objects don't have to)
0889: FileInputStream inStream;
0890: String canonicalFilePathName = fileNameField.getText();
0891: try {
0892: inStream = new FileInputStream(file);
0893:
0894: // it is handy to have the cannonical path name
0895: // to show user in error messages. Since getting
0896: // that name might involve an IOException, we need
0897: // to put it inside a try statement. However,
0898: // since the file does exist there is no good reason
0899: // for getting an IOException at this point, but
0900: // if we get one there is something seriously wrong
0901: // and we want to abort. Therefore it make sense
0902: // to get that name here and save it for later use.
0903: canonicalFilePathName = file.getCanonicalPath();
0904: } catch (Exception ex) {
0905:
0906: Object[] args = new Object[] { canonicalFilePathName,
0907: ex.getMessage() };
0908:
0909: JOptionPane
0910: .showMessageDialog(
0911: this ,
0912: // i18n[popupeditableIoPanel.fileOpenError=There was an error opening file {0}.\nThe error was:\n{1}]
0913: s_stringMgr
0914: .getString(
0915: "popupeditableIoPanel.fileOpenError",
0916: args),
0917: // i18n[popupeditableIoPanel.fileOpenErrorHeader=File Open Error]
0918: s_stringMgr
0919: .getString("popupeditableIoPanel.fileOpenErrorHeader"),
0920: JOptionPane.ERROR_MESSAGE);
0921: return;
0922: }
0923:
0924: // hand file input stream to DataType object for import
0925: // Also, handle File IO errors here so that DataType objects
0926: // do not have to.
0927: try {
0928:
0929: // The DataObject returns a string to put into the
0930: // popup which can later be converted to the appropriate
0931: // object type.
0932: String replacementText = CellComponentFactory.importObject(
0933: _colDef, inStream);
0934:
0935: // since the above did not throw an exception,
0936: // we now have a good new data object, so
0937: // change the text area to reflect that new object.
0938: //
0939:
0940: // If the user has selected a non-cannonical Binary format, we need
0941: // to convert the text appropriately
0942: if (radixList != null
0943: && !(radixList.getSelectedItem().equals("Hex") && showAscii
0944: .isSelected() == false)) {
0945: // we need to convert to a different format
0946: int base = 16; // default to hex
0947: if (radixList.getSelectedItem().equals("Decimal"))
0948: base = 10;
0949: else if (radixList.getSelectedItem().equals("Octal"))
0950: base = 8;
0951: else if (radixList.getSelectedItem().equals("Binary"))
0952: base = 2;
0953:
0954: Byte[] bytes = BinaryDisplayConverter.convertToBytes(
0955: replacementText, 16, false);
0956:
0957: // return the expected format for this data
0958: replacementText = BinaryDisplayConverter
0959: .convertToString(bytes, base, showAscii
0960: .isSelected());
0961: }
0962:
0963: ((RestorableJTextArea) _ta).updateText(replacementText);
0964: } catch (Exception ex) {
0965:
0966: Object[] args = new Object[] { canonicalFilePathName,
0967: ex.getMessage() };
0968: JOptionPane
0969: .showMessageDialog(
0970: this ,
0971: // i18n[popupeditableIoPanel.errorReadingFile=There was an error while reading file {0}.\nThe error was:\n{1}]
0972: s_stringMgr
0973: .getString(
0974: "popupeditableIoPanel.errorReadingFile",
0975: args),
0976: // i18n[popupeditableIoPanel.importError2=Import Error]
0977: s_stringMgr
0978: .getString("popupeditableIoPanel.importError2"),
0979: JOptionPane.ERROR_MESSAGE);
0980: return;
0981: }
0982:
0983: // cleanup resources used
0984: try {
0985: inStream.close();
0986: } catch (Exception ex) {
0987: // I cannot think of any reason for doing anything
0988: // at all here
0989: }
0990:
0991: /*
0992: * Issue: After importing the data, we could tell the user that
0993: * it has been successfully imported. This would give good
0994: * positive feedback. However, it would mean that they need
0995: * to click on yet another button to dismiss that message.
0996: * On the other hand, since the operation is synchronous,
0997: * the user cannot proceed to do anything until it is done.
0998: * If we assume that import usually works correctly unless
0999: * they are shown an error message, then we do not need to
1000: * display anything here.
1001: */
1002: }
1003:
1004: /**
1005: * Function to export data to a file.
1006: * This is a separate function because it is called from two places.
1007: * One is when the user asks to export, and the other is when they
1008: * as to run an external command, which involves exporting to file first.
1009: */
1010: private boolean exportData(FileOutputStream outStream,
1011: String canonicalFilePathName) {
1012:
1013: // hand file output stream to DataType object for export
1014: // Also, handle File IO errors here so that DataType objects
1015: // do not have to.
1016: try {
1017:
1018: // Hand the current text to the DataType object.
1019: // DataType object is responsible for validating
1020: // that the text makes sense for this type of object
1021: // and converting it to the proper form for output.
1022: // All errors are handled as IOExceptions
1023: CellComponentFactory.exportObject(_colDef, outStream,
1024: getTextAreaCannonicalForm());
1025:
1026: } catch (Exception ex) {
1027:
1028: Object[] args = new Object[] { canonicalFilePathName,
1029: ex.getMessage() };
1030:
1031: JOptionPane
1032: .showMessageDialog(
1033: this ,
1034: // i18n[popupeditableIoPanel.errorWritingFile=There was an error while writing file {0}.\nThe error was:\n{1}]
1035: s_stringMgr
1036: .getString(
1037: "popupeditableIoPanel.errorWritingFile",
1038: args),
1039: // i18n[popupeditableIoPanel.exportError100=Export Error]
1040: s_stringMgr
1041: .getString("popupeditableIoPanel.exportError100"),
1042: JOptionPane.ERROR_MESSAGE);
1043: return false;
1044: }
1045:
1046: return true;
1047: }
1048:
1049: /**
1050: * catch and handle mouse events to put up a menu
1051: */
1052: public void addNotify() {
1053: super .addNotify();
1054: if (_lis == null) {
1055: _lis = new MouseAdapter() {
1056: public void mousePressed(MouseEvent evt) {
1057: if (evt.isPopupTrigger()) {
1058: _popupMenu.show(evt);
1059: }
1060: }
1061:
1062: public void mouseReleased(MouseEvent evt) {
1063: if (evt.isPopupTrigger()) {
1064: _popupMenu.show(evt);
1065: }
1066: }
1067: };
1068: _ta.addMouseListener(_lis);
1069: }
1070: }
1071:
1072: public void removeNotify() {
1073: super .removeNotify();
1074: if (_lis != null) {
1075: _ta.removeMouseListener(_lis);
1076: _lis = null;
1077: }
1078: }
1079:
1080: private class LineWrapAction extends BaseAction {
1081: LineWrapAction() {
1082: // i18n[popupEditableIoPanel.wrapLines=Wrap Lines on/off]
1083: super (s_stringMgr
1084: .getString("popupEditableIoPanel.wrapLines"));
1085: }
1086:
1087: public void actionPerformed(ActionEvent evt) {
1088: if (_ta != null) {
1089: _ta.setLineWrap(!_ta.getLineWrap());
1090: }
1091: }
1092: }
1093:
1094: private class WordWrapAction extends BaseAction {
1095: private static final long serialVersionUID = 1L;
1096:
1097: WordWrapAction() {
1098: // i18n[popupEditableIoPanel.wrapWord=Wrap on Word on/off]
1099: super (s_stringMgr
1100: .getString("popupEditableIoPanel.wrapWord"));
1101: }
1102:
1103: public void actionPerformed(ActionEvent evt) {
1104: if (_ta != null) {
1105: _ta.setWrapStyleWord(!_ta.getWrapStyleWord());
1106: }
1107: }
1108: }
1109:
1110: private class XMLReformatAction extends BaseAction {
1111: private static final long serialVersionUID = 1L;
1112:
1113: XMLReformatAction() {
1114: // i18n[popupEditableIoPanel.reformatXml=Reformat XML]
1115: super (s_stringMgr
1116: .getString("popupEditableIoPanel.reformatXml"));
1117: }
1118:
1119: public void actionPerformed(ActionEvent evt) {
1120: if (_ta != null) {
1121: _ta.setText(XmlRefomatter.reformatXml(_ta.getText()));
1122: }
1123: }
1124: }
1125:
1126: /**
1127: * Helper function that ensures that the data is acceptable to the DataType
1128: * object. The issue addressed here is that Binary data can be represented
1129: * in multiple formats (Hex, Octal, etc), and to keep the DataTypes simple
1130: * we assume that they get only Hex data with ASCII chars shown as their
1131: * numeric value. This makes sense from the point of view that the different
1132: * formats are temporary views handled by this class rather than permanent
1133: * settings applied to either the column or the DataType. Therefore, when we
1134: * pass the data from the TextArea into the DataType, we may need to do a
1135: * conversion on the way.
1136: */
1137: private String getTextAreaCannonicalForm() {
1138: // handle null
1139: if (_ta.getText() == null || _ta.getText().equals("<null>")
1140: || _ta.getText().length() == 0)
1141: return _ta.getText();
1142:
1143: // if the data is not binary, then there is no need for conversion.
1144: // if the data is Hex with ASCII not shown as chars, then no conversion needed.
1145: if (radixList == null
1146: || (radixList.getSelectedItem().equals("Hex") && !showAscii
1147: .isSelected())) {
1148: // no need for conversion
1149: return _ta.getText();
1150: }
1151:
1152: // The field is binary and not in the format expected by the DataType
1153: int base = 16; // default to hex
1154: if (radixList.getSelectedItem().equals("Decimal"))
1155: base = 10;
1156: else if (radixList.getSelectedItem().equals("Octal"))
1157: base = 8;
1158: else if (radixList.getSelectedItem().equals("Binary"))
1159: base = 2;
1160:
1161: // the following can cause and exception if the text is not formatted correctly
1162: Byte[] bytes = BinaryDisplayConverter.convertToBytes(_ta
1163: .getText(), base, showAscii.isSelected());
1164:
1165: // return the expected format for this data
1166: return BinaryDisplayConverter.convertToString(bytes, 16, false);
1167: }
1168: }
|