KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdesktop > dataset > DataRelationTable


1 /*
2  * $Id: DataRelationTable.java,v 1.1 2005/02/23 17:51:30 rbair Exp $
3  *
4  * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
5  * Santa Clara, California 95054, U.S.A. All rights reserved.
6  */

7
8 package org.jdesktop.dataset;
9
10 import java.beans.PropertyChangeEvent JavaDoc;
11 import java.beans.PropertyChangeListener JavaDoc;
12 import java.util.ArrayList JavaDoc;
13 import java.util.Collections JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.Map JavaDoc;
17 import java.util.logging.Logger JavaDoc;
18
19 import org.jdesktop.dataset.event.TableChangeEvent;
20
21
22 /**
23  *
24  * @author rbair
25  */

26 public class DataRelationTable extends DataTable {
27     /**
28      * The Logger
29      */

30     private static final Logger JavaDoc LOG = Logger.getLogger(DataRelationTable.class.getName());
31     /**
32      * The relation that is used to populate this DataRelationTable
33      */

34     private DataRelation relation;
35     /**
36      * The selector in the parent table that is used to indicate what parent
37      * rows to retrieve child rows for
38      */

39     private DataSelector parentSelector;
40     /**
41      * A selection listener that is affixed to the parentSelector. Whenever the
42      * selection changes in the parentSelector, this is notified and refreshes
43      * this table
44      */

45     private SelectionListener listener = new SelectionListener();
46     
47     /**
48      * Creates a new instance of DataRelationTable
49      */

50     public DataRelationTable(DataSet ds) {
51         super(ds);
52     }
53     
54     /**
55      * Set the relation that this DataRelationTable uses
56      * @param relation the DataRelation to use
57      */

58     public void setRelation(DataRelation relation) {
59         //TODO affix a DataSet listener so that if this relation is removed
60
//from the DataSet, I can remove my reference to it as well.dssdf
61
this.relation = relation;
62     }
63     
64     /**
65      * @return the relation used to populate this DataRelationTable
66      */

67     public DataRelation getRelation() {
68         return relation;
69     }
70     
71     /**
72      * Sets the selector for this DataRelationTable. This selector is used to
73      * retrieve the currently selected rows from the parent table so that the
74      * proper child rows can be retrieved from the child table using the
75      * relation.
76      *
77      * @param selector The selector on the parent table. The selector must belong
78      * to the same table as the relation's parentColumn
79      */

80     public void setParentSelector(DataSelector selector) {
81         if (this.parentSelector != null) {
82             this.parentSelector.removePropertyChangeListener("rowIndices", listener);
83         }
84         this.parentSelector = selector;
85         if (this.parentSelector != null) {
86             this.parentSelector.addPropertyChangeListener("rowIndices", listener);
87         }
88     }
89     
90     /**
91      * @return the DataSelector used to retrieve rows from the DataRelation
92      */

93     public DataSelector getParentSelector() {
94         return parentSelector;
95     }
96
97     /**
98      * This method makes no sense for a DataRelationTable. A cleaner refactoring
99      * might remove the need to override this method. Nothing bad happens if
100      * this method is called, it just doesn't make sense. A Warning is logged
101      * if this method is called.
102      */

103     public void setDataProvider(DataProvider dataProvider) {
104         LOG.warning("An attempt was made to set the DataProvider for a " +
105                 "DataRelationTable. This is not honored because a " +
106                 "DataRelationTable, by definition, gets its records " +
107                 "by querying a DataSelector from the Parent table, and " +
108                 "the DataRelation leading to the Child table.");
109     }
110
111     /**
112      * This method makes no sense for a DataRelationTable. A cleaner refactoring
113      * might remove the need to override this method. Nothing bad happens if
114      * this method is called, it just doesn't make sense. A Warning is logged
115      * if this method is called
116      */

117     public void save() {
118         LOG.warning("An attempt was made to save a DataRelationTable. " +
119                 "This is not honored because a DataRelationTable, " +
120                 "by definition, gets its records by querying a DataSelector " +
121                 "from the Parent table, and the DataRelation leading to the " +
122                 "Child table. Therefore, to save, the child table should be " +
123                 "asked to save, not the DataRelationTable.");
124     }
125
126     /**
127      * Appends a row to the child table, retrieved by
128      * <code>relation.getChildColumn().getTable()</code>. If the DataTable
129      * doesn't exist, null is returned
130      *
131      * @returns the newly added DataRow, or null if no DataRow was added.
132      */

133     public DataRow appendRow() {
134         //append a row to the child table, and view it here.
135
//when the row is added to the child table, the child table
136
//will fire an event notification indicating that a new row has
137
//been added. When this happens, this child as well as all other
138
//relation tables will be notified that they need to refresh themselves.
139
if (relation != null && relation.getChildColumn() != null) {
140             DataTable t = relation.getChildColumn().getTable();
141             return t.appendRow();
142         }
143         return null;
144     }
145
146     /**
147      * Removes all of the rows from this DataRelationTable, and reloads it with
148      * the proper rows from the DataRelation using the parentSelector
149      * DataSelector. When rows are removed from a DataRelationTable, they still
150      * exist in the child DataTable, hence it is always safe to call this method
151      * without losing any data.<br>
152      *
153      * This method attempts to reset all of its selectors to the same rows,
154      * where possible. It does this by remembering all of the values for the
155      * rows key fields, and looking for them again after the refresh. If the
156      * row cannot be found, it is no longer selected<br>
157      *
158      * TODO This code for remembering the selected rows and trying to locate
159      * them again after refresh is horrible and needs to be rewritten.
160      */

161     public void refresh() {
162         //for each DataSelector, save its selection state.
163
Map JavaDoc<DataSelector,Object JavaDoc[][]> selectorState = new HashMap JavaDoc<DataSelector,Object JavaDoc[][]>();
164         List JavaDoc<DataColumn> keyColumns = new ArrayList JavaDoc<DataColumn>();
165         for (DataColumn c : columns.values()) {
166             if (c.isKeyColumn()) {
167                 keyColumns.add(c);
168             }
169         }
170         
171         for (DataSelector sel : selectors.values()) {
172             List JavaDoc<Integer JavaDoc> indices = sel.getRowIndices();
173             Object JavaDoc[][] values = new Object JavaDoc[indices.size()][keyColumns.size()];
174             for (int i=0; i<indices.size(); i++) {
175                 for (int j=0; j<keyColumns.size(); j++) {
176                     DataRow row = rows.get(indices.get(i));
177                     values[i][j] = getValue(row, keyColumns.get(j));
178                 }
179             }
180             sel.setRowIndices(new int[0]);
181             selectorState.put(sel, values);
182         }
183         
184         //clear out the DataRelationTable, and reload it
185
clear();
186         if (relation != null && relation.getChildColumn() != null) {
187             if (parentSelector != null) {
188                 //use the selector and the relation to get the rows
189
// assert relation.getParentColumn() != null && relation.getParentColumn().getTable() == selector.getTable();
190
super.rows.addAll(relation.getRows(parentSelector.getRowIndices()));
191             } else {
192                 //get all of the rows from the relations child table
193
super.rows.addAll(relation.getChildColumn().getTable().getRows());
194             }
195             //reset the selectors
196
if (super.rows.size() > 0) {
197                 //reset all of the selectors to 0
198
for (DataSelector s : selectors.values()) {
199                     s.setRowIndices(new int[]{0});
200                 }
201             }
202             fireDataTableChanged(new TableChangeEvent(this));
203         }
204         
205         //try to restore the selection state, where possible
206
for (DataSelector sel : selectors.values()) {
207             Object JavaDoc[][] values = selectorState.get(sel);
208             //for each selector, find its values. Look for a row who's columns
209
//values match the specified set, and match it.
210
//NOTE: This algorithm is a brute force algorithm, and not very
211
//efficient where multiple rows are selected in a large DataTable.
212
List JavaDoc<Integer JavaDoc> indices = new ArrayList JavaDoc<Integer JavaDoc>(values.length);
213             for (int i=0; i<values.length; i++) {
214                 boolean found = true;
215                 for (int rowIndex=0; rowIndex<rows.size(); rowIndex++) {
216                     found = true; //reset
217
for (int j=0; j<keyColumns.size(); j++) {
218                         DataRow row = rows.get(rowIndex);
219                         if (found && values[i][j].equals(row.getValue(keyColumns.get(j)))) {
220                             //do nothing
221
} else {
222                             found = false;
223                         }
224                     }
225                     if (found) {
226                         indices.add(rowIndex);
227                     }
228                 }
229             }
230             
231             //ok, set the selector state
232
int[] rows = new int[indices.size()];
233             for (int i=0; i<rows.length; i++) {
234                 rows[i] = indices.get(i);
235             }
236             sel.setRowIndices(rows);
237         }
238     }
239     
240     /**
241      * Overridden so that it will defer to the child Table first. A
242      * DataRelationTable contains all of the columns in the child Table, but
243      * may also contain its own set of calculated columns as well. This method
244      * must therefore check the child table for a given column before checking
245      * its own set of columns.
246      *
247      * @param colName
248      * @return
249      */

250     public DataColumn getColumn(String JavaDoc colName) {
251         DataColumn col = null;
252         if (relation != null && relation.getChildColumn() != null && relation.getChildColumn().getTable() != null) {
253             col = relation.getChildColumn().getTable().getColumn(colName);
254         }
255         
256         if (col == null) {
257             return super.getColumn(colName);
258         } else {
259             return col;
260         }
261     }
262
263     /**
264      * @eturn the set of columns that comprise this DataRelationTable. This is
265      * the union of the child tables columns, and any native columns to this
266      * DataRelationTable
267      */

268     public List JavaDoc<DataColumn> getColumns() {
269         List JavaDoc<DataColumn> cols = new ArrayList JavaDoc<DataColumn>();
270         if (relation != null && relation.getChildColumn() != null && relation.getChildColumn().getTable() != null) {
271             cols.addAll(relation.getChildColumn().getTable().getColumns());
272         }
273         
274         cols.addAll(super.getColumns());
275         return Collections.unmodifiableList(cols);
276     }
277     
278     /**
279      * A listener to the parentSelector. When selection change events occur in
280      * the parentSelector, this DataRelationTable is automatically refreshed.
281      */

282     private final class SelectionListener implements PropertyChangeListener JavaDoc {
283         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
284             refresh();
285         }
286     }
287 }
Popular Tags