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

History | View | Annotate | Download (16.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

    
33
import org.gvsig.fmap.dal.DALLocator;
34
import org.gvsig.fmap.dal.exception.DataException;
35
import org.gvsig.fmap.dal.feature.EditableFeature;
36
import org.gvsig.fmap.dal.feature.Feature;
37
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
38
import org.gvsig.fmap.dal.feature.FeatureQuery;
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
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
43
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
44
import org.gvsig.tools.exception.BaseException;
45
import org.gvsig.tools.observer.ComplexNotification;
46
import org.gvsig.tools.observer.ComplexObserver;
47
import org.gvsig.tools.observer.Observable;
48
import org.slf4j.Logger;
49
import org.slf4j.LoggerFactory;
50

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

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

    
68
    private FeaturePagingHelper helper;
69

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

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

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

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

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

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

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

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

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

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

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

    
193
        return false;
194
    }
195

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

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

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

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

    
263
    public void update(final Observable observable, final Object notification) {
264
        if (notification instanceof ComplexNotification) {
265
            // A lot of things might have happened in the store, so don't
266
            // bother looking into each notification.
267
            reloadAll();
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
                reloadIfFeatureCountChanged(fsNotification.getFeature());
279

    
280
            } else if (FeatureStoreNotification.AFTER_UPDATE.equals(type)) {
281
                reloadIfFeatureUpdated(fsNotification.getFeature());
282

    
283
            } else if (FeatureStoreNotification.AFTER_UPDATE_TYPE.equals(type)) {
284
                reloadIfTypeChanged(fsNotification.getFeatureType());
285

    
286
            } else if (FeatureStoreNotification.TRANSFORM_CHANGE.equals(type)
287
                    || FeatureStoreNotification.AFTER_UNDO.equals(type)
288
                    || FeatureStoreNotification.AFTER_REDO.equals(type)
289
                    || FeatureStoreNotification.AFTER_REFRESH.equals(type))  {
290
                reloadAll();
291

    
292
            } else if (FeatureStoreNotification.AFTER_FINISHEDITING.equals(type)
293
                    || FeatureStoreNotification.AFTER_STARTEDITING.equals(type)
294
                    || FeatureStoreNotification.AFTER_CANCELEDITING.equals(type)) {
295
                /*
296
                No tengo nada claro por que es necesario llamar al reloadFeatureType
297
                pero si no se incluye hay problemas si durante la edicion se a?aden
298
                campos a la tabla. Sin esto, al cerrar la edicion, los campos a?adidos 
299
                desaparecen de la tabla aunque estan en el fichero.
300
                Ver ticket #2434 https://devel.gvsig.org/redmine/issues/2434
301
                */
302
                reloadFeatureType();
303
                updatePaginHelperWithHiddenColums();
304
            }
305
        }
306
    }
307

    
308
    protected void updatePaginHelperWithHiddenColums() {
309
        FeatureQuery query = this.getHelper().getFeatureQuery();
310
        if (this.getHelper().getFeatureStore().isEditing()) {
311
            if (query.hasConstantsAttributeNames()) {
312
                query.clearConstantsAttributeNames();
313
            }
314
        } else {
315
            query.setConstantsAttributeNames(this.getHiddenColumnNames());
316
        }
317
        try {
318
            this.getHelper().reload();
319
        } catch (BaseException ex) {
320
            LOG.warn("Can't reload paging-helper.", ex);
321
        }
322
    }
323

    
324
    protected String[] getHiddenColumnNames() {
325
        return null;
326
    }
327
    
328
    /**
329
     * Returns the FeatureStore of the Collection.
330
     * 
331
     * @return the FeatureStore
332
     */
333
    public FeatureStore getFeatureStore() {
334
        return getHelper().getFeatureStore();
335
    }
336

    
337
    /**
338
     * Returns the descriptor of a Feature attribute for a table column.
339
     * 
340
     * @param columnIndex
341
     *            the column index
342
     */
343
    public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) {
344
        return internalGetFeatureDescriptorForColumn(columnIndex);
345
    }
