0

How do I create a sorter such that a specific row is always sorted to be the first row no matter if the column is sorted in either ascending or descending order? My code is below. Clicking on "name" 2 times "FIRSTROW" is sorted to be first row each time. Clicking on "age" until FIRSTROW is no longer sorted first, then clicking back on "name", FIRSTROW is no longer sorted fist. I want to have FIRSTROW be sorted first everytime I click on "name" how do I do that?

sample code is below:

import java.awt.BorderLayout;
import java.util.Comparator;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;



public class TableTest implements Comparator<String> {
        @Override
        public int compare(String obj1, String obj2) {
            if (obj1 == obj2) {
                return 0;
            }
            if (obj1 == null) {
                return -1;
            }
            if (obj2 == null) {
                return -1;
            }
            if (obj2 instanceof String && obj2.trim( ).equals("FIRSTROW")) {
                return 0;
            }
            if (obj1 instanceof String && obj1.trim( ).equals("FIRSTROW")) {
                return 0;
            }
            return obj1.compareTo(obj2);
        }


  public static void main(String args[]) {
        JFrame frame = new JFrame("Fixed First Row");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        String rows[][] = { { "FIRSTROW", "23" }, { "R", "24", }, { "E", "21", }, { "D", "27", }, { "A", "25", },
            { "S", "22", }, };
        /* Specify column names */
        String columns[] = { "Name", "Age" };
        /* Create a TableModel */
        DefaultTableModel model = new DefaultTableModel(rows, columns) {
          @Override
        public Class getColumnClass(int column) {
            Class returnValue;
            if ((column >= 0) && (column < getColumnCount())) {
              returnValue = getValueAt(0, column).getClass();
            } else {
              returnValue = Object.class;
            }
            return returnValue;
          }
        };

        JTable table = new JTable(model);

        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model);

        table.setRowSorter(sorter);

        sorter.setComparator( 0, new TableTest() ); // Field column uses the random string comparator.

        JScrollPane pane = new JScrollPane(table);

        frame.add(pane, BorderLayout.CENTER);

        frame.setSize(300, 150);
        frame.setVisible(true);
      }
}
user1144251
  • 327
  • 1
  • 3
  • 12
  • 1
    You don't compare `String` with `==`. See [How do I compare Strings in Java?](http://stackoverflow.com/q/513832/2180785) – Frakcool Feb 24 '17 at 19:12
  • If you cannot get a working solution using the tip provided by @Frakcool (which is probably the solution), post a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/) of code that fails. – Andrew Thompson Feb 24 '17 at 19:14
  • Do you really want `toLowerCase()` before comparing to "B"? – geneSummons Feb 24 '17 at 19:31
  • yeah it doesn't really matter. B is actually some other string I just changed it to B in the example and it could be a mix for my real code. – user1144251 Feb 24 '17 at 19:34
  • The specific issue is (once you actually get the sorter to work correctly) the fact that it will allow the first row to either appear at the bottom OR the top, because you can't determine in which direction the sort operation is occurring, you don't have enough information to change the sort comparison – MadProgrammer Feb 24 '17 at 21:28
  • So I've been digging around the `TableRowSorter` and `DefaultRowSorter` and basically, it's a mess. The major problem is, much of the information you would need is `private`, making it near impossible to actually change how the default implementation actually works – MadProgrammer Feb 24 '17 at 21:42

3 Answers3

0

If you were just doing a simple text compare, you would do return stringA.compareTo(stringB); in your comparator. To make sure a certain value always sort to the top, you need to explicitly make it so.

Be careful though, forcing comparisons away from "natural ordering" can easily lead to comparison "contract violations".

Edit:

