Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.impl / src / main / java / org / gvsig / fmap / dal / feature / impl / DefaultFeatureSelection.java @ 43088

History | View | Annotate | Download (18.9 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
package org.gvsig.fmap.dal.feature.impl;
25

    
26
import java.util.ArrayList;
27
import java.util.HashMap;
28
import java.util.Iterator;
29
import java.util.List;
30
import java.util.Map;
31
import java.util.Map.Entry;
32

    
33
import org.gvsig.fmap.dal.DataStoreNotification;
34
import org.gvsig.fmap.dal.exception.DataException;
35
import org.gvsig.fmap.dal.exception.DataRuntimeException;
36
import org.gvsig.fmap.dal.feature.EditableFeature;
37
import org.gvsig.fmap.dal.feature.Feature;
38
import org.gvsig.fmap.dal.feature.FeatureReference;
39
import org.gvsig.fmap.dal.feature.FeatureSelection;
40
import org.gvsig.fmap.dal.feature.FeatureSet;
41
import org.gvsig.fmap.dal.feature.FeatureStore;
42
import org.gvsig.fmap.dal.feature.FeatureType;
43
import org.gvsig.fmap.dal.feature.exception.ReversedSelectionIteratorException;
44
import org.gvsig.fmap.dal.feature.impl.dynobjectutils.DynObjectSetFeatureSetFacade;
45
import org.gvsig.tools.ToolsLocator;
46
import org.gvsig.tools.dispose.DisposableIterator;
47
import org.gvsig.tools.dispose.DisposeUtils;
48
import org.gvsig.tools.dynobject.DynObjectSet;
49
import org.gvsig.tools.dynobject.DynStruct;
50
import org.gvsig.tools.exception.BaseException;
51
import org.gvsig.tools.persistence.PersistenceManager;
52
import org.gvsig.tools.persistence.PersistentState;
53
import org.gvsig.tools.persistence.exception.PersistenceException;
54
import org.gvsig.tools.visitor.VisitCanceledException;
55
import org.gvsig.tools.visitor.Visitor;
56
import org.slf4j.Logger;
57
import org.slf4j.LoggerFactory;
58

    
59
/**
60
 * Default implementation of the FeatureSelection interface. Internally, only
61
 * FeatureReference values are stored.
62
 *
63
 * This implementation performs better if used with the selection related
64
 * methods: select, deselect and isSelected ones.
65
 *
66
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
67
 */
68
public class DefaultFeatureSelection extends DefaultFeatureReferenceSelection
69
                implements FeatureSelection {
70

    
71
        private static final Logger LOG = LoggerFactory
72
                        .getLogger(DefaultFeatureSelection.class);
73

    
74
        private Map featureTypeCounts = new HashMap(1);
75
        private Map<Feature,Iterator> featureIterators = new HashMap<>();
76

    
77
        /**
78
         * Creates a DefaultFeatureSelection, with a FeatureStore.
79
         *
80
         * @param featureStore
81
         *            the FeatureStore to load Features from
82
         * @throws DataException
83
         *             if there is an error while getting the total number of
84
         *             Features of the Store.
85
         * @see AbstractSetBasedDataSelection#DefaultSelection(int)
86
         */
87
        public DefaultFeatureSelection(DefaultFeatureStore featureStore)
88
                        throws DataException {
89
                super(featureStore);
90
        }
91

    
92
        /**
93
         * Creates a new Selection with the total size of Features from which the
94
         * selection will be performed.
95
         *
96
         * @param featureStore
97
         *            the FeatureStore of the selected FeatureReferences
98
         * @param helper
99
         *            to get some information of the Store
100
         * @throws DataException
101
         *             if there is an error while getting the total number of
102
         *             Features of the Store.
103
         */
104
        public DefaultFeatureSelection(FeatureStore featureStore,
105
                        FeatureSelectionHelper helper) throws DataException {
106
                super(featureStore, helper);
107
        }
108

    
109
        /**
110
         * Constructor used by the persistence manager. Don't use directly. After to
111
         * invoke this method, the persistence manager calls the the method
112
         * {@link #loadFromState(PersistentState)} to set the values of the internal
113
         * attributes that this class needs to work.
114
         */
115
        public DefaultFeatureSelection() {
116
                super();
117
        }
118

    
119
        public boolean select(Feature feature) {
120
                return select(feature, true);
121
        }
122

    
123
        /**
124
         * @see #select(Feature)
125
         * @param undoable
126
         *            if the action must be undoable
127
         */
128
        public boolean select(Feature feature, boolean undoable) {
129
                // TODO: should we check if the feature is from the same FeatureStore??
130
                if (feature == null) {
131
                        return false;
132
                }
133

    
134
                // LOGGER.debug("Selected feature: {}", feature);
135

    
136
                if (isReversed()) {
137
                        removeFeatureTypeCount(feature.getType());
138
                } else {
139
                        addFeatureTypeCount(feature.getType());
140
                }
141
                return select(feature.getReference(), undoable);
142
        }
143

    
144
        public boolean select(FeatureSet features) throws DataException {
145
                return select(features, true);
146
        }
147

    
148
        /**
149
         * @see #select(FeatureSet)
150
         * @param undoable
151
         *            if the action must be undoable
152
         */
153
        public boolean select(FeatureSet features, boolean undoable)
154
                        throws DataException {
155
                boolean change = false;
156
                boolean inComplex = false;
157
                if (undoable && getFeatureStore().isEditing()
158
                                && !getCommands().inComplex()) {
159

    
160
                        getCommands().startComplex("_selectionSelectFeatureSet");
161
                        inComplex = getCommands().inComplex();
162
                }
163

    
164
                disableNotifications();
165
                DisposableIterator iter = null;
166
                try {
167
                        for (iter = features.fastIterator(); iter.hasNext();) {
168
                                change |= select((Feature) iter.next(), undoable);
169
                        }
170
                } finally {
171
                        dispose(iter);
172
                }
173
                enableNotifications();
174
                if (undoable && getFeatureStore().isEditing() && inComplex) {
175
                        getCommands().endComplex();
176
                }
177
                if (change) {
178
                        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
179
                }
180
                return change;
181
        }
182

    
183
        public boolean deselect(Feature feature) {
184
                return deselect(feature, true);
185
        }
186

    
187
        /**
188
         * @see #deselect(Feature)
189
         * @param undoable
190
         *            if the action must be undoable
191
         */
192
        public boolean deselect(Feature feature, boolean undoable) {
193
                if (feature == null) {
194
                        return false;
195
                }
196

    
197
                LOG.debug("Deselected feature: {}", feature);
198

    
199
                if (isReversed()) {
200
                        addFeatureTypeCount(feature.getType());
201
                } else {
202
                        removeFeatureTypeCount(feature.getType());
203
                }
204
                return deselect(feature.getReference(), undoable);
205
        }
206

    
207
        public boolean deselect(FeatureSet features) throws DataException {
208
                return deselect(features, true);
209
        }
210

    
211
        /**
212
         * @see #deselect(FeatureSet)
213
         * @param undoable
214
         *            if the action must be undoable
215
         */
216
        public boolean deselect(FeatureSet features, boolean undoable)
217
                        throws DataException {
218
                boolean change = false;
219
                if (undoable && getFeatureStore().isEditing()) {
220
                        getCommands().startComplex("_selectionDeselectFeatureSet");
221
                }
222
                disableNotifications();
223
                DisposableIterator iter = null;
224
                try {
225
                        for (iter = features.fastIterator(); iter.hasNext();) {
226
                                change |= deselect((Feature) iter.next(), undoable);
227
                        }
228
                } finally {
229
                        dispose(iter);
230
                }
231
                enableNotifications();
232
                if (undoable && getFeatureStore().isEditing()) {
233
                        getCommands().endComplex();
234
                }
235
                if (change) {
236
                        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
237
                }
238
                return change;
239
        }
240

    
241
        public boolean isSelected(Feature feature) {
242
                if (feature == null) {
243
                        return false;
244
                }
245

    
246
        // Use the selection data size as a small optimization for the most
247
        // common case, when nothing is selected and every feature is checked
248
        // while drawing or painting the table document.
249
        if (selectionData.isReversed()) {
250
            return selectionData.getSize() == 0
251
                || !selectionData.contains(feature.getReference());
252
        } else {
253
            return selectionData.getSize() > 0
254
                && selectionData.contains(feature.getReference());
255
        }
256
        }
257

    
258
        public FeatureType getDefaultFeatureType() {
259
                try {
260
                        return getFeatureStore().getDefaultFeatureType();
261
                } catch (DataException ex) {
262
                        LOG.error("Error getting the default feature type "
263
                                        + "of the FeatureStore: " + getFeatureStore(), ex);
264
                }
265
                return null;
266
        }
267

    
268
        public List getFeatureTypes() {
269
                // Go through the map of FeatureTypes, and return only the ones that
270
                // have at least a Feature.
271
                List types = new ArrayList();
272
                for (java.util.Iterator iterator = featureTypeCounts.entrySet()
273
                                .iterator(); iterator.hasNext();) {
274
                        Map.Entry entry = (Entry) iterator.next();
275
                        FeatureType type = (FeatureType) entry.getKey();
276
                        Long count = (Long) entry.getValue();
277

    
278
                        if (count.longValue() > 0) {
279
                                types.add(type);
280
                        }
281
                }
282

    
283
                return types;
284
        }
285

    
286
        public long getSize() throws DataException {
287
                return getSelectedCount();
288
        }
289

    
290
        public boolean isEmpty() throws DataException {
291
                return getSelectedCount() == 0;
292
        }
293

    
294
        /**
295
         * Returns the list of selected values, or the deselected ones if the
296
         * selection has been reversed.
297
         */
298
        public DisposableIterator iterator() {
299
                return iterator(0);
300
        }
301

    
302
        /**
303
         * Returns the list of selected values, or the deselected ones if the
304
         * selection has been reversed.
305
         *
306
         * WARN: not very good performance implementation.
307
         */
308
        public DisposableIterator iterator(long index) {
309
                return iterator(index, false);
310
        }
311

    
312
        /**
313
         * Returns the list of selected values, or the deselected ones if the
314
         * selection has been reversed.
315
         *
316
         * WARN: not really a fast implementation.
317
         */
318
        public DisposableIterator fastIterator() {
319
                return fastIterator(0);
320
        }
321
        
322
        @Override
323
        public Feature first() {
324
            DisposableIterator it = null;
325
            try {
326
                it = this.iterator();
327
                if( it == null ) {
328
                    return null;
329
                }
330
                Feature f = (Feature) it.next();
331
                return f;
332
            } finally {
333
                DisposeUtils.disposeQuietly(it);
334
            }
335
        }
336

    
337
        /**
338
         * Returns the list of selected values, or the deselected ones if the
339
         * selection has been reversed.
340
         *
341
         * WARN: not really a fast implementation.
342
         */
343
        public DisposableIterator fastIterator(long index) {
344
                return iterator(index, true);
345
        }
346

    
347
        protected void clearFeatureReferences() {
348
                super.clearFeatureReferences();
349
                featureTypeCounts.clear();
350
        }
351

    
352
        /**
353
         * Creates an iterator for the Selection.
354
         */
355
        private DisposableIterator iterator(long index, boolean fastIterator) {
356
                if (isReversed()) {
357
                        DisposableIterator iter = new ReversedFeatureIteratorFacade(
358
                                        getData(), getFeatureStore(), fastIterator);
359
                        for (long l = 0; l < index && iter.hasNext(); l++) {
360
                                iter.next();
361
                        }
362
                        return iter;
363

    
364
                } else {
365
                        // TODO: maybe we could add a new referenceIterator(int index)
366
                        // method that could be implemented in a more performant way
367

    
368
                        java.util.Iterator iter = selectionData.getSelected().iterator();
369
                        for (long l = 0; l < index && iter.hasNext(); l++) {
370
                                iter.next();
371
                        }
372
                        return new FeatureIteratorFacade(iter, getFeatureStore());
373
                }
374
        }
375

    
376
        private Long removeFeatureTypeCount(FeatureType featureType) {
377
                Long count = (Long) featureTypeCounts.get(featureType);
378
                if (count == null) {
379
                        count = new Long(-1);
380
                } else {
381
                        count = new Long(count.longValue() - 1);
382
                }
383
                featureTypeCounts.put(featureType, count);
384
                return count;
385
        }
386

    
387
        private Long addFeatureTypeCount(FeatureType featureType) {
388
                Long count = (Long) featureTypeCounts.get(featureType);
389
                if (count == null) {
390
                        count = new Long(1);
391
                } else {
392
                        count = new Long(count.longValue() + 1);
393
                }
394
                featureTypeCounts.put(featureType, count);
395
                return count;
396
        }
397

    
398
        /**
399
         * Facade over a Iterator of FeatureReferences, to return Features instead.
400
         *
401
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
402
         */
403
        private class FeatureIteratorFacade implements DisposableIterator {
404

    
405
                private final Logger LOGGER = LoggerFactory
406
                                .getLogger(FeatureIteratorFacade.class);
407

    
408
                private java.util.Iterator refIterator;
409

    
410
                private FeatureStore featureStore;
411
                private Feature currentFeature = null;
412

    
413
                public FeatureIteratorFacade(java.util.Iterator iter,
414
                                FeatureStore featureStore) {
415
                        this.refIterator = iter;
416
                        this.featureStore = featureStore;
417
                }
418

    
419
                public boolean hasNext() {
420
                        return refIterator.hasNext();
421
                }
422

    
423
                public Object next() {
424
                        FeatureReference ref = nextFeatureReference();
425
                        try {
426
                            currentFeature = featureStore.getFeatureByReference(ref);
427
                                return currentFeature;
428
                        } catch (DataException ex) {
429
                                LOGGER.error(
430
                                                "Error loading the Feature with FeatureReference: "
431
                                                                + ref, ex);
432
                                return null;
433
                        }
434
                }
435

    
436
                /**
437
                 * Returns the next FeatureReference.
438
                 *
439
                 * @return the next FeatureReference
440
                 */
441
                public FeatureReference nextFeatureReference() {
442
                        return (FeatureReference) refIterator.next();
443
                }
444

    
445
                public void remove() {
446
                    try {
447
                featureStore.delete(currentFeature);
448
                refIterator.remove();
449
            } catch (DataException e) {
450
                throw new RemoveFromFeatureSelectionException(e);
451
            }
452
                }
453

    
454
        public class RemoveFromFeatureSelectionException extends DataRuntimeException {
455

    
456
            /**
457
             *
458
             */
459
            private static final long serialVersionUID = 2636692469445838928L;
460
            private final static String MESSAGE_FORMAT = "Can't remove feature from selection.";
461
            private final static String MESSAGE_KEY = "_RemoveFromFeatureSelectionException";
462

    
463
            public RemoveFromFeatureSelectionException(Throwable cause) {
464
                super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
465
                //setValue("store", store);
466
            }
467
        }
468

    
469
                public void dispose() {
470
                        if (refIterator instanceof DisposableIterator) {
471
                                ((DisposableIterator) refIterator).dispose();
472
                        }
473
                        refIterator = null;
474
                        featureStore = null;
475
                }
476
        }
477

    
478
        /**
479
         * Facade over a Iterator of FeatureReferences, to return Features instead,
480
         * when the Selection is reversed
481
         *
482
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
483
         */
484
        private class ReversedFeatureIteratorFacade implements DisposableIterator {
485

    
486
                private SelectionData selectionData;
487

    
488
                private DisposableIterator iterator;
489

    
490
        private Feature nextFeature = null;
491
        private Feature currentFeature = null;
492

    
493
                private FeatureSet featureSet;
494

    
495
                public ReversedFeatureIteratorFacade(SelectionData selectionData,
496
                                FeatureStore featureStore, boolean fastIterator) {
497
                        this.selectionData = selectionData;
498

    
499
                        // Load a Set with all the store features
500
                        try {
501
                                featureSet = featureStore.getFeatureSet();
502
                                //if (fastIterator) {
503
                                        iterator = featureSet.fastIterator();
504
//                                } else {
505
//                                        iterator = featureSet.iterator();
506
//                                }
507
                        } catch (DataException ex) {
508
                                throw new ReversedSelectionIteratorException(ex);
509
                        }
510

    
511
                        // Filter the features not selected and position in the next
512
                        // selected feature
513
                        positionInNextElement();
514
                }
515

    
516
                public boolean hasNext() {
517
                        return nextFeature != null;
518
                }
519

    
520
                public Object next() {
521
            featureIterators.remove(currentFeature);
522
                    currentFeature = nextFeature.getCopy();
523
                    featureIterators.put(currentFeature, this);
524
                        positionInNextElement();
525
                        return currentFeature ;
526
                }
527

    
528
                public void remove() {
529
                        try {
530
                featureSet.delete(currentFeature);
531
            } catch (DataException e) {
532
                throw new RemoveFromFeatureSelectionException(e);
533

    
534
            }
535
                }
536

    
537
            public class RemoveFromFeatureSelectionException extends DataRuntimeException {
538

    
539
                /**
540
             *
541
             */
542
            private static final long serialVersionUID = 2636692469445838928L;
543
            private final static String MESSAGE_FORMAT = "Can't remove feature from reversed selection.";
544
                private final static String MESSAGE_KEY = "_RemoveFromFeatureSelectionException";
545

    
546
                public RemoveFromFeatureSelectionException(Throwable cause) {
547
                    super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
548
                    //setValue("store", store);
549
                }
550
            }
551

    
552

    
553
                private void positionInNextElement() {
554
                        nextFeature = null;
555
                        while (iterator.hasNext()) {
556
                                nextFeature = (Feature) iterator.next();
557
                                if (selectionData.contains(nextFeature.getReference())) {
558
                                        nextFeature = null;
559
                                } else {
560
                                        break;
561
                                }
562
                        }
563
                }
564

    
565
                public void dispose() {
566
                        this.featureSet.dispose();
567
                        this.iterator.dispose();
568
                        this.selectionData = null;
569
                        this.nextFeature = null;
570
                }
571
        }
572

    
573
        public void delete(Feature feature) throws DataException {
574
            Iterator it = this.featureIterators.get(feature);
575
            if( it!=null ) {
576
                it.remove();
577
                return;
578
            }
579
            feature.getStore().delete(feature);
580
        }
581

    
582
        public void insert(EditableFeature feature) throws DataException {
583
        feature.getStore().insert(feature);
584
    }
585

    
586
        public void update(EditableFeature feature) throws DataException {
587
        feature.getStore().update(feature);
588
    }
589

    
590
        /*
591
         * (non-Javadoc)
592
         *
593
         * @seeorg.gvsig.fmap.dal.feature.impl.DefaultFeatureReferenceSelection#
594
         * loadFromState(org.gvsig.tools.persistence.PersistentState)
595
         */
596
        public void loadFromState(PersistentState state)
597
                        throws PersistenceException {
598
                super.loadFromState(state);
599

    
600
        }
601

    
602
        public void accept(Visitor visitor) throws BaseException {
603
                accept(visitor, 0);
604
        }
605

    
606
        public final void accept(Visitor visitor, long firstValueIndex)
607
                        throws BaseException {
608
                try {
609
                        doAccept(visitor, firstValueIndex);
610
                } catch (VisitCanceledException ex) {
611
                        // The visit has been cancelled by the visitor, so we finish here.
612
                        LOG.debug(
613
                                        "The visit, beggining on position {}, has been cancelled "
614
                                                        + "by the visitor: {}", new Long(firstValueIndex),
615
                                        visitor);
616
                }
617
        }
618

    
619
        private void doAccept(Visitor visitor, long firstValueIndex)
620
                        throws BaseException {
621
                DisposableIterator iterator = fastIterator(firstValueIndex);
622

    
623
                if (iterator != null) {
624
                        try {
625
                                while (iterator.hasNext()) {
626
                                        Feature feature = (Feature) iterator.next();
627
                                        visitor.visit(feature);
628
                                }
629
                        } finally {
630
                                iterator.dispose();
631
                        }
632
                }
633
        }
634

    
635
        protected void doDispose() throws BaseException {
636
                super.doDispose();
637
                featureTypeCounts.clear();
638
        }
639

    
640
        public static void registerPersistent() {
641
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
642
                DynStruct definition = manager.addDefinition(
643
                                DefaultFeatureSelection.class, "DefaultFeatureSelection",
644
                                "DefaultFeatureSelection Persistent definition", null, null);
645

    
646
                definition.extend(manager.getDefinition(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME));
647
                definition.addDynFieldMap("featureTypeCounts")
648
                                .setClassOfItems(Long.class).setMandatory(false);
649

    
650
        }
651

    
652
        public Object clone() throws CloneNotSupportedException {
653
                DefaultFeatureSelection clone = (DefaultFeatureSelection) super.clone();
654
                clone.featureTypeCounts = new HashMap(featureTypeCounts);
655
                return clone;
656
        }
657

    
658
    public DynObjectSet getDynObjectSet() {
659
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore());
660
    }
661

    
662
    public DynObjectSet getDynObjectSet(boolean fast) {
663
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore(), fast);
664
    }
665

    
666
}