Statistics
| Revision:

svn-gvsig-desktop / tags / v2_0_0_Build_2049 / libraries / libFMap_controls / src / org / gvsig / fmap / mapcontrol / dal / feature / swing / table / FeatureTableModel.java @ 38482

History | View | Annotate | Download (15.1 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
5
 *
6
 * 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
 *
11
 * 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
 *
16
 * 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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 {DiSiD Technologies}  {Create a JTable TableModel for a FeatureCollection}
26
 */
27
package org.gvsig.fmap.mapcontrol.dal.feature.swing.table;
28

    
29
import javax.swing.event.TableModelEvent;
30
import javax.swing.table.AbstractTableModel;
31

    
32
import org.gvsig.fmap.dal.DALLocator;
33
import org.gvsig.fmap.dal.exception.DataException;
34
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.FeatureStore;
39
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
40
import org.gvsig.fmap.dal.feature.FeatureType;
41
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
42
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
43
import org.gvsig.tools.exception.BaseException;
44
import org.gvsig.tools.observer.ComplexNotification;
45
import org.gvsig.tools.observer.ComplexObserver;
46
import org.gvsig.tools.observer.Observable;
47
import org.slf4j.Logger;
48
import org.slf4j.LoggerFactory;
49

    
50
/**
51
 * TableModel to access data of Features.
52
 * 
53
 * This table model can't handle a FeatureSet with more than Integer.MAX_VALUE
54
 * elements. In that case, only the first Integer.MAX_VALUE elements will be
55
 * shown.
56
 * 
57
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
58
 */
59
public class FeatureTableModel extends AbstractTableModel implements ComplexObserver {
60

    
61
        
62
        private static final Logger LOG = LoggerFactory
63
                        .getLogger(FeatureTableModel.class);
64
        
65
    private static final long serialVersionUID = -2488157521902851301L;
66

    
67
    private FeaturePagingHelper helper;
68

    
69
    /** Used to know if a modification in the FeatureStore is created by us. */
70
    private EditableFeature editableFeature;
71

    
72
    /**
73
     * Constructs a TableModel from the features of a FeatureStore, with the
74
     * default page size.
75
     * 
76
     * @param featureStore
77
     *            to extract the features from
78
     * @param featureQuery
79
     *            the query to get the features from the store
80
     * @throws BaseException
81
     *             if there is an error reading data from the FeatureStore
82
     */
83
    public FeatureTableModel(FeatureStore featureStore,
84
        FeatureQuery featureQuery) throws BaseException {
85
        this(featureStore, featureQuery, FeaturePagingHelper.DEFAULT_PAGE_SIZE);
86
    }
87

    
88
    /**
89
     * Constructs a TableModel from the features of a FeatureStore, with the
90
     * default page size.
91
     * 
92
     * @param featureStore
93
     *            to extract the features from
94
     * @param featureQuery
95
     *            the query to get the features from the store
96
     * @param pageSize
97
     *            the number of elements per page data
98
     * @throws BaseException
99
     *             if there is an error reading data from the FeatureStore
100
     */
101
    public FeatureTableModel(FeatureStore featureStore,
102
        FeatureQuery featureQuery, int pageSize) throws BaseException {
103
        this(DALLocator.getDataManager().createFeaturePagingHelper(
104
            featureStore, featureQuery, pageSize));
105
    }
106

    
107
    /**
108
     * Constructs a TableModel from a FeatureCollection and a Paging helper.
109
     * 
110
     * @param featureCollection
111
     *            to extract data from
112
     * @param helper
113
     *            the paging helper
114
     * @throws DataException
115
     *             if there is an error reading data from the FeatureStore
116
     */
117
    protected FeatureTableModel(FeaturePagingHelper helper) {
118
        this.helper = helper;
119
        initialize();
120
    }
121

    
122
    public int getColumnCount() {
123
        // Return the number of fields of the Features
124
        FeatureType featureType = getFeatureType();
125
        return featureType.size();
126
    }
127

    
128
    public int getRowCount() {
129
        // Return the total size of the collection
130
        // If the size is bigger than INTEGER.MAX_VALUE, return that instead
131
            try {
132
                long totalSize = getHelper().getTotalSize();
133
                if (totalSize > Integer.MAX_VALUE) {
134
                    return Integer.MAX_VALUE;
135
                } else {
136
                    return (int) totalSize;
137
                }
138
            } catch (ConcurrentDataModificationException e) {
139
                        LOG.debug("Error while getting the total size of the set", e);
140
                        return 0;
141
                }
142
    }
143

    
144
    public Object getValueAt(int rowIndex, int columnIndex) {
145
        // Get the Feature at row "rowIndex", and return the value of the
146
        // attribute at "columnIndex"
147
        Feature feature = getFeatureAt(rowIndex);
148
        return feature == null ? null : getFeatureValue(feature, columnIndex);
149
    }
150

    
151
    /**
152
     * Returns the value for a row position.
153
     * 
154
     * @param rowIndex
155
     *            the row position
156
     * @return the Feature
157
     */
158
    public Feature getFeatureAt(int rowIndex) {
159
        try {
160
            return getHelper().getFeatureAt(rowIndex);
161
        } catch (BaseException ex) {
162
            throw new GetFeatureAtException(rowIndex, ex);
163
        }
164
    }
165

    
166
    public Class<?> getColumnClass(int columnIndex) {
167
        // Return the class of the FeatureAttributeDescriptor for the value
168
        FeatureAttributeDescriptor attributeDesc =
169
            internalGetFeatureDescriptorForColumn(columnIndex);
170
        if (attributeDesc == null) {
171
                return super.getColumnClass(columnIndex);
172
        }
173
        Class<?> clazz = attributeDesc.getObjectClass();
174
        return (clazz == null ? super.getColumnClass(columnIndex) : clazz);
175
    }
176

    
177
    public String getColumnName(int column) {
178
        // Return the Feature attribute name
179
        FeatureAttributeDescriptor attributeDesc =
180
            internalGetFeatureDescriptorForColumn(column);
181
        return attributeDesc.getName();
182
    }
183

    
184
    @Override
185
    public boolean isCellEditable(int rowIndex, int columnIndex) {
186
        if (getFeatureStore().isEditing()) {
187
            FeatureAttributeDescriptor attributeDesc =
188
                internalGetFeatureDescriptorForColumn(columnIndex);
189
            return !attributeDesc.isReadOnly();
190
        }
191

    
192
        return false;
193
    }
194

    
195
    @Override
196
    public void setValueAt(Object value, int rowIndex, int columnIndex) {
197
        // Get the feature at rowIndex
198
        Feature feature = getFeatureAt(rowIndex);
199
        // Only set the value if the feature exists
200
        if (feature != null) {
201
            // We only need to update if the value to set is not equal to the
202
            // current value
203
            Object currentValue = getFeatureValue(feature, columnIndex);
204
            if (value != currentValue
205
                && (value == null || !value.equals(currentValue))) {
206
                try {
207
                    // Store the editable feature to ignore the related store
208
                    // change notification
209
                    editableFeature =
210
                        setFeatureValue(feature, columnIndex, value);
211
                    this.getHelper().update(editableFeature);
212
                    // We'll have already received the event, so we can forget
213
                    // about it
214
                    editableFeature = null;
215
                    getHelper().reloadCurrentPage();
216
                    fireTableCellUpdated(rowIndex, columnIndex);
217
                } catch (BaseException ex) {
218
                    throw new SetFeatureValueException(rowIndex, columnIndex,
219
                        value, ex);
220
                } finally {
221
                    // Just in case
222
                    editableFeature = null;
223
                }
224
            }
225
        }
226
    }
227

    
228
    /**
229
     * Returns a reference to the Paging Helper used to load the data from the
230
     * DataStore.
231
     * 
232
     * @return the paging helper
233
     */
234
    public FeaturePagingHelper getHelper() {
235
        return helper;
236
    }
237

    
238
    /**
239
     * Sets the FeatureType to show in the table. Used for FeatureStores with
240
     * many simultaneous FeatureTypes supported. Will cause a reload of the
241
     * current data.
242
     * 
243
     * @param featureType
244
     *            the FeatureType of the Features
245
     * @throws DataException
246
     *             if there is an error loading the data
247
     */
248
    public void setFeatureType(FeatureType featureType) {
249
        getFeatureQuery().setFeatureType(featureType);
250
        reloadFeatures();
251
        fireTableStructureChanged();
252
    }
253

    
254
    /**
255
     * Sets that the selected Features get returned first.
256
     */
257
    public void setSelectionUp(boolean selectionUp) {
258
        getHelper().setSelectionUp(selectionUp);
259
        fireTableChanged(new TableModelEvent(this, 0, getRowCount() - 1));
260
    }
261

    
262
    public void update(final Observable observable, final Object notification) {
263
            if (notification instanceof ComplexNotification) {
264
                    // A lot of things might have happened in the store, so don't
265
                    // bother looking into each notification.
266
                    reloadAll();
267
            }
268
            else if (observable.equals(getFeatureStore())
269
            && notification instanceof FeatureStoreNotification) {
270
            FeatureStoreNotification fsNotification =
271
                (FeatureStoreNotification) notification;
272
            String type = fsNotification.getType();
273

    
274
            // If there are new, updated or deleted features
275
            // reload the table data
276
            if (FeatureStoreNotification.AFTER_DELETE.equals(type)
277
                || FeatureStoreNotification.AFTER_INSERT.equals(type)) {
278

    
279
                reloadIfFeatureCountChanged(fsNotification.getFeature());
280

    
281
            } else if (FeatureStoreNotification.AFTER_UPDATE.equals(type)) {
282
                    
283
                    reloadIfFeatureUpdated(fsNotification.getFeature());
284
                    
285
                        } else if (FeatureStoreNotification.AFTER_UPDATE_TYPE.equals(type)) {
286

    
287
                                reloadIfTypeChanged(fsNotification.getFeatureType());
288

    
289
                        } else if (FeatureStoreNotification.TRANSFORM_CHANGE.equals(type)
290
                                        || FeatureStoreNotification.AFTER_UNDO.equals(type)
291
                                        || FeatureStoreNotification.AFTER_REFRESH.equals(type)
292
                                        || FeatureStoreNotification.AFTER_FINISHEDITING.equals(type)
293
                                        || FeatureStoreNotification.AFTER_CANCELEDITING.equals(type)) {
294

    
295
                                reloadAll();
296
                        }
297

    
298
                }
299
    }
300

    
301
    /**
302
     * Returns the FeatureStore of the Collection.
303
     * 
304
     * @return the FeatureStore
305
     */
306
    public FeatureStore getFeatureStore() {
307
        return getHelper().getFeatureStore();
308
    }
309

    
310
    /**
311
     * Returns the descriptor of a Feature attribute for a table column.
312
     * 
313
     * @param columnIndex
314
     *            the column index
315
     */
316
    public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) {
317
        return internalGetFeatureDescriptorForColumn(columnIndex);
318
    }
319

    
320
    /**
321
     * @param columnIndex
322
     * @return
323
     */
324
        protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
325
                        int columnIndex) {
326
                FeatureType featureType = getFeatureType();
327
                return featureType == null ? null : featureType
328
                                .getAttributeDescriptor(columnIndex);
329
        }
