TableSorter class question

I

IchBin

To use the TableSorter class from Sun, referenced in the JTable tutorial
http://java.sun.com/docs/books/tutorial/uiswing/components/table.html. I
have to create a MyTableModel that houses and manipulates the Table
data. The code they have can not really work in a real world
application. That is, hard coded model data and columns. This is not
practical. What I have done, is working as designed. (Attached below)

The pattern that Sun uses for the TableSorter and MyTableModel classes
is the decorator pattern. I have to fold MyTableModel into the
TableSorter class and then set the TableSorter as the JTable model. Code
wise it is done this this way:

TableSorter sorter = new TableSorter(new MyTableModel());
JTable table = new JTable(sorter);
sorter.setTableHeader(table.getTableHeader());

So in TableSorter there is a constructor defined to house the passed
MyTableModel:

public TableSorter(TableModel tableModel) {
this();
setTableModel(tableModel);
}

OK, MyTableModel works as designed. I have no problem at all with
loading data, Sorting..etc. All works the way it should. The problem I
am having is that when I have to reference the MyTableModel thru the
JTable owner I am not seeing it. Say I have to get the
table.getRowCount(), table.getColumnModel(), table.getColumnCount() or
attached any renders. And if I do table.getModel() I am not getting the
MyTableModel that has the data. The TableSorter HAS the reference to
MyTableModel else no sorting would work but it does.

Hope my question is understandable. It's like I can not reference the
MyTableModel that is in TableSorter set as the model for the JTable
owner.. MyTableModel and TableSorter both extend AbstractTableModel.

