Statistics
| Revision:

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

History | View | Annotate | Download (15.2 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_REDO.equals(type)
292
                                        || FeatureStoreNotification.AFTER_REFRESH.equals(type)
293
                                        || FeatureStoreNotification.AFTER_FINISHEDITING.equals(type)
294
                                        || FeatureStoreNotification.AFTER_CANCELEDITING.equals(type)) {
295

    
296
                                reloadAll();
297
                        }
298

    
299
                }
300
    }
301

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

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

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

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

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

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

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

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

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

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

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

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

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