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

History | View | Annotate | Download (26.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 java.awt.event.ActionEvent;
31
import java.awt.event.ActionListener;
32

    
33
import javax.swing.SwingUtilities;
34
import javax.swing.Timer;
35
import javax.swing.event.TableModelEvent;
36
import javax.swing.table.AbstractTableModel;
37

    
38
import org.gvsig.fmap.dal.DALLocator;
39
import org.gvsig.fmap.dal.EditingNotification;
40
import org.gvsig.fmap.dal.EditingNotificationManager;
41
import org.gvsig.fmap.dal.exception.DataException;
42
import org.gvsig.fmap.dal.feature.EditableFeature;
43
import org.gvsig.fmap.dal.feature.Feature;
44
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
45
import org.gvsig.fmap.dal.feature.FeatureQuery;
46
import org.gvsig.fmap.dal.feature.FeatureStore;
47
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
48
import org.gvsig.fmap.dal.feature.FeatureType;
49
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
50
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
51
import org.gvsig.fmap.dal.swing.DALSwingLocator;
52
import org.gvsig.tools.exception.BaseException;
53
import org.gvsig.tools.logger.FilteredLogger;
54
import org.gvsig.tools.observer.ComplexNotification;
55
import org.gvsig.tools.observer.ComplexObserver;
56
import org.gvsig.tools.observer.Observable;
57
import org.slf4j.Logger;
58
import org.slf4j.LoggerFactory;
59

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

    
71

    
72
        private static final Logger LOGGER = LoggerFactory
73
                        .getLogger(FeatureTableModel.class);
74
        
75
    private static final long serialVersionUID = -2488157521902851301L;
76

    
77
    private FeaturePagingHelper helper;
78

    
79
    /** Used to know if a modification in the FeatureStore is created by us. */
80
    private EditableFeature editableFeature;
81

    
82
    private boolean selectionLocked=false;
83
    private final FilteredLogger filterlogger;
84

    
85
    /**
86
     * Constructs a TableModel from the features of a FeatureStore, with the
87
     * default page size.
88
     *
89
     * @param featureStore
90
     *            to extract the features from
91
     * @param featureQuery
92
     *            the query to get the features from the store
93
     * @throws BaseException
94
     *             if there is an error reading data from the FeatureStore
95
     */
96
    public FeatureTableModel(FeatureStore featureStore,
97
        FeatureQuery featureQuery) throws BaseException {
98
        this(featureStore, featureQuery, FeaturePagingHelper.DEFAULT_PAGE_SIZE);
99
    }
100

    
101
    /**
102
     * Constructs a TableModel from the features of a FeatureStore, with the
103
     * default page size.
104
     *
105
     * @param featureStore
106
     *            to extract the features from
107
     * @param featureQuery
108
     *            the query to get the features from the store
109
     * @param pageSize
110
     *            the number of elements per page data
111
     * @throws BaseException
112
     *             if there is an error reading data from the FeatureStore
113
     */
114
    public FeatureTableModel(FeatureStore featureStore,
115
        FeatureQuery featureQuery, int pageSize) throws BaseException {
116
        this(DALLocator.getDataManager().createFeaturePagingHelper(
117
            featureStore, featureQuery, pageSize));
118
    }
119

    
120
    /**
121
     * Constructs a TableModel from a FeatureCollection and a Paging helper.
122
     *
123
     * @param featureCollection
124
     *            to extract data from
125
     * @param helper
126
     *            the paging helper
127
     * @throws DataException
128
     *             if there is an error reading data from the FeatureStore
129
     */
130
    protected FeatureTableModel(FeaturePagingHelper helper) {
131
        this.helper = helper;
132
        this.filterlogger = new FilteredLogger(LOGGER, "SimpleFeaturesTableModel", 10);
133
        this.filterlogger.setInterval(30000);
134
        initialize();
135
    }
136

    
137
    public int getColumnCount() {
138
        // Return the number of fields of the Features
139
        FeatureType featureType = getFeatureType();
140
        return featureType.size();
141
    }
142

    
143
    public int getRowCount() {
144
        // Return the total size of the collection
145
        // If the size is bigger than INTEGER.MAX_VALUE, return that instead
146
            try {
147
                long totalSize = getHelper().getTotalSize();
148
                if (totalSize > Integer.MAX_VALUE) {
149
                    return Integer.MAX_VALUE;
150
                } else {
151
                    return (int) totalSize;
152
                }
153
            } catch (ConcurrentDataModificationException e) {
154
                        LOGGER.debug("Error while getting the total size of the set", e);
155
                        return 0;
156
                }
157
    }
158

    
159
    public Object getValueAt(int rowIndex, int columnIndex) {
160
        // Get the Feature at row "rowIndex", and return the value of the
161
        // attribute at "columnIndex"
162
        try {
163
            Feature feature = getFeatureAt(rowIndex);
164
            if (feature == null) {
165
                return null;
166
            }
167
            Object value;
168
            value = getFeatureValue(feature, columnIndex);
169
            return value;
170
        } catch (Throwable ex) {
171
            filterlogger.warn("Not been able to retrieve feature value", ex);
172
            return null;
173
        }
174
    }
175

    
176
    /**
177
     * Returns the value for a row position.
178
     *
179
     * @param rowIndex
180
     *            the row position
181
     * @return the Feature
182
     */
183
    public Feature getFeatureAt(int rowIndex) {
184
        try {
185
            return getHelper().getFeatureAt(rowIndex);
186
        } catch (BaseException ex) {
187
            throw new GetFeatureAtException(rowIndex, ex);
188
        }
189
    }
190

    
191
    public Class<?> getColumnClass(int columnIndex) {
192
        // Return the class of the FeatureAttributeDescriptor for the value
193
        FeatureAttributeDescriptor attributeDesc =
194
            internalGetFeatureDescriptorForColumn(columnIndex);
195
        if (attributeDesc == null) {
196
                return super.getColumnClass(columnIndex);
197
        }
198
        Class<?> clazz = attributeDesc.getObjectClass();
199
        return (clazz == null ? super.getColumnClass(columnIndex) : clazz);
200
    }
201

    
202
    public String getColumnName(int column) {
203
        // Return the Feature attribute name
204
        FeatureAttributeDescriptor attributeDesc =
205
            internalGetFeatureDescriptorForColumn(column);
206
        return attributeDesc.getName();
207
    }
208

    
209
    @Override
210
    public boolean isCellEditable(int rowIndex, int columnIndex) {
211
        if (getFeatureStore().isEditing()) {
212
            FeatureAttributeDescriptor attributeDesc =
213
                internalGetFeatureDescriptorForColumn(columnIndex);
214
            return !attributeDesc.isReadOnly();
215
        }
216

    
217
        return false;
218
    }
219

    
220
    @Override
221
    public void setValueAt(Object value, int rowIndex, int columnIndex) {
222
        // Get the feature at rowIndex
223
        Feature feature = getFeatureAt(rowIndex);
224
        // Only set the value if the feature exists
225
        if (feature != null) {
226
            // We only need to update if the value to set is not equal to the
227
            // current value
228
            Object currentValue = getFeatureValue(feature, columnIndex);
229
            if (value != currentValue
230
                && (value == null || !value.equals(currentValue))) {
231
                try {
232
                    // Store the editable feature to ignore the related store
233
                    // change notification
234
                    editableFeature =
235
                        setFeatureValue(feature, columnIndex, value);
236
                    EditingNotificationManager editingNotificationManager = DALSwingLocator.getEditingNotificationManager();
237
                    EditingNotification notification = editingNotificationManager.notifyObservers(
238
                            this,
239
                            EditingNotification.BEFORE_UPDATE_FEATURE,
240
                            null,
241
                            this.getHelper().getFeatureStore(),
242
                            editableFeature);
243
                    if( notification.isCanceled() ) {
244
                        return;
245
                    }
246
                    if( notification.shouldValidateTheFeature() ) {
247
                        if ( !editingNotificationManager.validateFeature(feature) ) {
248
                            return;
249
                        }
250
                    }
251
                    this.getHelper().update(editableFeature);
252
                    // We'll have already received the event, so we can forget
253
                    // about it
254
                    getHelper().reloadCurrentPage();
255
                    fireTableCellUpdated(rowIndex, columnIndex);
256

    
257
                    editingNotificationManager.notifyObservers(
258
                            this,
259
                            EditingNotification.AFTER_UPDATE_FEATURE,
260
                            null,
261
                            this.getHelper().getFeatureStore(),
262
                            editableFeature);
263
                    editableFeature = null;
264

    
265
                } catch (BaseException ex) {
266
                    throw new SetFeatureValueException(rowIndex, columnIndex,
267
                        value, ex);
268
                } finally {
269
                    // Just in case
270
                    editableFeature = null;
271
                }
272
            }
273
        }
274
    }
275

    
276
    /**
277
     * Returns a reference to the Paging Helper used to load the data from the
278
     * DataStore.
279
     *
280
     * @return the paging helper
281
     */
282
    public FeaturePagingHelper getHelper() {
283
        return helper;
284
    }
285

    
286
    /**
287
     * Sets the FeatureType to show in the table. Used for FeatureStores with
288
     * many simultaneous FeatureTypes supported. Will cause a reload of the
289
     * current data.
290
     *
291
     * @param featureType
292
     *            the FeatureType of the Features
293
     * @throws DataException
294
     *             if there is an error loading the data
295
     */
296
    public void setFeatureType(FeatureType featureType) {
297
        getFeatureQuery().setFeatureType(featureType);
298
        reloadFeatures();
299
        //Selection must be locked to avoid losing it when the table is refreshed
300
        selectionLocked=true;
301
        //The table is refreshed
302
        try {
303
            fireTableStructureChanged();
304
        } catch (Exception e) {
305
            LOGGER.warn("Couldn't reload changed table");
306
        }finally{
307
            //The locked selection is unlocked.
308
            selectionLocked=false;
309
        }
310
    }
311

    
312
    /**
313
     * Sets that the selected Features get returned first.
314
     */
315
    public void setSelectionUp(boolean selectionUp) {
316
        getHelper().setSelectionUp(selectionUp);
317
        fireTableChanged(new TableModelEvent(this, 0, getRowCount() - 1));
318
    }
319

    
320
    private class DelayAction extends Timer implements ActionListener, Runnable {
321
        private static final int STATE_NONE = 0;
322
        private static final int STATE_NEED_RELOADALL = 1;
323
        private static final int STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED = 2;
324
        private static final int STATE_NEED_RELOAD_IF_FEATURE_UPDATED = 4;
325
        private static final int STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED = 8;
326
        private static final int STATE_NEED_RELOAD_FEATURE_TYPE = 16;
327
        private static final int STATE_NEED_SELECTION_UP = 32;
328
        private static final int STATE_NEED_RELOAD_ALL_FEATURES=64;
329

    
330
        private int state = STATE_NONE;
331
        private Feature feature;
332
        private FeatureType featureType;
333
        private boolean isSelecctionUp;
334

    
335
        public DelayAction() {
336
            super(1000,null);
337
            this.setRepeats(false);
338
            this.reset();
339
            this.addActionListener(this);
340
        }
341

    
342
        public void reset() {
343
            this.state = STATE_NONE;
344
            this.isSelecctionUp = false;
345
            this.feature = null;
346
            this.featureType = null;
347
        }
348

    
349
        public void actionPerformed(ActionEvent ae) {
350
            this.run();
351
        }
352

    
353
        public void run() {
354
            if( !SwingUtilities.isEventDispatchThread() ) {
355
                SwingUtilities.invokeLater(this);
356
                return;
357
            }
358
            this.stop();
359
            LOGGER.info("DelayAction.run["+this.state+"] begin");
360
            switch(this.state) {
361
            case STATE_NEED_RELOADALL:
362
                reloadAll();
363
                break;
364
            case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
365
                reloadIfFeatureCountChanged(feature);
366
                break;
367
            case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
368
                reloadIfFeatureUpdated(feature);
369
                break;
370
            case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
371
                reloadIfTypeChanged(featureType);
372
                break;
373
            case STATE_NEED_RELOAD_FEATURE_TYPE:
374
                reloadFeatureType();
375
                updatePaginHelperWithHiddenColums();
376
                break;
377
            case STATE_NEED_RELOAD_ALL_FEATURES:
378
                reloadFeatures();
379
                fireTableChanged(new TableModelEvent(FeatureTableModel.this, 0, getRowCount()));
380
                break;
381
            case STATE_NEED_SELECTION_UP:
382
            case STATE_NONE:
383
            default:
384
                break;
385
            }
386
            if( isSelecctionUp ) {
387
                getHelper().setSelectionUp(true);
388
            }
389
            this.reset();
390
            LOGGER.info("DelayAction.run["+this.state+"] end");
391
        }
392

    
393
        public void nextState(int nextstate) {
394
            this.nextState(nextstate, null, null);
395
        }
396

    
397
        public void nextState(int nextstate, Feature feature) {
398
            this.nextState(nextstate, feature, null);
399
        }
400

    
401
        public void nextState(int nextstate, FeatureType featureType) {
402
            this.nextState(nextstate, null, featureType);
403
        }
404

    
405
        public void nextState(int nextstate, Feature feature, FeatureType featureType) {
406
            this.feature = feature;
407
            this.featureType = featureType;
408
            switch(nextstate) {
409
            case STATE_NEED_RELOADALL:
410
            case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
411
            case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
412
                switch(this.state) {
413
                case STATE_NEED_RELOADALL:
414
                case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
415
                //case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
416
                    this.state = STATE_NEED_RELOADALL;
417
                    break;
418
                case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
419
                case STATE_NEED_RELOAD_FEATURE_TYPE:
420
                    this.state = STATE_NEED_RELOAD_FEATURE_TYPE;
421
                    break;
422
                case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
423
                case STATE_NEED_RELOAD_ALL_FEATURES:
424
                    this.state=STATE_NEED_RELOAD_ALL_FEATURES;
425
                    break;
426
                case STATE_NEED_SELECTION_UP:
427
                    this.state = nextstate;
428
                    this.isSelecctionUp = true;
429
                    break;
430
                case STATE_NONE:
431
                default:
432
                    this.state = nextstate;
433
                    break;
434
                }
435
                break;
436
            case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
437
            case STATE_NEED_RELOAD_FEATURE_TYPE:
438
                switch(this.state) {
439
                case STATE_NEED_RELOADALL:
440
                case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
441
                case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
442
                case STATE_NEED_RELOAD_ALL_FEATURES:
443
                case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
444
                case STATE_NEED_RELOAD_FEATURE_TYPE:
445
                    this.state = STATE_NEED_RELOAD_FEATURE_TYPE;
446
                    break;
447
                case STATE_NEED_SELECTION_UP:
448
                    this.state = nextstate;
449
                    this.isSelecctionUp = true;
450
                    break;
451
                case STATE_NONE:
452
                default:
453
                    this.state = nextstate;
454
                    break;
455
                }
456
                break;
457
            case STATE_NEED_SELECTION_UP:
458
                switch(this.state) {
459
                case STATE_NEED_RELOADALL:
460
                case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
461
                case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
462
                case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
463
                case STATE_NEED_RELOAD_ALL_FEATURES:
464
                case STATE_NEED_RELOAD_FEATURE_TYPE:
465
                case STATE_NEED_SELECTION_UP:
466
                    this.isSelecctionUp = true;
467
                    break;
468
                case STATE_NONE:
469
                default:
470
                    this.state = nextstate;
471
                    this.isSelecctionUp = true;
472
                    break;
473
                }
474
                break;
475
            case STATE_NONE:
476
            default:
477
                this.state = STATE_NONE;
478
                break;
479
            }
480
            if( this.state != STATE_NONE ) {
481
                this.start();
482
            }
483
        }
484

    
485
    }
486

    
487
    private DelayAction delayAction = new DelayAction();
488

    
489
    public void update(final Observable observable, final Object notification) {
490
        if (notification instanceof ComplexNotification) {
491
            // A lot of things might have happened in the store, so don't
492
            // bother looking into each notification.
493
            this.delayAction.nextState(DelayAction.STATE_NEED_RELOADALL);
494
//            reloadAll();
495
        } else if (observable.equals(getFeatureStore())
496
                && notification instanceof FeatureStoreNotification) {
497
            FeatureStoreNotification fsNotification
498
                    = (FeatureStoreNotification) notification;
499
            String type = fsNotification.getType();
500

    
501
            // If there are new, updated or deleted features
502
            // reload the table data
503
            if (FeatureStoreNotification.AFTER_DELETE.equals(type)
504
                    || FeatureStoreNotification.AFTER_INSERT.equals(type)) {
505
//                reloadIfFeatureCountChanged(fsNotification.getFeature());
506
                this.delayAction.nextState(DelayAction.STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED, fsNotification.getFeature());
507

    
508
            } else if (FeatureStoreNotification.AFTER_UPDATE.equals(type)) {
509
//                reloadIfFeatureUpdated(fsNotification.getFeature());
510
                this.delayAction.nextState(DelayAction.STATE_NEED_RELOAD_IF_FEATURE_UPDATED, fsNotification.getFeature());
511

    
512
            } else if (FeatureStoreNotification.AFTER_UPDATE_TYPE.equals(type)) {
513
//                reloadIfTypeChanged(fsNotification.getFeatureType());
514
                this.delayAction.nextState(DelayAction.STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED, fsNotification.getFeatureType());
515

    
516
            } else if (FeatureStoreNotification.TRANSFORM_CHANGE.equals(type)
517
                    || FeatureStoreNotification.AFTER_UNDO.equals(type)
518
                    || FeatureStoreNotification.AFTER_REDO.equals(type)
519
                    || FeatureStoreNotification.AFTER_REFRESH.equals(type))  {
520
//                reloadAll();
521
                this.delayAction.nextState(DelayAction.STATE_NEED_RELOADALL);
522

    
523
            } else if (FeatureStoreNotification.AFTER_FINISHEDITING.equals(type)
524
                    || FeatureStoreNotification.AFTER_STARTEDITING.equals(type)
525
                    || FeatureStoreNotification.AFTER_CANCELEDITING.equals(type)) {
526
                /*
527
                No tengo nada claro por que es necesario llamar al reloadFeatureType
528
                pero si no se incluye hay problemas si durante la edicion se a?aden
529
                campos a la tabla. Sin esto, al cerrar la edicion, los campos a?adidos
530
                desaparecen de la tabla aunque estan en el fichero.
531
                Ver ticket #2434 https://devel.gvsig.org/redmine/issues/2434
532
                */
533
//                reloadFeatureType();
534
//                updatePaginHelperWithHiddenColums();
535
                this.delayAction.nextState(DelayAction.STATE_NEED_RELOAD_FEATURE_TYPE, fsNotification.getFeatureType());
536
            } else if (FeatureStoreNotification.SELECTION_CHANGE.equals(type)) {
537
                if( this.getHelper().isSelectionUp() ) {
538
                    getHelper().setSelectionUp(true);
539
                    this.delayAction.nextState(DelayAction.STATE_NEED_SELECTION_UP);
540
                }
541
            }
542
        }
543
    }
544

    
545
    protected void updatePaginHelperWithHiddenColums() {
546
        FeatureQuery query = this.getHelper().getFeatureQuery();
547
        if (this.getHelper().getFeatureStore().isEditing()) {
548
            if (query.hasConstantsAttributeNames()) {
549
                query.clearConstantsAttributeNames();
550
            }
551
        } else {
552
            query.setConstantsAttributeNames(this.getHiddenColumnNames());
553
        }
554
        try {
555
            this.getHelper().reload();
556
        } catch (BaseException ex) {
557
            LOGGER.warn("Can't reload paging-helper.", ex);
558
        }
559
    }
560

    
561
    protected String[] getHiddenColumnNames() {
562
        return null;
563
    }
564

    
565
    /**
566
     * Returns the FeatureStore of the Collection.
567
     *
568
     * @return the FeatureStore
569
     */
570
    public FeatureStore getFeatureStore() {
571
        return getHelper().getFeatureStore();
572
    }
573

    
574
    /**
575
     * Returns the descriptor of a Feature attribute for a table column.
576
     *
577
     * @param columnIndex
578
     *            the column index
579
     */
580
    public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) {
581
        return internalGetFeatureDescriptorForColumn(columnIndex);
582
    }
