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

History | View | Annotate | Download (36 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
@SuppressWarnings("UseSpecificCatch")
70
public class FeatureTableModel extends AbstractTableModel implements ComplexObserver {
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
    /**
80
     * Used to know if a modification in the FeatureStore is created by us.
81
     */
82
    private EditableFeature editableFeature;
83

    
84
    private boolean selectionLocked = false;
85
    private final FilteredLogger filterlogger;
86

    
87
    /**
88
     * Constructs a TableModel from the features of a FeatureStore, with the
89
     * default page size.
90
     *
91
     * @param featureStore to extract the features from
92
     * @param featureQuery the query to get the features from the store
93
     * @throws BaseException if there is an error reading data from the
94
     * 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 to extract the features from
106
     * @param featureQuery the query to get the features from the store
107
     * @param pageSize the number of elements per page data
108
     * @throws BaseException if there is an error reading data from the
109
     * FeatureStore
110
     */
111
    public FeatureTableModel(FeatureStore featureStore,
112
            FeatureQuery featureQuery, int pageSize) throws BaseException {
113
        this(DALLocator.getDataManager().createFeaturePagingHelper(
114
                featureStore, featureQuery, pageSize));
115
    }
116

    
117
    /**
118
     * Constructs a TableModel from a FeatureCollection and a Paging helper.
119
     *
120
     * @param helper
121
     */
122
    @SuppressWarnings("OverridableMethodCallInConstructor")
123
    protected FeatureTableModel(FeaturePagingHelper helper) {
124
        this.helper = helper;
125
        this.filterlogger = new FilteredLogger(LOGGER, "SimpleFeaturesTableModel", 10);
126
        this.filterlogger.setInterval(30000);
127
        initialize();
128
    }
129

    
130
    @Override
131
    public int getColumnCount() {
132
        // Return the number of fields of the Features
133
        FeatureType featureType = getFeatureType();
134
        return featureType.size();
135
    }
136

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

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

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

    
186
    @Override
187
    public Class<?> getColumnClass(int columnIndex) {
188
        // Return the class of the FeatureAttributeDescriptor for the value
189
        FeatureAttributeDescriptor attributeDesc
190
                = internalGetFeatureDescriptorForColumn(columnIndex);
191
        if (attributeDesc == null) {
192
            return super.getColumnClass(columnIndex);
193
        }
194
        Class<?> clazz = attributeDesc.getObjectClass();
195
        return (clazz == null ? super.getColumnClass(columnIndex) : clazz);
196
    }
197

    
198
    @Override
199
    public String getColumnName(int column) {
200
        // Return the Feature attribute name
201
        FeatureAttributeDescriptor attributeDesc
202
                = internalGetFeatureDescriptorForColumn(column);
203
        return attributeDesc.getName();
204
    }
205

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

    
214
        return false;
215
    }
216

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

    
254
                    editingNotificationManager.notifyObservers(
255
                            this,
256
                            EditingNotification.AFTER_UPDATE_FEATURE,
257
                            null,
258
                            this.getHelper().getFeatureStore(),
259
                            editableFeature);
260
                    editableFeature = null;
261

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

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

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

    
306
    /**
307
     * Sets that the selected Features get returned first.
308
     *
309
     * @param selectionUp
310
     */
311
    public void setSelectionUp(boolean selectionUp) {
312
        if (selectionUp) {
313
            delayAction.addAction(DelayAction.ACTION_ENABLE_SELECTION_UP);
314
        } else {
315
            delayAction.addAction(DelayAction.ACTION_DISABLE_SELECTION_UP);
316
        }
317
    }
318

    
319
    protected void fireTableChanged() {
320
        LOGGER.info("fireTableChanged()");
321
        fireTableChanged(new TableModelEvent(this, 0, getRowCount() - 1));
322
    }
323
    public static class Bitmask  {
324
        private int mask;
325
        
326
        public Bitmask(int initialmask) {
327
            this.mask = initialmask;
328
        }
329
        
330
        public boolean isSetBit(int pos) {
331
            return (mask & (1<<pos)) != 0;
332
        }
333
        
334
        public boolean isSet(int bits) {
335
            return (mask & bits) == bits;
336
        }
337
        
338
        public boolean isSetAny() {
339
            return mask!=0;
340
        }
341
        
342
        public boolean isCleanAll() {
343
            return mask == 0;
344
        }
345

    
346
        public void set(int bits) {
347
            mask = mask | bits;
348
        }
349
        
350
        public int get() {
351
            return mask;
352
        }
353
        
354
        public void clear(int bits) {
355
            mask = ~bits & mask;
356
        }
357

    
358
        public void clearBit(int pos) {
359
            mask = ~(1<<pos) & mask;
360
        }
361

    
362
        public void clearAll() {
363
            mask = 0;
364
        }
365
        
366
    }
367
    
368
    private class DelayAction extends Timer implements ActionListener, Runnable {
369

    
370
        private static final int ACTION_NONE = 0;
371
        private static final int ACTION_RELOADALL = 1;
372
        private static final int ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED = 2;
373
        private static final int ACTION_RELOAD_IF_FEATURE_UPDATED = 4;
374
        private static final int ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED = 8;
375
        private static final int ACTION_RELOAD_FEATURE_TYPE = 16;
376
        private static final int ACTION_ENABLE_SELECTION_UP = 32;
377
        private static final int ACTION_RELOAD_ALL_FEATURES = 64;
378
        private static final int ACTION_DISABLE_SELECTION_UP = 128;
379
        private static final int ACTION_UPDATE_SELECTION = 256;
380

    
381
        private final Bitmask current_actions = new Bitmask(ACTION_NONE);
382
        private Feature feature;
383
        private FeatureType featureType;
384

    
385
        @SuppressWarnings("OverridableMethodCallInConstructor")
386
        public DelayAction() {
387
            super(1000, null);
388
            this.setRepeats(false);
389
            this.reset();
390
            this.addActionListener(this);
391
        }
392

    
393
        public final void reset() {
394
            this.current_actions.clearAll();
395
            this.feature = null;
396
            this.featureType = null;
397
        }
398

    
399
        @Override
400
        public void actionPerformed(ActionEvent ae) {
401
            this.run();
402
        }
403

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

    
593
        public void addAction(int action) {
594
            this.addAction(action, null, null);
595
        }
596

    
597
        public void addAction(int action, Feature feature) {
598
            this.addAction(action, feature, null);
599
        }
600

    
601
        public void addAction(int action, FeatureType featureType) {
602
            this.addAction(action, null, featureType);
603
        }
604

    
605
        public void addAction(int action, Feature feature, FeatureType featureType) {
606
            this.feature = feature;
607
            this.featureType = featureType;
608
            this.current_actions.set(action);
609
            LOGGER.info("addAction: "+ this.getActionsLabel(current_actions));
610
//            switch (nextstate) {
611
//                case STATE_NEED_RELOADALL:
612
//                case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
613
//                case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
614
//                    switch (this.current_state) {
615
//                        case STATE_NEED_RELOADALL:
616
//                        case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
617
//                            //case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
618
//                            this.current_state = STATE_NEED_RELOADALL;
619
//                            break;
620
//                        case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
621
//                        case STATE_NEED_RELOAD_FEATURE_TYPE:
622
//                            this.current_state = STATE_NEED_RELOAD_FEATURE_TYPE;
623
//                            break;
624
//                        case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
625
//                        case STATE_NEED_RELOAD_ALL_FEATURES:
626
//                            this.current_state = STATE_NEED_RELOAD_ALL_FEATURES;
627
//                            break;
628
//                        case STATE_ENABLE_SELECTION_UP:
629
//                            this.current_state = nextstate;
630
//                            this.isSelecctionUp = true;
631
//                            break;
632
//                        case STATE_DISABLE_SELECTION_UP:
633
//                            this.current_state = nextstate;
634
//                            this.isSelecctionUp = false;
635
//                            break;
636
//                        case STATE_NONE:
637
//                        default:
638
//                            this.current_state = nextstate;
639
//                            break;
640
//                    }
641
//                    break;
642
//                case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
643
//                case STATE_NEED_RELOAD_FEATURE_TYPE:
644
//                    switch (this.current_state) {
645
//                        case STATE_NEED_RELOADALL:
646
//                        case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
647
//                        case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
648
//                        case STATE_NEED_RELOAD_ALL_FEATURES:
649
//                        case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
650
//                        case STATE_NEED_RELOAD_FEATURE_TYPE:
651
//                            this.current_state = STATE_NEED_RELOAD_FEATURE_TYPE;
652
//                            break;
653
//                        case STATE_ENABLE_SELECTION_UP:
654
//                            this.current_state = nextstate;
655
//                            this.isSelecctionUp = true;
656
//                            break;
657
//                        case STATE_NONE:
658
//                        default:
659
//                            this.current_state = nextstate;
660
//                            break;
661
//                    }
662
//                    break;
663
//                case STATE_ENABLE_SELECTION_UP:
664
//                    switch (this.current_state) {
665
//                        case STATE_NEED_RELOADALL:
666
//                        case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
667
//                        case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
668
//                        case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
669
//                        case STATE_NEED_RELOAD_ALL_FEATURES:
670
//                        case STATE_NEED_RELOAD_FEATURE_TYPE:
671
//                        case STATE_ENABLE_SELECTION_UP:
672
//                            this.isSelecctionUp = true;
673
//                            break;
674
//                        case STATE_NONE:
675
//                        default:
676
//                            this.current_state = nextstate;
677
//                            this.isSelecctionUp = true;
678
//                            break;
679
//                    }
680
//                    break;
681
//                case STATE_DISABLE_SELECTION_UP:
682
//                    switch (this.current_state) {
683
//                        case STATE_NEED_RELOADALL:
684
//                        case STATE_NEED_RELOAD_IF_FEATURE_COUNT_CHANGED:
685
//                        case STATE_NEED_RELOAD_IF_FEATURE_UPDATED:
686
//                        case STATE_NEED_RELOAD_IF_FEATURE_TYPE_CHANGED:
687
//                        case STATE_NEED_RELOAD_ALL_FEATURES:
688
//                        case STATE_NEED_RELOAD_FEATURE_TYPE:
689
//                        case STATE_ENABLE_SELECTION_UP:
690
//                            this.isSelecctionUp = false;
691
//                            break;
692
//                        case STATE_NONE:
693
//                        default:
694
//                            this.current_state = nextstate;
695
//                            this.isSelecctionUp = true;
696
//                            break;
697
//                    }
698
//                    break;
699
//                case STATE_NONE:
700
//                default:
701
//                    this.current_state = STATE_NONE;
702
//                    break;
703
//            }
704
            if ( this.current_actions.isSetAny() ) {
705
                LOGGER.info("addAction: start");
706
                this.start();
707
            }
708
        }
709

    
710
    }
711

    
712
    private final DelayAction delayAction = new DelayAction();
713

    
714
    @Override
715
    public void update(final Observable observable, final Object notification) {
716
        LOGGER.info("update: " +
717
                (observable==null? "null":observable.getClass().getSimpleName()) +
718
                ", " +
719
                (notification==null? "null":notification.getClass().getSimpleName())
720
        );
721
        if (notification instanceof ComplexNotification) {
722
            // A lot of things might have happened in the store, so don't
723
            // bother looking into each notification.
724
            this.delayAction.addAction(DelayAction.ACTION_RELOADALL);
725
        } else if (observable.equals(getFeatureStore())
726
                && notification instanceof FeatureStoreNotification) {
727
            FeatureStoreNotification fsNotification
728
                    = (FeatureStoreNotification) notification;
729
            String type = fsNotification.getType();
730

    
731
            LOGGER.info("update: "+type);
732
            switch(type) {
733
                case FeatureStoreNotification.AFTER_DELETE:
734
                case FeatureStoreNotification.AFTER_INSERT:
735
                    this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_COUNT_CHANGED, fsNotification.getFeature());
736
                    break;
737

    
738
                case FeatureStoreNotification.AFTER_UPDATE:
739
                    this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_UPDATED, fsNotification.getFeature());
740
                    break;
741

    
742
                case FeatureStoreNotification.AFTER_UPDATE_TYPE:
743
                    this.delayAction.addAction(DelayAction.ACTION_RELOAD_IF_FEATURE_TYPE_CHANGED, fsNotification.getFeatureType());
744
                    break;
745

    
746
                case FeatureStoreNotification.TRANSFORM_CHANGE:
747
                case FeatureStoreNotification.AFTER_UNDO:
748
                case FeatureStoreNotification.AFTER_REDO:
749
                case FeatureStoreNotification.AFTER_REFRESH:
750
                    this.delayAction.addAction(DelayAction.ACTION_RELOADALL);
751
                    break;
752

    
753
                case FeatureStoreNotification.AFTER_FINISHEDITING:
754
                case FeatureStoreNotification.AFTER_STARTEDITING:
755
                case FeatureStoreNotification.AFTER_CANCELEDITING:
756
                    /*
757
                    No tengo nada claro por que es necesario llamar al reloadFeatureType
758
                    pero si no se incluye hay problemas si durante la edicion se a?aden
759
                    campos a la tabla. Sin esto, al cerrar la edicion, los campos a?adidos
760
                    desaparecen de la tabla aunque estan en el fichero.
761
                    Ver ticket #2434 https://devel.gvsig.org/redmine/issues/2434
762
                     */
763
                    this.delayAction.addAction(DelayAction.ACTION_RELOAD_FEATURE_TYPE, fsNotification.getFeatureType());
764
                    break;
765
                    
766
                case FeatureStoreNotification.SELECTION_CHANGE:
767
                    this.delayAction.addAction(DelayAction.ACTION_UPDATE_SELECTION);
768
                    break;
769
                    
770
                default:
771
                    LOGGER.info("update: skip notification("+type+")");
772
            }
773
        }
