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

History | View | Annotate | Download (33.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 java.awt.event.ActionEvent;
31
import java.awt.event.ActionListener;
32
import javax.swing.SwingUtilities;
33
import javax.swing.Timer;
34
import javax.swing.event.TableModelEvent;
35
import javax.swing.table.AbstractTableModel;
36
import org.gvsig.fmap.dal.DALLocator;
37
import org.gvsig.fmap.dal.EditingNotification;
38
import org.gvsig.fmap.dal.EditingNotificationManager;
39
import org.gvsig.fmap.dal.exception.DataException;
40
import org.gvsig.fmap.dal.feature.EditableFeature;
41
import org.gvsig.fmap.dal.feature.Feature;
42
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
43
import org.gvsig.fmap.dal.feature.FeatureQuery;
44
import org.gvsig.fmap.dal.feature.FeatureStore;
45
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
46
import org.gvsig.fmap.dal.feature.FeatureType;
47
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
48
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
49
import org.gvsig.fmap.dal.swing.DALSwingLocator;
50
import org.gvsig.tools.dispose.DisposeUtils;
51
import org.gvsig.tools.dispose.SupportDisposable;
52
import org.gvsig.tools.dispose.impl.DisposableHelper;
53
import org.gvsig.tools.exception.BaseException;
54
import org.gvsig.tools.logger.FilteredLogger;
55
import org.gvsig.tools.observer.ComplexNotification;
56
import org.gvsig.tools.observer.ComplexObserver;
57
import org.gvsig.tools.observer.Observable;
58
import org.slf4j.Logger;
59
import org.slf4j.LoggerFactory;
60

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

    
73
    private static final Logger LOGGER = LoggerFactory
74
            .getLogger(FeatureTableModel.class);
75

    
76
    private static final long serialVersionUID = -2488157521902851301L;
77

    
78
    private FeaturePagingHelper pagingHelper;
79
    
80
    private final DisposableHelper disposableHelper;
81

    
82

    
83
    /**
84
     * Used to know if a modification in the FeatureStore is created by us.
85
     */
86
    private EditableFeature editableFeature;
87

    
88
    private boolean selectionLocked = false;
89
    private final FilteredLogger filterlogger;
90
    
91
    private static final int STATUS_OK = 0;
92
    private static final int STATUS_ERR_LOADFEATURES = 1;
93
    private static final int STATUS_ERR_LOADFEATURETYPE = 2;
94
    
95
    private int status;
96

    
97
    /**
98
     * Constructs a TableModel from the features of a FeatureStore, with the
99
     * default page size.
100
     *
101
     * @param featureStore to extract the features from
102
     * @param featureQuery the query to get the features from the store
103
     * @throws BaseException if there is an error reading data from the
104
     * FeatureStore
105
     */
106
    public FeatureTableModel(FeatureStore featureStore,
107
            FeatureQuery featureQuery) throws BaseException {
108
        this(featureStore, featureQuery, FeaturePagingHelper.DEFAULT_PAGE_SIZE);
109
    }
110

    
111
    /**
112
     * Constructs a TableModel from the features of a FeatureStore, with the
113
     * default page size.
114
     *
115
     * @param featureStore to extract the features from
116
     * @param featureQuery the query to get the features from the store
117
     * @param pageSize the number of elements per page data
118
     * @throws BaseException if there is an error reading data from the
119
     * FeatureStore
120
     */
121
    public FeatureTableModel(FeatureStore featureStore,
122
            FeatureQuery featureQuery, int pageSize) throws BaseException {
123
        this(DALLocator.getDataManager().createFeaturePagingHelper(
124
                featureStore, featureQuery, pageSize));
125
    }
126

    
127
    /**
128
     * Constructs a TableModel from a FeatureCollection and a Paging pagingHelper.
129
     *
130
     * @param helper
131
     */
132
    @SuppressWarnings("OverridableMethodCallInConstructor")
133
    protected FeatureTableModel(FeaturePagingHelper helper) {
134
        this.disposableHelper = new DisposableHelper(this);
135
        this.status = STATUS_OK;
136
        this.pagingHelper = helper;
137
        this.filterlogger = new FilteredLogger(LOGGER, "SimpleFeaturesTableModel", 30000L);
138
        initialize();
139
    }
140

    
141
    @Override
142
    public int getColumnCount() {
143
        switch(this.status) {
144
            case STATUS_OK:
145
            case STATUS_ERR_LOADFEATURES:
146
                break;
147
            case STATUS_ERR_LOADFEATURETYPE:
148
                return 0;
149
        }
150
        // Return the number of fields of the Features
151
        FeatureType featureType = getFeatureType();
152
        return featureType.size();
153
    }
154

    
155
    @Override
156
    public int getRowCount() {
157
        switch(this.status) {
158
            case STATUS_OK:
159
                break;
160
            case STATUS_ERR_LOADFEATURETYPE:
161
            case STATUS_ERR_LOADFEATURES:
162
                return 0;
163
        }
164
        // Return the total size of the collection
165
        // If the size is bigger than INTEGER.MAX_VALUE, return that instead
166
        try {
167
            long totalSize = getHelper().getTotalSize();
168
            if (totalSize > Integer.MAX_VALUE) {
169
                return Integer.MAX_VALUE;
170
            } else {
171
                return (int) totalSize;
172
            }
173
        } catch (ConcurrentDataModificationException e) {
174
            LOGGER.debug("Error while getting the total size of the set", e);
175
            this.delayAction.addAction(DelayAction.ACTION_RELOADALL);
176
            return 0;
177
        }
178
    }
179

    
180
    @Override
181
    public Object getValueAt(int rowIndex, int columnIndex) {
182
        switch(this.status) {
183
            case STATUS_OK:
184
                break;
185
            case STATUS_ERR_LOADFEATURETYPE:
186
            case STATUS_ERR_LOADFEATURES:
187
                return null;
188
        }
189
        // Get the Feature at row "rowIndex", and return the value of the
190
        // attribute at "columnIndex"
191
        try {
192
            Feature feature = getFeatureAt(rowIndex);
193
            if (feature == null) {
194
                return null;
195
            }
196
            Object value;
197
            value = getFeatureValue(feature, columnIndex);
198
            return value;
199
        } catch (Throwable ex) {
200
            filterlogger.warn("Not been able to retrieve feature value", ex);
201
            return null;
202
        }
203
    }
204

    
205
    /**
206
     * Returns the value for a row position.
207
     *
208
     * @param rowIndex the row position
209
     * @return the Feature
210
     */
211
    public Feature getFeatureAt(int rowIndex) {
212
        try {
213
            return getHelper().getFeatureAt(rowIndex);
214
        } catch (BaseException ex) {
215
            throw new GetFeatureAtException(rowIndex, ex);
216
        }
217
    }
218

    
219
    @Override
220
    public Class<?> getColumnClass(int columnIndex) {
221
        // Return the class of the FeatureAttributeDescriptor for the value
222
        FeatureAttributeDescriptor attributeDesc
223
                = internalGetFeatureDescriptorForColumn(columnIndex);
224
        if (attributeDesc == null) {
225
            return super.getColumnClass(columnIndex);
226
        }
227
        Class<?> clazz = attributeDesc.getObjectClass();
228
        return (clazz == null ? super.getColumnClass(columnIndex) : clazz);
229
    }
230

    
231
    @Override
232
    public String getColumnName(int column) {
233
        // Return the Feature attribute name
234
        FeatureAttributeDescriptor attributeDesc
235
                = internalGetFeatureDescriptorForColumn(column);
236
        return attributeDesc.getName();
237
    }
238

    
239
    @Override
240
    public boolean isCellEditable(int rowIndex, int columnIndex) {
241
        if (getFeatureStore().isEditing()) {
242
            FeatureAttributeDescriptor attributeDesc
243
                    = internalGetFeatureDescriptorForColumn(columnIndex);
244
            return !attributeDesc.isReadOnly();
245
        }
246

    
247
        return false;
248
    }
249

    
250
    @Override
251
    public void setValueAt(Object value, int rowIndex, int columnIndex) {
252
        // Get the feature at rowIndex
253
        Feature feature = getFeatureAt(rowIndex);
254
        // Only set the value if the feature exists
255
        if (feature != null) {
256
            // We only need to update if the value to set is not equal to the
257
            // current value
258
            Object currentValue = getFeatureValue(feature, columnIndex);
259
            if (value != currentValue
260
                    && (value == null || !value.equals(currentValue))) {
261
                try {
262
                    // Store the editable feature to ignore the related store
263
                    // change notification
264
                    editableFeature
265
                            = setFeatureValue(feature, columnIndex, value);
266
                    EditingNotificationManager editingNotificationManager = DALSwingLocator.getEditingNotificationManager();
267
                    EditingNotification notification = editingNotificationManager.notifyObservers(
268
                            this,
269
                            EditingNotification.BEFORE_UPDATE_FEATURE,
270
                            null,
271
                            this.getHelper().getFeatureStore(),
272
                            editableFeature);
273
                    if (notification.isCanceled()) {
274
                        return;
275
                    }
276
                    if (notification.shouldValidateTheFeature()) {
277
                        if (!editingNotificationManager.validateFeature(feature)) {
278
                            return;
279
                        }
280
                    }
281
                    this.getHelper().update(editableFeature);
282
                    // We'll have already received the event, so we can forget
283
                    // about it
284
                    getHelper().reloadCurrentPage();
285
                    fireTableCellUpdated(rowIndex, columnIndex);
286

    
287
                    editingNotificationManager.notifyObservers(
288
                            this,
289
                            EditingNotification.AFTER_UPDATE_FEATURE,
290
                            null,
291
                            this.getHelper().getFeatureStore(),
292
                            editableFeature);
293
                    editableFeature = null;
294

    
295
                } catch (BaseException ex) {
296
                    throw new SetFeatureValueException(rowIndex, columnIndex,
297
                            value, ex);
298
                } finally {
299
                    // Just in case
300
                    editableFeature = null;
301
                }
302
            }
303
        }
304
    }
305

    
306
    /**
307
     * Returns a reference to the Paging Helper used to load the data from the
308
     * DataStore.
309
     *
310
     * @return the paging pagingHelper
311
     */
312
    public FeaturePagingHelper getHelper() {
313
        return pagingHelper;
314
    }
315

    
316
    /**
317
     * Sets the FeatureType to show in the table. Used for FeatureStores with
318
     * many simultaneous FeatureTypes supported. Will cause a reload of the
319
     * current data.
320
     *
321
     * @param featureType the FeatureType of the Features
322
     */
323
    public void setFeatureType(FeatureType featureType) {
324
        getFeatureQuery().setFeatureType(featureType);
325
        reloadFeatures();
326
        //Selection must be locked to avoid losing it when the table is refreshed
327
        selectionLocked = true;
328
        //The table is refreshed
329
        try {
330
            fireTableStructureChanged();
331
        } catch (Exception e) {
332
            LOGGER.warn("Couldn't reload changed table");
333
        } finally {
334
            //The locked selection is unlocked.
335
            selectionLocked = false;
336
        }
337
    }
338

    
339
    /**
340
     * Sets that the selected Features get returned first.
341
     *
342
     * @param selectionUp
343
     */
344
    public void setSelectionUp(boolean selectionUp) {
345
        if (selectionUp) {
346
            delayAction.addAction(DelayAction.ACTION_ENABLE_SELECTION_UP);
347
        } else {
348
            delayAction.addAction(DelayAction.ACTION_DISABLE_SELECTION_UP);
349
        }
350
    }
351

    
352
    protected void fireTableChanged() {
353
//        LOGGER.info("fireTableChanged()");
354
        fireTableChanged(new TableModelEvent(this, 0, getRowCount() - 1));
355
    }
356
    public static class Bitmask  {
357
        private int mask;
358
        
359
        public Bitmask(int initialmask) {
360
            this.mask = initialmask;
361
        }
362
        
363
        public boolean isSetBit(int pos) {
364
            return (mask & (1<<pos)) != 0;
365
        }
366
        
367
        public boolean isSet(int bits) {
368
            return (mask & bits) == bits;
369
        }
370
        
371
        public boolean isSetAny() {
372
            return mask!=0;
373
        }
374
        
375
        public boolean isCleanAll() {
376
            return mask == 0;
377
        }
378

    
379
        public void set(int bits) {
380
            mask = mask | bits;
381
        }
382
        
383
        public int get() {
384
            return mask;
385
        }
386
        
387
        public void clear(int bits) {
388
            mask = ~bits & mask;
389
        }
390

    
391
        public void clearBit(int pos) {
392
            mask = ~(1<<pos) & mask;
393
        }
394

    
395
        public void clearAll() {
396
            mask = 0;
397
        }
398
        
399
    }
400
    
401
    private class DelayAction extends Timer implements ActionListener, Runnable {
402

    
403
        private static final int ACTION_NONE = 0;
404
        private static final int ACTION_RELOADALL = 1;
405
        private static final int ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED = 2;
406
        private static final int ACTION_RELOAD_IF_FEATURE_UPDATED = 4;
407
        private static final int ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED = 8;
408
        private static final int ACTION_RELOAD_FEATURE_TYPE = 16;
409
        private static final int ACTION_ENABLE_SELECTION_UP = 32;
410
        private static final int ACTION_RELOAD_ALL_FEATURES = 64;
411
        private static final int ACTION_DISABLE_SELECTION_UP = 128;
412
        private static final int ACTION_UPDATE_SELECTION = 256;
413

    
414
        private final Bitmask current_actions = new Bitmask(ACTION_NONE);
415
        private Feature feature;
416
        private FeatureType featureType;
417

    
418
        @SuppressWarnings("OverridableMethodCallInConstructor")
419
        public DelayAction() {
420
            super(1000, null);
421
            this.setRepeats(false);
422
            this.reset();
423
            this.addActionListener(this);
424
        }
425

    
426
        public final void reset() {
427
            this.current_actions.clearAll();
428
            this.feature = null;
429
            this.featureType = null;
430
        }
431

    
432
        @Override
433
        public void actionPerformed(ActionEvent ae) {
434
            this.run();
435
        }
436

    
437
        @SuppressWarnings("UnusedAssignment")
438
        private String getActionsLabel(Bitmask actions) {
439
            if( actions.isCleanAll() ) {
440
                return "NONE";
441
            }
442
            StringBuilder builder = new StringBuilder();
443
            boolean needSeparator = false;
444
            if( actions.isSet(ACTION_RELOADALL) ) {
445
                if( needSeparator ) {
446
                    builder.append("|");
447
                }
448
                needSeparator = true;
449
                builder.append("RELOADALL");
450
            }
451
            if( actions.isSet(ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED) ) {
452
                if( needSeparator ) {
453
                    builder.append("|");
454
                }
455
                needSeparator = true;
456
                builder.append("RELOAD_IF_FEATURE_COUNT_CHANGED");
457
            }
458
            if( actions.isSet(ACTION_RELOAD_IF_FEATURE_UPDATED) ) {
459
                if( needSeparator ) {
460
                    builder.append("|");
461
                }
462
                needSeparator = true;
463
                builder.append("RELOAD_IF_FEATURE_UPDATED");
464
            }
465
            if( actions.isSet(ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED) ) {
466
                if( needSeparator ) {
467
                    builder.append("|");
468
                }
469
                needSeparator = true;
470
                builder.append("RELOAD_IF_FEATURE_TYPE_CHANGED");
471
            }
472
            if( actions.isSet(ACTION_RELOAD_FEATURE_TYPE) ) {
473
                if( needSeparator ) {
474
                    builder.append("|");
475
                }
476
                needSeparator = true;
477
                builder.append("RELOAD_FEATURE_TYPE");
478
            }
479
            if( actions.isSet(ACTION_RELOAD_ALL_FEATURES) ) {
480
                if( needSeparator ) {
481
                    builder.append("|");
482
                }
483
                needSeparator = true;
484
                builder.append("RELOAD_ALL_FEATURES");
485
            }
486
            if( actions.isSet(ACTION_DISABLE_SELECTION_UP) ) {
487
                if( needSeparator ) {
488
                    builder.append("|");
489
                }
490
                needSeparator = true;
491
                builder.append("DISABLE_SELECTION_UP");
492
            }
493
            if( actions.isSet(ACTION_ENABLE_SELECTION_UP) ) {
494
                if( needSeparator ) {
495
                    builder.append("|");
496
                }
497
                needSeparator = true;
498
                builder.append("ENABLE_SELECTION_UP");
499
            }
500
            if( actions.isSet(ACTION_UPDATE_SELECTION) ) {
501
                if( needSeparator ) {
502
                    builder.append("|");
503
                }
504
                needSeparator = true;
505
                builder.append("UPDATE_SELECTION");
506
            }
507
            if( !needSeparator ) {
508
                builder.append(actions.get());
509
            }
510
            return builder.toString();
511
        }
512
        
513
        @Override
514
        public void run() {
515
            if (!SwingUtilities.isEventDispatchThread()) {
516
                SwingUtilities.invokeLater(this);
517
                return;
518
            }
519
            this.stop();
520
//            LOGGER.info("DelayAction.run begin ["+ getActionsLabel(this.current_actions) + "]");
521
            boolean needFireTableChanged = false;
522
            if( this.current_actions.isSet(ACTION_RELOADALL) ) {
523
                this.current_actions.clear(
524
                        ACTION_RELOADALL |
525
                        ACTION_RELOAD_FEATURE_TYPE |
526
                        ACTION_RELOAD_ALL_FEATURES |
527
                        ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED |
528
                        ACTION_RELOAD_IF_FEATURE_UPDATED |
529
                        ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED
530
                );
531
                reloadAll();
532
            }
533
            if( this.current_actions.isSet(ACTION_RELOAD_FEATURE_TYPE) ) {
534
                this.current_actions.clear(
535
                        ACTION_RELOAD_FEATURE_TYPE |
536
                        ACTION_RELOAD_ALL_FEATURES |
537
                        ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED |
538
                        ACTION_RELOAD_IF_FEATURE_UPDATED |
539
                        ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED
540
                );
541
                reloadFeatureType();
542
                updatePaginHelperWithHiddenColums();
543
            }
544
            if( this.current_actions.isSet(ACTION_RELOAD_ALL_FEATURES) ) {
545
                this.current_actions.clear(
546
                        ACTION_RELOAD_ALL_FEATURES |
547
                        ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED |
548
                        ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED |
549
                        ACTION_RELOAD_IF_FEATURE_UPDATED
550
                );
551
                reloadFeatures();
552
                needFireTableChanged = true;
553
            }
554
            if( this.current_actions.isSet(ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED) ) {
555
                if( reloadFeatureTypeIfTypeChanged(featureType) ) {
556
                    this.current_actions.clear(
557
                            ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED |
558
                            ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED |
559
                            ACTION_RELOAD_IF_FEATURE_UPDATED
560
                    );
561
                }
562
            }
563
            if( this.current_actions.isSet(ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED) ) {
564
                if( reloadFeaturesIfFeatureCountChanged(feature) ) {
565
                    this.current_actions.clear(
566
                            ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED |
567
                            ACTION_RELOAD_IF_FEATURE_UPDATED
568
                    );
569
                }
570
            }
571
            if( this.current_actions.isSet(ACTION_RELOAD_IF_FEATURE_UPDATED) ) {
572
                if( reloadFeaturesIfFeatureUpdated(feature) ) {
573
                    this.current_actions.clear(
574
                            ACTION_RELOAD_IF_FEATURE_UPDATED
575
                    );
576
                    needFireTableChanged = true;
577
                }
578
            }
579
            
580
            if( this.current_actions.isSet(ACTION_ENABLE_SELECTION_UP) ) {
581
                if (!getHelper().isSelectionUp()) {
582
                    getHelper().setSelectionUp(true);
583
                    this.current_actions.clear(
584
                        ACTION_UPDATE_SELECTION
585
                    );
586
                }
587
                this.current_actions.clear(
588
                        ACTION_ENABLE_SELECTION_UP |
589
                        ACTION_DISABLE_SELECTION_UP
590
                );
591
                needFireTableChanged = true;
592
            } else if( this.current_actions.isSet(ACTION_DISABLE_SELECTION_UP) ) {
593
                if (getHelper().isSelectionUp()) {
594
                    getHelper().setSelectionUp(false);
595
                    this.current_actions.clear(
596
                        ACTION_UPDATE_SELECTION
597
                    );
598
                }
599
                this.current_actions.clear(
600
                        ACTION_ENABLE_SELECTION_UP |
601
                        ACTION_DISABLE_SELECTION_UP
602
                );
603
                needFireTableChanged = true;
604
            }
605
            
606
            if( this.current_actions.isSet(ACTION_UPDATE_SELECTION) ) {
607
                if (getHelper().isSelectionUp()) {
608
                    // Se ha a?adido o quitado elementos de la seleccion, y estamos
609
                    // en "seleccion arriba", asi que forzamos un refresco.
610
                    try {
611
                        getHelper().reloadCurrentPage();
612
                    } catch(Throwable th) {
613
                        LOGGER.debug("No se ha podido recargar la pagina actual de la tabla", th);
614
                    }
615
                }
616
                needFireTableChanged = true;
617
            }
618
            
619
            if( needFireTableChanged ) {
620
                fireTableChanged();
621
            }
622
            this.reset();
623
//            LOGGER.info("DelayAction.run end ["+ getActionsLabel(this.current_actions) + "]");
624
        }
625

    
626
        public void addAction(int action) {
627
            this.addAction(action, null, null);
628
        }
629

    
630
        public void addAction(int action, Feature feature) {
631
            this.addAction(action, feature, null);
632
        }
633

    
634
        public void addAction(int action, FeatureType featureType) {
635
            this.addAction(action, null, featureType);
636
        }
637

    
638
        public void addAction(int action, Feature feature, FeatureType featureType) {
639
            this.feature = feature;
640
            this.featureType = featureType;
641
            this.current_actions.set(action);
642
//            LOGGER.info("addAction: "+ this.getActionsLabel(current_actions));
643
            if ( this.current_actions.isSetAny() ) {
644
//                LOGGER.info("addAction: start");
645
                this.start();
646
            }
647
        }
648

    
649
    }
650

    
651
    private final DelayAction delayAction = new DelayAction();
652

    
653
    @Override
654
    public void update(final Observable observable, final Object notification) {
655
//        LOGGER.info("update: " +
656
//                (observable==null? "null":observable.getClass().getSimpleName()) +
657
//                ", " +
658
//                (notification==null? "null":notification.getClass().getSimpleName())
659
//        );
660
        if (notification instanceof ComplexNotification) {
661
            // A lot of things might have happened in the store, so don't
662
            // bother looking into each notification.
663
            this.delayAction.addAction(DelayAction.ACTION_RELOADALL);
664
        } else if (observable.equals(getFeatureStore())
665
                && notification instanceof FeatureStoreNotification) {
666
            FeatureStoreNotification fsNotification
667
                    = (FeatureStoreNotification) notification;
668
            String type = fsNotification.getType();
669

    
670
//            LOGGER.info("update: "+type);
671
            switch(type) {
672
                case FeatureStoreNotification.AFTER_DELETE:
673
                case FeatureStoreNotification.AFTER_INSERT:
674
                    this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED, fsNotification.getFeature());
675
                    break;
676

    
677
                case FeatureStoreNotification.AFTER_UPDATE:
678
                    this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_UPDATED, fsNotification.getFeature());
679
                    break;
680

    
681
                case FeatureStoreNotification.AFTER_UPDATE_TYPE:
682
                    this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED, fsNotification.getFeatureType());
683
                    break;
684

    
685
                case FeatureStoreNotification.TRANSFORM_CHANGE:
686
                case FeatureStoreNotification.AFTER_UNDO:
687
                case FeatureStoreNotification.AFTER_REDO:
688
                case FeatureStoreNotification.AFTER_REFRESH:
689
                    this.delayAction.addAction(DelayAction.ACTION_RELOADALL);
690
                    break;
691

    
692
                case FeatureStoreNotification.AFTER_FINISHEDITING:
693
                case FeatureStoreNotification.AFTER_STARTEDITING:
694
                case FeatureStoreNotification.AFTER_CANCELEDITING:
695
                    /*
696
                    No tengo nada claro por que es necesario llamar al reloadFeatureType
697
                    pero si no se incluye hay problemas si durante la edicion se a?aden
698
                    campos a la tabla. Sin esto, al cerrar la edicion, los campos a?adidos
699
                    desaparecen de la tabla aunque estan en el fichero.
700
                    Ver ticket #2434 https://devel.gvsig.org/redmine/issues/2434
701
                     */
702
                    this.delayAction.addAction(DelayAction.ACTION_RELOAD_FEATURE_TYPE, fsNotification.getFeatureType());
703
                    break;
704
                    
705
                case FeatureStoreNotification.SELECTION_CHANGE:
706
                    this.delayAction.addAction(DelayAction.ACTION_UPDATE_SELECTION);
707
                    break;
708
                    
709
                default:
710
                    LOGGER.debug("update: skip notification("+type+")");
711
            }
