Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.fmap.control / src / main / java / org / gvsig / fmap / mapcontrol / dal / feature / swing / table / FeatureTableModel.java @ 41323

History | View | Annotate | Download (17.6 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
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 3
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
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
/*
25
 * AUTHORS (In addition to CIT):
26
 * 2008 {DiSiD Technologies}  {Create a JTable TableModel for a FeatureCollection}
27
 */
28
package org.gvsig.fmap.mapcontrol.dal.feature.swing.table;
29

    
30
import javax.swing.event.TableModelEvent;
31
import javax.swing.table.AbstractTableModel;
32
import org.gvsig.editing.EditingNotification;
33
import org.gvsig.editing.EditingNotificationManager;
34

    
35
import org.gvsig.fmap.dal.DALLocator;
36
import org.gvsig.fmap.dal.exception.DataException;
37
import org.gvsig.fmap.dal.feature.EditableFeature;
38
import org.gvsig.fmap.dal.feature.Feature;
39
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
40
import org.gvsig.fmap.dal.feature.FeatureQuery;
41
import org.gvsig.fmap.dal.feature.FeatureStore;
42
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
43
import org.gvsig.fmap.dal.feature.FeatureType;
44
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
45
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
46
import org.gvsig.fmap.mapcontrol.MapControlLocator;
47
import org.gvsig.tools.exception.BaseException;
48
import org.gvsig.tools.observer.ComplexNotification;
49
import org.gvsig.tools.observer.ComplexObserver;
50
import org.gvsig.tools.observer.Observable;
51
import org.slf4j.Logger;
52
import org.slf4j.LoggerFactory;
53

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

    
65
        
66
        private static final Logger LOG = LoggerFactory
67
                        .getLogger(FeatureTableModel.class);
68
        
69
    private static final long serialVersionUID = -2488157521902851301L;
70

    
71
    private FeaturePagingHelper helper;
72

    
73
    /** Used to know if a modification in the FeatureStore is created by us. */
74
    private EditableFeature editableFeature;
75

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

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

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

    
126
    public int getColumnCount() {
127
        // Return the number of fields of the Features
128
        FeatureType featureType = getFeatureType();
129
        return featureType.size();
130
    }
131

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

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

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

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

    
181
    public String getColumnName(int column) {
182
        // Return the Feature attribute name
183
        FeatureAttributeDescriptor attributeDesc =
184
            internalGetFeatureDescriptorForColumn(column);
185
        return attributeDesc.getName();
186
    }
187

    
188
    @Override
189
    public boolean isCellEditable(int rowIndex, int columnIndex) {
190
        if (getFeatureStore().isEditing()) {
191
            FeatureAttributeDescriptor attributeDesc =
192
                internalGetFeatureDescriptorForColumn(columnIndex);
193
            return !attributeDesc.isReadOnly();
194
        }
195

    
196
        return false;
197
    }
198

    
199
    @Override
200
    public void setValueAt(Object value, int rowIndex, int columnIndex) {
201
        // Get the feature at rowIndex
202
        Feature feature = getFeatureAt(rowIndex);
203
        // Only set the value if the feature exists
204
        if (feature != null) {
205
            // We only need to update if the value to set is not equal to the
206
            // current value
207
            Object currentValue = getFeatureValue(feature, columnIndex);
208
            if (value != currentValue
209
                && (value == null || !value.equals(currentValue))) {
210
                try {
211
                    // Store the editable feature to ignore the related store
212
                    // change notification
213
                    editableFeature =
214
                        setFeatureValue(feature, columnIndex, value);
215
                    EditingNotificationManager editingNotification = MapControlLocator.getEditingNotificationManager();
216
                    EditingNotification notification = editingNotification.notifyObservers(
217
                            this, 
218
                            EditingNotification.BEFORE_UPDATE_FEATURE, 
219
                            null,
220
                            this.getHelper().getFeatureStore(),
221
                            editableFeature);
222
                    if( notification.isCanceled() ) {
223
                        return;
224
                    }
225

    
226
                    this.getHelper().update(editableFeature);
227
                    // We'll have already received the event, so we can forget
228
                    // about it
229
                    getHelper().reloadCurrentPage();
230
                    fireTableCellUpdated(rowIndex, columnIndex);
231
                    
232
                    editingNotification.notifyObservers(
233
                            this, 
234
                            EditingNotification.AFTER_UPDATE_FEATURE, 
235
                            null,
236
                            this.getHelper().getFeatureStore(),
237
                            editableFeature);
238
                    editableFeature = null;
239
                    
240
                } catch (BaseException ex) {
241
                    throw new SetFeatureValueException(rowIndex, columnIndex,
242
                        value, ex);
243
                } finally {
244
                    // Just in case
245
                    editableFeature = null;
246
                }
247
            }
248
        }
249
    }
250

    
251
    /**
252
     * Returns a reference to the Paging Helper used to load the data from the
253
     * DataStore.
254
     * 
255
     * @return the paging helper
256
     */
257
    public FeaturePagingHelper getHelper() {
258
        return helper;
259
    }
260

    
261
    /**
262
     * Sets the FeatureType to show in the table. Used for FeatureStores with
263
     * many simultaneous FeatureTypes supported. Will cause a reload of the
264
     * current data.
265
     * 
266
     * @param featureType
267
     *            the FeatureType of the Features
268
     * @throws DataException
269
     *             if there is an error loading the data
270
     */
271
    public void setFeatureType(FeatureType featureType) {
272
        getFeatureQuery().setFeatureType(featureType);
273
        reloadFeatures();
274
        fireTableStructureChanged();
275
    }
276

    
277
    /**
278
     * Sets that the selected Features get returned first.
279
     */
280
    public void setSelectionUp(boolean selectionUp) {
281
        getHelper().setSelectionUp(selectionUp);
282
        fireTableChanged(new TableModelEvent(this, 0, getRowCount() - 1));
283
    }
284

    
285
    public void update(final Observable observable, final Object notification) {
286
        if (notification instanceof ComplexNotification) {
287
            // A lot of things might have happened in the store, so don't
288
            // bother looking into each notification.
289
            reloadAll();
290
        } else if (observable.equals(getFeatureStore())
291
                && notification instanceof FeatureStoreNotification) {
292
            FeatureStoreNotification fsNotification
293
                    = (FeatureStoreNotification) notification;
294
            String type = fsNotification.getType();
295

    
296
            // If there are new, updated or deleted features
297
            // reload the table data
298
            if (FeatureStoreNotification.AFTER_DELETE.equals(type)
299
                    || FeatureStoreNotification.AFTER_INSERT.equals(type)) {
300
                reloadIfFeatureCountChanged(fsNotification.getFeature());
301

    
302
            } else if (FeatureStoreNotification.AFTER_UPDATE.equals(type)) {
303
                reloadIfFeatureUpdated(fsNotification.getFeature());
304

    
305
            } else if (FeatureStoreNotification.AFTER_UPDATE_TYPE.equals(type)) {
306
                reloadIfTypeChanged(fsNotification.getFeatureType());
307

    
308
            } else if (FeatureStoreNotification.TRANSFORM_CHANGE.equals(type)
309
                    || FeatureStoreNotification.AFTER_UNDO.equals(type)
310
                    || FeatureStoreNotification.AFTER_REDO.equals(type)
311
                    || FeatureStoreNotification.AFTER_REFRESH.equals(type))  {
312
                reloadAll();
313

    
314
            } else if (FeatureStoreNotification.AFTER_FINISHEDITING.equals(type)
315
                    || FeatureStoreNotification.AFTER_STARTEDITING.equals(type)
316
                    || FeatureStoreNotification.AFTER_CANCELEDITING.equals(type)) {
317
                /*
318
                No tengo nada claro por que es necesario llamar al reloadFeatureType
319
                pero si no se incluye hay problemas si durante la edicion se a?aden
320
                campos a la tabla. Sin esto, al cerrar la edicion, los campos a?adidos 
321
                desaparecen de la tabla aunque estan en el fichero.
322
                Ver ticket #2434 https://devel.gvsig.org/redmine/issues/2434
323
                */
324
                reloadFeatureType();
325
                updatePaginHelperWithHiddenColums();
326
            }
327
        }
328
    }
329

    
330
    protected void updatePaginHelperWithHiddenColums() {
331
        FeatureQuery query = this.getHelper().getFeatureQuery();
332
        if (this.getHelper().getFeatureStore().isEditing()) {
333
            if (query.hasConstantsAttributeNames()) {
334
                query.clearConstantsAttributeNames();
335
            }
336
        } else {
337
            query.setConstantsAttributeNames(this.getHiddenColumnNames());
338
        }
339
        try {
340
            this.getHelper().reload();
341
        } catch (BaseException ex) {
342
            LOG.warn("Can't reload paging-helper.", ex);
343
        }
344
    }
345

    
346
    protected String[] getHiddenColumnNames() {
347
        return null;
348
    }
349
    
350
    /**
351
     * Returns the FeatureStore of the Collection.
352
     * 
353
     * @return the FeatureStore
354
     */
355
    public FeatureStore getFeatureStore() {
356
        return getHelper().getFeatureStore();
357
    }
358

    
359
    /**
360
     * Returns the descriptor of a Feature attribute for a table column.
361
     * 
362
     * @param columnIndex
363
     *            the column index
364
     */
365
    public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) {
366
        return internalGetFeatureDescriptorForColumn(columnIndex);
367
    }
