December 11, 2010

Cut and Paste from Java Swing JTable to Clipboard (and Excel)

Tags: Java, Technical

For a project, I needed to cut, copy and paste from a Java JTable to Excel. The KeyAdapter I ended up writing is below. As it turns out, there is another similar version online from 1999 at Javaworld (I borrowed the contiguous block check from this code, as I hadn’t previously considered that possibility) - but I like mine better.

To copy to Excel, a Java program must put the data onto the clipboard in a format Excel understands. The easiest way to do this is to use a String, structured as a series of cells separated by tabs and rows separated by line breaks. To read in data Excel puts on the clipboard, is just a matter of parsing a similarly structured String. There are other data formats available on the clipboard, but just using plain text is all that was needed for my requirements. However if you need to escape tabs and line breaks in your data then it might be worth investigating them. The code below just converts tabs and line breaks to spaces when copying to the clipboard, and ignore them when reading in (as tabs can’t be in an Excel cell and I couldn’t see how to detect line breaks in a cell rather than separating lines). To use this class just add it as a KeyListener on the table (JTable.addKeyLisenter).

package com.cordinc.util.gui;

import java.awt.Toolkit; 
import java.awt.datatransfer.Clipboard; 
import java.awt.datatransfer.DataFlavor; 
import java.awt.datatransfer.StringSelection; 
import java.awt.event.KeyAdapter; 
import java.awt.event.KeyEvent;

import javax.swing.JOptionPane; 
import javax.swing.JTable;

 * KeyAdapter to detect Windows standard cut, copy and paste keystrokes on a JTable and put them to the clipboard 
 * in Excel friendly plain text format. Assumes that null represents an empty column for cut operations. 
 * Replaces line breaks and tabs in copied cells to spaces in the clipboard. 
 * @see java.awt.event.KeyAdapter 
 * @see javax.swing.JTable 
public class ClipboardKeyAdapter extends KeyAdapter {

        private static final String LINE_BREAK = "\n"; 
        private static final String CELL_BREAK = "\t"; 
        private static final Clipboard CLIPBOARD = Toolkit.getDefaultToolkit().getSystemClipboard(); 
        private final JTable table; 
        public ClipboardKeyAdapter(JTable table) { 
                this.table = table; 
        public void keyReleased(KeyEvent event) { 
                if (event.isControlDown()) { 
                        if (event.getKeyCode()==KeyEvent.VK_C) { // Copy                        
                        } else if (event.getKeyCode()==KeyEvent.VK_X) { // Cut 
                        } else if (event.getKeyCode()==KeyEvent.VK_V) { // Paste 
        private void copyToClipboard(boolean isCut) { 
                int numCols=table.getSelectedColumnCount(); 
                int numRows=table.getSelectedRowCount(); 
                int[] rowsSelected=table.getSelectedRows(); 
                int[] colsSelected=table.getSelectedColumns(); 
                if (numRows!=rowsSelected[rowsSelected.length-1]-rowsSelected[0]+1 || numRows!=rowsSelected.length || 
                                numCols!=colsSelected[colsSelected.length-1]-colsSelected[0]+1 || numCols!=colsSelected.length) {

                        JOptionPane.showMessageDialog(null, "Invalid Copy Selection", "Invalid Copy Selection", JOptionPane.ERROR_MESSAGE);
                StringBuffer excelStr=new StringBuffer(); 
                for (int i=0; i<numRows; i++) { 
                        for (int j=0; j<numCols; j++) { 
                                excelStr.append(escape(table.getValueAt(rowsSelected[i], colsSelected[j]))); 
                                if (isCut) { 
                                        table.setValueAt(null, rowsSelected[i], colsSelected[j]); 
                                if (j<numCols-1) { 
                StringSelection sel  = new StringSelection(excelStr.toString()); 
                CLIPBOARD.setContents(sel, sel); 
        private void pasteFromClipboard() { 
                int startRow=table.getSelectedRows()[0]; 
                int startCol=table.getSelectedColumns()[0];

                String pasteString = ""; 
                try { 
                        pasteString = (String)(CLIPBOARD.getContents(this).getTransferData(DataFlavor.stringFlavor)); 
                } catch (Exception e) { 
                        JOptionPane.showMessageDialog(null, "Invalid Paste Type", "Invalid Paste Type", JOptionPane.ERROR_MESSAGE);
                String[] lines = pasteString.split(LINE_BREAK); 
                for (int i=0 ; i<lines.length; i++) { 
                        String[] cells = lines[i].split(CELL_BREAK); 
                        for (int j=0 ; j<cells.length; j++) { 
                                if (table.getRowCount()>startRow+i && table.getColumnCount()>startCol+j) { 
                                        table.setValueAt(cells[j], startRow+i, startCol+j); 
        private void cancelEditing() { 
                if (table.getCellEditor() != null) { 
        private String escape(Object cell) { 
                return cell.toString().replace(LINE_BREAK, " ").replace(CELL_BREAK, " ");