ArrayIndexOutOfBoundsException when making recursive call to a JTable - java

I apologise in advance for the length of this post but I have provided a rather in depth description of the design and implementation of my program.
Background
I am currently doing a group (there are 2 of us) programming project for a third year computer science course at university.
The goal of this program is to essentially use a spreadsheet program to represent XML file data where each XML file is a historical record.
Design:
Each record(row) in the spreadsheet corresponds to a single XML file and the columns of the record correspond to the elements in the XML file. We deal with repeated elements (i.e. elements with the same tag) by setting the cell component to a button that, when clicked, opens up another spreadsheet which contains a list of all elements with repeated names (for the corresponding file). Child elements are dealt with in a similar manner whereby if an element ha child elements then the corresponding cell in the XML file contains a button which, when clicked, opens up a spreadsheet containing all the child elements of that element.
Implementation:
The implementation of our system is written in Java. Our main class (name SpreadSheetGUI) extends JFrame to which we add a JTable (using the default table model). We have three different cell renderers: one for when a cell just has text, one for when a cell has a button and one for when we have text and a button in a cell. When a button is clicked to open up a new spreadsheet (for either child elements or repeated element names) we make a recursive call to our spreadsheet constructor which will then create our sub-spreadsheet. The renderers are added in the following order: if the cell corresponds to an element with a tag that is used more than once a button is added to the cell, otherwise if the cell corresponds to an element with child nodes we add both text and a button to the cell and if the cell just has text we add the text to that cell.
The constructor for the GUI is as such
/**
* Parameterised constructor
* #param dataVector - Vector of vectors of objects that represent the cell values
* #param columnNames - The vector of objects that represent the column names
*/
#SuppressWarnings("unchecked")
public SpreadSheetGUI(Vector<Vector<LevelElementT>> dataVector, Vector<String> columnNames, boolean hasRepeatedColumns, boolean initialFrame)
{
this.hasRepeatedColumns = hasRepeatedColumns;
this.initialFrame = initialFrame;
if (initialFrame)
populateTable(dataVector, columnNames);
else if (!hasRepeatedColumns)
populateTable((Vector<Vector<LevelElementT>>)findRepeatedColumns(dataVector).get(0),
(Vector<String>)findRepeatedColumns(dataVector).get(1));
else
populateTable(dataVector, columnNames);
//Get repeated column names and add to repeated column hashmap
//parseElements(dataVector);
}
where the populateTable method initialises the table model.
And as I stated earlier we have three different renderers and editors for our cells and two
of the renderers have buttons in them that, when clicked, create a new spreadsheet (i.e. we make a call to our spreadsheet constructor), for example in our one cell editor has the following code
public Object getCellEditorValue()
{
if (isPushed)
{
//will have the child elements of the current cell element
Vector<Vector<LevelElementT>> children = new Vector<Vector<LevelElementT>>();
children.add(new Vector<LevelElementT>());
List<Element> tempChildren = elements.get(row).get(column).getChildren();
for (Element child : tempChildren)
children.get(0).add(new LevelElementT(child));
//creates our subspreadsheet
new Thread(new SpreadSheetGUI(children, new Vector<String>(), false, false)).start();
}
isPushed = false;
return new String(bLabel);
}
LevelElementT is just a class we made that extends Element (found in the JDOM2 package) that overrides the toString method.
The Problem:
As I mentioned before we have created a set of renderers to handle the adding of buttons to cells but it seems that when a "child" spreadsheet is created the renderers are trying to render cells that are out of bounds in accordance with the child spreadsheets number of rows and columns and throw an array index out of bounds exception.
More specifically the errors are thrown in the following piece of code taken from the populateTable() method. I initialises the JTable with an instance of the defaultTableModel and sets up the methods to determine the renderer of each component
table = new JTable(tableModel)
{
private static final long serialVersionUID = 1L;
public TableCellRenderer getCellRenderer(int row, int column)
{
//System.out.println(elements.get(row).get(column).getChildren().size());
if (column == 0)
{
Class<? extends Object> cellClass = getValueAt(row, column).getClass();
return getDefaultRenderer(cellClass);
}
else if(repeatedColumns.containsKey(table.getColumnModel().getColumn(column).getIdentifier() + " " + elements.get(row).get(0).getText()))
return getDefaultRenderer(JButton.class);
else if(!elements.get(row).get(column).getChildren().isEmpty())
return getDefaultRenderer(JPanel.class);
else
return getDefaultRenderer(JTextArea.class);
}
public TableCellEditor getCellEditor(int row, int column)
{
if (column == 0)
{
Class<? extends Object> cellClass = getValueAt(row, column).getClass();
return getDefaultEditor(cellClass);
}
else if(repeatedColumns.containsKey(table.getColumnModel().getColumn(column).getIdentifier() + " " + elements.get(row).get(0).getText()))
return getDefaultEditor(JButton.class);
else if(!elements.get(row).get(column).getChildren().isEmpty())
return getDefaultEditor(JPanel.class);
else
return getDefaultEditor(JTextArea.class);
}
};
The errors are thrown at the if statements (depending on the button you clicked) and it is definitely not because of elements (a two dimensional vector of levelElements) or repeatedColumns(a hashtable of with a string as the key and a vector of elements as the value).
I'm guessing the problem extends from the fact that we are making a recursive call to our spreadsheet constructor. A friend also suggested that this problem might be caused by the default table model, should I consider creating a custom table model?
I haven't included my code because it is rather lengthy (Around 2000 lines in total) but I am willing to give it on request. I have been wracking my brains with this one and I have been completely unsuccessful in finding any threads related to this problem.