330

    
331
    /**
332
     * Initialize the TableModel
333
     */
334
    protected void initialize() {
335
        // Add as observable to the FeatureStore, to detect data and selection
336
        // changes
337
        helper.getFeatureStore().addObserver(this);
338
    }
339

    
340
    /**
341
     * Returns the value of a Feature attribute, at the given position.
342
     * 
343
     * @param feature
344
     *            the feature to get the value from
345
     * @param columnIndex
346
     *            the Feature attribute position
347
     * @return the value
348
     */
349
    protected Object getFeatureValue(Feature feature, int columnIndex) {
350
        return feature.get(columnIndex);
351
    }
352

    
353
    /**
354
     * Sets the value of an Feature attribute at the given position.
355
     * 
356
     * @param feature
357
     *            the feature to update
358
     * @param columnIndex
359
     *            the attribute position
360
     * @param value
361
     *            the value to set
362
     * @throws IsNotFeatureSettingException
363
     *             if there is an error setting the value
364
     */
365
    protected EditableFeature setFeatureValue(Feature feature, int columnIndex,
366
        Object value) {
367
        EditableFeature editableFeature = feature.getEditable();
368
        editableFeature.set(columnIndex, value);
369
        return editableFeature;
370
    }
371

    
372
    /**
373
     * Returns the FeatureQuery used to get the Features.
374
     * 
375
     * @return the FeatureQuery
376
     */