583

    
584
    /**
585
     * @param columnIndex
586
     * @return
587
     */
588
        protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
589
                        int columnIndex) {
590
                FeatureType featureType = getFeatureType();
591
                return featureType == null ? null : featureType
592
                                .getAttributeDescriptor(columnIndex);
593
        }
594

    
595
    /**
596
     * Initialize the TableModel
597
     */
598
    protected void initialize() {
599
        // Add as observable to the FeatureStore, to detect data and selection
600
        // changes
601
        helper.getFeatureStore().addObserver(this);
602
    }
603

    
604
    /**
605
     * Returns the value of a Feature attribute, at the given position.
606
     *
607
     * @param feature
608
     *            the feature to get the value from
609
     * @param columnIndex
610
     *            the Feature attribute position
611
     * @return the value
612
     */
613
    protected Object getFeatureValue(Feature feature, int columnIndex) {
614
        return feature.get(columnIndex);
615
    }
616

    
617
    /**
618
     * Sets the value of an Feature attribute at the given position.
619
     *
620
     * @param feature
621
     *            the feature to update
622
     * @param columnIndex
623
     *            the attribute position
624
     * @param value
625
     *            the value to set
626
     * @throws IsNotFeatureSettingException
627
     *             if there is an error setting the value
628
     */