Since no code is provided, I'm just guessing:
the recursive call can cause this if it does not return correctly. We don't know what you do exactly...
this phenomena can also be caused by accidentally passing the parent table's cells to the renderer, not the child one's.
I'd debug for these. But again, without code snippets, it is really hard to answer, I would say.
Hope that this helps. :)

new SpreadSheetGUI(children, new Vector<String>(), false, false) is suspect (guess only) as afterwards the new Vector is not accessible normally.
There is no provision with .get(row).get(column) for index checks. Especially whether every row has sufficient strings.
The DefaultTableModel is sufficient.
Vector, though thread safe, is quite old; makes a better impression to use List / ArrayList, with concurrency: CopyOnWriteArrayList.
There already exist XML table models and tree table models. I do not know about your requirements with respect to programming.
A stack trace, break points, debugging, IDE, should have helped. NetBeans IDE is easy, eclipse IDE more wide spread.

Related

JavaFX TableView: Rapid change in items list not reflected

It is hard to explain so I'll use an example:
#Override
public void start(Stage primaryStage) throws Exception
{
final VBox vbox = new VBox();
final Scene sc = new Scene(vbox);
primaryStage.setScene(sc);
final TableView<Person> table = new TableView<>();
final TableColumn<Person, String> columnName = new TableColumn<Person, String>("Name");
table.getColumns().add(columnName);
final ObservableList<Person> list = FXCollections.observableArrayList();
list.add(new Person("Hello"));
list.add(new Person("World"));
Bindings.bindContent(table.getItems(), list);
columnName.setCellValueFactory(new PropertyValueFactory<>("name"));
vbox.getChildren().add(table);
final Button button = new Button("test");
button.setOnAction(event ->
{
final Person removed = list.remove(0);
removed.setName("Bye");
list.add(0, removed);
});
vbox.getChildren().add(button);
primaryStage.show();
}
public static class Person
{
private String name = "";
public Person(String n)
{
name = n;
}
public String getName()
{
return name;
}
public void setName(String n)
{
name = n;
}
}
In this example, I show a TableView with a single column named "Name". Running this sample code, you will get two rows: first row with "Hello" in "Name" column; and second row with "World" in "Name" column.
Additionally, there is a button, this button removes the first Person object from the list, then makes some changes to the object, then adds it back in at the same index. Doing so would cause any ListChangeListener added to the ObservableList to be triggered, and I have tested this to be true.
I would expect the row with "Hello" to be replaced with "Bye", but it seems like the TableView continues to show "Hello". If I used a TimeLine to add delay before I add the removed Person object back to the list, it would change to "Bye".
final Timeline tl = new Timeline(new KeyFrame(Duration.millis(30), ae -> list.add(0, removed)));
tl.play();
Is there something weird with the API? Is there any way to do this without this problem?
This is essentially expected behavior.
Note that (and I'm guessing you are trying to work around this issue), if you simply called
list.get(0).setName("Bye");
which has the same effect in terms of the underlying data, the table would not update as it has no way of being notified that the String field name in the element of the list has changed.
The code
Person removed = list.remove(0);
removed.setName("Bye");
list.add(0, removed);
is really equivalent to list.get(0).setName("Bye");: you just temporarily remove the item from the list before changing it, and then add it back. As far as the list is concerned, the net result is the same. I guess you are doing this in the hope that removing and replacing the item from the list will persuade the table to notice the state of the item has changed. There's no guarantee this will be the case. Here is what's happening:
The binding between your two lists:
Bindings.bindContent(table.getItems(), list);
works like any other binding: it defines how to get the value of the binding (the elements of list), and marks the data as invalid if list is invalidated at any time. The latter happens when you add and remove elements from list.
The TableView will not perform layout every time the binding to the list changes; instead, when then binding is invalidated (add or remove an element), then the table view marks itself as potentially needing to be redrawn. Then, on the next rendering pulse, the table will check the data and see if it really needs to be redrawn, and re-render if needed. There are obvious performance-saving features of this implementation.
So what happens with your code is that an item is removed from the list, causing the binding to be marked as invalid. The item is then changed (by calling setName(...)), and the same item is then added back into the list at the same position. This also causes the binding to be marked as invalid, which has no effect (it is already invalid).
No rendering pulse can occur between the removal and re-addition of this element. Consequently, the first time the table actually looks at the changes that were made to the list has to be after the entire remove-change-add process. At that point, the table will see that the list still contains the exact same elements in the exact same order that it previously contained. (The internal state of one of the elements has changed, but since this is not an observable value - not a JavaFX property - the table is unaware of this.) Consequently, the table sees no changes (or sees that all the changes have cancelled each other out), and doesn't re-render.
In the case where you add the pause, then a rendering frame (or two) occurs between the removal of the item and its re-addition. Consequently, the table actually renders one or two frames without the item, and when it is added back in, it adds it back and renders the current value. (You might, possibly, be able to make the behavior unpredictable, by pausing for 16 or 17 milliseconds, which is right on the cusp of the time for one rendering frame.)
It's not clear what you really intend to do. If you are trying to persuade the table to update without using JavaFX properties, you can do
list.get(0).setName("Bye");
table.refresh();
though this is not a very satisfactory solution.
Note too that
list.remove(0);
list.add(0, new Person("Bye"));
will also work (since now the added element is not the same as the removed element).
The better approach is to implement your model class with JavaFX properties:
public static class Person
{
private final StringProperty name = new SimpleStringProperty("");
public Person(String n)
{
setName(n);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName()
{
return nameProperty().get();
}
public final void setName(String n)
{
nameProperty().set(n);
}
}
and then simply calling
list.get(0).setName("Bye");
will update the table (because the cell will be observing the property).

validation data in jtable

I have a JTable, and I want validate data in the first column. When the user type the entry in any cell in the first column and click in another cell (focus lost), I want show message that the entry is false, and focus again in the cell until the entry is valid.
First I thought that the cell is like the JTextFiled, so I have tried the method addFocusListener(...) but it doesn't work!
table.getValueAt(0, 0).addFocusListener(
new FocusListener() {
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
for (int n = 0; n <= table.getValueAt(0, 0).toString().length(); n++) {
if (Character.isDigit(table.getValueAt(0, 0).toString().charAt(n)) == false) {
JOptionPane.showMessageDialog(null,
"Error: code is a number !", "Error Message",
JOptionPane.ERROR_MESSAGE);
break;
} else
break;
}
}
}
}
});
creation of JTable:
String [][]data={ {"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"} };
String[] header = {"Code Projet", "Description", "Duree", "Taches anterieurs"};
table = new JTable(data, header);
The TableCellEditor that is added to a JTable will do any validation on the values that are entered into a JTable.
From the Java Table Tutorial:
The automatic checking of user-entered strings occurs when the default
editor attempts to create a new instance of the class associated with
the cell's column. The default editor creates this instance using a
constructor that takes a String as an argument. For example, in a
column whose cells have type Integer, when the user types in "123" the
default editor creates the corresponding Integer using code equivalent
to new Integer("123"). If the constructor throws an exception, the
cell's outline turns red and refuses to let focus move out of the
cell. If you implement a class used as a column data type, you can use
the default editor if your class supplies a constructor that takes a
single argument of type String.
So, if you want to just make sure the user enters in an Integer (which is what it looks like from your code), you can set the column type to a type of Integer .
table.setDefaultEditor(Integer.class,
new IntegerEditor(0, 100));
Now the DefaultCellEditor will do the checking for you, and throw an exception if the type is not correct. You will just have to make sure you catch the exception and deal with it properly.
I think tutorial I linked above on creating tables would be very helpful for you, especially the part I linked to which talks about validating user text.