346

    
347
    /**
348
     * @param columnIndex
349
     * @return
350
     */
351
        protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
352
                        int columnIndex) {
353
                FeatureType featureType = getFeatureType();
354
                return featureType == null ? null : featureType
355
                                .getAttributeDescriptor(columnIndex);
356
        }
357

    
358
    /**
359
     * Initialize the TableModel
360
     */
361
    protected void initialize() {
362
        // Add as observable to the FeatureStore, to detect data and selection
363
        // changes
364
        helper.getFeatureStore().addObserver(this);
365
    }
366

    
367
    /**
368
     * Returns the value of a Feature attribute, at the given position.
369
     * 
370
     * @param feature
371
     *            the feature to get the value from
372
     * @param columnIndex
373
     *            the Feature attribute position
374
     * @return the value
375
     */
376
    protected Object getFeatureValue(Feature feature, int columnIndex) {
377
        return feature.get(columnIndex);
378
    }
379

    
380
    /**
381
     * Sets the value of an Feature attribute at the given position.
382
     * 
383
     * @param feature
384
     *            the feature to update
385
     * @param columnIndex
386
     *            the attribute position
387
     * @param value
388
     *            the value to set
389
     * @throws IsNotFeatureSettingException
390
     *             if there is an error setting the value
391
     */
392
    protected EditableFeature setFeatureValue(Feature feature, int columnIndex,
393
        Object value) {
394
        EditableFeature editableFeature = feature.getEditable();
395
        editableFeature.set(columnIndex, value);
396
        return editableFeature;
397
    }
398

    
399
    /**
400
     * Returns the FeatureQuery used to get the Features.
401
     * 
402
     * @return the FeatureQuery
403
     */
404
    public FeatureQuery getFeatureQuery() {
405
        return getHelper().getFeatureQuery();
406
    }
407

    
408
    /**
409
     * Returns the type of the features.
410
     */
411
    private FeatureType getFeatureType() {
412
        return getHelper().getFeatureType();
413
    }
414

    
415
    /**
416
     * Reloads the table data if a feature has been changed, not through the
417
     * table.
418
     */
419
    private void reloadIfFeatureCountChanged(Feature feature) {
420
        // Is any data is changed in the FeatureStore, notify the model
421
        // listeners. Ignore the case where the updated feature is
422
        // changed through us.
423
        if (editableFeature == null || !editableFeature.equals(feature)) {
424
            reloadFeatures();            
425
            fireTableDataChanged();
426
        }
427
    }
428
    
429
    private void reloadIfFeatureUpdated(Feature feature) {
430
        // Is any data is changed in the FeatureStore, notify the model
431
        // listeners. Ignore the case where the updated feature is
432
        // changed through us.
433
        if (editableFeature == null || !editableFeature.equals(feature)) {
434
            reloadFeatures();
435
            fireTableChanged(new TableModelEvent(this, 0, getRowCount()));            
436
        }
437
    }
438

    
439
    /**
440
     * Reloads data and structure if the {@link FeatureType} of the features
441
     * being shown has changed.
442
     */
443
    private void reloadIfTypeChanged(FeatureType updatedType) {
444
        // If the updated featured type is the one currently being
445
        // shown, reload the table.
446
        if (updatedType != null
447
            && updatedType.getId().equals(getFeatureType().getId())) {
448
            setFeatureType(updatedType);
449
        }
450
    }
451

    
452
    private void reloadAll() {
453
            reloadFeatureType();
454
    }
455

    
456
    private void reloadFeatureType() {
457
        try {
458
            setFeatureType(getHelper().getFeatureStore().getFeatureType(
459
                getHelper().getFeatureType().getId()));
460
        } catch (DataException e) {
461
            throw new FeaturesDataReloadException(e);
462
        }
463
    }
464

    
465
    /**
466
     * Reloads the features shown on the table.
467
     */
468
    private void reloadFeatures() {
469
        try {
470
            getHelper().reload();
471
        } catch (BaseException ex) {
472
            throw new FeaturesDataReloadException(ex);
473
        }
474
    }
475
}