svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libFMap_controls / src / org / gvsig / fmap / mapcontrol / dal / feature / swing / table / FeatureTableModel.java @ 37604
History | View | Annotate | Download (14.8 KB)
1 | 23697 | cordinyana | /* gvSIG. Geographic Information System of the Valencian Government
|
---|---|---|---|
2 | *
|
||
3 | * Copyright (C) 2007-2008 Infrastructures and Transports Department
|
||
4 | 31544 | cordinyana | * of the Valencian Government (CIT)
|
5 | 27234 | jmvivo | *
|
6 | 23697 | cordinyana | * This program is free software; you can redistribute it and/or
|
7 | * modify it under the terms of the GNU General Public License
|
||
8 | * as published by the Free Software Foundation; either version 2
|
||
9 | * of the License, or (at your option) any later version.
|
||
10 | 27234 | jmvivo | *
|
11 | 23697 | cordinyana | * This program is distributed in the hope that it will be useful,
|
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
14 | * GNU General Public License for more details.
|
||
15 | 27234 | jmvivo | *
|
16 | 23697 | cordinyana | * You should have received a copy of the GNU General Public License
|
17 | * along with this program; if not, write to the Free Software
|
||
18 | 27234 | jmvivo | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 | 23697 | cordinyana | * MA 02110-1301, USA.
|
20 | 27234 | jmvivo | *
|
21 | 23697 | cordinyana | */
|
22 | |||
23 | /*
|
||
24 | * AUTHORS (In addition to CIT):
|
||
25 | * 2008 {DiSiD Technologies} {Create a JTable TableModel for a FeatureCollection}
|
||
26 | */
|
||
27 | 31616 | cordinyana | package org.gvsig.fmap.mapcontrol.dal.feature.swing.table; |
28 | 23697 | cordinyana | |
29 | 33378 | cordinyana | import javax.swing.event.TableModelEvent; |
30 | 23697 | cordinyana | import javax.swing.table.AbstractTableModel; |
31 | |||
32 | 33378 | cordinyana | import org.gvsig.fmap.dal.DALLocator; |
33 | 24621 | cordinyana | import org.gvsig.fmap.dal.exception.DataException; |
34 | 31034 | cordinyana | import org.gvsig.fmap.dal.feature.EditableFeature; |
35 | import org.gvsig.fmap.dal.feature.Feature; |
||
36 | import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor; |
||
37 | import org.gvsig.fmap.dal.feature.FeatureQuery; |
||
38 | import org.gvsig.fmap.dal.feature.FeatureSet; |
||
39 | import org.gvsig.fmap.dal.feature.FeatureStore; |
||
40 | import org.gvsig.fmap.dal.feature.FeatureStoreNotification; |
||
41 | import org.gvsig.fmap.dal.feature.FeatureType; |
||
42 | 24621 | cordinyana | import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper; |
43 | 33205 | cordinyana | import org.gvsig.tools.exception.BaseException; |
44 | 25742 | cordinyana | import org.gvsig.tools.observer.Observable; |
45 | import org.gvsig.tools.observer.Observer; |
||
46 | 23697 | cordinyana | |
47 | /**
|
||
48 | 23799 | cordinyana | * TableModel to access data of Features.
|
49 | 33385 | cordinyana | *
|
50 | 23991 | cordinyana | * This table model can't handle a FeatureSet with more than Integer.MAX_VALUE
|
51 | * elements. In that case, only the first Integer.MAX_VALUE elements will be
|
||
52 | * shown.
|
||
53 | 33385 | cordinyana | *
|
54 | 23697 | cordinyana | * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
|
55 | */
|
||
56 | 25742 | cordinyana | public class FeatureTableModel extends AbstractTableModel implements Observer { |
57 | 23697 | cordinyana | |
58 | private static final long serialVersionUID = -2488157521902851301L; |
||
59 | |||
60 | 23715 | cordinyana | private FeaturePagingHelper helper;
|
61 | 23697 | cordinyana | |
62 | 25742 | cordinyana | /** Used to know if a modification in the FeatureStore is created by us. */
|
63 | private EditableFeature editableFeature;
|
||
64 | |||
65 | 23697 | cordinyana | /**
|
66 | 24621 | cordinyana | * Constructs a TableModel from the features of a FeatureStore, with the
|
67 | * default page size.
|
||
68 | 33385 | cordinyana | *
|
69 | 24621 | cordinyana | * @param featureStore
|
70 | * to extract the features from
|
||
71 | * @param featureQuery
|
||
72 | * the query to get the features from the store
|
||
73 | 33385 | cordinyana | * @throws BaseException
|
74 | 23697 | cordinyana | * if there is an error reading data from the FeatureStore
|
75 | */
|
||
76 | 33385 | cordinyana | public FeatureTableModel(FeatureStore featureStore,
|
77 | FeatureQuery featureQuery) throws BaseException {
|
||
78 | this(featureStore, featureQuery, FeaturePagingHelper.DEFAULT_PAGE_SIZE);
|
||
79 | } |
||
80 | 23697 | cordinyana | |
81 | /**
|
||
82 | 24621 | cordinyana | * Constructs a TableModel from the features of a FeatureStore, with the
|
83 | * default page size.
|
||
84 | 33385 | cordinyana | *
|
85 | 24621 | cordinyana | * @param featureStore
|
86 | * to extract the features from
|
||
87 | * @param featureQuery
|
||
88 | * the query to get the features from the store
|
||
89 | 23697 | cordinyana | * @param pageSize
|
90 | * the number of elements per page data
|
||
91 | 33385 | cordinyana | * @throws BaseException
|
92 | 23697 | cordinyana | * if there is an error reading data from the FeatureStore
|
93 | */
|
||
94 | 33385 | cordinyana | public FeatureTableModel(FeatureStore featureStore,
|
95 | FeatureQuery featureQuery, int pageSize) throws BaseException { |
||
96 | this(DALLocator.getDataManager().createFeaturePagingHelper(
|
||
97 | featureStore, featureQuery, pageSize)); |
||
98 | } |
||
99 | 23697 | cordinyana | |
100 | /**
|
||
101 | * Constructs a TableModel from a FeatureCollection and a Paging helper.
|
||
102 | 33385 | cordinyana | *
|
103 | 23697 | cordinyana | * @param featureCollection
|
104 | * to extract data from
|
||
105 | * @param helper
|
||
106 | * the paging helper
|
||
107 | 23791 | cordinyana | * @throws DataException
|
108 | 23697 | cordinyana | * if there is an error reading data from the FeatureStore
|
109 | */
|
||
110 | 24621 | cordinyana | protected FeatureTableModel(FeaturePagingHelper helper) {
|
111 | 23697 | cordinyana | this.helper = helper;
|
112 | 23715 | cordinyana | initialize(); |
113 | 23697 | cordinyana | } |
114 | |||
115 | public int getColumnCount() { |
||
116 | // Return the number of fields of the Features
|
||
117 | FeatureType featureType = getFeatureType(); |
||
118 | 23787 | cordinyana | return featureType.size();
|
119 | 23697 | cordinyana | } |
120 | |||
121 | public int getRowCount() { |
||
122 | 33385 | cordinyana | // Return the total size of the collection
|
123 | // If the size is bigger than INTEGER.MAX_VALUE, return that instead
|
||
124 | long totalSize = getHelper().getTotalSize();
|
||
125 | if (totalSize > Integer.MAX_VALUE) { |
||
126 | return Integer.MAX_VALUE; |
||
127 | } else {
|
||
128 | return (int) totalSize; |
||
129 | 23991 | cordinyana | } |
130 | 23697 | cordinyana | } |
131 | |||
132 | public Object getValueAt(int rowIndex, int columnIndex) { |
||
133 | // Get the Feature at row "rowIndex", and return the value of the
|
||
134 | // attribute at "columnIndex"
|
||
135 | 23715 | cordinyana | Feature feature = getFeatureAt(rowIndex); |
136 | return getFeatureValue(feature, columnIndex);
|
||
137 | 23697 | cordinyana | } |
138 | |||
139 | 25742 | cordinyana | /**
|
140 | * Returns the value for a row position.
|
||
141 | 33385 | cordinyana | *
|
142 | 25742 | cordinyana | * @param rowIndex
|
143 | * the row position
|
||
144 | * @return the Feature
|
||
145 | */
|
||
146 | public Feature getFeatureAt(int rowIndex) { |
||
147 | try {
|
||
148 | return getHelper().getFeatureAt(rowIndex);
|
||
149 | 33385 | cordinyana | } catch (BaseException ex) {
|
150 | 25742 | cordinyana | throw new GetFeatureAtException(rowIndex, ex); |
151 | } |
||
152 | } |
||
153 | |||
154 | 24801 | cordinyana | public Class<?> getColumnClass(int columnIndex) { |
155 | 23697 | cordinyana | // Return the class of the FeatureAttributeDescriptor for the value
|
156 | 33385 | cordinyana | FeatureAttributeDescriptor attributeDesc = |
157 | internalGetFeatureDescriptorForColumn(columnIndex); |
||
158 | 24801 | cordinyana | Class<?> clazz = attributeDesc.getObjectClass();
|
159 | 23697 | cordinyana | return (clazz == null ? super.getColumnClass(columnIndex) : clazz); |
160 | } |
||
161 | 23715 | cordinyana | |
162 | 23697 | cordinyana | public String getColumnName(int column) { |
163 | // Return the Feature attribute name
|
||
164 | 33385 | cordinyana | FeatureAttributeDescriptor attributeDesc = |
165 | internalGetFeatureDescriptorForColumn(column); |
||
166 | 23697 | cordinyana | return attributeDesc.getName();
|
167 | } |
||
168 | |||
169 | 23715 | cordinyana | @Override
|
170 | public boolean isCellEditable(int rowIndex, int columnIndex) { |
||
171 | 25848 | cordinyana | if (getFeatureStore().isEditing()) {
|
172 | 33385 | cordinyana | FeatureAttributeDescriptor attributeDesc = |
173 | internalGetFeatureDescriptorForColumn(columnIndex); |
||
174 | 25848 | cordinyana | return !attributeDesc.isReadOnly();
|
175 | } |
||
176 | |||
177 | return false; |
||
178 | 23715 | cordinyana | } |
179 | |||
180 | @Override
|
||
181 | public void setValueAt(Object value, int rowIndex, int columnIndex) { |
||
182 | // Get the feature at rowIndex
|
||
183 | Feature feature = getFeatureAt(rowIndex); |
||
184 | // Only set the value if the feature exists
|
||
185 | if (feature != null) { |
||
186 | // We only need to update if the value to set is not equal to the
|
||
187 | // current value
|
||
188 | Object currentValue = getFeatureValue(feature, columnIndex);
|
||
189 | if (value != currentValue
|
||
190 | 33385 | cordinyana | && (value == null || !value.equals(currentValue))) {
|
191 | 23715 | cordinyana | try {
|
192 | 25742 | cordinyana | // Store the editable feature to ignore the related store
|
193 | // change notification
|
||
194 | 33385 | cordinyana | editableFeature = |
195 | setFeatureValue(feature, columnIndex, value); |
||
196 | 27234 | jmvivo | this.getHelper().update(editableFeature);
|
197 | 25742 | cordinyana | // We'll have already received the event, so we can forget
|
198 | // about it
|
||
199 | editableFeature = null;
|
||
200 | 23787 | cordinyana | getHelper().reloadCurrentPage(); |
201 | 23715 | cordinyana | fireTableCellUpdated(rowIndex, columnIndex); |
202 | 33385 | cordinyana | } catch (BaseException ex) {
|
203 | 23715 | cordinyana | throw new SetFeatureValueException(rowIndex, columnIndex, |
204 | 33385 | cordinyana | value, ex); |
205 | 25742 | cordinyana | } finally {
|
206 | // Just in case
|
||
207 | editableFeature = null;
|
||
208 | 23715 | cordinyana | } |
209 | } |
||
210 | } |
||
211 | } |
||
212 | |||
213 | /**
|
||
214 | 23697 | cordinyana | * Returns a reference to the Paging Helper used to load the data from the
|
215 | * DataStore.
|
||
216 | 33385 | cordinyana | *
|
217 | 23697 | cordinyana | * @return the paging helper
|
218 | */
|
||
219 | 23715 | cordinyana | public FeaturePagingHelper getHelper() {
|
220 | 23697 | cordinyana | return helper;
|
221 | } |
||
222 | 23715 | cordinyana | |
223 | 23697 | cordinyana | /**
|
224 | 23787 | cordinyana | * Sets the FeatureType to show in the table. Used for FeatureStores with
|
225 | * many simultaneous FeatureTypes supported. Will cause a reload of the
|
||
226 | * current data.
|
||
227 | 33385 | cordinyana | *
|
228 | 23787 | cordinyana | * @param featureType
|
229 | * the FeatureType of the Features
|
||
230 | * @throws DataException
|
||
231 | * if there is an error loading the data
|
||
232 | */
|
||
233 | 25842 | cordinyana | public void setFeatureType(FeatureType featureType) { |
234 | 23787 | cordinyana | getFeatureQuery().setFeatureType(featureType); |
235 | 25842 | cordinyana | reloadFeatures(); |
236 | 23787 | cordinyana | fireTableStructureChanged(); |
237 | } |
||
238 | |||
239 | 33385 | cordinyana | /**
|
240 | * Sets that the selected Features get returned first.
|
||
241 | */
|
||
242 | public void setSelectionUp(boolean selectionUp) { |
||
243 | getHelper().setSelectionUp(selectionUp); |
||
244 | fireTableChanged(new TableModelEvent(this, 0, getRowCount() - 1)); |
||
245 | } |
||
246 | 33378 | cordinyana | |
247 | 25742 | cordinyana | public void update(Observable observable, Object notification) { |
248 | if (observable.equals(getFeatureStore())
|
||
249 | 33385 | cordinyana | && notification instanceof FeatureStoreNotification) {
|
250 | FeatureStoreNotification fsNotification = |
||
251 | (FeatureStoreNotification) notification; |
||
252 | 25742 | cordinyana | String type = fsNotification.getType();
|
253 | |||
254 | 25842 | cordinyana | // If there are new, updated or deleted features
|
255 | // reload the table data
|
||
256 | if (FeatureStoreNotification.AFTER_DELETE.equals(type)
|
||
257 | 37604 | jpiera | || FeatureStoreNotification.AFTER_INSERT.equals(type)) { |
258 | 25842 | cordinyana | |
259 | 37604 | jpiera | reloadIfFeatureCountChanged(fsNotification.getFeature()); |
260 | 25842 | cordinyana | |
261 | 37604 | jpiera | } else if (FeatureStoreNotification.AFTER_UPDATE.equals(type)) { |
262 | |||
263 | reloadIfFeatureUpdated(fsNotification.getFeature()); |
||
264 | 33385 | cordinyana | } else
|
265 | if (FeatureStoreNotification.AFTER_UPDATE_TYPE.equals(type)) {
|
||
266 | 25842 | cordinyana | |
267 | 33385 | cordinyana | reloadIfTypeChanged(fsNotification.getFeatureType()); |
268 | 25842 | cordinyana | |
269 | 33385 | cordinyana | } else
|
270 | if (FeatureStoreNotification.TRANSFORM_CHANGE.equals(type)) {
|
||
271 | reloadIfTypeTransformed(); |
||
272 | 25842 | cordinyana | |
273 | 33385 | cordinyana | } else
|
274 | if (FeatureStoreNotification.AFTER_FINISHEDITING
|
||
275 | .equals(type) |
||
276 | || FeatureStoreNotification.AFTER_CANCELEDITING |
||
277 | .equals(type)) { |
||
278 | reloadIfTypeTransformed(); |
||
279 | } |
||
280 | 25842 | cordinyana | |
281 | 25742 | cordinyana | } |
282 | } |
||
283 | |||
284 | 23787 | cordinyana | /**
|
285 | 25742 | cordinyana | * Returns the FeatureStore of the Collection.
|
286 | 33385 | cordinyana | *
|
287 | 25742 | cordinyana | * @return the FeatureStore
|
288 | 23715 | cordinyana | */
|
289 | 25742 | cordinyana | public FeatureStore getFeatureStore() {
|
290 | return getHelper().getFeatureStore();
|
||
291 | 23715 | cordinyana | } |
292 | |||
293 | /**
|
||
294 | 25838 | cordinyana | * Returns the descriptor of a Feature attribute for a table column.
|
295 | 33385 | cordinyana | *
|
296 | 25838 | cordinyana | * @param columnIndex
|
297 | * the column index
|
||
298 | */
|
||
299 | public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) { |
||
300 | 27897 | cordinyana | return internalGetFeatureDescriptorForColumn(columnIndex);
|
301 | } |
||
302 | |||
303 | /**
|
||
304 | * @param columnIndex
|
||
305 | * @return
|
||
306 | */
|
||
307 | protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
|
||
308 | 33385 | cordinyana | int columnIndex) {
|
309 | 25838 | cordinyana | return getFeatureType().getAttributeDescriptor(columnIndex);
|
310 | } |
||
311 | |||
312 | /**
|
||
313 | 25742 | cordinyana | * Initialize the TableModel
|
314 | 23715 | cordinyana | */
|
315 | 25742 | cordinyana | protected void initialize() { |
316 | // Add as observable to the FeatureStore, to detect data and selection
|
||
317 | // changes
|
||
318 | helper.getFeatureStore().addObserver(this);
|
||
319 | 23715 | cordinyana | } |
320 | |||
321 | /**
|
||
322 | * Returns the value of a Feature attribute, at the given position.
|
||
323 | 33385 | cordinyana | *
|
324 | 23715 | cordinyana | * @param feature
|
325 | * the feature to get the value from
|
||
326 | * @param columnIndex
|
||
327 | * the Feature attribute position
|
||
328 | * @return the value
|
||
329 | */
|
||
330 | protected Object getFeatureValue(Feature feature, int columnIndex) { |
||
331 | return feature.get(columnIndex);
|
||
332 | } |
||
333 | |||
334 | /**
|
||
335 | * Sets the value of an Feature attribute at the given position.
|
||
336 | 33385 | cordinyana | *
|
337 | 23715 | cordinyana | * @param feature
|
338 | * the feature to update
|
||
339 | * @param columnIndex
|
||
340 | * the attribute position
|
||
341 | * @param value
|
||
342 | * the value to set
|
||
343 | * @throws IsNotFeatureSettingException
|
||
344 | * if there is an error setting the value
|
||
345 | */
|
||
346 | 23787 | cordinyana | protected EditableFeature setFeatureValue(Feature feature, int columnIndex, |
347 | 33385 | cordinyana | Object value) {
|
348 | 23787 | cordinyana | EditableFeature editableFeature = feature.getEditable(); |
349 | editableFeature.set(columnIndex, value); |
||
350 | return editableFeature;
|
||
351 | 23715 | cordinyana | } |
352 | |||
353 | /**
|
||
354 | 23991 | cordinyana | * Returns the FeatureSet used to get the data.
|
355 | 33385 | cordinyana | *
|
356 | 23991 | cordinyana | * @return the FeatureSet
|
357 | 23697 | cordinyana | */
|
358 | 23991 | cordinyana | protected FeatureSet getFeatureSet() {
|
359 | return getHelper().getFeatureSet();
|
||
360 | 23697 | cordinyana | } |
361 | |||
362 | /**
|
||
363 | 23715 | cordinyana | * Returns the FeatureQuery used to get the Features.
|
364 | 33385 | cordinyana | *
|
365 | 23715 | cordinyana | * @return the FeatureQuery
|
366 | */
|
||
367 | 36479 | cordinyana | public FeatureQuery getFeatureQuery() {
|
368 | 23715 | cordinyana | return getHelper().getFeatureQuery();
|
369 | } |
||
370 | 23787 | cordinyana | |
371 | 23715 | cordinyana | /**
|
372 | 23697 | cordinyana | * Returns the type of the features.
|
373 | */
|
||
374 | private FeatureType getFeatureType() {
|
||
375 | 27234 | jmvivo | return getHelper().getFeatureType();
|
376 | 23697 | cordinyana | } |
377 | 25842 | cordinyana | |
378 | /**
|
||
379 | * Reloads the table data if a feature has been changed, not through the
|
||
380 | * table.
|
||
381 | */
|
||
382 | 37604 | jpiera | private void reloadIfFeatureCountChanged(Feature feature) { |
383 | 25842 | cordinyana | // Is any data is changed in the FeatureStore, notify the model
|
384 | 33385 | cordinyana | // listeners. Ignore the case where the updated feature is
|
385 | // changed through us.
|
||
386 | if (editableFeature == null || !editableFeature.equals(feature)) { |
||
387 | 37604 | jpiera | reloadFeatures(); |
388 | 33385 | cordinyana | fireTableDataChanged(); |
389 | } |
||
390 | 25842 | cordinyana | } |
391 | 37604 | jpiera | |
392 | private void reloadIfFeatureUpdated(Feature feature) { |
||
393 | // Is any data is changed in the FeatureStore, notify the model
|
||
394 | // listeners. Ignore the case where the updated feature is
|
||
395 | // changed through us.
|
||
396 | if (editableFeature == null || !editableFeature.equals(feature)) { |
||
397 | reloadFeatures(); |
||
398 | fireTableChanged(new TableModelEvent(this, 0, getRowCount())); |
||
399 | } |
||
400 | } |
||
401 | 25842 | cordinyana | |
402 | /**
|
||
403 | * Reloads data and structure if the {@link FeatureType} of the features
|
||
404 | * being shown has changed.
|
||
405 | */
|
||
406 | private void reloadIfTypeChanged(FeatureType updatedType) { |
||
407 | // If the updated featured type is the one currently being
|
||
408 | // shown, reload the table.
|
||
409 | 33385 | cordinyana | if (updatedType != null |
410 | && updatedType.getId().equals(getFeatureType().getId())) { |
||
411 | 25842 | cordinyana | setFeatureType(updatedType); |
412 | } |
||
413 | } |
||
414 | |||
415 | /**
|
||
416 | * Reloads data and structure if the {@link FeatureType} of the features
|
||
417 | * being shown has been transformed.
|
||
418 | */
|
||
419 | 27310 | jmvivo | private void reloadIfTypeTransformed() { |
420 | 33385 | cordinyana | try {
|
421 | setFeatureType(getHelper().getFeatureStore().getFeatureType( |
||
422 | getHelper().getFeatureType().getId())); |
||
423 | } catch (DataException e) {
|
||
424 | throw new FeaturesDataReloadException(e); |
||
425 | } |
||
426 | 25842 | cordinyana | } |
427 | |||
428 | /**
|
||
429 | * Reloads the features shown on the table.
|
||
430 | */
|
||
431 | private void reloadFeatures() { |
||
432 | try {
|
||
433 | getHelper().reload(); |
||
434 | 33385 | cordinyana | } catch (BaseException ex) {
|
435 | 25842 | cordinyana | throw new FeaturesDataReloadException(ex); |
436 | } |
||
437 | } |
||
438 | 33385 | cordinyana | } |