Null error when attempting to add custom row to CellTable in gwt

I have a Cell Table that I am using to output some search results. The cell table uses a list data provider to update info. I want to separate different sections so I am attempting to add a custom row in between different sections that has one cell that spans all of the columns. I am extending AbstractCellTableBuilder to do this, but my issue comes when I use TableRowBuilder and startRow(), calling startRow() returns a null pointer exception, to AbstractCellTableBuilder.java:243, which refers to tbody. So this is leading me to believe that my cell table is not getting passed into AbstractCellTableBuilder properly. My understanding of gwt and java is pretty basic, so I might just not be understanding how exactly this is supposed to work, and the showcase example is pretty complicated for me to understand. If anyone can tell where I'm messing up or has any simpler examples of this that might help me I would appreciate it!
I had found a similar answer and tried to implement it, and that is how I came up with what I have, but it answer wasn't quite detailed enough for me to fully understand how it works. Here is what I referenced:
Building a custom row on demand with GWT CellTableBuilder
EDITED:
Basic format of how I add normal rows to the cell table
searchProvider = new ListDataProvider<SearchColumn>();
cellTable_2 = new CellTable<SearchColumn>();
//Add columns to the cellTable
searchProvider.addDataDisplay(cellTable_2);
//What I call when adding a row to the cellTable using the ListDataProvider
searchProvider.getList().add(new SearchColumn("",label,"","","","","","",""));
Adding the CustomCellTableBuilder to the cell table:
//Passing the CustomCellTableBuilder to the cell table
CustomCellTableBuilder buildRow = new CustomCellTableBuilder();
cellTable_2.setTableBuilder(buildRow);
The CustomCellTableBuilder for adding custom rows:
public class CustomCellTableBuilder extends AbstractCellTableBuilder<SearchColumn>{
public CustomCellTableBuilder() {
super(cellTable_2);
}
#Override
protected void buildRowImpl(SearchColumn rowValue, int absRowIndex){
//building main rows logic
if (labelrow == 1){
System.out.println("Going to build extra row if");
buildExtraRow(absRowIndex, rowValue);
}
else {
System.out.println("Getting into normal buildRow");
buildRow(rowValue,absRowIndex);
}
}
private void buildExtraRow(int absRowIndex, SearchColumn rowValue){
start(true);
TableRowBuilder row = startRow();
TableCellBuilder td = row.startTD().colSpan(getColumns().size());
td.text("Testing this out").endTD();
row.endTR();
}}
I think you should call start(true) before calling startRow() because tbody is initialized to null. Start() call will initialize tbody to HtmlBuilderFactory.get().createTBodyBuilder().
The source doesn't lie.
Just like that:
private void buildExtraRow(int absRowIndex, SearchColumn rowValue) {
start(true); // true makes builder to rebuild all rows
TableRowBuilder row = startRow();
// whatever
}