368

    
369
    /**
370
     * @param columnIndex
371
     * @return
372
     */
373
        protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
374
                        int columnIndex) {
375
                FeatureType featureType = getFeatureType();
376
                return featureType == null ? null : featureType
377
                                .getAttributeDescriptor(columnIndex);
378
        }
379

    
380
    /**
381
     * Initialize the TableModel
382
     */
383
    protected void initialize() {
384
        // Add as observable to the FeatureStore, to detect data and selection
385
        // changes
386
        helper.getFeatureStore().addObserver(this);
387
    }
388

    
389
    /**
390
     * Returns the value of a Feature attribute, at the given position.
391
     * 
392
     * @param feature
393
     *            the feature to get the value from
394
     * @param columnIndex
395
     *            the Feature attribute position
396
     * @return the value
397
     */
398
    protected Object getFeatureValue(Feature feature, int columnIndex) {
399
        return feature.get(columnIndex);
400
    }
401

    
402
    /**
403
     * Sets the value of an Feature attribute at the given position.
404
     * 
405
     * @param feature
406
     *            the feature to update
407
     * @param columnIndex
408
     *            the attribute position
409
     * @param value
410
     *            the value to set
411
     * @throws IsNotFeatureSettingException
412
     *             if there is an error setting the value
413
     */