712
        }
713
    }
714

    
715
    protected void updatePaginHelperWithHiddenColums() {
716
        FeatureQuery query = this.getHelper().getFeatureQuery();
717
        if (this.getHelper().getFeatureStore().isEditing()) {
718
            if (query.hasConstantsAttributeNames()) {
719
                query.clearConstantsAttributeNames();
720
            }
721
        } else {
722
            query.setConstantsAttributeNames(this.getHiddenColumnNames());
723
        }
724
        try {
725
            this.getHelper().reload();
726
        } catch (BaseException ex) {
727
            LOGGER.warn("Can't reload paging-helper.", ex);
728
        }
729
    }
730

    
731
    protected String[] getHiddenColumnNames() {
732
        return null;
733
    }
734

    
735
    /**
736
     * Returns the FeatureStore of the Collection.
737
     *
738
     * @return the FeatureStore
739
     */
740
    public FeatureStore getFeatureStore() {
741
        return getHelper().getFeatureStore();
742
    }
743

    
744
    /**
745
     * Returns the descriptor of a Feature attribute for a table column.
746
     *
747
     * @param columnIndex, the column index
748
     * @return
749
     */
750
    public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) {
751
        return internalGetFeatureDescriptorForColumn(columnIndex);
752
    }
753

    
754
    /**
755
     * @param columnIndex
756
     * @return
757
     */