public class MyTableModel extends AbstractTableModel
{
/**
* Debugging switch for problem determination
*/
private boolean DEBUG = false;
/**
* Define as string Object and not Vector to maintain Columns
*/
private String[] columnNames;
// private String[] columnNames = {"Index","Entry
Name","URL","Results","Coment"};
/**
* Define as new with one element to eliminate error
* on intial reference from TableSorter
*/
private Object[][] data = new Object[0][0];

/**
* Default Class constructor on intial refference from TableSorter
*/
public JBookMarksTableModel() {
}

/**
* Added a constructor to load in data column names.
*
* @param data row data to add to row model
* @param columnNames columms with colummns names to add to model
*/
protected JBookMarksTableModel(Object[][] data, String[] columnNames) {
//
// Copy all Columns data to instance Columns
addColumn(columnNames);
//
// Redefine the instance row size
this.data = new Object[data.length][data[0].length];
//
// Copy all row data to instance row data
copyObjects(data, this.data);
}
/**
* Added a constructor to load in data column names.
*
* @param data row data to add to row model
* @param columnNames columms with colummns names to add to model
*/
protected void addDataObject(Object[][] data) {
//
// Redefine the instance row size
this.data = new Object[data.length][data[0].length];
//
// Copy all row data to instance row data
copyObjects(data, this.data);
}
/**
* Delete all data.
*/
protected void deleteDataObject() {
//
// Redefine the instance row size to zero
this.data = new Object[0][0];
}
/**
* Non-Standard Added this method to return the all the Models data
* @return data[][]
*/
protected Object[][] getTableData(){
return data;
}
/**
* @see javax.swing.table.TableModel#getColumnCount()
*/
public int getColumnCount() {
return columnNames.length;
}
/**
* @see javax.swing.table.TableModel#getRowCount()
*/
public int getRowCount() {
return data.length;
}
/**
* Added Method to add Columns[]. There is no equivwent method
* in Sun's table models.
* It overrides addColumn(String columnName)
* (Mine only works for seq column adds)
*
* @param columnNames All Table Column names
*/
protected void addColumn(String[] columnNames) {
//
// Copy all Columns data to instance Columns
this.columnNames = new String[columnNames.length];
this.columnNames = columnNames;
}
/**
* I added Method to add Columns
* DefaultTableModel uses a addColumn(int column, String[] column)
* (Mine only works for seq column adds)
*
* @param columnName Column name
*/
protected void addColumn(String columnName) {

if (columnNames == null) {
//
// First time
this.columnNames = new String[1];
this.columnNames[0] = columnName;
} else {

// Create temp Object to hold current columns data plus one new
// empty element
Object temp[] = new Object[columnNames.length + 1];

//
// Copy all current Column data to temp
System.arraycopy(columnNames, 0, temp, 0, columnNames.length);

//
// Added the new column name to the temp Object
temp[temp.length - 1] = columnName;

//
// Rebuild the Instance Column object with all of the new data
this.columnNames = new String[temp.length];
System.arraycopy(temp, 0, columnNames, 0, columnNames.length);
}
}

/**
* Overrides javax.swing.table.AbstractTableModel.getColumnName(int)
* @see javax.swing.table.AbstractTableModel#getColumnName(int)
*/
public String getColumnName(int col) {
return columnNames[col];
}

/**
* @see javax.swing.table.TableModel#getValueAt(int, int)
*/
public Object getValueAt(int row, int col) {
return data[row][col];
}

/**
* JTable uses this method to determine the default renderer/
editor for
* each cell. If we didn't implement this method, then the last
column would
* contain text ("true"/"false"), rather than a check box.
*
* @see javax.swing.table.AbstractTableModel#getColumnClass(int)
*/
public Class getColumnClass(int c) {

Class value = null;
try {
value = getValueAt(0, c).getClass();
} catch (Exception e) {
// TODO: handle exception
}
return value;
}

/**
* Don't need to implement this method unless your table's editable.
*
* @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
*/
public boolean isCellEditable(int row, int col) {
// Note that the data/cell address is constant,
// no matter where the cell appears onscreen.
if (col < 2) {
return false;
}
return true;
}

/**
* Don't need to implement this method unless your table's data can
change.
* Overrides javax.swing.table.AbstractTableModel.setValueAt(Object
value, int row, int col)
* @see
javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int)
*/
public void setValueAt(Object value, int row, int col) {

if (DEBUG){
out.println("Setting value at " + row + "," + col + " to "
+ value + " (an instance of " + value.getClass() +
")");
}
data[row][col] = value;
fireTableCellUpdated(row, col);

if (DEBUG) printDebugData(this.data);
}

/**
* I added Methods to add row data conforms to DefaultTableModel.
* DefaultTableModel uses a addRow(int row, Object[] data)
* (I only append the row data)
*
* @param data a one dimension row element
*/
protected void addRow(Object[] data) {

if (this.data == null) {
this.data = new Object[1][data.length];
//
// loop and add the new first data row elements
for (int j = 0; j < data.length; j++) {
this.data[0][j] = data[j];
}
} else {
//
// Create temp Object to hold current columns data plus one new
// empty element
Object temp[][] = new Object[this.data.length +
1][data.length];

//
// Copy all current Column data to temp
copyObjects(this.data, temp);

//
// Add the new column data elements
for (int outer = 0; outer < data.length; outer++){
temp[temp.length - 1][outer] = data[outer];
}
//
// Rebuild the Instance data object with all of the new data
this.data = new Object[temp.length][data.length];
copyObjects(temp, this.data);

//
// Display the data if un debug mode
if (DEBUG) printDebugData(this.data);

//
// Update the JTable display with new data
fireTableDataChanged();
}
}

/**
* I added this Method as a helper to manage the data[][] expansion
*
* @param fromdata the data[]][] object to copy from
* @param todata the data[]][] object build target
*
* @return Object[][] return the build data[][] object
*/
protected Object[][] copyObjects(Object[][] fromdata, Object[][]
todata) {

for (int outer = 0; outer < fromdata.length; outer++) {
for (int inner = 0; inner < fromdata[outer].length; inner++){
todata[outer][inner] = fromdata[outer][inner];
}
}
return todata;
}

private void printDebugData(Object[][] data) {

int numRows = data.length;
int numCols = data[0].length;

out.println();

for (int i = 0; i < numRows; i++) {
out.print(" row " + i + ":");
for (int j = 0; j < numCols; j++) {
out.print(" " + data[j]);
}
out.println();
}
}
}

