Mujahid
Mujahid

Reputation: 1237

JTable sorting and Jasper report output

In my one of Java swing desktop based applications, I am using a JTable and I added the sorting functionality for a column. I need to sort it according to the number values added by the user and then get the jasper report output.

Currently after the sorting and print the report, the report is not showing the sorted order. But the order when the values taken from the DB. How can I print the report which the user's table sorting order?

This is my jasper report generating code

try {
    DefaultTableModel de = (DefaultTableModel)Dashboard.catalogTbl.getModel();
    JRTableModelDataSource jr = new JRTableModelDataSource(de);
    String reportName = reportPath + "reports/AuctionSale/Catalogue/catalouge_frm_tbl.jrxml";
    String compiledName = reportPath + "reports/AuctionSale/Catalogue/catalouge_frm_tbl.jasper";
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("Lot_No", "Lot No");
    params.put("Mark", "Mark");
    params.put("Invoice", "Invoice");
    params.put("Grade", "Grade");
    params.put("Weight", "Weight");
    params.put("Price", "Price");
    params.put("Buyer", "Buyer");
    JasperCompileManager.compileReportToFile(reportName, compiledName);
    JasperPrint jasperPrint = JasperFillManager.fillReport(compiledName, params, jr);
    JasperViewer.viewReport(jasperPrint, false);
} catch (Exception e) {
    e.printStackTrace();
}

Upvotes: 2

Views: 3362

Answers (3)

Isaias Cruz E.
Isaias Cruz E.

Reputation: 1

Something late, but I hope it helps someone:

In this code, what I do is that after applying the filters in the jTable1, place the rows obtained in the auxiliary model.

Then I assigned the auxiliary model to the auxiliary table. And that table is the one I'll send to JasperReports.

//** jTable1 is the table in the jFrame where the data is loaded and I apply
//the RowFilter or RowSorter filters

    DefaultTableModel dataModel_tableFiltered = null; //auxiliary model
    JTable tableAuxiliary = null; //table where we will put the auxiliary model

    public constructor {
            
        dataModel_tableFiltered = new DefaultTableModel ();
        // Set the number and name of the columns in the auxiliary model
        for (int i = 0; i <jTable1.getColumnCount(); i ++) {
            dataModel_tableFiltered.addColumn(jTable1.getColumnName (i));
        }
        
        tableAuxiliary = new JTable ();
    }



    private void btn_PrintActionPerformed (java.awt.event.ActionEvent evt) {

        fillModel_filtered ();

        try {
            Map params = new HashMap ();
            params.put ("nameCustomer", "**");

            JRDataSource dataSource = new JRTableModelDataSource (tableAuxiliary.getModel ());
            JasperPrint print = JasperFillManager.fillReport (reportPath, params, dataSource);
            JasperViewer.viewReport (print, false); // true == Exit on Close
        } catch (JRException ex) {
            ex.printStackTrace ();
        }

     }

    // Put resulting rows in the model after applying filters in jTable1
    public void fillModel_filtered () {
        dataModel_tableFiltered.setRowCount (0); // Empty rows of the model

        for (int i = 0; i <jTable1.getRowCount (); i ++) {
            Object row [] = new Object [jTable1.getColumnCount ()];
            for (int j = 0; j <jTable1.getColumnCount (); j ++) {
                row [j] = jTable1.getValueAt (i, j);
            }
            dataModel_tableFiltered.addRow (row);
        }
        tableAuxiliary.setModel(dataModel_tableFiltered); // Very Important
    }        

Upvotes: 0

kleopatra
kleopatra

Reputation: 51525

@Robin answer is basically correct, just translating to jasper speak :-)

The "decorator" is a custom implementation of JRDataSource or (here) JRRewindableDataSource. Make it data-only and base on the table's RowSorter, something like (beware: just compiled, not tested!)

public class JRTableSorterDataSource implements JRRewindableDataSource {

    private RowSorter<? extends TableModel> sorter;

    private int currentRow = -1;

    private HashMap<String, Integer> columnNames = new HashMap<String, Integer>();

    public JRTableSorterDataSource(RowSorter<? extends TableModel> sorter) {
        if (sorter == null) return; // do nothing, no sorter
        this.sorter = sorter;
        TableModel tableModel = sorter.getModel();
        if (tableModel != null) {
            for (int i = 0; i < tableModel.getColumnCount(); i++) {
                this.columnNames.put(tableModel.getColumnName(i),
                        Integer.valueOf(i));
            }
        }

    }

    @Override
    public Object getFieldValue(JRField field) throws JRException {
        String fieldName = field.getName();
        Integer columnIndex = this.columnNames.get(fieldName);
        return sorter.getModel().getValueAt(sorter.convertRowIndexToModel(currentRow), columnIndex.intValue());
    }


    @Override
    public boolean next() throws JRException {
        if (sorter == null || sorter.getModel() == null)
            return false;
        this.currentRow++;
        return (this.currentRow < sorter.getViewRowCount());
    }

    @Override
    public void moveFirst() throws JRException {
        this.currentRow = -1;
    }

    protected int getColumnIndex(JRField field) throws JRException {
        String fieldName = field.getName();
        Integer columnIndex = this.columnNames.get(fieldName);

        if (columnIndex != null) {
            return columnIndex;
        } else if (fieldName.startsWith("COLUMN_")) {
            return Integer.parseInt(fieldName.substring(7));
        }
        throw new JRException("Unknown column name : " + fieldName);
    }

}

Then use it when setting up your report:

JRDataSource jr = new JRTableSorterDataSource(Dashboard.catalogTbl.getRowSorter());
/// ... same as your example

Edit

just a very quick runnable snippet (too lazy to do a full report, forgot how those files work ;-) - so here we create a table (with a standard SwingX model), create a dataSource on its RowSorter and loop over the values of the first column, no problem:

    JTable table = new JXTable(new AncientSwingTeam());
    JRDataSource source = new JRTableSorterDataSource(table.getRowSorter());
    table.getRowSorter().toggleSortOrder(0);
    JRField field = createField("First Name");
    String firstNames = "First Name: ";
    while (source.next()) {
        firstNames += "\n  " + source.getFieldValue(field);
    }
    LOG.info(firstNames);

Upvotes: 2

Robin
Robin

Reputation: 36611

I guess the report is generated based on the TableModel, while typical sorting only affect the JTable itself, not the model.

What you could do is decorate the table model which you pass to your report generator so that it takes over the ordering of your JTable. Something in the style of

public class TableModelDecorator implements TableModel{
  private TableModel delegate;
  private JTable table;

  @Override
  public Object getValueAt( int rowIndex, int columnIndex ) {
    return delegate.getValueAt( table.convertRowIndexToView( rowIndex ), table.convertColumnIndexToView( columnIndex ) );
  }
}

but then for all relevant methods.

Upvotes: 2

Related Questions