DataTable.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:38k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: DataTable.java,v 1.16 2005/10/15 11:43:20 pdoubleya Exp $
  3.  *
  4.  * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.dataset;
  22. import java.beans.PropertyChangeEvent;
  23. import java.beans.PropertyChangeListener;
  24. import java.beans.PropertyChangeSupport;
  25. import java.util.ArrayList;
  26. import java.util.Collections;
  27. import java.util.Comparator;
  28. import java.util.HashMap;
  29. import java.util.LinkedHashMap;
  30. import java.util.List;
  31. import java.util.Map;
  32. import java.util.logging.Logger;
  33. import javax.swing.event.EventListenerList;
  34. import org.jdesktop.dataset.event.DataTableListener;
  35. import org.jdesktop.dataset.event.RowChangeEvent;
  36. import org.jdesktop.dataset.event.TableChangeEvent;
  37. import static org.jdesktop.dataset.event.TableChangeEvent.EventType.*;
  38. /**
  39.  * A <CODE>DataTable</CODE> represents a two-dimensional non-visual data structure composed of {@link DataRow DataRows}, where each row has
  40.  * one or more {@link DataColumn DataColumns}; the set of columns is specified in the table, and applies to all rows. A
  41.  * <CODE>DataTable</CODE> always belongs to a {@link DataSet}, and in fact must be created using the {@link DataSet#createTable()}
  42.  * method, as a table cannot exist outside of a <CODE>DataSet</CODE>. For an overview of the DataSet API, see
  43.  * {@link https://jdnc.dev.java.net/documentation/dataset}.
  44.  *
  45.  * Table data is loaded either programmatically (by your code, one row and one value at a time) or by a {@link DataProvider}. A
  46.  * table can have a single <CODE>DataProvider</CODE>, which is responsible for loading the table from, and storing table changes back
  47.  * to, some data source. See documentation for <CODE>DataProvider</CODE> and its subclasses to see what sources are supported.
  48.  *
  49.  * A table has a name property, which is unique within the
  50.  * <CODE>DataSet</CODE> that owns it. Names can be specified when the table is created, or will be assigned automatically.
  51.  *
  52.  * A table must have columns assigned to it in order to store data in any row. The intersection of a column and row is called a
  53.  * cell. Columns are instances of <CODE>DataColumn</CODE>, and each
  54.  * has a name unique within the table. Columns add added using {@link #createColumn()}, {@link #createColumn(String)} or
  55.  * {@link #createColumns(String...)} and are attached to the table from which they are created. As a rule, any methods that
  56.  * are column-specific (like {@link #setValue(DataColumn, Object)} can take either the <CODE>DataColumn</CODE> instance or the name
  57.  * of the <CODE>DataColumn</CODE> as a parameter. Columns can be removed from the table using {@link #dropColumn(String)}. Adding
  58.  * a column to a table with rows in it will give the cells for the new column the column's {@link DataColumn#getDefaultValue() default value}.
  59.  * Dropping a column from a table will cause the data in the cells of that column to be lost.
  60.  *
  61.  * A table has 0...n rows at any time. Blank rows are added using {@link #appendRow()} and {@link #appendRowNoEvent()} and remain
  62.  * in the table in the order they were added to it, so can be retrieved by a 0-based index using {@link getRow(int)}. New rows start with
  63.  * the current set of columns in the table, and adding or dropping columns from a table will have those automatically added or dropped
  64.  * from all rows in the table. To remove a row, use {@link #deleteRow(int)} or {@link #deleteRow(DataRow)} if you want the table's
  65.  * DataProvider to remove the row as well from the table's data source. Deleted rows are maked with a row status of DELETED and
  66.  * remain in memory until the DataProvider removes them. To drop a row completely, use {@link #discardRow(int)} or {@link #discardRow(DataRow)}.
  67.  * Discarding rows causes them to be dropped immediately from the table, and the deletion will not be recorded by the table's DataProvider.
  68.  *
  69.  * Once you have rows in the table, you can change values in the row using {@link #setValue(int, String, Object)}, or through the
  70.  * equivalent methods on the {@link DataRow} itself. You can retrieve row values using {@link #getValue(int, String)} or equivalent
  71.  * methods on the row. Rows have a certain status within the table, as described in the documentation for the {@link DataRow} class. To list
  72.  * rows in the table, use {@link getRows()} or {@link getRow(int)}.
  73.  * @author Richard Bair
  74.  * @author Patrick Wright
  75.  */
  76. public class DataTable {
  77.     /**
  78.      * The Logger
  79.      */
  80.     private static final Logger LOG = Logger.getLogger(DataTable.class.getName());
  81.     
  82.     //protected for testing
  83.     /** Used as a prefix for auto-generated names. */
  84.     protected static final String DEFAULT_NAME_PREFIX = "DataTable";
  85.     
  86.     /** The shared instance of the NameGenerator for DataTables not assigned a name. */
  87.     private static final NameGenerator NAMEGEN = new NameGenerator(DEFAULT_NAME_PREFIX);
  88.     
  89.     //used for communicating changes to this JavaBean, especially necessary for
  90.     //IDE tools, but also handy for any other component listening to this table
  91.     /**
  92.      * Used for communicating changes to this JavaBean, especially necessary for IDE tools, but also handy for any other component listening to this table.
  93.      */
  94.     private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
  95.     
  96.     /**
  97.      * A reference to the DataSet to which this DataTable belongs
  98.      */
  99.     private DataSet dataSet;
  100.     
  101.     /**
  102.      * The DataProvider that is used to manage this DataTables data. It is
  103.      * technically possible for a DataProvider to populate a DataTable to which
  104.      * it is not associated, but the results are undefined
  105.      */
  106.     private DataProvider dataProvider;
  107.     
  108.     /**
  109.      * The name of this DataTable
  110.      */
  111.     private String name;
  112.     
  113.     /**
  114.      * Mapping of DataColumn.name to DataColumn. Extremely useful for
  115.      * discovering the column object associated with a name. The resolver code
  116.      * responsible for locating a particular data cell will first convert the
  117.      * column name to a DataColumn, and then use the DataColumn in the hash for
  118.      * locating the data. This is done so that:
  119.      * <ol>
  120.      * <li>The lookup is constant time (or close to it)</li>
  121.      * <li>Avoid looping through a columns list looking for the column</li>
  122.      * <li>More efficient handling if the column name changes in a large table,
  123.      * which would not be possible if the column name was used in the hash</li>
  124.      * </ol>
  125.      */
  126.     protected Map<String,DataColumn> columns = new LinkedHashMap<String,DataColumn>();
  127.     
  128.     /**
  129.      * A map of Comparators by Class type; only one Comparator is allowed per class.
  130.      * This is used by DataRows to compare a new value for a cell with the
  131.      * reference value, to determine if the cell is "changed" or not.
  132.      */
  133.     protected Map<Class,Comparator> classComparators = new HashMap<Class,Comparator>();
  134.     
  135.     /**
  136.      * A map of Comparators by DataColumn; only one Comparator is allowed per column.
  137.      * This is used by DataRows to compare a new value for a cell with the
  138.      * reference value, to determine if the cell is "changed" or not.
  139.      */
  140.     protected Map<DataColumn,Comparator> columnComparators = new HashMap<DataColumn,Comparator>();
  141.     
  142.     /**
  143.      * A comparator for checking for changes between reference and current values for a column
  144.      * in a row, used when identityComparisonEnabled is false, and no column or class comparators
  145.      * are assigned.
  146.      */
  147.     private final static Comparator EQUALS_COMPARATOR = new Comparator() {
  148.         public boolean equals(Object obj) {
  149.             return obj == this;
  150.         }
  151.         
  152.         public int compare(Object o1, Object o2) {
  153.             return ( o1.equals(o2) ? 0 : -1 );
  154.         }
  155.     };
  156.     
  157.     /**
  158.      * The list of DataRows in this DataTable. The DataRow actually contains the
  159.      * data at the various cells.
  160.      */
  161.     protected List<DataRow> rows = new ArrayList<DataRow>();
  162.     
  163.     /**
  164.      * Every DataTable contains a list of selectors, which manage tracking
  165.      * "selected" state in a DataTable. This is necessary for proper handling
  166.      * of master/detail relationships, also known as parent/child. This data
  167.      * structure maps the data selectors name to the selector itself.
  168.      */
  169.     protected Map<String,DataSelector> selectors = new HashMap<String,DataSelector>();
  170.     
  171.     /**
  172.      * Indicates whether deleting rows is supported. Attempts to
  173.      * delete a row when row deletion is not supported will be ignored
  174.      */
  175.     private boolean deleteRowSupported = true;
  176.     
  177.     /**
  178.      * Indicates whether appending rows is supported. Attempts to append a
  179.      * row when row appending is not supported will be ignored.
  180.      */
  181.     private boolean appendRowSupported = true;
  182.     
  183.     /**
  184.      * Indicates whether new values applied to any row are compared with the
  185.      * cell's reference value using == (instance identity comparison). If true,
  186.      * identity comparison is used; if false, defaults to using .equals() on
  187.      * the two values, unless a Comparator is assigned to the column class or
  188.      * the column. See class docs for {@link DataRow} and {@link DataTable} for
  189.      * more details on value comparison and row status. Defaults to true for new
  190.      * tables.
  191.      */
  192.     private boolean identityComparisonEnabled;
  193.     
  194.     /**
  195.      * A list of DataTableListeners to notify of various events
  196.      */
  197.     private List<DataTableListener> listeners = new ArrayList<DataTableListener>();
  198.     
  199.     /**
  200.      * A PropertyChangeListener for listening to name property change events
  201.      * on DataSelectors and DataColumns. The listener makes sure that the name
  202.      * used as the hash key in the selectors and columns maps is always
  203.      * accurate.
  204.      */
  205.     private final PropertyChangeListener nameChangeListener = new PropertyChangeListener() {
  206.         public void propertyChange(PropertyChangeEvent evt) {
  207.             if (evt.getSource() instanceof DataSelector) {
  208.                 DataSelector sel = selectors.remove(evt.getOldValue());
  209.                 if (sel != null) {
  210.                     //name changed
  211.                     selectors.put((String)evt.getNewValue(), sel);
  212.                 }
  213.             } else if (evt.getSource() instanceof DataColumn) {
  214.                 DataColumn c = columns.remove(evt.getOldValue());
  215.                 if (c != null) {
  216.                     //name changed
  217.                     columns.put((String)evt.getNewValue(), c);
  218.                 }
  219.             }
  220.         }
  221.     };
  222.     
  223.     /**
  224.      * Create a new DataTable for a specified DataSet, with an
  225.      * automatically-generated name unique to the DataSet.
  226.      *
  227.      * @param ds The DataSet to which the table will be added.
  228.      */
  229.     protected DataTable(DataSet ds) {
  230.         assert ds != null;
  231.         this.dataSet = ds;
  232.         this.name = NAMEGEN.generateName(this);
  233.         this.identityComparisonEnabled = true;
  234.     }
  235.     
  236.     /**
  237.      * Create a new DataTable for a specified DataSet, with the
  238.      * specified name.
  239.      *
  240.      * @param ds The DataSet to which the table will be added.
  241.      * @param name The name of the table.
  242.      */
  243.     protected DataTable(DataSet ds, String name) {
  244.         this(ds);
  245.         if (name != null) {
  246.             setName(name);
  247.         }
  248.     }
  249.     
  250.     /**
  251.      * creates a new DataColumn, and adds it to this DataTable. A name will
  252.      * be automatically generated for the DataColumn.
  253.      *
  254.      * @return the DataColumn that was created
  255.      */
  256.     public DataColumn createColumn() {
  257.         return createColumn(null);
  258.     }
  259.     
  260.     /**
  261.      * Creates a new DataColumn with the given name, and adds it to this
  262.      * DataTable.
  263.      *
  264.      * @param colName the name to give the DataColumn. If the name is invalid
  265.      * or has already been used in the DataSet, an assertion will be raised
  266.      *
  267.      * @return the new DataColumn
  268.      */
  269.     public DataColumn createColumn(String colName) {
  270.         DataColumn col = new DataColumn(this);
  271.         if (colName != null) {
  272.             col.setName(colName);
  273.         }
  274.         columns.put(col.getName(),  col);
  275.         col.addPropertyChangeListener("name", nameChangeListener);
  276.         
  277.         fireDataTableChanged(TableChangeEvent.newColumnAddedEvent(this, col));
  278.         return col;
  279.     }
  280.     
  281.     /**
  282.      * Adds a column to the table for each name in the supplied array of Strings.
  283.      * @param colNames Array of column names to add to the table.
  284.      */
  285.     public void createColumns(String... colNames) {
  286.         for (String name : colNames) {
  287.             createColumn(name);
  288.         }
  289.     }
  290.     
  291.     /**
  292.      * Drops the column with the given name. If there is no column by the
  293.      * specified name, then nothing is done.
  294.      * @param colName Name of the column to drop.
  295.      */
  296.     public void dropColumn(String colName) {
  297.         DataColumn col = columns.remove(colName);
  298.         if (col != null) {
  299.             col.removePropertyChangeListener("name",  nameChangeListener);
  300.             //remove any remaining references to this column in the DataRelations
  301.             for (DataRelation r : dataSet.getRelations()) {
  302.                 DataColumn c = r.getChildColumn();
  303.                 if (c == col) {
  304.                     r.setChildColumn(null);
  305.                 }
  306.                 c = r.getParentColumn();
  307.                 if (c == col) {
  308.                     r.setParentColumn(null);
  309.                 }
  310.             }
  311.             columnComparators.remove(col);
  312.             
  313.             fireDataTableChanged(TableChangeEvent.newColumnRemovedEvent(this, col));
  314.         }
  315.     }
  316.     
  317.     /**
  318.      * @return a List of DataColumns representing all of the columns in this
  319.      * DataTable.
  320.      */
  321.     public List<DataColumn> getColumns() {
  322.         return Collections.unmodifiableList(new ArrayList<DataColumn>(columns.values()));
  323.     }
  324.     
  325.     /**
  326.      * @param colName the name of the column that you want to retrieve
  327.      * @return the DataColumn with the given name. If the given name does not
  328.      * map to a DataColumn in this DataTable, then null is returned.
  329.      */
  330.     public DataColumn getColumn(String colName) {
  331.         return columns.get(colName);
  332.     }
  333.     
  334.     /**
  335.      * @return an unmodifiable list of all of the rows in this DataTable. The
  336.      * individual DataRow elements are modifiable, but the List is not.
  337.      * This includes all rows, regardless of status--inserted, updated, deleted
  338.      * and unchanged.
  339.      */
  340.     public List<DataRow> getRows() {
  341.         return Collections.unmodifiableList(rows);
  342.     }
  343.     
  344.     /**
  345.      * @param index the Index in this table associated with the DataRow to be
  346.      * retrieved. This must be &gt;0, and &lt;rows.size()
  347.      * @return the DataRow at the given 0 based index. The index must be valid
  348.      */
  349.     public DataRow getRow(int index) {
  350.         assert index >= 0 && index < rows.size();
  351.         return rows.get(index);
  352.     }
  353.     
  354.     /**
  355.      * @return the number of DataRows in this DataTable; this includes all rows, regardless
  356.      * of status--inserted, updated, deleted and unchanged.
  357.      */
  358.     public int getRowCount() {
  359.         return rows.size();
  360.     }
  361.     
  362.     /**
  363.      * Creates and returns a selector. The DataSelector will have a generated
  364.      * name by default.
  365.      * @return a selector on the table.
  366.      */
  367.     public DataSelector createSelector() {
  368.         return createSelector(null);
  369.     }
  370.     
  371.     /**
  372.      * Creates and returns a selector with the given name. If the name is
  373.      * invalid, an assertion will be raised
  374.      * @param name the name for the new DataSelector
  375.      */
  376.     public DataSelector createSelector(String name) {
  377.         DataSelector sel = new DataSelector(this);
  378.         if (name != null) {
  379.             sel.setName(name);
  380.         }
  381.         if (rows.size() > 0) {
  382.             sel.setRowIndex(0);
  383.         }
  384.         selectors.put(sel.getName(),  sel);
  385.         sel.addPropertyChangeListener("name", nameChangeListener);
  386.         return sel;
  387.     }
  388.     
  389.     /**
  390.      * @return a List of DataSelectors associated with this DataTable.
  391.      */
  392.     public List<DataSelector> getSelectors() {
  393.         return Collections.unmodifiableList(new ArrayList<DataSelector>(selectors.values()));
  394.     }
  395.     
  396.     /**
  397.      * @param name the name of the selector to create or return
  398.      * @return the DataSelector with the given name. If no such DataSelector
  399.      * exists, then a new DataSelector will be created and added to this
  400.      * DataTable by the given name.
  401.      */
  402.     public DataSelector getSelector(String name) {
  403.         //if the given selector doesn't exist, create it implicitly
  404.         if (!selectors.containsKey(name)) {
  405.             return createSelector(name);
  406.         } else {
  407.             return selectors.get(name);
  408.         }
  409.     }
  410.     
  411.     /**
  412.      * Drop the given selector
  413.      * @param selector the selector to drop
  414.      */
  415.     public void dropSelector(DataSelector selector) {
  416.         dropSelector(selector.getName());
  417.     }
  418.     
  419.     /**
  420.      * drops the given named selector. If a selector by that name does not
  421.      * exist, then do nothing
  422.      * @param selectorName the name of the selector to drop
  423.      */
  424.     public void dropSelector(String selectorName) {
  425.         DataSelector sel = selectors.remove(selectorName);
  426.         if (sel != null) {
  427.             sel.removePropertyChangeListener("name",  nameChangeListener);
  428.         }
  429.     }
  430.     
  431.     /**
  432.      * Sets the given name to be the name of this DataTable
  433.      * @param name The new name of the table; should be unique within the table's DataSet.
  434.      */
  435.     public void setName(String name) {
  436.         if (this.name != name) {
  437.             assert DataSetUtils.isValidName(name);
  438.             assert !dataSet.hasElement(name);
  439.             String oldName = this.name;
  440.             this.name = name;
  441.             pcs.firePropertyChange("name", oldName, name);
  442.         }
  443.     }
  444.     
  445.     /**
  446.      * Returns the name of this <CODE>DataTable</CODE>.
  447.      * @return the name of this DataTable
  448.      */
  449.     public String getName() {
  450.         return name;
  451.     }
  452.     
  453.     /**
  454.      * Returns true if rows in this table compare reference with current values
  455.      * using the Java identity comparison (==); see class
  456.      * JavaDocs, and docs for {@link DataRow}.
  457.      * @return true if rows in this table compare reference with current values
  458.      * using the Java identity comparison (==); see class
  459.      * JavaDocs, and docs for {@link DataRow}.
  460.      */
  461.     public boolean isIdentityComparisonEnabled() {
  462.         return identityComparisonEnabled;
  463.     }
  464.     
  465.     /**
  466.      * Sets whether rows in this table compare reference with current values
  467.      * using the Java identity comparison (==); see class
  468.      * JavaDocs, and docs for {@link DataRow}.
  469.      * @param identityComparisonEnabled if true, table will use identity (<CODE>==</CODE>) comparison in checking for row changes.
  470.      */
  471.     public void setIdentityComparisonEnabled(boolean identityComparisonEnabled) {
  472.         this.identityComparisonEnabled = identityComparisonEnabled;
  473.     }
  474.     
  475.     /**
  476.      * Assigns a Comparator to use in comparing current and reference values
  477.      * in DataRow cells, according to a DataColumn's type/class; see class
  478.      * JavaDocs, and docs for {@link DataRow}. Only one Comparator can be assigned
  479.      * per class; calling this multiple times on the same class will overwrite
  480.      * the Comparator assignment.
  481.      *
  482.      * @param klass The Class to bind the Comparator with.
  483.      * @param comp The Comparator used.
  484.      */
  485.     public void setClassComparator(Class klass, Comparator comp) {
  486.         assert klass != null;
  487.         assert comp != null;
  488.         classComparators.put(klass, comp);
  489.     }
  490.     
  491.     /**
  492.      * Removes assignment of a Comparator for a DataColumn type/class; see class
  493.      * JavaDocs, and docs for {@link DataRow}.
  494.      *
  495.      * @param klass The class for which the Comparator will be removed. Fails silently
  496.      * if none is assigned.
  497.      */
  498.     public void removeClassComparator(Class klass) {
  499.         assert klass != null;
  500.         classComparators.remove(klass);
  501.     }
  502.     
  503.     /**
  504.      * @return true if the given Class has a Comparator assigned; see class
  505.      * JavaDocs, and docs for {@link DataRow}.
  506.      */
  507.     public boolean hasClassComparator(Class klass) {
  508.         return classComparators.get(klass) != null;
  509.     }
  510.     
  511.     /**
  512.      * Returns a Comparator bound to a given Class for comparison of
  513.      * current and reference values in a DataRow. Will return a
  514.      * default Comparator that uses .equals() comparison if none
  515.      * was explicitly assigned.
  516.      *
  517.      * @param klass The class for which to retrieve a Comparator
  518.      * @return The Comparator bound to the Class, or a default Comparator.
  519.      */
  520.     public Comparator getClassComparator(Class klass) {
  521.         Comparator comp = classComparators.get(klass);
  522.         if ( comp == null ) {
  523.             comp = EQUALS_COMPARATOR;
  524.         }
  525.         return comp;
  526.     }
  527.     
  528.     /**
  529.      * Assigns a specific Comparator to a DataColumn, for use in comparing
  530.      * changes to DataRow cells; see class
  531.      * JavaDocs, and docs for {@link DataRow}.
  532.      *
  533.      * @param col The DataColumn to which the Comparator is bound.
  534.      * @param comp The Comparator instance.
  535.      */
  536.     public void setColumnComparator(DataColumn col, Comparator comp) {
  537.         assert col != null;
  538.         assert comp != null;
  539.         columnComparators.put(col, comp);
  540.     }
  541.     
  542.     /**
  543.      * Removes the specific Comparator assigned to the column, if any; fails
  544.      * silently if none assigned; see class
  545.      * JavaDocs, and docs for {@link DataRow}.
  546.      *
  547.      * @param col The DataColumn for which to remove the bound Comparator.
  548.      */
  549.     public void removeColumnComparator(DataColumn col) {
  550.         assert col != null;
  551.         columnComparators.remove(col);
  552.     }
  553.     
  554.     /**
  555.      * Returns true if the column has a specific comparator assigned to it;
  556.      * with {@link setColumnComparator(DataColumn, Comparator)}; see class
  557.      * JavaDocs, and docs for {@link DataRow}.
  558.      *
  559.      * @param col The DataColumn to find a Comparator for.
  560.      * @return true if a Comparator has been bound to this DataColumn.
  561.      */
  562.     public boolean hasColumnComparator(DataColumn col) {
  563.         return columnComparators.get(col) != null;
  564.     }
  565.     
  566.     /**
  567.      * Returns a Comparator bound to a given Column. Will return the comparator
  568.      * for the column's class if no specific comparator was assigned to the column; see class
  569.      * JavaDocs, and docs for {@link DataRow}.
  570.      *
  571.      * @param col The DataColumn for which to find the Comparator.
  572.      * @return The Comparator bound to this column, or the Comparator bound to the
  573.      * column's class; see {@link #getClassComparator(Class klass)}
  574.      */
  575.     public Comparator getColumnComparator(DataColumn col) {
  576.         assert col != null;
  577.         Comparator comp = columnComparators.get(col);
  578.         if ( comp == null ) {
  579.             comp = getClassComparator(col.getType());
  580.         }
  581.         return comp;
  582.     }
  583.     
  584.     /**
  585.      * Returns true if deletion of rows is supported.
  586.      * @return true if deletion of rows is supported
  587.      */
  588.     public boolean isDeleteRowSupported() {
  589.         return deleteRowSupported;
  590.     }
  591.     
  592.     /**
  593.      * Sets the deleteRowSupported flag
  594.      * @param deleteRowSupported the new value for deleteRowSupported
  595.      */
  596.     public void setDeleteRowSupported(boolean deleteRowSupported) {
  597.         if (this.deleteRowSupported != deleteRowSupported) {
  598.             boolean oldValue = this.deleteRowSupported;
  599.             this.deleteRowSupported = deleteRowSupported;
  600.             pcs.firePropertyChange("deleteRowSupported", oldValue, deleteRowSupported);
  601.         }
  602.     }
  603.     
  604.     /**
  605.      * Returns true if appending rows is supported.
  606.      * @return true if appending rows is supported
  607.      */
  608.     public boolean isAppendRowSupported() {
  609.         return appendRowSupported;
  610.     }
  611.     
  612.     /**
  613.      * Sets whether appending rows is supported
  614.      * @param appendRowSupported the new value for appendRowSupported
  615.      */
  616.     public void setAppendRowSupported(boolean appendRowSupported) {
  617.         if (this.appendRowSupported != appendRowSupported) {
  618.             boolean oldValue = this.appendRowSupported;
  619.             this.appendRowSupported = appendRowSupported;
  620.             pcs.firePropertyChange("appendRowSupported", oldValue, appendRowSupported);
  621.         }
  622.     }
  623.     
  624.     /**
  625.      * Returns the {@link DataProvider} for this <CODE>DataTable</CODE>. May be null.
  626.      * @return the DataProvider for this DataTable. May be null.
  627.      */
  628.     public DataProvider getDataProvider() {
  629.         return dataProvider;
  630.     }
  631.     
  632.     /**
  633.      * Sets the DataProvider for this DataTable.
  634.      * @param dataProvider the DataProvider for this DataTable. This may be null.
  635.      */
  636.     public void setDataProvider(DataProvider dataProvider) {
  637.         if (this.dataProvider != dataProvider) {
  638.             DataProvider oldValue = this.dataProvider;
  639.             this.dataProvider = dataProvider;
  640.             pcs.firePropertyChange("dataProvider", oldValue, dataProvider);
  641.         }
  642.     }
  643.     
  644.     /**
  645.      * Returns the {@link DataSet} that is associated with this <CODE>DataTable</CODE>.
  646.      * @return the DataSet that is associated with this DataTable
  647.      */
  648.     public DataSet getDataSet() {
  649.         return dataSet;
  650.     }
  651.     
  652.     /**
  653.      * Append a new DataRow to this DataTable, and return the newly appended
  654.      * Row. If appendRowSupported is false, then this method returns null
  655.      * @return null if !appendRowSupported, else the newly created row
  656.      */
  657.     public DataRow appendRow() {
  658.         final DataRow row = appendRowNoEvent();
  659.         if (row != null) {
  660.             fireDataTableChanged(TableChangeEvent.newRowAddedEvent(this, row));
  661.             
  662.             //I added this, don't know if I really want it here
  663.             if (selectors.get("current") == null) {
  664.                 createSelector("current");
  665.             }
  666.             // ASK: why the PCL here?
  667.             row.addPropertyChangeListener(new PropertyChangeListener() {
  668.                 public void propertyChange(PropertyChangeEvent evt) {
  669.                     fireDataTableChanged(TableChangeEvent.newRowAddedEvent(DataTable.this, row));
  670.                 }
  671.             });
  672.             selectors.get("current").setRowIndices(new int[]{indexOfRow(row)});
  673.         }
  674.         return row;
  675.     }
  676.     
  677.     /**
  678.      * Appends a new DataRow to this DataTable, and returns the newly appended
  679.      * row. This method differs from {@link #appendRow()} in that it does not fire
  680.      * any event. This is useful to the DataProvider, which will be adding many
  681.      * rows and may not want many event notifications
  682.      * @return The row added to the table.
  683.      */
  684.     public DataRow appendRowNoEvent() {
  685.         if (appendRowSupported) {
  686.             DataRow row = new DataRow(this);
  687.             int oldSize = rows.size();
  688.             rows.add(row);
  689.             if (oldSize == 0 && rows.size() > 0) {
  690.                 //check the selectors. Any selectors that are not
  691.                 //set, set.
  692.                 for (DataSelector s : selectors.values()) {
  693.                     s.setRowIndices(new int[]{0});
  694.                 }
  695.             }
  696.             return row;
  697.         } else {
  698.             return null;
  699.         }
  700.     }
  701.     
  702.     /**
  703.      * Doesn't actually remove the row, just marks it for deletion. If
  704.      * deletion of rows is not supported, nothing happens.
  705.      * @param rowIndex the index of the row to delete. If the index is invalid,
  706.      * an assertion will be raised
  707.      */
  708.     public void deleteRow(int rowIndex) {
  709.         assert rowIndex >= 0 && rowIndex < rows.size();
  710.         deleteRow(rows.get(rowIndex));
  711.     }
  712.     
  713.     /**
  714.      * Doesn't actually remove the row, just marks it for deletion. If
  715.      * deletion of rows is not supported, nothing happens.
  716.      * @param row the row to delete. The row must belong to this DataTable
  717.      */
  718.     public void deleteRow(DataRow row) {
  719.         assert row.getTable() == this;
  720.         if (deleteRowSupported) {
  721.             row.setStatus(DataRow.DataRowStatus.DELETED);
  722.             fireDataTableChanged(TableChangeEvent.newRowDeletedEvent(this, row));
  723.         }
  724.     }
  725.     
  726.     /**
  727.      * Actually removes the row from the DataTable. Unlike the #deleteRow method,
  728.      * this does not later remove the record from the data store. Rather, it simply
  729.      * discards the row entirely
  730.      * @param rowIndex Index of the row to discard.
  731.      */
  732.     public void discardRow(int rowIndex) {
  733.         assert rowIndex > 0 && rowIndex < rows.size();
  734.         DataRow row = rows.remove(rowIndex);
  735.         fireDataTableChanged(TableChangeEvent.newRowDiscardedEvent(this, row));
  736.     }
  737.     
  738.     /**
  739.      * Actually removes the row from the DataTable. Unlike the {@link #deleteRow(DataRow)} method,
  740.      * this does not later remove the record from the data store. Rather, it simply
  741.      * discards the row entirely.
  742.      * @param row The row to discard.
  743.      */
  744.     public void discardRow(DataRow row) {
  745.         discardRow(indexOfRow(row));
  746.     }
  747.     
  748.     /**
  749.      * Loads this DataTable using this tables DataProvider. If DataProvider is
  750.      * null, then nothing is loaded. This method <b>does not</b> clear out the
  751.      * DataTable prior to loading. Calling load() <i>n</i> times will cause the
  752.      * DataTable to contain <i>rowCount * n</i> rows to be added.
  753.      */
  754.     public void load() {
  755.         if (dataProvider != null) {
  756.             fireDataTableChanged(TableChangeEvent.newLoadStartEvent(this));
  757.             dataProvider.load(this);
  758.         }
  759.     }
  760.     
  761.     /**
  762.      * Loads this DataTable <b>synchronously</b> using this table&amp;s
  763.      * DataProvider. That is, this method blocks until the load is completed.
  764.      * If DataProvider
  765.      * is null, then nothing is loaded. This method <b>does not</b> clear out
  766.      * the DataTable prior to loading. Calling load() <i>n</i> times will cause
  767.      * the DataTable to contain <i>rowCount * n</i> rows to be added
  768.      */
  769.     public void loadAndWait() {
  770.         if (dataProvider != null) {
  771.             fireDataTableChanged(TableChangeEvent.newLoadStartEvent(this));
  772.             dataProvider.loadAndWait(this);
  773.         }
  774.     }
  775.     
  776.     /**
  777.      * Saves this DataTable to the underlying DataStore. This calls the
  778.      * DataProviders save method. If no DataProvider is specified, then nothing
  779.      * is done.
  780.      */
  781.     public void save() {
  782.         if (dataProvider != null) {
  783.             fireDataTableChanged(TableChangeEvent.newSaveStartEvent(this));
  784.             dataProvider.save(this);
  785.         }
  786.     }
  787.     
  788.     /**
  789.      * Saves this DataTable to the underyling DataStore <b>synchronously</b>.
  790.      * That is, this method blocks until the save is complete. This calls the
  791.      * DataProvider&amp;s save method. If no DataProvider is specified, then
  792.      * nothing is done.
  793.      */
  794.     public void saveAndWait() {
  795.         if (dataProvider != null) {
  796.             fireDataTableChanged(TableChangeEvent.newSaveStartEvent(this));
  797.             dataProvider.saveAndWait(this);
  798.         }
  799.     }
  800.     
  801.     /**
  802.      * Clears all of the rows from this DataTable. <em>If any rows were altered,
  803.      * these changes are lost!</em>. Call #save to save the changes before
  804.      * clearing. An {@linkplain TableChangeEvent event} is posted indicating that the table was cleared.
  805.      */
  806.     public void clear() {
  807.         rows.clear();
  808.         //clear out all of the DataSelectors
  809.         for (DataSelector sel : selectors.values()) {
  810.             sel.setRowIndices(new int[0]);
  811.         }
  812.         fireDataTableChanged(TableChangeEvent.newTableClearedEvent(this));
  813.     }
  814.     
  815.     /**
  816.      * Refreshes the DataSet. This is symantically the same as:
  817.      * <code>
  818.      * clear();
  819.      * load();
  820.      * </code>
  821.      */
  822.     public void refresh() {
  823.         clear();
  824.         load();
  825.     }
  826.     
  827.     /**
  828.      * Refreshes the DataSet <b>synchronously</b>. That is, this method blocks
  829.      * until the refresh is completed. This is symantically the same as:
  830.      * <code>
  831.      * clear();
  832.      * loadAndWait();
  833.      * </code>
  834.      */
  835.     public void refreshAndWait() {
  836.         clear();
  837.         loadAndWait();
  838.     }
  839.     
  840.     /**
  841.      * Returns the current value for the row at the given row index, for the given column.
  842.      * @param index the row index of the row that you want to retrieve a value
  843.      * for
  844.      * @param columnName the name of the column who's value you want retrieved
  845.      * @return the value at the given rowIndex, for the given columnName. If
  846.      * either the index is invalid or the columnName, assertions will be raised.
  847.      */
  848.     public Object getValue(int index, String columnName) {
  849.         assert index >= 0 && index < rows.size();
  850.         assert columns.containsKey(columnName);
  851.         return rows.get(index).getValue(columnName);
  852.     }
  853.     
  854.     /**
  855.      * Sets the field value at the given row idnex and column to the given
  856.      * value. If either the index is invalid or the columnName, assertions will
  857.      * be raised.
  858.      * @param index The row index at which to set the value
  859.      * @param columnName Name of the column to change
  860.      * @param value The new value for the cell
  861.      */
  862.     public void setValue(int index, String columnName, Object value) {
  863.         assert index >= 0 && index < rows.size();
  864.         assert columns.containsKey(columnName);
  865.         rows.get(index).setValue(columnName, value);
  866.     }
  867.     
  868.     /**
  869.      * Retrieves the current value for the given row and column.
  870.      * @param row The DataRow to retrieve the value from. This row must be
  871.      * a member of this table, or an assertion will be raised. If null, a
  872.      * NullPointerException will be thrown.
  873.      * @param col The DataColumn to retrieve the value from. This col must
  874.      * be a member of this table, or an assertion will be raised. If null, a
  875.      * NullPointerException will be thrown.
  876.      * @return  the current value for the given row and column.
  877.      */
  878.     public Object getValue(DataRow row, DataColumn col) {
  879.         assert row.getTable() == this;
  880.         assert col.getTable() == this;
  881.         return row.getValue(col);
  882.     }
  883.     
  884.     /**
  885.      * Adds a PropertyChangeListener to this class for any changes to bean
  886.      * properties.
  887.      *
  888.      * @param listener The PropertyChangeListener to notify of changes to this
  889.      * instance.
  890.      */
  891.     public void addPropertyChangeListener(PropertyChangeListener listener) {
  892.         pcs.addPropertyChangeListener(listener);
  893.     }
  894.     
  895.     /**
  896.      * Adds a PropertyChangeListener to this class for specific property changes.
  897.      *
  898.      * @param property The name of the property to listen to changes for.
  899.      * @param listener The PropertyChangeListener to notify of changes to this
  900.      * instance.
  901.      */
  902.     public void addPropertyChangeListener(String property, PropertyChangeListener listener) {
  903.         pcs.addPropertyChangeListener(property,  listener);
  904.     }
  905.     
  906.     /**
  907.      * Stops notifying a specific listener of any changes to bean properties.
  908.      *
  909.      * @param listener The listener to stop receiving notifications.
  910.      */
  911.     public void removePropertyChangeListener(PropertyChangeListener listener) {
  912.         pcs.removePropertyChangeListener(listener);
  913.     }
  914.     
  915.     /**
  916.      * Stops notifying a specific listener of changes to a specific property.
  917.      *
  918.      * @param propertyName The name of the property to ignore from now on.
  919.      * @param listener The listener to stop receiving notifications.
  920.      */
  921.     public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
  922.         pcs.removePropertyChangeListener(propertyName,  listener);
  923.     }
  924.     
  925.     /**
  926.      * Adds a {@link DataTableListener} to the table for event propagation. If the DTL is also a {@link PropertyChangeListener},
  927.      * like {@link DataTableEventAdapter}, then this is automatically added as a property change listener on the table as well.
  928.      * @param listener The listener to add for event notification.
  929.      */
  930.     public void addDataTableListener(DataTableListener listener) {
  931.         if (!listeners.contains(listener)) {
  932.             listeners.add(listener);
  933.         }
  934.         if ( listener instanceof PropertyChangeListener ) {
  935.             addPropertyChangeListener((PropertyChangeListener)listener);
  936.         }
  937.     }
  938.     
  939.     // CHANGED
  940.     /**
  941.      * Removes an event listener from the table; if the listener is also a {@link PropertyChangeListener}, then
  942.      * it is also removed as a property change listener on the table as well.
  943.      * @param listener The event listener to remove from the table.
  944.      */
  945.     public void removeDataTableListener(DataTableListener listener) {
  946.         listeners.remove(listener);
  947.         if ( listener instanceof PropertyChangeListener ) {
  948.             removePropertyChangeListener((PropertyChangeListener)listener);
  949.         }
  950.     }
  951.     
  952.     /**
  953.      * Broadcasts to listeners on the table that the table has changed, using a {@link TableChangeEvent}.
  954.      * @param evt The {@link TableChangeEvent} recording the event.
  955.      */
  956.     public void fireDataTableChanged(TableChangeEvent evt) {
  957.         for (DataTableListener listener : new ArrayList<DataTableListener>(listeners)) {
  958.             listener.tableChanged(evt);
  959.         }
  960.     }
  961.     
  962.     /**
  963.      * Broadcasts to listeners on the table that a row has changed using a {@link RowChangeEvent}.
  964.      * @param evt The {@link RowChangeEvent} capturing the event on a row.
  965.      */
  966.     public void fireRowChanged(RowChangeEvent evt) {
  967.         for (DataTableListener listener : new ArrayList<DataTableListener>(listeners)) {
  968.             listener.rowChanged(evt);
  969.         }
  970.     }
  971.     
  972.     /**
  973.      * Internal method that returns the int index of the given DataRow. This is
  974.      * currently only used for constructing toString on DataRow, and testing.
  975.      */
  976.     protected int indexOfRow(DataRow row) {
  977.         return rows.indexOf(row);
  978.     }
  979. }