--

Thanks in Advance...
IchBin, Pocono Lake, Pa, USA
http://weconsultants.servebeer.com/JHackerAppManager
__________________________________________________________________________

'If there is one, Knowledge is the "Fountain of Youth"'
-William E. Taylor, Regular Guy (1952-)
 
C

Carsten Hammer

The TableSorter class from Sun is indeed incomplete for real world
problems.

One example to map back to the original tablemodel is used in the
SelectedPages plugin of http://www.lowagie.com/iText/itext.jnlp.
There you can open a pdf file. A JTable showing the content (pages) of
the pdf file comes up and you can sort the pages according to the width
or height and independent of the shown order the selected pages are
computed right.

To find out what tablerows in the original tablemodel are selected this
method is used(PageSelectionTableDialog.java, look in
http://itext.cvs.sourceforge.net/itext/src/com/lowagie/tools/arguments/):

TableSorter mysorter = (TableSorter) jTable1.getModel();
int[] values = jTable1.getSelectedRows();
int max = jTable1.getSelectedRowCount();
int[] swappedvalues = new int[max];

if (jToggleButton1.getModel().isSelected()) {
for (int i = 0; i < max; i+=2) {
int second=(i+1)<max?i+1:i;
swappedvalues = mysorter.getModelrow(values[second]) + 1;
swappedvalues[second] = mysorter.getModelrow(values) + 1;
}
}
else {
for (int i = 0; i < max; i++) {
swappedvalues = mysorter.getModelrow(values) + 1;
}

}
}

Therefore it uses a slightly changed TableSorter class.
Best regards,
Carsten

To use the TableSorter class from Sun, referenced in the JTable tutorial
http://java.sun.com/docs/books/tutorial/uiswing/components/table.html. I
have to create a MyTableModel that houses and manipulates the Table
data. The code they have can not really work in a real world
application. That is, hard coded model data and columns. This is not
practical. What I have done, is working as designed. (Attached below)

The pattern that Sun uses for the TableSorter and MyTableModel classes
is the decorator pattern. I have to fold MyTableModel into the
TableSorter class and then set the TableSorter as the JTable model. Code
wise it is done this this way:

TableSorter sorter = new TableSorter(new MyTableModel());
JTable table = new JTable(sorter);
sorter.setTableHeader(table.getTableHeader());

So in TableSorter there is a constructor defined to house the passed
MyTableModel:

public TableSorter(TableModel tableModel) {
this();
setTableModel(tableModel);
}

OK, MyTableModel works as designed. I have no problem at all with
loading data, Sorting..etc. All works the way it should. The problem I
am having is that when I have to reference the MyTableModel thru the
JTable owner I am not seeing it. Say I have to get the
table.getRowCount(), table.getColumnModel(), table.getColumnCount() or
attached any renders. And if I do table.getModel() I am not getting the
MyTableModel that has the data. The TableSorter HAS the reference to
MyTableModel else no sorting would work but it does.

Hope my question is understandable. It's like I can not reference the
MyTableModel that is in TableSorter set as the model for the JTable
owner.. MyTableModel and TableSorter both extend AbstractTableModel.

