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 @ 41630

History | View | Annotate | Download (18.1 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 editingNotificationManager = MapControlLocator.getEditingNotificationManager();
216
                    EditingNotification notification = editingNotificationManager.notifyObservers(
217
                            this, 
218
                            EditingNotification.BEFORE_UPDATE_FEATURE, 
219
                            null,
220
                            this.getHelper().getFeatureStore(),
221
                            editableFeature);
222
                    if( notification.isCanceled() ) {
223
                        return;
224
                    }
225
                    if( notification.shouldValidateTheFeature() ) {
226
                        if ( !editingNotificationManager.validateFeature(feature) ) {
227
                            return;
228
                        }
229
                    }                    
230
                    this.getHelper().update(editableFeature);
231
                    // We'll have already received the event, so we can forget
232
                    // about it
233
                    getHelper().reloadCurrentPage();
234
                    fireTableCellUpdated(rowIndex, columnIndex);
235
                    
236
                    editingNotificationManager.notifyObservers(
237
                            this, 
238
                            EditingNotification.AFTER_UPDATE_FEATURE, 
239
                            null,
240
                            this.getHelper().getFeatureStore(),
241
                            editableFeature);
242
                    editableFeature = null;
243
                    
244
                } catch (BaseException ex) {
245
                    throw new SetFeatureValueException(rowIndex, columnIndex,
246
                        value, ex);
247
                } finally {
248
                    // Just in case
249
                    editableFeature = null;
250
                }
251
            }
252
        }
253
    }
254

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

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

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

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

    
300
            // If there are new, updated or deleted features
301
            // reload the table data
302
            if (FeatureStoreNotification.AFTER_DELETE.equals(type)
303
                    || FeatureStoreNotification.AFTER_INSERT.equals(type)) {
304
                reloadIfFeatureCountChanged(fsNotification.getFeature());
305

    
306
            } else if (FeatureStoreNotification.AFTER_UPDATE.equals(type)) {
307
                reloadIfFeatureUpdated(fsNotification.getFeature());
308

    
309
            } else if (FeatureStoreNotification.AFTER_UPDATE_TYPE.equals(type)) {
310
                reloadIfTypeChanged(fsNotification.getFeatureType());
311

    
312
            } else if (FeatureStoreNotification.TRANSFORM_CHANGE.equals(type)
313
                    || FeatureStoreNotification.AFTER_UNDO.equals(type)
314
                    || FeatureStoreNotification.AFTER_REDO.equals(type)
315
                    || FeatureStoreNotification.AFTER_REFRESH.equals(type))  {
316
                reloadAll();
317

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

    
338
    protected void updatePaginHelperWithHiddenColums() {
339
        FeatureQuery query = this.getHelper().getFeatureQuery();
340
        if (this.getHelper().getFeatureStore().isEditing()) {
341
            if (query.hasConstantsAttributeNames()) {
342
                query.clearConstantsAttributeNames();
343
            }
344
        } else {
345
            query.setConstantsAttributeNames(this.getHiddenColumnNames());
346
        }
347
        try {
348
            this.getHelper().reload();
349
        } catch (BaseException ex) {
350
            LOG.warn("Can't reload paging-helper.", ex);
351
        }
352
    }
353

    
354
    protected String[] getHiddenColumnNames() {
355
        return null;
356
    }
357
    
358
    /**
359
     * Returns the FeatureStore of the Collection.
360
     * 
361
     * @return the FeatureStore
362
     */
363
    public FeatureStore getFeatureStore() {
364
        return getHelper().getFeatureStore();
365
    }
366

    
367
    /**
368
     * Returns the descriptor of a Feature attribute for a table column.
369
     * 
370
     * @param columnIndex
371
     *            the column index
372
     */
373
    public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) {
374
        return internalGetFeatureDescriptorForColumn(columnIndex);
375
    }
376

    
377
    /**
378
     * @param columnIndex
379
     * @return
380
     */
381
        protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
382
                        int columnIndex) {
383
                FeatureType featureType = getFeatureType();
384
                return featureType == null ? null : featureType
385
                                .getAttributeDescriptor(columnIndex);
386
        }
387

    
388
    /**
389
     * Initialize the TableModel
390
     */
391
    protected void initialize() {
392
        // Add as observable to the FeatureStore, to detect data and selection
393
        // changes
394
        helper.getFeatureStore().addObserver(this);
395
    }
396

    
397
    /**
398
     * Returns the value of a Feature attribute, at the given position.
399
     * 
400
     * @param feature
401
     *            the feature to get the value from
402
     * @param columnIndex
403
     *            the Feature attribute position
404
     * @return the value
405
     */
406
    protected Object getFeatureValue(Feature feature, int columnIndex) {
407
        return feature.get(columnIndex);
408
    }
409

    
410
    /**
411
     * Sets the value of an Feature attribute at the given position.
412
     * 
413
     * @param feature
414
     *            the feature to update
415
     * @param columnIndex
416
     *            the attribute position
417
     * @param value
418
     *            the value to set
419
     * @throws IsNotFeatureSettingException
420
     *             if there is an error setting the value
421
     */
422
    protected EditableFeature setFeatureValue(Feature feature, int columnIndex,
423
        Object value) {
424
        EditableFeature editableFeature = feature.getEditable();
425
        editableFeature.set(columnIndex, value);
426
        return editableFeature;
427
    }
428

    
429
    /**
430
     * Returns the FeatureQuery used to get the Features.
431
     * 
432
     * @return the FeatureQuery
433
     */
434
    public FeatureQuery getFeatureQuery() {
435
        return getHelper().getFeatureQuery();
436
    }
437

    
438
    /**
439
     * Returns the type of the features.
440
     */
441
    private FeatureType getFeatureType() {
442
        return getHelper().getFeatureType();
443
    }
444

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

    
469
    /**
470
     * Reloads data and structure if the {@link FeatureType} of the features
471
     * being shown has changed.
472
     */
473
    private void reloadIfTypeChanged(FeatureType updatedType) {
474
        // If the updated featured type is the one currently being
475
        // shown, reload the table.
476
        if (updatedType != null
477
            && updatedType.getId().equals(getFeatureType().getId())) {
478
            setFeatureType(updatedType);
479
        }
480
    }
481

    
482
    private void reloadAll() {
483
            reloadFeatureType();
484
    }
485

    
486
    private void reloadFeatureType() {
487
        try {
488
            setFeatureType(getHelper().getFeatureStore().getFeatureType(
489
                getHelper().getFeatureType().getId()));
490
        } catch (DataException e) {
491
            throw new FeaturesDataReloadException(e);
492
        }
493
    }
494

    
495
    /**
496
     * Reloads the features shown on the table.
497
     */
498
    private void reloadFeatures() {
499
        try {
500
            getHelper().reload();
501
        } catch (BaseException ex) {
502
            throw new FeaturesDataReloadException(ex);
503
        }
504
    }
505
}