2

My tableModel contains multiple columns (for ex: Columns A - F), of which when I first display, I should display the table sorted by firstly Column B and later by D. Now, the issue that am facing is: Col. D is timestamp. Col. B contains identifiers such as :

  1. (Col. B) "Clt A - 001" (Col. D) 11:34 AM
  2. (Col. B) "Clt B - 001" (Col. D) 12:42 AM
  3. (Col. B) "Clt A - 001" (Col. D) 1:18 PM
  4. (Col. B) "Clt A - 002" (Col. D) 1:18PM
  5. (Col. B) "Clt C - 001" (Col. D) 10:30 AM
  6. (Col. B) "Clt C - 001" (Col. D) 2:45 PM

The kind of output I need is:

  1. (Col. B) "Clt A - 001" (Col. D) 11:34 AM
  2. (Col. B) "Clt A - 001" (Col. D) 1:18 PM
  3. (Col. B) "Clt A - 002" (Col. D) 1:18PM
  4. (Col. B) "Clt B - 001" (Col. D) 12:42 AM
  5. (Col. B) "Clt C - 001" (Col. D) 10:30 AM
  6. (Col. B) "Clt C - 001" (Col. D) 2:45 PM

What I don't understand is, when I write a custom compare() method, I am only getting the object of a particular column. But if you observe in my sample data above, the sort depends on the information in teh secondary column. (Am not able to overcome this issue by using sortPrecedence because the value of Col. B is not actually same when the timestamp in col. D is the same. If I sorted initially by Col. D, and later by Col. B then the sort order is primarily driven by timestamp rather than by alphabetical order of Col. B. Is TableRowSorter my option? If so, how should I implement the comparator? Any help greatly appreciated.

Thanks.

user2406106
  • 23
  • 1
  • 3

1 Answers1

3

Take a look at How to use tables, Sorting.

There is an example which demonstrates how to setup multi-column sorting...

I quote...

To specify the sort order and sort precedence for columns, invoke setSortKeys. Here is an example that sorts the table used in the examples by the first two columns. The precedence of the columns in the sort is indicated by the order of the sort keys in the sort key list. In this case, the second column has the first sort key, so they rows are sorted by first name, then last name.

List <RowSorter.SortKey> sortKeys 
    = new ArrayList<RowSorter.SortKey>();
sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING));
sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING));
sorter.setSortKeys(sortKeys);

Working Example

enter image description here

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableRowSorter;

public class TestMuiltColumnTableSort {

    public static void main(String[] args) {
        new TestMuiltColumnTableSort();
    }
    public static final SimpleDateFormat SDF = new SimpleDateFormat("hh:mm aa");

    public TestMuiltColumnTableSort() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                try {
                    MyTableModel model = new MyTableModel();
                    model.addRow("Job #1.1", "Clt A - 001", "11:34 AM");
                    model.addRow("Job #2.1", "Clt B - 001", "12:42 AM");
                    model.addRow("Job #1.1", "Clt A - 001", "01:18 PM");
                    model.addRow("Job #1.2", "Clt A - 002", "01:18 PM");
                    model.addRow("Job #3.1", "Clt C - 001", "10:30 AM");
                    model.addRow("Job #3.1", "Clt C - 001", "02:45 PM");
                    model.addRow("Job #1.2", "Clt A - 002", "12:00 PM");
                    JTable table = new JTable(model);
                    table.setAutoCreateRowSorter(false);
                    table.setDefaultRenderer(Date.class, new TimeCellRenderer());

                    TableRowSorter<MyTableModel> sorter = new TableRowSorter<MyTableModel>(model);
                    table.setRowSorter(sorter);

                    List<RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
                    sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING));
                    sortKeys.add(new RowSorter.SortKey(2, SortOrder.ASCENDING));
                    sorter.setSortKeys(sortKeys);

                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new JScrollPane(table));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (ParseException exp) {
                    exp.printStackTrace();
                    System.exit(0);
                }
            }
        });
    }

    public class TimeCellRenderer extends DefaultTableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); //To change body of generated methods, choose Tools | Templates.
            if (value instanceof Date) {
                setText(SDF.format(value));
            }
            return this;
        }
    }

    public class MyTableModel extends AbstractTableModel {

        private List<Row> rows = new ArrayList<>(25);

        public void addRow(String name, String cat, String date) throws ParseException {

            rows.add(new Row(name, cat, SDF.parse(date)));

        }

        @Override
        public int getRowCount() {
            return rows.size();
        }

        @Override
        public int getColumnCount() {
            return 3;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            Class clazz = String.class;
            switch (columnIndex) {
                case 2:
                    clazz = Date.class;
                    break;
            }
            return clazz;
        }

        @Override
        public String getColumnName(int column) {
            String name = "??";
            switch (column) {
                case 0:
                    name = "Name";
                    break;
                case 1:
                    name = "Catagory";
                    break;
                case 2:
                    name = "Date";
                    break;
            }
            return name;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Row row = rows.get(rowIndex);
            Object value = null;
            switch (columnIndex) {
                case 0:
                    value = row.getName();
                    break;
                case 1:
                    value = row.getCat();
                    break;
                case 2:
                    value = row.getDate();
                    break;
            }
            return value;
        }
    }

    public class Row {

        private String name;
        private String cat;
        private Date date;

        public Row(String name, String cat, Date date) {
            this.name = name;
            this.cat = cat;
            this.date = date;
        }

        public String getName() {
            return name;
        }

        public String getCat() {
            return cat;
        }

        public Date getDate() {
            return date;
        }
    }
}