774
    }
775

    
776
    protected void updatePaginHelperWithHiddenColums() {
777
        FeatureQuery query = this.getHelper().getFeatureQuery();
778
        if (this.getHelper().getFeatureStore().isEditing()) {
779
            if (query.hasConstantsAttributeNames()) {
780
                query.clearConstantsAttributeNames();
781
            }
782
        } else {
783
            query.setConstantsAttributeNames(this.getHiddenColumnNames());
784
        }
785
        try {
786
            this.getHelper().reload();
787
        } catch (BaseException ex) {
788
            LOGGER.warn("Can't reload paging-helper.", ex);
789
        }
790
    }
791

    
792
    protected String[] getHiddenColumnNames() {
793
        return null;
794
    }
795

    
796
    /**
797
     * Returns the FeatureStore of the Collection.
798
     *
799
     * @return the FeatureStore
800
     */
801
    public FeatureStore getFeatureStore() {
802
        return getHelper().getFeatureStore();
803
    }
804

    
805
    /**
806
     * Returns the descriptor of a Feature attribute for a table column.
807
     *
808
     * @param columnIndex, the column index
809
     * @return
810
     */
811
    public FeatureAttributeDescriptor getDescriptorForColumn(int columnIndex) {
812
        return internalGetFeatureDescriptorForColumn(columnIndex);
813
    }