public int compare(String obj1, String obj2) {
  if (obj1 == obj2) {// if they are the same object
    return 0;
  }
  if (obj1 == null) {// sort null above not null
    return -1;
  }
  if (obj2 == null) {// sort not null below null
    return 1;
  }
  if (obj1.trim().toUpperCase().equals("B") &&
    !obj2.trim().toUpperCase().equals("B")) {// sort B above not-B
    return -1;
  }
  if (!obj1.trim().toUpperCase().equals("B") &&
    obj2.trim().toUpperCase().equals("B")) {// sort not-B below B
    return 1;
  }
  return obj1.compareTo(obj2);
}
geneSummons
  • 907
  • 5
  • 15
  • that's basically what I have. But clicking on the sort in the header, B then gets sorted to the bottom. and then clicking on the sort again B then gets sorted to the top. I want it so it doesn't move around. here's my comparator: – user1144251 Feb 24 '17 at 19:26
0

enter image description here

When you want to keep a row at the top after selection, you probably will not do that using only one of its properties because it could be the same for another, the best way is the object itself.

To do this:

  1. Get the selected row
  2. Sort the list using one of the objects properties //as you like
  3. Remove the selected row from the list
  4. Add the selected row at the top of the list.

The following is a tested example to this behaviour:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

import com.sun.prism.impl.Disposer.Record;

import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.JLabel;
import javax.swing.SwingConstants;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;

import javax.swing.JTextField;
import javax.swing.JComboBox;
import javax.swing.DefaultComboBoxModel;

import traitement.CustomRenderer;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class TestSort extends JFrame implements ActionListener{
    private JTable table1;
    private JButton btnSort;
    private int selectedCombCriteria=-1;
    private JComboBox combCriteria;
    private Boolean showSelection=false;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
      TestSort ts=new TestSort();
      ts.setVisible(true);
    }
    TestSort()
    {
        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
        this.setLocation(dim.width/2-this.getSize().width/2, dim.height/2-this.getSize().height/2);
        setSize(new Dimension(439, 325));
        setPreferredSize(new Dimension(600,400));
        JPanel pt=new JPanel();
        getContentPane().add(pt);
        pt.setLayout(null);

        combCriteria = new JComboBox();
        combCriteria.setModel(new DefaultComboBoxModel(new String[] {"Age", "Score"}));
        combCriteria.setSelectedIndex(0);
        combCriteria.setBounds(115, 205, 67, 20);
        combCriteria.addActionListener(this);
        pt.add(combCriteria);
        btnSort = new JButton("Sort");
        btnSort.addActionListener(this);
        btnSort.setBounds(324, 204, 89, 23);
        pt.add(btnSort);
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBounds(20, 41, 393, 127);
        pt.add(scrollPane);
        table1 = new JTable();
        SortModel sm=new SortModel();
        table1.setModel(sm);
        table1.setBounds(50, 26, 329, 130);
        scrollPane.setViewportView(table1);
        table1.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {
                    public void valueChanged(ListSelectionEvent event) {
                        SortModel.selectedRecord=((SortModel)table1.getModel()).getRowAt(table1.getSelectedRow());
                    showSelection=true;
                    }
                });
        table1.setDefaultRenderer(Object.class, new DefaultTableCellRenderer()
        {
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
            {
                final Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                c.setBackground(row==0?Color.YELLOW:isSelected&&showSelection?Color.lightGray:Color.white);
                return c;
            }
        });

        JLabel lblSelectARow = new JLabel("Select a row to keep at the top and sort using one of the sorting criteria");
        lblSelectARow.setFont(new Font("Traditional Arabic", Font.PLAIN, 14));
        lblSelectARow.setHorizontalAlignment(SwingConstants.CENTER);
        lblSelectARow.setBounds(20, 11, 393, 23);
        pt.add(lblSelectARow);

        JLabel lblSortCreteria = new JLabel("Sorting criteria:");
        lblSortCreteria.setBounds(20, 208, 96, 14);
        pt.add(lblSortCreteria);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==btnSort)
            if(SortModel.selectedRecord!=null)
            {
                ((SortModel)table1.getModel()).sort(selectedCombCriteria);
                table1.repaint();
                showSelection=false;

            }
            if(e.getSource()==combCriteria)
            {
                selectedCombCriteria=combCriteria.getSelectedIndex();

            }
    }
}
 class SortModel extends AbstractTableModel 
{
      public  SortModel(){
            data=fillOnce();
        }
    private String[] columns = {"First Name", "Last Name", "Profession", "Age","Score","Comment"};
    public  List<MyDataRecord> data=new ArrayList<MyDataRecord>();
    public static MyDataRecord selectedRecord;