Additional

Simply be providing a Comparator to the required column of the TableRowSorter you should be able to achieve your basic requirements...

enter image description here

sorter.setComparator(1, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.substring(0, Math.min(o1.length(), 5)).compareTo(o2.substring(0, Math.min(o2.length(), 5)));
    }
});

You should be able to supply a camparator for each column, thus modifying how the sub/group sorting works...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • @camickr I can't remember who pointed it out to me, but I'm glad they did :D – MadProgrammer Jun 03 '13 at 02:38
  • @MadProgrammer: I had seen this example you mentioned. But in the example I mentioned, if there was another row of data as: (Col. B) "Clt A - 002" (Col. D) 12:00 PM - and let's say I set the sortKeys and the SortOrder, then will the new row that I just mentioned appear as the second row in the output table? Am asking coz I can't try it right away and need to wait till tomorrow to use the system am working on for this. Hence, will mark teh answer as "worked best" once I give it a shot. Thanks. – user2406106 Jun 03 '13 at 02:47
  • @MadProgrammer: wow, firstly accolades to your sincere effort. But, just as I had expected, if you notice the third record (Clt A - 002 @ 12:00pM is (per my requirement) expected as second record. Because, although sorting by Col. B, it sonly partially sorted by that, Coz I need to sort by the the Clt (A, B, C, etc)., but irresp of whether it is 001 job or 002 of that clt, it shud be sorted by time. In the even that its the same client (different extension such as 001/002) but same time stamp, it shud again appear in sorted order (Clt A -001 appears before Clt A - 002 of the same time stamp) – user2406106 Jun 03 '13 at 05:17
  • is it possible for me to write different comparators and do the following: 1. sort by col.b (call the comparator that filters if its the same client or not) 2. if same client, sort by timestamp column. 3. if same timestamp, sort by col. b again (but call a diff comparator impl that looks at the second part of clt name string) – user2406106 Jun 03 '13 at 05:19
  • Have you looked at the [JavaDocs](http://docs.oracle.com/javase/7/docs/api/javax/swing/DefaultRowSorter.html)? You can specify the comparator for a given column – MadProgrammer Jun 03 '13 at 05:34
  • Will try first thing tomorrow. the idea of diff comparator impl on same col but at differents tages of sort occured only as I was writing earlier. But thanks for trying through this with me. Shall keep informed of solution, when i land on one :) – user2406106 Jun 03 '13 at 06:07
  • I think it should be possible with the default dieter, but you may need to manually sort the model before hand – MadProgrammer Jun 03 '13 at 06:10
  • @MadProgrammer (please do not take as attack) I'm love your interests, skills ..., but this [works for me without](http://stackoverflow.com/a/16664124/714968) custom comparator, implemented collator is faster than... (please could be really the mess without optimalizations there are 2D array, not about compare, sorting elements in (J)List), sorry put there 50 * 5k to JTable and then to listen by System.out.println – mKorbel Jun 03 '13 at 10:18
  • @MadProgrammer - Yes, adding the comparator helped indeed ! Thank you very much for staying with me through this :) – user2406106 Jun 03 '13 at 13:58