814

    
815
    /**
816
     * @param columnIndex
817
     * @return
818
     */
819
    protected FeatureAttributeDescriptor internalGetFeatureDescriptorForColumn(
820
            int columnIndex) {
821
        FeatureType featureType = getFeatureType();
822
        return featureType == null ? null : featureType
823
                .getAttributeDescriptor(columnIndex);
824
    }
825

    
826
    /**
827
     * Initialize the TableModel
828
     */
829
    protected void initialize() {
830
        // Add as observable to the FeatureStore, to detect data and selection
831
        // changes
832
        helper.getFeatureStore().addObserver(this);
833
    }
834

    
835
    /**
836
     * Returns the value of a Feature attribute, at the given position.
837
     *
838
     * @param feature the feature to get the value from
839
     * @param columnIndex the Feature attribute position
840
     * @return the value
841
     */
842
    protected Object getFeatureValue(Feature feature, int columnIndex) {
843
        return feature.get(columnIndex);
844
    }
845

    
846
    /**
847
     * Sets the value of an Feature attribute at the given position.
848
     *
849
     * @param feature the feature to update
850
     * @param columnIndex the attribute position
851
     * @param value the value to set
852
     * @return
853
     */
854
    protected EditableFeature setFeatureValue(Feature feature, int columnIndex,
855
            Object value) {
856
        EditableFeature theEditableFeature = feature.getEditable();
857
        theEditableFeature.set(columnIndex, value);
858
        return theEditableFeature;
859
    }