public class MyTableModel extends AbstractTableModel
{
/**
* Debugging switch for problem determination
*/
private boolean DEBUG = false;
/**
* Define as string Object and not Vector to maintain Columns
*/
private String[] columnNames;
// private String[] columnNames = {"Index","Entry
Name","URL","Results","Coment"};
/**
* Define as new with one element to eliminate error
* on intial reference from TableSorter
*/
private Object[][] data = new Object[0][0];

/**
* Default Class constructor on intial refference from TableSorter
*/
public JBookMarksTableModel() {
}

/**
* Added a constructor to load in data column names.
*
* @param data row data to add to row model
* @param columnNames columms with colummns names to add to model
*/
protected JBookMarksTableModel(Object[][] data, String[] columnNames) {
//
// Copy all Columns data to instance Columns
addColumn(columnNames);
//
// Redefine the instance row size
this.data = new Object[data.length][data[0].length];
//
// Copy all row data to instance row data
copyObjects(data, this.data);
}
/**
* Added a constructor to load in data column names.
*
* @param data row data to add to row model
* @param columnNames columms with colummns names to add to model
*/
protected void addDataObject(Object[][] data) {
//
// Redefine the instance row size
this.data = new Object[data.length][data[0].length];
//
// Copy all row data to instance row data
copyObjects(data, this.data);
}
/**
* Delete all data.
*/
protected void deleteDataObject() {
//
// Redefine the instance row size to zero
this.data = new Object[0][0];
}
/**
* Non-Standard Added this method to return the all the Models data
* @return data[][]
*/
protected Object[][] getTableData(){
return data;
}
/**
* @see javax.swing.table.TableModel#getColumnCount()
*/
public int getColumnCount() {
return columnNames.length;
}
/**
* @see javax.swing.table.TableModel#getRowCount()
*/
public int getRowCount() {
return data.length;
}
/**
* Added Method to add Columns[]. There is no equivwent method
* in Sun's table models.
* It overrides addColumn(String columnName)
* (Mine only works for seq column adds)
*
* @param columnNames All Table Column names
*/
protected void addColumn(String[] columnNames) {
//
// Copy all Columns data to instance Columns
this.columnNames = new String[columnNames.length];
this.columnNames = columnNames;
}
/**
* I added Method to add Columns
* DefaultTableModel uses a addColumn(int column, String[] column)
* (Mine only works for seq column adds)
*
* @param columnName Column name
*/
protected void addColumn(String columnName) {

if (columnNames == null) {
//
// First time
this.columnNames = new String[1];
this.columnNames[0] = columnName;
} else {

// Create temp Object to hold current columns data plus one new
// empty element
Object temp[] = new Object[columnNames.length + 1];

//
// Copy all current Column data to temp
System.arraycopy(columnNames, 0, temp, 0, columnNames.length);

//
// Added the new column name to the temp Object
temp[temp.length - 1] = columnName;

//
// Rebuild the Instance Column object with all of the new data
this.columnNames = new String[temp.length];
System.arraycopy(temp, 0, columnNames, 0, columnNames.length);
}
}

/**
* Overrides javax.swing.table.AbstractTableModel.getColumnName(int)
* @see javax.swing.table.AbstractTableModel#getColumnName(int)
*/
public String getColumnName(int col) {
return columnNames[col];
}

/**
* @see javax.swing.table.TableModel#getValueAt(int, int)
*/
public Object getValueAt(int row, int col) {
return data[row][col];
}

/**
* JTable uses this method to determine the default renderer/ editor
for
* each cell. If we didn't implement this method, then the last
column would
* contain text ("true"/"false"), rather than a check box.
*
* @see javax.swing.table.AbstractTableModel#getColumnClass(int)
*/
public Class getColumnClass(int c) {

Class value = null;
try {
value = getValueAt(0, c).getClass();
} catch (Exception e) {
// TODO: handle exception
}
return value;
}

/**
* Don't need to implement this method unless your table's editable.
*
* @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
*/
public boolean isCellEditable(int row, int col) {
// Note that the data/cell address is constant,
// no matter where the cell appears onscreen.
if (col < 2) {
return false;
}
return true;
}

/**
* Don't need to implement this method unless your table's data can
change.
* Overrides javax.swing.table.AbstractTableModel.setValueAt(Object
value, int row, int col)
* @see
javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int)
*/
public void setValueAt(Object value, int row, int col) {

if (DEBUG){
out.println("Setting value at " + row + "," + col + " to "
+ value + " (an instance of " + value.getClass() +
")");
}
data[row][col] = value;
fireTableCellUpdated(row, col);

if (DEBUG) printDebugData(this.data);
}

/**
* I added Methods to add row data conforms to DefaultTableModel.
* DefaultTableModel uses a addRow(int row, Object[] data)
* (I only append the row data)
*
* @param data a one dimension row element
*/
protected void addRow(Object[] data) {

if (this.data == null) {
this.data = new Object[1][data.length];
//
// loop and add the new first data row elements
for (int j = 0; j < data.length; j++) {
this.data[0][j] = data[j];
}
} else {
//
// Create temp Object to hold current columns data plus one new
// empty element
Object temp[][] = new Object[this.data.length +
1][data.length];

//
// Copy all current Column data to temp
copyObjects(this.data, temp);

//
// Add the new column data elements
for (int outer = 0; outer < data.length; outer++){
temp[temp.length - 1][outer] = data[outer];
}
//
// Rebuild the Instance data object with all of the new data
this.data = new Object[temp.length][data.length];
copyObjects(temp, this.data);

//
// Display the data if un debug mode
if (DEBUG) printDebugData(this.data);

//
// Update the JTable display with new data
fireTableDataChanged();
}
}

/**
* I added this Method as a helper to manage the data[][] expansion
*
* @param fromdata the data[]][] object to copy from
* @param todata the data[]][] object build target
*
* @return Object[][] return the build data[][] object
*/
protected Object[][] copyObjects(Object[][] fromdata, Object[][]
todata) {

for (int outer = 0; outer < fromdata.length; outer++) {
for (int inner = 0; inner < fromdata[outer].length; inner++){
todata[outer][inner] = fromdata[outer][inner];
}
}
return todata;
}

private void printDebugData(Object[][] data) {

int numRows = data.length;
int numCols = data[0].length;

out.println();

for (int i = 0; i < numRows; i++) {
out.print(" row " + i + ":");
for (int j = 0; j < numCols; j++) {
out.print(" " + data[j]);
}
out.println();
}
}
}
 