377
    public FeatureQuery getFeatureQuery() {
378
        return getHelper().getFeatureQuery();
379
    }
380

    
381
    /**
382
     * Returns the type of the features.
383
     */
384
    private FeatureType getFeatureType() {
385
        return getHelper().getFeatureType();
386
    }
387

    
388
    /**
389
     * Reloads the table data if a feature has been changed, not through the
390
     * table.
391
     */
392
    private void reloadIfFeatureCountChanged(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
            fireTableDataChanged();
399
        }
400
    }
401
    
402
    private void reloadIfFeatureUpdated(Feature feature) {
403
        // Is any data is changed in the FeatureStore, notify the model
404
        // listeners. Ignore the case where the updated feature is
405
        // changed through us.
406
        if (editableFeature == null || !editableFeature.equals(feature)) {
407
            reloadFeatures();
408
            fireTableChanged(new TableModelEvent(this, 0, getRowCount()));            
409
        }
410
    }
411

    
412
    /**
413
     * Reloads data and structure if the {@link FeatureType} of the features
414
     * being shown has changed.
415
     */
416
    private void reloadIfTypeChanged(FeatureType updatedType) {
417
        // If the updated featured type is the one currently being
418
        // shown, reload the table.
419
        if (updatedType != null
420
            && updatedType.getId().equals(getFeatureType().getId())) {
421
            setFeatureType(updatedType);
422
        }
423
    }
424

    
425
    private void reloadAll() {
426
            reloadFeatureType();
427
    }
428

    
429
    private void reloadFeatureType() {
430
        try {
431
            setFeatureType(getHelper().getFeatureStore().getFeatureType(
432
                getHelper().getFeatureType().getId()));
433
        } catch (DataException e) {
434
            throw new FeaturesDataReloadException(e);
435
        }
436
    }
437

    
438
    /**
439
     * Reloads the features shown on the table.
440
     */
441
    private void reloadFeatures() {
442
        try {
443
            getHelper().reload();
444
        } catch (BaseException ex) {
445
            throw new FeaturesDataReloadException(ex);
446
        }
447
    }
448
}