860

    
861
    /**
862
     * Returns the FeatureQuery used to get the Features.
863
     *
864
     * @return the FeatureQuery
865
     */
866
    public FeatureQuery getFeatureQuery() {
867
        return getHelper().getFeatureQuery();
868
    }
869

    
870
    /**
871
     * Returns the type of the
872
     *
873
     * @return features.
874
     */
875
    protected FeatureType getFeatureType() {
876
        return getHelper().getFeatureType();
877
    }
878

    
879
    /**
880
     * Reloads the table data if a feature has been changed, not through the
881
     * table.
882
     */
883
    private boolean reloadFeaturesIfFeatureCountChanged(Feature feature) {
884
        // Is any data is changed in the FeatureStore, notify the model
885
        // listeners. Ignore the case where the updated feature is
886
        // changed through us.
887
        if (editableFeature == null || !editableFeature.equals(feature)) {
888
            reloadFeatures();
889
            //Selection must be locked to avoid losing it when the table is refreshed
890
            selectionLocked = true;
891
            //The table is refreshed
892
            try {
893
                fireTableDataChanged();
894
            } catch (Exception e) {
895
                LOGGER.warn("Couldn't reload changed table");
896
            } finally {
897
                //The locked selection is unlocked.
898
                selectionLocked = false;
899
            }
900
            return true;
901
        }
902
        return false;
903
    }