I

IchBin

Carsten said:
The TableSorter class from Sun is indeed incomplete for real world
problems.

One example to map back to the original tablemodel is used in the
SelectedPages plugin of http://www.lowagie.com/iText/itext.jnlp.
There you can open a pdf file. A JTable showing the content (pages) of
the pdf file comes up and you can sort the pages according to the width
or height and independent of the shown order the selected pages are
computed right.

To find out what tablerows in the original tablemodel are selected this
method is used(PageSelectionTableDialog.java, look in
http://itext.cvs.sourceforge.net/itext/src/com/lowagie/tools/arguments/):

TableSorter mysorter = (TableSorter) jTable1.getModel();
int[] values = jTable1.getSelectedRows();
int max = jTable1.getSelectedRowCount();
int[] swappedvalues = new int[max];

if (jToggleButton1.getModel().isSelected()) {
for (int i = 0; i < max; i+=2) {
int second=(i+1)<max?i+1:i;
swappedvalues = mysorter.getModelrow(values[second]) + 1;
swappedvalues[second] = mysorter.getModelrow(values) + 1;
}
}
else {
for (int i = 0; i < max; i++) {
swappedvalues = mysorter.getModelrow(values) + 1;
}

}
}


[snip code]

Thanks Carsten for your response. I am looking at the code you mentioned.

I am still having a problem with it. It is something stupid sitting
right in front of me. I am using seven JTables that are each embedded
into an instance TableSorter. What I wanted to do is only cosmetic after
all of the JTables have been loaded. That is, correct the widths of all
the columns size to the largest data element in that column. I have code
that does that and I know it works. Just need to get it working with the
seven JTables in this app.

Also, if any one wants to use the MyTableModel.java with Sun's
TableSorter.java(I posted in the first msg of this thread). You need to
do the following as I mentioned before:

TableSorter sorter = new TableSorter(new MyTableModel());
JTable table = new JTable(sorter);
sorter.setTableHeader(table.getTableHeader());

But also add this line of code after the above code:
//
// Need to force the sorting status so that the column values will be
// repainted correctly (0 = sortAsending)
jBookMarksTableSorter.setSortingStatus(firstColumn,0);