758
    protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
759
            int columnIndex) {
760
        FeatureType featureType = getFeatureType();
761
        return featureType == null ? null : featureType
762
                .getAttributeDescriptor(columnIndex);
763
    }
764

    
765
    /**
766
     * Initialize the TableModel
767
     */
768
    protected void initialize() {
769
        // Add as observable to the FeatureStore, to detect data and selection
770
        // changes
771
        pagingHelper.getFeatureStore().addObserver(this);
772
    }
773

    
774
    /**
775
     * Returns the value of a Feature attribute, at the given position.
776
     *
777
     * @param feature the feature to get the value from
778
     * @param columnIndex the Feature attribute position
779
     * @return the value
780
     */
781
    protected Object getFeatureValue(Feature feature, int columnIndex) {
782
        return feature.get(columnIndex);
783
    }
784

    
785
    /**
786
     * Sets the value of an Feature attribute at the given position.
787
     *
788
     * @param feature the feature to update
789
     * @param columnIndex the attribute position
790
     * @param value the value to set
791
     * @return
792
     */
793
    protected EditableFeature setFeatureValue(Feature feature, int columnIndex,
794
            Object value) {
795
        EditableFeature theEditableFeature = feature.getEditable();
796
        theEditableFeature.set(columnIndex, value);
797
        return theEditableFeature;
798
    }