904

    
905
    private boolean reloadFeaturesIfFeatureUpdated(Feature feature) {
906
        // Is any data is changed in the FeatureStore, notify the model
907
        // listeners. Ignore the case where the updated feature is
908
        // changed through us.
909
        if (editableFeature == null || !editableFeature.equals(feature)) {
910
            reloadFeatures();
911
            return true;
912
        }
913
        return false;
914
    }
915

    
916
    /**
917
     * Reloads data and structure if the {@link FeatureType} of the features
918
     * being shown has changed.
919
     */
920
    private boolean reloadFeatureTypeIfTypeChanged(FeatureType updatedType) {
921
        // If the updated featured type is the one currently being
922
        // shown, reload the table.
923
        if (updatedType != null && updatedType.getId().equals(getFeatureType().getId())) {
924
            setFeatureType(updatedType);
925
            return true;
926
        }
927
        return false;
928
    }
929

    
930
    private void reloadAll() {
931
        reloadFeatureType();
932
    }
933

    
934
    private void reloadFeatureType() {
935
        try {
936
            setFeatureType(getHelper().getFeatureStore().getFeatureType(
937
                    getHelper().getFeatureType().getId()));
938
        } catch (DataException e) {
939
            throw new FeaturesDataReloadException(e);
940
        }
941
    }
942

    
943
    /**
944
     * Reloads the features shown on the table.
945
     */
946
    private void reloadFeatures() {
947
        try {
948
            getHelper().reload();
949
        } catch (BaseException ex) {
950
            throw new FeaturesDataReloadException(ex);
951
        }
952
    }
953

    
954
    /**
955
     * Returns true if selection must not be changed.
956
     *
957
     * @return
958
     */
959
    public boolean isSelectionLocked() {
960
        return selectionLocked;
961
    }
962

    
963
}