SwingX JXTable: use ColorHighlighter to color rows based on a "row object"

I'm using JXTable and I know how to do this based on DefaultRenderers for JTable, but I want to know how to do it in a way that's JXTable-friendly based on HighlighterPipeline.
I have a list of objects displayed in a table, and each row represents one object. I would like to color the rows displaying objects of a certain type a different color.
It looks like I should be using ColorHighlighter. But I can't find examples for this, other than the simple highlighters like "color every other row" or some such thing.
I need the row number since there's no such thing as a "row object" in the JTable/TableModel paradigm, but if I can do that, I can easily test a predicate and return true/false to tell the highlighter to kick in or not.
Can someone help me figure out the right direction to get this to work?
never mind, I figured it out. It was just hard to figure out the way to use ComponentAdapter propertly.
JXTable table = ...
final List<Item> itemList = ...
final HighlightPredicate myPredicate = new HighlightPredicate() {
#Override
public boolean isHighlighted(
Component renderer,
ComponentAdapter adapter) {
Item item = itemList.get(adapter.row);
return testItem(item);
}
public boolean testItem(Item item) { ... }
}
ColorHighlighter highlighter = new ColorHighlighter(
myPredicate,
Color.RED, // background color
null); // no change in foreground color
table.addHighlighter(highlighter);