414
    protected EditableFeature setFeatureValue(Feature feature, int columnIndex,
415
        Object value) {
416
        EditableFeature editableFeature = feature.getEditable();
417
        editableFeature.set(columnIndex, value);
418
        return editableFeature;
419
    }
420

    
421
    /**
422
     * Returns the FeatureQuery used to get the Features.
423
     * 
424
     * @return the FeatureQuery
425
     */
426
    public FeatureQuery getFeatureQuery() {
427
        return getHelper().getFeatureQuery();
428
    }
429

    
430
    /**
431
     * Returns the type of the features.
432
     */
433
    private FeatureType getFeatureType() {
434
        return getHelper().getFeatureType();
435
    }
436

    
437
    /**
438
     * Reloads the table data if a feature has been changed, not through the
439
     * table.
440
     */
441
    private void reloadIfFeatureCountChanged(Feature feature) {
442
        // Is any data is changed in the FeatureStore, notify the model
443
        // listeners. Ignore the case where the updated feature is
444
        // changed through us.
445
        if (editableFeature == null || !editableFeature.equals(feature)) {
446
            reloadFeatures();            
447
            fireTableDataChanged();
448
        }
449
    }
450
    
451
    private void reloadIfFeatureUpdated(Feature feature) {
452
        // Is any data is changed in the FeatureStore, notify the model
453
        // listeners. Ignore the case where the updated feature is
454
        // changed through us.
455
        if (editableFeature == null || !editableFeature.equals(feature)) {
456
            reloadFeatures();
457
            fireTableChanged(new TableModelEvent(this, 0, getRowCount()));            
458
        }
459
    }
460

    
461
    /**
462
     * Reloads data and structure if the {@link FeatureType} of the features
463
     * being shown has changed.
464
     */
465
    private void reloadIfTypeChanged(FeatureType updatedType) {
466
        // If the updated featured type is the one currently being
467
        // shown, reload the table.
468
        if (updatedType != null
469
            && updatedType.getId().equals(getFeatureType().getId())) {
470
            setFeatureType(updatedType);
471
        }
472
    }
473

    
474
    private void reloadAll() {
475
            reloadFeatureType();
476
    }
477

    
478
    private void reloadFeatureType() {
479
        try {
480
            setFeatureType(getHelper().getFeatureStore().getFeatureType(
481
                getHelper().getFeatureType().getId()));
482
        } catch (DataException e) {
483
            throw new FeaturesDataReloadException(e);
484
        }
485
    }
486

    
487
    /**
488
     * Reloads the features shown on the table.
489
     */
490
    private void reloadFeatures() {
491
        try {
492
            getHelper().reload();
493
        } catch (BaseException ex) {
494
            throw new FeaturesDataReloadException(ex);
495
        }
496
    }
497
}