DSlomer64
DSlomer64

Reputation: 4283

JTable with Comparator accessed wrong row data after sort; removing getModel() fixed it. Why?

It was remarkably easy to introduce sorting for my JTable:

  //Existing code
  dftTableModel = new DefaultTableModel(0 , 4);
  tblOutput     = new JTable(dftTableModel);

  //Added code
  RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(dftTableModel);
  tblOutput.setRowSorter(sorter);

But since I formatted the Size column as text with commas, it didn't sort:

enter image description here

I had never used a Comparator but found an example that I modified.

public class RowSorterWithComparator 
{
  static Comparator               compareNumericStringsWithCommas;
  static TableRowSorter           sorter;
  static JTable                   tblOutput;
  static JScrollPane              pane;
  static JFrame                   frame;
  static DefaultTableModel        dftTableModel; 
//  static TableColumnAdjuster      tca ;
  static DefaultTableCellRenderer rightRenderer;
  static JButton                  btnOpen;

  static String[] columnNames = { "Date", "Size" };
  static Object rows[][] = 
{
{"7/27/2015","96","mavenVersion.xml","C:\\Users\\Dov\\.AndroidStudio1.2\\config\\options\\"},
{"7/27/2015","120","keymap.xml","C:\\Users\\Dov\\.AndroidStudio1.2\\config\\options\\"},
{"7/27/2015","108","Default.xml","C:\\Users\\Dov\\.AndroidStudio1.2\\config\\inspection\\"},
{"4/27/2015","392","key pay.txt","C:\\Users\\Dov\\A\\"},
{"6/13/2015","161","BuildConfig.java","C:\\Users\\Dov\\androidfp2_examples\\eclipse_projects\\FlagQuiz\\gen\\com\\deitel\\flagquiz\\"}
};

  public static void main(String args[]) 
  {
    compareNumericStringsWithCommas = (Comparator) new Comparator() 
    {
      @Override public int compare(Object oo1, Object oo2) 
      {
        String o1 = oo1.toString().replace(",", "");
        String o2 = oo2.toString().replace(",", "");
        return Integer.valueOf(o1).compareTo(Integer.valueOf(o2));
      }
    };

    dftTableModel = new DefaultTableModel(0 , columnNames.length);
    tblOutput     = new JTable(dftTableModel);
    dftTableModel.setColumnIdentifiers(new Object[]{"Date", "Size", "File name", "Path to file"});

    rightRenderer = new DefaultTableCellRenderer();
    rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
    tblOutput.getColumnModel().getColumn(1).setCellRenderer(rightRenderer);

    sorter      = new TableRowSorter<>(dftTableModel);
    sorter.setModel(tblOutput.getModel());
    sorter.setComparator(1,compareNumericStringsWithCommas);

    tblOutput.setRowSorter(sorter);
    tblOutput.setAutoResizeMode(AUTO_RESIZE_OFF);

//    tca = new tablecolumnadjuster.TableColumnAdjuster(tblOutput);
//    tca.setDynamicAdjustment(true);

    tblOutput.setFont(new Font("Courier New",Font.PLAIN,12));

    pane = new JScrollPane(tblOutput);

    for (int i = 0; i < 5; i++)
      dftTableModel.addRow(rows[i]);

    btnOpen = new JButton("Open selected file");

    btnOpen.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e)
        {       
          int row = tblOutput.getSelectedRow();
          String entry = (String)tblOutput.getModel().getValueAt(row, 3) 
                + "\\" + (String)tblOutput.getModel().getValueAt(row, 2);
          try 
          {
            Desktop.getDesktop().open(new File((entry.trim())));
          } catch (IOException ex) {System.out.println("Can't open file"); }
      }
    });

    frame = new JFrame("Sort Table Demo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(pane, BorderLayout.CENTER);
    frame.add(btnOpen, BorderLayout.AFTER_LAST_LINE);
    frame.setSize(800, 350);
    frame.setVisible(true);
  }
}

Works great.

enter image description here

It was easy to right-justify the size column:

rightRenderer = new DefaultTableCellRenderer();
rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT);

I added a button to open the selected file. But after sorting, the wrong file opened.

So I removed getModel from the statement in the mouse listener:

          String entry = (String)tblOutput.getValueAt(row, 3) 
                + "\\" + (String)tblOutput.getValueAt(row, 2);

I don't know why it was there in the first place since I've been stealing all kinds of code from various places.

Anyway, everything works now.

But I have questions:

(1) When would getModel be required in the context of getting a table row's values? I thought since dftTableModel was used for tblOutput that surely it was proper.

(2) Is the row returned by getModel the row that the data originally was at? If so, I guess that could be useful on occasion. Not sure when.... To "un-sort"?

(3) Is the fact that I used TableRowSorter the reason getModel didn't get the right data? (I.e., are the two incompatible?)

(4) Is dftTableModel equivalent to tblOutput.getModel()?

...............................

imports for program:

import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.Comparator;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import static javax.swing.JTable.AUTO_RESIZE_OFF;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
//import tablecolumnadjuster.TableColumnAdjuster;

Upvotes: 1

Views: 1790

Answers (2)

DSlomer64
DSlomer64

Reputation: 4283

This IS NOW A SSCCE (see comments) but it is a revision of the original code based on suggestions in comments above and below by @camickr.

THIS PROBLEM IS FIXED At present, after dragging column, if filename or path column position has changed, clicking button results in error such as The file: xml\C:\Users\Dov\.AndroidStudio1.2\config\options doesn't exist. This occurred after moving Extension column (which was before Filename column) to after Path column.

