org.jdesktop.swingbinding.JTableBinding Maven / Gradle / Ivy
/*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.swingbinding;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import org.jdesktop.beansbinding.BindingListener;
import org.jdesktop.beansbinding.Binding.*;
import org.jdesktop.beansbinding.AutoBinding;
import org.jdesktop.beansbinding.Property;
import org.jdesktop.beansbinding.PropertyStateEvent;
import org.jdesktop.beansbinding.PropertyStateListener;
import org.jdesktop.swingbinding.impl.AbstractColumnBinding;
import org.jdesktop.swingbinding.impl.ListBindingManager;
import static org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.*;
/**
* Binds a {@code List} of objects to act as the rows of a {@code JTable}.
* Each object in the source {@code List} represents one row in the {@code JTable}.
* Mappings from properties of the source objects to columns are created by
* adding {@link org.jdesktop.swingbinding.JTableBinding.ColumnBinding ColumnBindings}
* to a {@code JTableBinding}. Instances of {@code JTableBinding} are obtained by
* calling one of the {@code createJTableBinding} methods in the {@code SwingBindings}
* class.
*
* Here is an example of creating a binding from a {@code List} of {@code Person}
* objects to a {@code JTable}:
*
*
* // create the person List
* List people = createPersonList();
*
* // create the binding from List to JTable
* JTableBinding tb = SwingBindings.createJTableBinding(READ, people, jTable);
*
* // define the properties to be used for the columns
* BeanProperty firstNameP = BeanProperty.create("firstName");
* BeanProperty lastNameP = BeanProperty.create("lastName");
* BeanProperty ageP = BeanProperty.create("age");
*
* // configure how the properties map to columns
* tb.addColumnBinding(firstNameP).setColumnName("First Name");
* tb.addColumnBinding(lastNameP).setColumnName("Last Name");
* tb.addColumnBinding(ageP).setColumnName("Age").setColumnClass(Integer.class);
*
* // realize the binding
* tb.bind();
*
*
* The {@code JTable} target of a {@code JTableBinding} acts as a live view of
* the objects in the source {@code List},
* regardless of the update strategy (the meaning of the update strategy is
* clarified later in this document). {@code JTableBinding}
* listens to the properties specified for the {@code ColumnBindings},
* for all objects in the {@code List}, and updates the values
* displayed in the {@code JTable} in response to change. All successful
* edits made to {@code JTable} cell values are immediately committed back to
* corresponding objects in the source {@code List}. If the {@code List} is an
* instance of {@code ObservableList}, then changes to the {@code List} contents
* (such as adding, removing or replacing an object) are also reflected in the
* {@code JTable}. Important: Changing the contents of a non-observable
* {@code List} while it is participating in a {@code JTableBinding} is unsupported,
* resulting in undefined behavior and possible exceptions.
*
* A cell in the {@code JTable} is editable for any given row and
* column when all of the following are true: the property specified for that column
* by its {@code ColumnBinding} is writeable for the object representing that row,
* the {@code "editable"} property of the {@code JTableBinding} is {@code true}
* (the default), and the {@code "editable"} property of the {@code ColumnBinding}
* is {@code true} (the default).
*
* {@code JTableBinding} requires
* extra clarification on the operation of the
* {@code refresh} and {@code save} methods and the meaning of the update
* strategy. The target property of a {@code JTableBinding} is not the
* target {@code JTable} property provided in the constructor, but rather a
* private synthetic property representing the {@code List} of objects to show
* in the target {@code JTable}. This synthetic property is readable/writeable
* only when the {@code JTableBinding} is bound and the target {@code JTable}
* property is readable with a {@code non-null} value.
*
* It is this private synthetic property on which the {@code refresh} and
* {@code save} methods operate; meaning that these methods simply cause syncing
* between the value of the source {@code List} property and the value of the
* synthetic target property (representing the {@code List} to be shown in the
* target {@code JTable}). These methods do not, therefore, have anything to do
* with refreshing or saving values in the {@code JTable}. Likewise, the update
* strategy, which simply controls when {@code refresh} and {@code save} are
* automatically called, also has nothing to do with refreshing or saving
* values in the {@code JTable}.
*
* Note: At the current time, the {@code READ_WRITE} update strategy
* is not useful for {@code JTableBinding}. To prevent unwanted confusion,
* {@code READ_WRITE} is translated to {@code READ} by {@code JTableBinding's}
* constructor.
*
* {@code JTableBinding} works by installing a custom model on the target
* {@code JTable}, as appropriate, to represent the source {@code List}. The
* model is installed on a target {@code JTable} with the first succesful call
* to {@code refresh} with that {@code JTable} as the target. Subsequent calls
* to {@code refresh} update the elements in this already-installed model.
* The model is uninstalled from a target {@code JTable} when either the
* {@code JTableBinding} is unbound or when the target {@code JTable} property
* changes to no longer represent that {@code JTable}. Note: When the model is
* uninstalled from a {@code JTable}, it is replaced with a {@code DefaultTableModel},
* in order to leave the {@code JTable} functional.
*
* Some of the above is easier to understand with an example. Let's consider
* a {@code JTableBinding} ({@code binding}), with update strategy
* {@code READ}, between a property representing a {@code List} ({@code listP})
* and a property representing a {@code JTable} ({@code jTableP}). {@code listP}
* and {@code jTableP} both start off readable, referring to a {@code non-null}
* {@code List} and {@code non-null} {@code JTable} respectively. Let's look at
* what happens for each of a sequence of events:
*
*
* Sequence Event Result
*
* 1
* explicit call to {@code binding.bind()}
*
* - synthetic target property becomes readable/writeable
*
* - {@code refresh()} is called
*
* - model is installed on target {@code JTable}, representing list of objects
*
*
*
* 2
* {@code listP} changes to a new {@code List}
*
* - {@code refresh()} is called
*
* - model is updated with new list of objects
*
*
*
* 3
* {@code jTableP} changes to a new {@code JTable}
*
* - model is uninstalled from old {@code JTable}
*
*
*
* 4
* explicit call to {@code binding.refresh()}
*
* - model is installed on target {@code JTable}, representing list of objects
*
*
*
* 5
* {@code listP} changes to a new {@code List}
*
* - {@code refresh()} is called
*
* - model is updated with new list of objects
*
*
*
* 6
* explicit call to {@code binding.unbind()}
*
* - model is uninstalled from target {@code JTable}
*
*
*
*
* Notice that in step 3, when the value
* of the {@code JTable} property changed, the new {@code JTable} did not
* automatically get the model with the elements applied to it. A change to the
* target value should not cause an {@code AutoBinding} to sync the target from
* the source. Step 4 forces a sync by explicitly calling {@code refresh}.
* Alternatively, it could be caused by any other action that results
* in a {@code refresh} (for example, the source property changing value, or an
* explicit call to {@code unbind} followed by {@code bind}).
*
* {@code ColumnBindings} are managed by the {@code JTableBinding}. They are not
* to be explicitly bound, unbound, added to a {@code BindingGroup}, or accessed
* in a way that is not allowed for a managed binding. {@code BindingListeners}
* added to a {@code ColumnBinding} are notified at the time an edited {@code JTable} value
* is to be committed back to the source {@code List}. They receive notification of either
* {@code synced} or {@code syncFailed}. {@code BindingListeners} added to the
* {@code JTableBinding} itself are also notified of {@code sync} and {@code syncFailed}
* for the {@code JTableBinding's ColumnBindings}.
*
* In addition to binding the elements of a {@code JTable}, it is possible to
* bind to the selection of a {@code JTable}. When binding to the selection of a {@code JTable}
* backed by a {@code JTableBinding}, the selection is always in terms of elements
* from the source {@code List}. See the list of
* interesting swing properties in the package summary for more details.
*
* @param the type of elements in the source {@code List}
* @param the type of source object (on which the source property resolves to {@code List})
* @param the type of target object (on which the target property resolves to {@code JTable})
*
* @author Shannon Hickey
*/
public final class JTableBinding extends AutoBinding, TS, List> {
private Property tableP;
private ElementsProperty elementsP;
private Handler handler = new Handler();
private JTable table;
private BindingTableModel model;
private boolean editable = true;
private List columnBindings = new ArrayList();
/**
* Constructs an instance of {@code JTableBinding}.
*
* @param strategy the update strategy
* @param sourceObject the source object
* @param sourceListProperty a property on the source object that resolves to the {@code List} of elements
* @param targetObject the target object
* @param targetJTableProperty a property on the target object that resolves to a {@code JTable}
* @param name a name for the {@code JTableBinding}
* @throws IllegalArgumentException if the source property or target property is {@code null}
*/
protected JTableBinding(UpdateStrategy strategy, SS sourceObject, Property> sourceListProperty, TS targetObject, Property targetJTableProperty, String name) {
super(strategy == READ_WRITE ? READ : strategy,
sourceObject, sourceListProperty, targetObject, new ElementsProperty(), name);
if (targetJTableProperty == null) {
throw new IllegalArgumentException("target JTable property can't be null");
}
tableP = targetJTableProperty;
elementsP = (ElementsProperty)getTargetProperty();
}
protected void bindImpl() {
elementsP.setAccessible(isTableAccessible());
tableP.addPropertyStateListener(getTargetObject(), handler);
elementsP.addPropertyStateListener(null, handler);
super.bindImpl();
}
protected void unbindImpl() {
elementsP.removePropertyStateListener(null, handler);
tableP.removePropertyStateListener(getTargetObject(), handler);
elementsP.setAccessible(false);
cleanupForLast();
super.unbindImpl();
}
private boolean isTableAccessible() {
return tableP.isReadable(getTargetObject()) && tableP.getValue(getTargetObject()) != null;
}
private boolean isTableAccessible(Object value) {
return value != null && value != PropertyStateEvent.UNREADABLE;
}
private void cleanupForLast() {
if (table == null) {
return;
}
table.setModel(new DefaultTableModel());
table = null;
model.setElements(null, true);
model = null;
}
/**
* Sets whether or not the cells of the table should be editable.
* The default for this property is {@code true}.
* See this paragraph in the class level
* documentation on editability.
*
* @param editable whether or not the cells of the table should be editable
*/
public void setEditable(boolean editable) {
this.editable = editable;
}
/**
* Returns whether or not the cells of the table should be editable.
* The default for this property is {@code true}.
* See this paragraph in the class level
* documentation on editability.
*
* @return whether or not the cells of the table should be editable
*/
public boolean isEditable() {
return editable;
}
/**
* Creates a {@code ColumnBinding} and adds it to the end of the list of {@code ColumnBindings}
* maintained by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param columnProperty the property with which to derive cell values from the
* elements of the source {@code List}
* @return the {@code ColumnBinding}
* @throws IllegalArgumentException if {@code columnProperty} is {@code null}
* @see org.jdesktop.swingbinding.JTableBinding.ColumnBinding
*/
public ColumnBinding addColumnBinding(Property columnProperty) {
return addColumnBinding(columnProperty, null);
}
/**
* Creates a named {@code ColumnBinding} and adds it to the end of the list of {@code ColumnBindings}
* maintained by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param columnProperty the property with which to derive cell values from the
* elements of the source {@code List}
* @param name a name for the column binding
* @return the {@code ColumnBinding}
* @throws IllegalArgumentException if {@code columnProperty} is {@code null}
* @see org.jdesktop.swingbinding.JTableBinding.ColumnBinding
*/
public ColumnBinding addColumnBinding(Property columnProperty, String name) {
throwIfBound();
if (columnProperty == null) {
throw new IllegalArgumentException("can't have null column property");
}
if (name == null && JTableBinding.this.getName() != null) {
name = JTableBinding.this.getName() + ".COLUMN_BINDING";
}
ColumnBinding binding = new ColumnBinding(columnBindings.size(), columnProperty, name);
columnBindings.add(binding);
return binding;
}
/**
* Creates a {@code ColumnBinding} and inserts it at the given index into the list
* of {@code ColumnBindings} maintained by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param index the index at which to insert the {@code ColumnBinding}
* @param columnProperty the property with which to derive cell values from the
* elements of the source {@code List}
* @return the {@code ColumnBinding}
* @throws IllegalArgumentException if {@code columnProperty} is {@code null}
* @see org.jdesktop.swingbinding.JTableBinding.ColumnBinding
*/
public ColumnBinding addColumnBinding(int index, Property columnProperty) {
return addColumnBinding(index, columnProperty, null);
}
/**
* Creates a {@code ColumnBinding} and inserts it at the given index into the list
* of {@code ColumnBindings} maintained by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param index the index at which to insert the {@code ColumnBinding}
* @param columnProperty the property with which to derive cell values from the
* elements of the source {@code List}
* @param name a name for the {@code ColumnBinding}
* @return the {@code ColumnBinding}
* @throws IllegalArgumentException if {@code columnProperty} is {@code null}
* @see org.jdesktop.swingbinding.JTableBinding.ColumnBinding
*/
public ColumnBinding addColumnBinding(int index, Property columnProperty, String name) {
throwIfBound();
if (columnProperty == null) {
throw new IllegalArgumentException("can't have null column property");
}
if (name == null && JTableBinding.this.getName() != null) {
name = JTableBinding.this.getName() + ".COLUMN_BINDING";
}
ColumnBinding binding = new ColumnBinding(index, columnProperty, name);
columnBindings.add(index, binding);
adjustIndices(index + 1, true);
return binding;
}
/**
* Removes the given {@code ColumnBinding} from the list maintained
* by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param binding the {@code ColumnBinding} to remove
* @see #addColumnBinding(Property, String)
*/
public boolean removeColumnBinding(ColumnBinding binding) {
throwIfBound();
boolean retVal = columnBindings.remove(binding);
if (retVal) {
adjustIndices(binding.getColumn(), false);
}
return retVal;
}
/**
* Removes the {@code ColumnBinding} with the given index from the list maintained
* by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param index the index of the {@code ColumnBinding} to remove
* @see #addColumnBinding(Property, String)
*/
public ColumnBinding removeColumnBinding(int index) {
throwIfBound();
ColumnBinding retVal = columnBindings.remove(index);
if (retVal != null) {
adjustIndices(index, false);
}
return retVal;
}
/**
* Returns the {@code ColumnBinding} with the given index in the list maintained
* by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @param index the index of the {@code ColumnBinding} to return
* @return the {@code ColumnBinding} at the given index
* @see #addColumnBinding(Property, String)
*/
public ColumnBinding getColumnBinding(int index) {
return columnBindings.get(index);
}
/**
* Returns an unmodifiable copy of the list of {@code ColumnBindings} maintained
* by this {@code JTableBinding}.
*
* The list of {@code ColumnBindings} dictates the columns to be displayed in the
* {@code JTable}, with a {@code ColumnBinding's} order in the list determining its
* table model index.
*
* @return the list of {@code ColumnBindings}
* @see #addColumnBinding(Property, String)
*/
public List getColumnBindings() {
return Collections.unmodifiableList(columnBindings);
}
private void adjustIndices(int start, boolean up) {
int size = columnBindings.size();
for (int i = start; i < size; i++) {
ColumnBinding cb = columnBindings.get(i);
cb.adjustColumn(cb.getColumn() + (up ? 1 : -1));
}
}
private final class ColumnProperty extends Property {
private ColumnBinding binding;
public Class extends Object> getWriteType(Object source) {
return binding.columnClass == null ? Object.class : binding.columnClass;
}
public Object getValue(Object source) {
if (binding.isBound()) {
return binding.editingObject;
}
throw new UnsupportedOperationException();
}
public void setValue(Object source, Object value) {
throw new UnsupportedOperationException();
}
public boolean isReadable(Object source) {
return binding.isBound();
}
public boolean isWriteable(Object source) {
return true;
}
public void addPropertyStateListener(Object source, PropertyStateListener listener) {
}
public void removePropertyStateListener(Object source, PropertyStateListener listener) {
}
public PropertyStateListener[] getPropertyStateListeners(Object source) {
return new PropertyStateListener[0];
}
}
/**
* {@code ColumnBinding} represents a binding between a property of the elements
* in the {@code JTableBinding's} source {@code List}, and a column in the table. Each
* {@code ColumnBinding} added to a {@code JTableBinding} represents a column
* to be displayed by the {@code JTable}. A value for any given row in a column
* is aquired by fetching the value of the associated {@code ColumnBinding's}
* source property for the element in the source {@code List} representing that row.
*
* A {@code Converter} may be specified on a {@code ColumnBinding}, as may be
* a {@code Validator}. Validation occurs at the time a cell value is to be
* committed back to the source {@code List}.
*
* {@code BindingListeners} registered on
* a {@code ColumnBinding} are notified of successful {@code sync} or
* {@code syncFailure}. These notifications are also sent to the
* {@code JTableBinding's} {@code BindingListeners}.
*
* {@code ColumnBindings} are managed by their {@code JTableBinding}. They are not
* to be explicitly bound, unbound, added to a {@code BindingGroup}, or accessed
* in a way that is not allowed for a managed binding.
*
* @see org.jdesktop.swingbinding.JTableBinding#addColumnBinding(Property, String)
*/
public final class ColumnBinding extends AbstractColumnBinding {
private Class> columnClass;
private boolean editable = true;
private boolean editableSet;
private String columnName;
private Object editingObject;
private ColumnBinding(int column, Property columnProperty, String name) {
super(column, columnProperty, new ColumnProperty(), name);
((ColumnProperty) getTargetProperty()).binding = this;
}
private void setEditingObject(Object editingObject) {
this.editingObject = editingObject;
}
private void adjustColumn(int newCol) {
setColumn(newCol);
}
/**
* Sets a name for the column represented by this {@code ColumnBinding}.
* This is used to initialize the table's column header name. If
* {@code null} is specified, the {@code toString()} value of the
* {@code ColumnBinding's} source property is used.
*
* @param name the name
* @return the {@code ColumnBinding} itself, to allow for method chaining
* @see javax.swing.table.TableModel#getColumnName
*/
public ColumnBinding setColumnName(String name) {
JTableBinding.this.throwIfBound();
this.columnName = name;
return this;
}
/**
* Sets the column class to be used by {@code JTable} to determine
* the renderer and editor for the column represented by this
* {@code ColumnBinding}.
*
* @param columnClass the column class
* @return the {@code ColumnBinding} itself, to allow for method chaining
* @see javax.swing.table.TableModel#getColumnClass
*/
public ColumnBinding setColumnClass(Class> columnClass) {
JTableBinding.this.throwIfBound();
this.columnClass = columnClass;
return this;
}
/**
* Returns the column class to be used by {@code JTable} to determine
* the renderer and editor for the column represented by this
* {@code ColumnBinding}.
*
* @see #setColumnClass
* @see javax.swing.table.TableModel#getColumnClass
*/
public Class> getColumnClass() {
return columnClass == null ? Object.class : columnClass;
}
/**
* Returns the name for the column represented by this {@code ColumnBinding}.
* This is used to initialize the table's column header name. If no name
* has been specified, or if it has been set to {@code null}, the
* {@code toString()} value of the {@code ColumnBinding's} source property is returned.
*
* @return the name for the column
* @see #setColumnName
* @see javax.swing.table.TableModel#getColumnName
*/
public String getColumnName() {
return columnName == null ? getSourceProperty().toString() : columnName;
}
/**
* Sets whether or not the cells of the column should be editable.
* The default for this property is {@code true}.
* See this paragraph in the class level
* documentation on editability.
*
* @param editable whether or not the cells of the column should be editable
* @return the {@code ColumnBinding} itself, to allow for method chaining
*/
public ColumnBinding setEditable(boolean editable) {
this.editable = editable;
return this;
}
/**
* Returns whether or not the cells of the column should be editable.
* The default for this property is {@code true}.
* See this paragraph in the class level
* documentation on editability.
*
* @return whether or not the cells of the column should be editable
*/
public boolean isEditable() {
return editable;
}
private void bindUnmanaged0() {
bindUnmanaged();
}
private void unbindUnmanaged0() {
unbindUnmanaged();
}
private SyncFailure saveUnmanaged0() {
return saveUnmanaged();
}
private void setSourceObjectUnmanaged0(Object source) {
setSourceObjectUnmanaged(source);
}
}
private class Handler implements PropertyStateListener {
public void propertyStateChanged(PropertyStateEvent pse) {
if (!pse.getValueChanged()) {
return;
}
if (pse.getSourceProperty() == tableP) {
cleanupForLast();
boolean wasAccessible = isTableAccessible(pse.getOldValue());
boolean isAccessible = isTableAccessible(pse.getNewValue());
if (wasAccessible != isAccessible) {
elementsP.setAccessible(isAccessible);
} else if (elementsP.isAccessible()) {
elementsP.setValueAndIgnore(null, null);
}
} else {
if (((ElementsProperty.ElementsPropertyStateEvent)pse).shouldIgnore()) {
return;
}
if (table == null) {
table = tableP.getValue(getTargetObject());
model = new BindingTableModel();
table.setModel(model);
}
model.setElements((List)pse.getNewValue(), true);
}
}
}
private final class BindingTableModel extends ListBindingManager implements TableModel {
private final List listeners;
public BindingTableModel() {
listeners = new CopyOnWriteArrayList();
}
protected AbstractColumnBinding[] getColBindings() {
AbstractColumnBinding[] bindings = new AbstractColumnBinding[getColumnBindings().size()];
bindings = getColumnBindings().toArray(bindings);
return bindings;
}
public int getRowCount() {
return size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
return valueAt(rowIndex, columnIndex);
}
public void setValueAt(Object value, int rowIndex, int columnIndex) {
ColumnBinding cb = JTableBinding.this.getColumnBinding(columnIndex);
BindingListener[] cbListeners = cb.getBindingListeners();
BindingListener[] tbListeners = getBindingListeners();
cb.setSourceObjectUnmanaged0(this.getElement(rowIndex));
cb.setEditingObject(value);
cb.bindUnmanaged0();
for (BindingListener listener : tbListeners) {
listener.bindingBecameBound(cb);
}
PropertyStateEvent pse = new PropertyStateEvent(cb.getTargetProperty(),
cb.getTargetObject(),
true,
getValueAt(rowIndex, columnIndex),
value,
false,
cb.getSourceProperty().isWriteable(cb.getSourceObject()));
for (BindingListener listener : cbListeners) {
listener.targetChanged(cb, pse);
}
for (BindingListener listener : tbListeners) {
listener.targetChanged(cb, pse);
}
SyncFailure failure = cb.saveUnmanaged0();
if (failure == null) {
for (BindingListener listener : cbListeners) {
listener.synced(cb);
}
for (BindingListener listener : tbListeners) {
listener.synced(cb);
}
} else {
for (BindingListener listener : cbListeners) {
listener.syncFailed(cb, failure);
}
for (BindingListener listener : tbListeners) {
listener.syncFailed(cb, failure);
}
}
cb.unbindUnmanaged0();
for (BindingListener listener : tbListeners) {
listener.bindingBecameUnbound(cb);
}
cb.setEditingObject(null);
cb.setSourceObjectUnmanaged0(null);
}
public Class> getColumnClass(int columnIndex) {
Class> klass = JTableBinding.this.getColumnBinding(columnIndex).getColumnClass();
return klass == null ? Object.class : klass;
}
protected void allChanged() {
fireTableModelEvent(new TableModelEvent(this, 0, Integer.MAX_VALUE));
}
protected void valueChanged(int row, int column) {
fireTableModelEvent(new TableModelEvent(this, row, row, column));
}
protected void added(int row, int length) {
assert length > 0; // enforced by ListBindingManager
fireTableModelEvent(new TableModelEvent(this, row, row + length - 1, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
}
protected void removed(int row, int length) {
assert length > 0; // enforced by ListBindingManager
fireTableModelEvent(new TableModelEvent(this, row, row + length - 1, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
}
protected void changed(int row) {
fireTableModelEvent(new TableModelEvent(this, row, row, TableModelEvent.ALL_COLUMNS));
}
public String getColumnName(int columnIndex) {
ColumnBinding binding = JTableBinding.this.getColumnBinding(columnIndex);
return binding.getColumnName() == null ? binding.getSourceProperty().toString() : binding.getColumnName();
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (!JTableBinding.this.isEditable()) {
return false;
}
ColumnBinding binding = JTableBinding.this.getColumnBinding(columnIndex);
if (!binding.isEditable()) {
return false;
}
return binding.getSourceProperty().isWriteable(getElement(rowIndex));
}
public void addTableModelListener(TableModelListener l) {
listeners.add(l);
}
public void removeTableModelListener(TableModelListener l) {
listeners.remove(l);
}
private void fireTableModelEvent(TableModelEvent e) {
for (TableModelListener listener : listeners) {
listener.tableChanged(e);
}
}
public int getColumnCount() {
return columnCount();
}
}
}