799

    
800
    /**
801
     * Returns the FeatureQuery used to get the Features.
802
     *
803
     * @return the FeatureQuery
804
     */
805
    public FeatureQuery getFeatureQuery() {
806
        return getHelper().getFeatureQuery();
807
    }
808

    
809
    /**
810
     * Returns the type of the
811
     *
812
     * @return features.
813
     */
814
    protected FeatureType getFeatureType() {
815
        return getHelper().getFeatureType();
816
    }
817

    
818
    /**
819
     * Reloads the table data if a feature has been changed, not through the
820
     * table.
821
     */
822
    private boolean reloadFeaturesIfFeatureCountChanged(Feature feature) {
823
        // Is any data is changed in the FeatureStore, notify the model
824
        // listeners. Ignore the case where the updated feature is
825
        // changed through us.
826
        if (editableFeature == null || !editableFeature.equals(feature)) {
827
            reloadFeatures();
828
            //Selection must be locked to avoid losing it when the table is refreshed
829
            selectionLocked = true;
830
            //The table is refreshed
831
            try {
832
                fireTableDataChanged();
833
            } catch (Exception e) {
834
                LOGGER.warn("Couldn't reload changed table");
835
            } finally {
836
                //The locked selection is unlocked.
837
                selectionLocked = false;
838
            }
839
            return true;
840
        }
841
        return false;
842
    }