629
    protected EditableFeature setFeatureValue(Feature feature, int columnIndex,
630
        Object value) {
631
        EditableFeature editableFeature = feature.getEditable();
632
        editableFeature.set(columnIndex, value);
633
        return editableFeature;
634
    }
635

    
636
    /**
637
     * Returns the FeatureQuery used to get the Features.
638
     *
639
     * @return the FeatureQuery
640
     */
641
    public FeatureQuery getFeatureQuery() {
642
        return getHelper().getFeatureQuery();
643
    }
644

    
645
    /**
646
     * Returns the type of the features.
647
     */
648
    protected FeatureType getFeatureType() {
649
        return getHelper().getFeatureType();
650
    }
651

    
652
    /**
653
     * Reloads the table data if a feature has been changed, not through the
654
     * table.
655
     */
656
    private void reloadIfFeatureCountChanged(Feature feature) {
657
        // Is any data is changed in the FeatureStore, notify the model
658
        // listeners. Ignore the case where the updated feature is
659
        // changed through us.
660
        if (editableFeature == null || !editableFeature.equals(feature)) {
661
            reloadFeatures();
662
            //Selection must be locked to avoid losing it when the table is refreshed
663
            selectionLocked=true;
664
            //The table is refreshed
665
            try {
666
                fireTableDataChanged();
667
            } catch (Exception e) {
668
                LOGGER.warn("Couldn't reload changed table");
669
            }finally{
670
                //The locked selection is unlocked.
671
                selectionLocked=false;
672
            }
673
        }
674
    }