    @Override
    public int getRowCount() {
        // TODO Auto-generated method stub
        return data.size();
    }
    @Override
    public String getColumnName(int column) {
        return columns[column];
    }
    @Override
    public int getColumnCount() {
        return columns.length;
    }
    public Object getValueAt(int rowIndex, int columnIndex) {
        switch (columnIndex) {
        case 0:
            return data.get(rowIndex).getfName();
        case 1:
            return data.get(rowIndex).getlName();
        case 2:
            return data.get(rowIndex).getProfession();
        case 3:
            return data.get(rowIndex).getAge();
        case 4:
            return data.get(rowIndex).getScore();
        case 5:
            return data.get(rowIndex).getComment();
        default:
            throw new IllegalArgumentException();
        }
    }

    public MyDataRecord getRowAt(int rowIndex)
    {
        return data.get(rowIndex);
    }
    //sorting method
    public void sort(int sort)
    {
        //sort 
        Collections.sort(data, new Comparator<MyDataRecord>() {
            public int compare(MyDataRecord object1, MyDataRecord object2) {
                if(sort!=1)
                return Integer.compare(object1.getAge(), object2.getAge());
                else
                return Integer.compare(object1.getScore(), object2.getScore());
            }
        });
        //then remove he selected element
        data.remove(selectedRecord);
        //put the selected element on the top of the list
        if(selectedRecord!=null)
        {
        data.add(0, selectedRecord);
        }
    }

    public List<MyDataRecord> fillOnce()
    {
        List<MyDataRecord> tempList=new ArrayList<MyDataRecord>();
        Random r = new Random();
        for(int i=1;i<=6;i++)
        {
            MyDataRecord mdr=new MyDataRecord("FNtest"+i, "LNtest"+i, "PROFtest"+i, "COMtest"+i, r.nextInt(100-1) + 1, r.nextInt(100-1) + 1);
            tempList.add(mdr);
        }
        return tempList;
    }
}

 class MyDataRecord{
     private String fName,lName,profession,comment;
     private int age,score;

    public MyDataRecord(String fName, String lName, String profession,
            String comment, int age, int score) {
        super();
        this.fName = fName;
        this.lName = lName;
        this.profession = profession;
        this.comment = comment;
        this.age = age;
        this.score = score;
    }

    public String getfName() {
        return fName;
    }

    public void setfName(String fName) {
        this.fName = fName;
    }

    public String getlName() {
        return lName;
    }

    public void setlName(String lName) {
        this.lName = lName;
    }

    public String getProfession() {
        return profession;
    }

    public void setProfession(String profession) {
        this.profession = profession;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
 }
TiyebM
  • 2,684
  • 3
  • 40
  • 66
  • I want it such sort works when clicking on table header. If you add the line "table1.setAutoCreateRowSorter(true);" to your table initialization. Click on the table column heading to sort and you will see that your selected row is NOT at the top. How do you make it go to the top in this scenario? – user1144251 Mar 01 '17 at 17:01
0

Had make a CustomRowSorter basically a copy of the default DefaultRowSorter. I couldn't extend the class as I had to make some changes to the private functions with regards to sorting.

Additionally I had to make my own copy of TableRowSorter which used the new CustomRowSorter. My table would then use the CustomTableRowSorter to get the job done. It's not a pretty implementation as copying the two files was 1.5k lines of change.

user1144251
  • 327
  • 1
  • 3
  • 12