CheckboxCellEditor shows text and not a check box

I'm using the following
org.eclipse.jface.viewers.CheckboxCellEditor.CheckboxCellEditor(Composite parent)
I'm creating a table viewer with cellEditors and doing the following
CellEditor[] editors = new CellEditor[columnNames.length];
editors[7] = new CheckboxCellEditor(table);
I have a CellModifier that has the following
public Object getValue(Object element, String property) {
Object result = null;
...
result = Boolean.valueOf(task.isDfRequested());
return result;
}
public void modify(Object element, String property, Object value) {
item.isSelected(((Boolean)value).booleanValue());
}
Finally I have a LabelProvider that has the following
public String getColumnText(Object element, int columnIndex) {
String result = "";
try {
result = Boolean.toString(item.isSelected());
} catch (Exception ex) { }
break;
However, in my UI instead of having a check box I have the word true or false && clicking it results in switching state to false or true. Any ideas on why I don't have a checkbox??
I've searched in the source code of CheckboxCellEditor class and in the constructor the control associated to the CellEditor is created in the createControl(Composite parent) method. This method is abstract in CellEditor class and it's implemented like this in CheckboxCellEditor:
protected Control createControl(Composite parent) {
return null;
}
So a control is not created, that's why you don't see the checkbox. In the documentation of the Class you can read:
Note that this implementation simply
fakes it and does does not create any
new controls. The mere activation of
this editor means that the value of
the check box is being toggled by the
end users; the listener method
applyEditorValue is immediately called
to signal the change.
I solved this using a ComboBoxCellEditor with yes and no items.
Regards.
Well, I have no idea how SWT works or what component you are even talking about.
But I do know that when using Swing you can have custom editors for a column in a JTable. If you don't tell the table the class of data for the column then the toString() method of the data is invoked. But if you tell the table that Boolean data is displayed in the column then the table will use the check box editor.
Sounds like a similiar symptom, but I don't know your particular solution.
What I've decided to do is to just implement a dirty hack others have been using.
Create two images of check boxes, one checked the other not checked. Switch the state between the two based on the boolean.
It's not perfect, but for now it gets the job done

Categories

Resources