import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.Comparator;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import static javax.swing.JTable.AUTO_RESIZE_OFF;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
//import tablecolumnadjuster.TableColumnAdjuster;

public class RowSorterWithComparator 
{
  static final int    DATE_COLUMN     = 0;
  static final int    SIZE_COLUMN     = 1;
  static final int    EXTENSION_COLUMN= 2;
  static final int    FILENAME_COLUMN = 3;
  static final int    PATH_COLUMN     = 4;

  public static void main(String args[]) 
  {
    Comparator               compareNumericStringsWithCommas;
    TableRowSorter           sorter;
    JTable                   tblOutput;
    JScrollPane              pane;
    JFrame                   frame;
    DefaultTableModel        dftTableModel; 
//    TableColumnAdjuster      tca ;
    DefaultTableCellRenderer rightRenderer;
    JButton                  btnOpen;

    Object[] columnNames = new Object[]{"Date", "Size", "Extension", "File name", "Path to file"};
    Object rows[][] = 
      {
      {"7/27/2015","9,600","xml","mavenVersion.xml","C:/Users/Dov/.AndroidStudio1.2/config/options/"},
      {"7/27/2015","120,000","xml","keymap.xml","C:/Users/Dov/.AndroidStudio1.2/config/options/"},
      {"7/27/2015","108","xml","Default.xml","C:/Users/Dov/.AndroidStudio1.2/config/inspection/"},
      {"4/27/2015","39,200","txt","key pay.txt","C:/Users/Dov/A/"},
      {"6/13/2015","91","java","BuildConfig.java","C:/Users/Dov/androidfp2_examples/eclipse_projects/FlagQuiz/gen/com/deitel/flagquiz/"}
      };

    compareNumericStringsWithCommas = (Comparator) new Comparator() 
    {
      @Override public int compare(Object oo1, Object oo2) 
      {
        String o1 = oo1.toString().replace(",", "");
        String o2 = oo2.toString().replace(",", "");
        return Integer.valueOf(o1).compareTo(Integer.valueOf(o2));
      }
    };

    dftTableModel = new DefaultTableModel(0 , columnNames.length);
    tblOutput     = new JTable(dftTableModel);
    dftTableModel.setColumnIdentifiers(columnNames);

    rightRenderer = new DefaultTableCellRenderer();
    rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
    tblOutput.getColumnModel().getColumn(1).setCellRenderer(rightRenderer);

    sorter      = new TableRowSorter<>(dftTableModel);
    sorter.setModel(tblOutput.getModel());
    sorter.setComparator(1,compareNumericStringsWithCommas);

    tblOutput.setRowSorter(sorter);
    tblOutput.setAutoResizeMode(AUTO_RESIZE_OFF);

    //    tca = new tablecolumnadjuster.TableColumnAdjuster(tblOutput);
    //    tca.setDynamicAdjustment(true);

    tblOutput.setFont(new Font("Courier New",Font.PLAIN,12));

    pane = new JScrollPane(tblOutput);

    for (Object[] row : rows)
      dftTableModel.addRow(row);

    btnOpen = new JButton("Open selected file");

    btnOpen.addMouseListener(new MouseAdapter() 
    {
        @Override
        public void mouseClicked(MouseEvent e)
        {       
          int viewFilenameCol = tblOutput.convertColumnIndexToView(FILENAME_COLUMN);
          int viewPathCol     = tblOutput.convertColumnIndexToView(PATH_COLUMN);
          int row = tblOutput.getSelectedRow();
          String entry = (String)tblOutput.getValueAt(row, viewPathCol) 
                      +  (String)tblOutput.getValueAt(row, viewFilenameCol);
          try 
          {
            Desktop.getDesktop().open(new File((entry.trim())));
          } 
          catch 
          (
            Exception ex) {System.out.println("Can't open file <" + entry + ">"); 
          }
      }
    });

    frame = new JFrame("Sort Table Demo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(pane, BorderLayout.CENTER);
    frame.add(btnOpen, BorderLayout.SOUTH);
    frame.setSize(800, 350);
    frame.setVisible(true);
  }
}

Upvotes: 0

camickr
camickr

Reputation: 324167

So I removed getModel from the statement in the mouse listener:

The data in the TableModel is NOT sorted. The View (JTable) displays the data in a sorted order

You get the data from the view using:

table.getValueAt(row, column);

if you want to get the data from the TableModel you use:

int modelRow = table.convertRpwIndexToModel(row);
table.getModel().getValueAt(modelRow, column);

There are also method for converting the column indexes back and forth.

So you always need to know whether you are trying to access the data the way it is displayed on the JTable or the way it was loaded into the TableModel and then use the appropriate index which may (or may) not involve an index conversion.

String entry = (String)tblOutput.getValueAt(row, 3) ...

When you hardcode a value you need to know what the hard coded value represents. In this case you are accessing a dynamic row value from the table and a column from the model.

So in the above statement you need to convert the "3" from the model to the view since you are using the getValueAt(...) method of the table.

int viewColumn3 = table.convertColumnIndexToView(3);
String entry = (String)tblOutput.getValueAt(row, viewColumn3) 

Or, since you are referencing hardcoded values multiple times (ie. columns 2 and 3) it may be easier to keep the hardcoded column indexes and only convert the row index once and then access the data from the TableModel:

String entry = (String)tblOutput.getModel().getValueAt(modelRow, 3) ... 

Upvotes: 5

Related Questions