# This patch file was generated by NetBeans IDE # Following Index: paths are relative to: /home/matthias/NetBeansProjects/core-main # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: db.dataview/src/org/netbeans/modules/db/dataview/output/DataViewTableUI.java --- db.dataview/src/org/netbeans/modules/db/dataview/output/DataViewTableUI.java Base (BASE) +++ db.dataview/src/org/netbeans/modules/db/dataview/output/DataViewTableUI.java Locally Modified (Based On LOCAL) @@ -326,7 +326,8 @@ public void actionPerformed(ActionEvent e) { try { Object o = getValueAt(selectedRow, selectedColumn); - String output = (o != null) ? o.toString() : ""; //NOI18N + // TODO: Evaluate if 1 MB/1 Million Characters are a good limit + String output = convertToClipboardString(o, 1024 * 1024); ExClipboard clipboard = Lookup.getDefault().lookup(ExClipboard.class); StringSelection strSel = new StringSelection(output); @@ -390,7 +391,7 @@ int modelIndex = convertRowIndexToModel(rows[j]); Object[] insertRow = dataView.getDataViewPageContext().getCurrentRows().get(modelIndex); String sql = dataView.getSQLStatementGenerator().generateRawInsertStatement(insertRow); - insertSQL += sql.replaceAll("\n", "").replaceAll("\t", "") + ";\n"; // NOI18N + insertSQL += sql + ";\n"; // NOI18N } ShowSQLDialog dialog = new ShowSQLDialog(); dialog.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); @@ -532,55 +533,4 @@ } }); } - - private void copyRowValues(boolean withHeader) { - try { - int[] rows = getSelectedRows(); - int[] columns; - if (getRowSelectionAllowed()) { - columns = new int[getColumnCount()]; - for (int a = 0; a < columns.length; a++) { - columns[a] = a; } - } else { - columns = getSelectedColumns(); - } - if (rows != null && columns != null) { - StringBuilder output = new StringBuilder(); - - if (withHeader) { - for (int column = 0; column < columns.length; column++) { - if (column > 0) { - output.append('\t'); //NOI18N - - } - Object o = getColumnModel().getColumn(column).getIdentifier(); - output.append(o != null ? o.toString() : ""); //NOI18N - - } - output.append('\n'); //NOI18N - - } - - for (int row = 0; row < rows.length; row++) { - for (int column = 0; column < columns.length; column++) { - if (column > 0) { - output.append('\t'); //NOI18N - - } - Object o = getValueAt(rows[row], columns[column]); - output.append(o != null ? o.toString() : ""); //NOI18N - - } - output.append('\n'); //NOI18N - - } - ExClipboard clipboard = Lookup.getDefault().lookup(ExClipboard.class); - StringSelection strSel = new StringSelection(output.toString()); - clipboard.setContents(strSel, strSel); - } - } catch (ArrayIndexOutOfBoundsException exc) { - Exceptions.printStackTrace(exc); - } - } -} Index: db.dataview/src/org/netbeans/modules/db/dataview/output/SQLStatementGenerator.java --- db.dataview/src/org/netbeans/modules/db/dataview/output/SQLStatementGenerator.java Base (BASE) +++ db.dataview/src/org/netbeans/modules/db/dataview/output/SQLStatementGenerator.java Locally Modified (Based On LOCAL) @@ -43,10 +43,15 @@ */ package org.netbeans.modules.db.dataview.output; +import java.sql.Blob; +import java.sql.Clob; import java.sql.Connection; +import java.sql.SQLException; import java.sql.Types; import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.table.TableModel; import org.netbeans.modules.db.dataview.meta.DBColumn; import org.netbeans.modules.db.dataview.meta.DBConnectionFactory; @@ -54,6 +59,7 @@ import org.netbeans.modules.db.dataview.meta.DBMetaDataFactory; import org.netbeans.modules.db.dataview.meta.DBPrimaryKey; import org.netbeans.modules.db.dataview.meta.DBTable; +import org.netbeans.modules.db.dataview.util.BinaryToStringConverter; import org.netbeans.modules.db.dataview.util.DataViewUtils; import org.openide.util.NbBundle; @@ -63,7 +69,7 @@ * @author Ahimanikya Satapathy */ class SQLStatementGenerator { - + private static final Logger LOG= Logger.getLogger(SQLStatementGenerator.class.getName()); private DataViewDBTable tblMeta; private DataView dataView; @@ -458,10 +464,26 @@ return "b'" + val + "'"; // NOI18N } else if (DataViewUtils.isNumeric(type)) { return val; - } else { - return "'" + val + "'"; // NOI18N + } else if (val instanceof Clob) { + try { + Clob lob = (Clob) val; + String result = lob.getSubString(1, (int) lob.length()); + return "'" + result.replace("'", "''") + "'"; // NOI18N + } catch (SQLException ex) { + LOG.log(Level.INFO, "Failed to read CLOB", ex); } + } else if (val instanceof Blob) { + try { + Blob lob = (Blob) val; + byte[] result = lob.getBytes(1, (int) lob.length()); + return "x'" + BinaryToStringConverter.convertToString(result, 16, false) + "'"; // NOI18N + } catch (SQLException ex) { + LOG.log(Level.INFO, "Failed to read BLOB", ex); } + } + // Fallback if previous converts fail + return "'" + val.toString().replace("'", "''") + "'"; // NOI18N + } private String getAutoIncrementText(int dbType) throws Exception { switch (dbType) { Index: db.dataview/src/org/netbeans/modules/db/dataview/table/ResultSetJXTable.java --- db.dataview/src/org/netbeans/modules/db/dataview/table/ResultSetJXTable.java Base (BASE) +++ db.dataview/src/org/netbeans/modules/db/dataview/table/ResultSetJXTable.java Locally Modified (Based On LOCAL) @@ -42,22 +42,29 @@ package org.netbeans.modules.db.dataview.table; import java.awt.Color; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.sql.Blob; import java.sql.Clob; +import java.sql.SQLException; import java.sql.Timestamp; +import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.RowSorter; import javax.swing.SwingUtilities; +import javax.swing.TransferHandler; +import javax.swing.plaf.UIResource; import javax.swing.table.*; import org.jdesktop.swingx.JXTable; import org.jdesktop.swingx.JXTableHeader; @@ -71,17 +78,27 @@ import org.netbeans.modules.db.dataview.meta.DBColumn; import org.netbeans.modules.db.dataview.output.DataView; import org.netbeans.modules.db.dataview.table.celleditor.*; +import org.netbeans.modules.db.dataview.util.BinaryToStringConverter; import org.netbeans.modules.db.dataview.util.DataViewUtils; import org.netbeans.modules.db.dataview.util.DateType; +import org.netbeans.modules.db.dataview.util.TimeType; import org.netbeans.modules.db.dataview.util.TimestampType; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.datatransfer.ExClipboard; /** - * A better-looking table than JTable, implements JXTable and a decorator to draw empty rows + * A better-looking table than JTable, implements JXTable and a decorator to + * draw empty rows * * @author Ahimanikya Satapathy */ public class ResultSetJXTable extends JXTableDecorator { + private DateFormat timeFormat = new SimpleDateFormat(TimeType.DEFAULT_FOMAT_PATTERN); + private DateFormat dateFormat = new SimpleDateFormat(DateType.DEFAULT_FOMAT_PATTERN); + private DateFormat timestampFormat = new SimpleDateFormat(TimestampType.DEFAULT_FORMAT_PATTERN); + private String[] columnToolTips; private final int multiplier; private static final String data = "WE WILL EITHER FIND A WAY, OR MAKE ONE."; // NOI18N @@ -92,6 +109,8 @@ @SuppressWarnings("OverridableMethodCallInConstructor") public ResultSetJXTable(final DataView dataView) { + this.setTransferHandler(new TableTransferHandler()); + this.dView = dataView; setShowGrid(true, true); @@ -291,8 +310,122 @@ return false; } - // This is mainly used for set Tooltip for column headers + /** + * Quote string for use in TSV (tab-separated values file + * + * Assumptions: column separator is \t and row separator is \n + */ + protected String quoteIfNecessary(String value) { + if (value.contains("\t") || value.contains("\n")) { //NOI18N + return "\"" + value.replace("\"", "\"\"") + "\""; + } else { + return value; + } + } + /** + * Convert object to string representation + * + * @param o object to convert + * @param limitSize in case of CLOBs and BLOBs limit to limitSize + * bytes/chars + * @return string representation of o + */ + protected String convertToClipboardString(Object o, int limitSize) { + if (o instanceof Blob) { + Blob b = (Blob) o; + try { + if (b.length() <= limitSize) { + return BinaryToStringConverter.convertToString(b.getBytes(1, (int) b.length()), 16, false); + } + } catch (SQLException ex) { + } + } else if (o instanceof Clob) { + Clob c = (Clob) o; + try { + if (c.length() <= limitSize) { + return c.getSubString(1, (int) c.length()); + } + } catch (SQLException ex) { + } + } else if (o instanceof java.sql.Time) { + return timeFormat.format((java.util.Date) o); + } else if (o instanceof java.sql.Date) { + return dateFormat.format((java.util.Date) o); + } else if (o instanceof java.util.Date) { + return timestampFormat.format((java.util.Date) o); + } else if (o == null) { + return ""; //NOI18N + } + return o.toString(); + } + + /** + * Create TSV (tab-separated values) string from row data + * + * @param withHeader include column headers? + * @return Transferable for clipboard transfer + */ + private StringSelection createTransferableTSV(boolean withHeader) { + try { + int[] rows = getSelectedRows(); + int[] columns; + if (getRowSelectionAllowed()) { + columns = new int[getColumnCount()]; + for (int a = 0; a < columns.length; a++) { + columns[a] = a; + } + } else { + columns = getSelectedColumns(); + } + if (rows != null && columns != null) { + StringBuilder output = new StringBuilder(); + + if (withHeader) { + for (int column = 0; column < columns.length; column++) { + if (column > 0) { + output.append('\t'); //NOI18N + + } + Object o = getColumnModel().getColumn(column).getIdentifier(); + String s = o != null ? o.toString() : ""; + output.append(quoteIfNecessary(s)); + } + output.append('\n'); //NOI18N + + } + + for (int row = 0; row < rows.length; row++) { + for (int column = 0; column < columns.length; column++) { + if (column > 0) { + output.append('\t'); //NOI18N + + } + Object o = getValueAt(rows[row], columns[column]); + // TODO: Evaluate if 1 MB/1 Million Characters are a good limit + String s = convertToClipboardString(o, 1024 * 1024); + output.append(quoteIfNecessary(s)); + + } + output.append('\n'); //NOI18N + + } + return new StringSelection(output.toString()); + } + return null; + } catch (ArrayIndexOutOfBoundsException exc) { + Exceptions.printStackTrace(exc); + return null; + } + } + + protected void copyRowValues(boolean withHeader) { + ExClipboard clipboard = Lookup.getDefault().lookup(ExClipboard.class); + StringSelection selection = createTransferableTSV(withHeader); + clipboard.setContents(selection, selection); + } + + // This is mainly used for set Tooltip for column headers private class JTableHeaderImpl extends JXTableHeader { public JTableHeaderImpl(TableColumnModel cm) { @@ -316,6 +449,22 @@ } } } + + private class TableTransferHandler extends TransferHandler implements UIResource { + /** + * Map Transferable to createTransferableTSV from ResultSetJXTable + * + * This is needed so that CTRL-C Action of JTable gets the same treatment + * as the transfer via the copy Methods of DataTableUI + */ + @Override + protected Transferable createTransferable(JComponent c) { + return createTransferableTSV(false); } - + @Override + public int getSourceActions(JComponent c) { + return COPY; + } + } +} Index: db.dataview/src/org/netbeans/modules/db/dataview/util/BinaryToStringConverter.java --- db.dataview/src/org/netbeans/modules/db/dataview/util/BinaryToStringConverter.java Base (BASE) +++ db.dataview/src/org/netbeans/modules/db/dataview/util/BinaryToStringConverter.java Locally Modified (Based On LOCAL) @@ -74,7 +74,7 @@ /** * Convert from an array of Bytes into a string. */ - public static String convertToString(Byte[] data, int base, boolean showAscii) { + public static String convertToString(byte[] data, int base, boolean showAscii) { if (data == null) { return null; @@ -85,7 +85,7 @@ // Convert each byte and put into string buffer for (int i = 0; i < data.length; i++) { - int value = data[i].byteValue(); + int value = data[i]; String s = null; // if user wants to see ASCII chars as characters, Index: db.dataview/src/org/netbeans/modules/db/dataview/util/DBReadWriteHelper.java --- db.dataview/src/org/netbeans/modules/db/dataview/util/DBReadWriteHelper.java Base (BASE) +++ db.dataview/src/org/netbeans/modules/db/dataview/util/DBReadWriteHelper.java Locally Modified (Based On LOCAL) @@ -192,7 +192,7 @@ if (rs.wasNull() || bdata == null) { return null; } else { - Byte[] internal = new Byte[bdata.length]; + byte[] internal = new byte[bdata.length]; for (int i = 0; i < bdata.length; i++) { internal[i] = new Byte(bdata[i]); } Index: db.dataview/test/unit/src/org/netbeans/modules/db/dataview/util/BinaryToStringConverterTest.java --- db.dataview/test/unit/src/org/netbeans/modules/db/dataview/util/BinaryToStringConverterTest.java Base (BASE) +++ db.dataview/test/unit/src/org/netbeans/modules/db/dataview/util/BinaryToStringConverterTest.java Locally Modified (Based On LOCAL) @@ -39,10 +39,11 @@ * * Portions Copyrighted 2008 Sun Microsystems, Inc. */ - package org.netbeans.modules.db.dataview.util; +import java.io.UnsupportedEncodingException; import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.db.dataview.meta.DBException; /** * @@ -54,8 +55,8 @@ super(testName); } - public static org.netbeans.junit.NbTest suite() { - org.netbeans.junit.NbTestSuite suite = new org.netbeans.junit.NbTestSuite(BinaryToStringConverterTest.class); + public static org.netbeans.junit.NbTest suite() { + org.netbeans.junit.NbTestSuite suite = new org.netbeans.junit.NbTestSuite(BinaryToStringConverterTest.class); return suite; } @@ -73,7 +74,7 @@ * Test of convertToString method, of class BinaryToStringConverter. */ public void testConvertToString() { - Byte[] data = new Byte[1]; + byte[] data = new byte[1]; int base = 10; boolean showAscii = false; String expResult = "022"; @@ -82,4 +83,20 @@ assertEquals(expResult, result); } + /** + * Test SQL hexadecimal encoding - adapted from http://dev.mysql.com/doc/refman/5.0/en/hexadecimal-literals.html + */ + public void testConvertToString2() throws UnsupportedEncodingException, DBException { + byte[] data = "\u0000MySQL".getBytes("ASCII"); + int base = 16; + boolean showAscii = false; + String expResult = "004d7953514c"; + String result = BinaryToStringConverter.convertToString(data, base, showAscii); + assertEquals(expResult, result); + + data = "Paul".getBytes("ASCII"); + expResult = "5061756c"; + result = BinaryToStringConverter.convertToString(data, base, showAscii); + assertEquals(expResult, result); } +}