2

I need to sort the column with "File creation date", but if the column "File type" == FyleType.DIRECTORY this row should be at the top. That is, first sorting should be by type, and then by date.

My JTable:


Type Filename Date
DIRECTORY filename1 2025.04.03
DIRECTORY filename2 2025.02.01
FILE filename3.txt 2025.01.01
FILE filename4.txt 2025.02.03

I tried using

TableRowSorter<FileTableModel> tableRowSorter = new TableRowSorter<>(tableModel);
tableRowSorter.setComparator(2, ((o1, o2) -> {}

Comparator but it only gets the value of the current cell and I can't get the file type because the date is not unique

2
  • first sorting should be by type, and then by date. - you can set the "sort keys" that you want the data sorted by. This allows you to specify multiple columns. See: stackoverflow.com/a/65093631/131872 for a simple example to get you started. You would set the SortKeys when you load data into the table, not in an ActionListener. Commented Apr 3 at 19:05
  • You need something like TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel()); table.setRowSorter(sorter);sorter.setSortKeys(List.of(new RowSorter.SortKey(0, SortOrder.ASCENDING), new RowSorter.SortKey(2, SortOrder.ASCENDING))); Commented Apr 3 at 22:24

1 Answer 1

1

Another option is to create a JTable that contains a File copy of every data column. This way, you would need to change the TableCellRenderer to display the file type, file name, or file creation date from the File depending on the column, but the comparators for each column would be able to get the file type and file creation date from the File at the same time.

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class GroupDirectoriesFirstComparatorTest {
  private Component makeUI() {
    String[] columnNames = {"Type", "Filename", "Date"};
    DefaultTableModel model = new DefaultTableModel(columnNames, 0) {
      @Override
      public Class<?> getColumnClass(int column) {
        return File.class;
      }
    };
    JTable table = new JTable(model);
    table.setAutoCreateRowSorter(true);
    table.setDefaultRenderer(File.class, new FileCellRenderer());
    RowSorter<? extends TableModel> sorter = table.getRowSorter();
    if (sorter instanceof TableRowSorter) {
      TableRowSorter<? extends TableModel> rs =
          (TableRowSorter<? extends TableModel>) sorter;
      for (int i = 0; i < model.getColumnCount(); i++) {
        rs.setComparator(i, new GroupDirectoriesFirstComparator(table, i));
      }
    }
    JButton button = new JButton("Choose directory");
    button.addActionListener(e -> {
      JFileChooser chooser = new JFileChooser();
      chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      int ret = chooser.showOpenDialog(button.getRootPane());
      if (ret == JFileChooser.APPROVE_OPTION) {
        model.setRowCount(0);
        File[] files = chooser.getSelectedFile().listFiles();
        if (files != null) {
          Arrays.stream(files)
              .map(f -> Collections.nCopies(3, f).toArray())
              .forEach(model::addRow);
        }
      }
    });
    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    p.add(button, BorderLayout.SOUTH);
    return p;
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame frame = new JFrame();
      frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      frame.getContentPane().add(
          new GroupDirectoriesFirstComparatorTest().makeUI());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
    });
  }
}

class FileCellRenderer extends DefaultTableCellRenderer {
  private final DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd");

  @Override
  public Component getTableCellRendererComponent(
      JTable table, Object value,
      boolean isSelected, boolean hasFocus,
      int row, int column) {
    Component c = super.getTableCellRendererComponent(
        table, value, isSelected, hasFocus, row, column);
    if (c instanceof JLabel && value instanceof File) {
      JLabel l = (JLabel) c;
      l.setHorizontalAlignment(LEFT);
      File file = (File) value;
      switch (table.convertColumnIndexToModel(column)) {
        case 0:
          l.setText(file.isDirectory() ? "DIRECTORY" : "FILE");
          break;
        case 1:
          l.setText(file.getName());
          break;
        case 2:
          l.setHorizontalAlignment(RIGHT);
          try {
            BasicFileAttributes attr =
                Files.readAttributes(file.toPath(), BasicFileAttributes.class);
            l.setText(dateFormat.format(attr.creationTime().toMillis()));
          } catch (IOException e) {
            throw new RuntimeException(e);
          }
          break;
        default:
          break;
      }
    }
    return c;
  }
}

class FileComparator implements Comparator<File> {
  private final int column;

  public FileComparator(int column) {
    this.column = column;
  }

  @Override
  public int compare(File a, File b) {
    switch (column) {
      case 0: return getWeight(a) - getWeight(b);
      case 2: return Long.compare(getCreationTime(a), getCreationTime(b));
      default: return a.getName().compareToIgnoreCase(b.getName());
    }
  }

  public int getColumn() {
    return column;
  }

  public static int getWeight(File file) {
    return file.isDirectory() ? 1 : 2;
  }

  public static Long getCreationTime(File file) {
    BasicFileAttributes attr;
    try {
      attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    return Optional.ofNullable(attr)
        .map(BasicFileAttributes::creationTime)
        .map(FileTime::toMillis)
        .orElse(0L);
  }
}

// https://java-swing-tips.blogspot.com/2011/11/jtable-group-directories-first-sorting.html
class GroupDirectoriesFirstComparator extends FileComparator {
  private final JTable table;

  public GroupDirectoriesFirstComparator(JTable table, int column) {
    super(column);
    this.table = table;
  }

  @Override
  public int compare(File a, File b) {
    int v = getWeight(a) - getWeight(b);
    return v == 0 ? super.compare(a, b) : v * getSortOrderDirection();
  }

  private int getSortOrderDirection() {
    int dir = 1;
    List<? extends RowSorter.SortKey> keys = table.getRowSorter().getSortKeys();
    if (!keys.isEmpty()) {
      RowSorter.SortKey sortKey = keys.get(0);
      if (sortKey.getColumn() == getColumn() &&
          sortKey.getSortOrder() == SortOrder.DESCENDING) {
        dir = -1;
      }
    }
    return dir;
  }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.