675

    
676
    private void reloadIfFeatureUpdated(Feature feature) {
677
        // Is any data is changed in the FeatureStore, notify the model
678
        // listeners. Ignore the case where the updated feature is
679
        // changed through us.
680
        if (editableFeature == null || !editableFeature.equals(feature)) {
681
            reloadFeatures();
682
            fireTableChanged(new TableModelEvent(this, 0, getRowCount()));
683
        }
684
    }
685

    
686
    /**
687
     * Reloads data and structure if the {@link FeatureType} of the features
688
     * being shown has changed.
689
     */
690
    private void reloadIfTypeChanged(FeatureType updatedType) {
691
        // If the updated featured type is the one currently being
692
        // shown, reload the table.
693
        if (updatedType != null
694
            && updatedType.getId().equals(getFeatureType().getId())) {
695
            setFeatureType(updatedType);
696
        }
697
    }
698

    
699
    private void reloadAll() {
700
            reloadFeatureType();
701
    }
702

    
703
    private void reloadFeatureType() {
704
        try {
705
            setFeatureType(getHelper().getFeatureStore().getFeatureType(
706
                getHelper().getFeatureType().getId()));
707
        } catch (DataException e) {
708
            throw new FeaturesDataReloadException(e);
709
        }
710
    }
711

    
712
    /**
713
     * Reloads the features shown on the table.
714
     */
715
    private void reloadFeatures() {
716
        try {
717
            getHelper().reload();
718
        } catch (BaseException ex) {
719
            throw new FeaturesDataReloadException(ex);
720
        }
721
    }
722

    
723
    /**
724
     * Returns true if selection must not be changed.
725
     * @return
726
     */
727
    public boolean isSelectionLocked() {
728
        return selectionLocked;
729
    }
730

    
731
}