843

    
844
    private boolean reloadFeaturesIfFeatureUpdated(Feature feature) {
845
        // Is any data is changed in the FeatureStore, notify the model
846
        // listeners. Ignore the case where the updated feature is
847
        // changed through us.
848
        if (editableFeature == null || !editableFeature.equals(feature)) {
849
            reloadFeatures();
850
            return true;
851
        }
852
        return false;
853
    }
854

    
855
    /**
856
     * Reloads data and structure if the {@link FeatureType} of the features
857
     * being shown has changed.
858
     */
859
    private boolean reloadFeatureTypeIfTypeChanged(FeatureType updatedType) {
860
        // If the updated featured type is the one currently being
861
        // shown, reload the table.
862
        if (updatedType != null && updatedType.getId().equals(getFeatureType().getId())) {
863
            setFeatureType(updatedType);
864
            return true;
865
        }
866
        return false;
867
    }
868

    
869
    private void reloadAll() {
870
        reloadFeatureType();
871
    }
872

    
873
    private void reloadFeatureType() {
874
        try {
875
            String ftypeid = getHelper().getFeatureType().getId();
876
            FeatureType ftype = getHelper().getFeatureStore().getFeatureType(ftypeid);
877
            setFeatureType(ftype);
878
        } catch (DataException e) {
879
            LOGGER.warn("Can't reload featuretype",e);
880
            this.status = STATUS_ERR_LOADFEATURETYPE;
881
//            throw new FeaturesDataReloadException(e);
882
        }
883
    }
884

    
885
    /**
886
     * Reloads the features shown on the table.
887
     */
888
    private void reloadFeatures() {
889
        switch(this.status) {
890
            case STATUS_OK:
891
            case STATUS_ERR_LOADFEATURES:
892
                break;
893
            case STATUS_ERR_LOADFEATURETYPE:
894
                return;
895
        }
896
        try {
897
            getHelper().reload();
898
        } catch (BaseException ex) {
899
            LOGGER.warn("Can't reload features",ex);
900
            this.status = STATUS_ERR_LOADFEATURES;
901
//            throw new FeaturesDataReloadException(ex);
902
        }
903
    }
904

    
905
    /**
906
     * Returns true if selection must not be changed.
907
     *
908
     * @return
909
     */
910
    public boolean isSelectionLocked() {
911
        return selectionLocked;
912
    }
913
    
914
    
915
    @Override
916
    public void dispose() {
917
        this.disposableHelper.dispose(this);
918
    }
919

    
920
    @Override
921
    public void doDispose() throws BaseException {
922
        DisposeUtils.disposeQuietly(this.pagingHelper);
923
        this.pagingHelper = null;
924
    }
925

    
926

    
927
}