Thanks in Advance...
IchBin, Pocono Lake, Pa, USA
http://weconsultants.servebeer.com/JHackerAppManager
__________________________________________________________________________

'If there is one, Knowledge is the "Fountain of Youth"'
-William E. Taylor, Regular Guy (1952-)
 
I

IchBin

Carsten said:
The TableSorter class from Sun is indeed incomplete for real world
problems.

One example to map back to the original tablemodel is used in the
SelectedPages plugin of http://www.lowagie.com/iText/itext.jnlp.
There you can open a pdf file. A JTable showing the content (pages) of
the pdf file comes up and you can sort the pages according to the width
or height and independent of the shown order the selected pages are
computed right.

To find out what tablerows in the original tablemodel are selected this
method is used(PageSelectionTableDialog.java, look in
http://itext.cvs.sourceforge.net/itext/src/com/lowagie/tools/arguments/):

TableSorter mysorter = (TableSorter) jTable1.getModel();
int[] values = jTable1.getSelectedRows();
int max = jTable1.getSelectedRowCount();
int[] swappedvalues = new int[max];

if (jToggleButton1.getModel().isSelected()) {
for (int i = 0; i < max; i+=2) {
int second=(i+1)<max?i+1:i;
swappedvalues = mysorter.getModelrow(values[second]) + 1;
swappedvalues[second] = mysorter.getModelrow(values) + 1;
}
}
else {
for (int i = 0; i < max; i++) {
swappedvalues = mysorter.getModelrow(values) + 1;
}

}
}

Therefore it uses a slightly changed TableSorter class.
Best regards,
Carsten

To use the TableSorter class from Sun, referenced in the JTable
tutorial
http://java.sun.com/docs/books/tutorial/uiswing/components/table.html.
I have to create a MyTableModel that houses and manipulates the Table
data. The code they have can not really work in a real world
application. That is, hard coded model data and columns. This is not
practical. What I have done, is working as designed. (Attached below)

The pattern that Sun uses for the TableSorter and MyTableModel classes
is the decorator pattern. I have to fold MyTableModel into the
TableSorter class and then set the TableSorter as the JTable model.
Code wise it is done this this way:

TableSorter sorter = new TableSorter(new MyTableModel());
JTable table = new JTable(sorter);
sorter.setTableHeader(table.getTableHeader());


[snip code]

Thanks Carsten for your response. I am looking at the code you mentioned.

I am still having a problem with it. It is something stupid sitting
right in front of me. I am using seven JTables that are each embedded
into an instance TableSorter. What I wanted to do is only cosmetic after
all of the JTables have been loaded. That is, correct the widths of all
the columns size to the largest data element in that column. I have code
that does that and I know it works. Just need to get it working with the
seven JTables in this app.

Also, if any one wants to use the MyTableModel.java with Sun's
TableSorter.java(I posted in the first msg of this thread). You need to
do the following as I mentioned before:

TableSorter sorter = new TableSorter(new MyTableModel());
JTable table = new JTable(sorter);
sorter.setTableHeader(table.getTableHeader());

But also add this line of code after the above code:
//
// Need to force the sorting status so that the column values will be
// repainted correctly (0 = sort ascending)
sorter.setSortingStatus(firstColumn,0);

Thanks in Advance...
IchBin, Pocono Lake, Pa, USA
http://weconsultants.servebeer.com/JHackerAppManager
__________________________________________________________________________

'If there is one, Knowledge is the "Fountain of Youth"'
-William E. Taylor, Regular Guy (1952-)
 
H

hilizbabe

There are lot of gotchas with the JTable sorting provided in the sun
website. I would suggest you to use free QuickTable java component
(http://quicktable.org) which is an implementation on top of JTable ,
but hides all the complexities and allows to use the JTable methods. So
you can just replace Jtable with QuickTable

In this component, clicking on the table header does the sorting for
you. You don't need to write a single line of code. All the problems
with selection etc are already taken care of. In addition you can also
do sorting programtically
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top