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

History | View | Annotate | Download (16.3 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.List;
29
import java.util.Map;
30
import java.util.Map.Entry;
31

    
32
import org.slf4j.Logger;
33
import org.slf4j.LoggerFactory;
34

    
35
import org.gvsig.fmap.dal.DataStoreNotification;
36
import org.gvsig.fmap.dal.exception.DataException;
37
import org.gvsig.fmap.dal.feature.EditableFeature;
38
import org.gvsig.fmap.dal.feature.Feature;
39
import org.gvsig.fmap.dal.feature.FeatureReference;
40
import org.gvsig.fmap.dal.feature.FeatureSelection;
41
import org.gvsig.fmap.dal.feature.FeatureSet;
42
import org.gvsig.fmap.dal.feature.FeatureStore;
43
import org.gvsig.fmap.dal.feature.FeatureType;
44
import org.gvsig.fmap.dal.feature.exception.ReversedSelectionIteratorException;
45
import org.gvsig.fmap.dal.feature.impl.featureset.DynObjectSetFeatureSetFacade;
46
import org.gvsig.tools.ToolsLocator;
47
import org.gvsig.tools.dispose.DisposableIterator;
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

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

    
69
        private static final Logger LOG = LoggerFactory
70
                        .getLogger(DefaultFeatureSelection.class);
71

    
72
        private Map featureTypeCounts = new HashMap(1);
73

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

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

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

    
116
        public boolean select(Feature feature) {
117
                return select(feature, true);
118
        }
119

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

    
131
                // LOGGER.debug("Selected feature: {}", feature);
132

    
133
                if (isReversed()) {
134
                        removeFeatureTypeCount(feature.getType());
135
                } else {
136
                        addFeatureTypeCount(feature.getType());
137
                }
138
                return select(feature.getReference(), undoable);
139
        }
140

    
141
        public boolean select(FeatureSet features) throws DataException {
142
                return select(features, true);
143
        }
144

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

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

    
180
        public boolean deselect(Feature feature) {
181
                return deselect(feature, true);
182
        }
183

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

    
194
                LOG.debug("Deselected feature: {}", feature);
195

    
196
                if (isReversed()) {
197
                        addFeatureTypeCount(feature.getType());
198
                } else {
199
                        removeFeatureTypeCount(feature.getType());
200
                }
201
                return deselect(feature.getReference(), undoable);
202
        }
203

    
204
        public boolean deselect(FeatureSet features) throws DataException {
205
                return deselect(features, true);
206
        }
207

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

    
238
        public boolean isSelected(Feature feature) {
239
                if (feature == null) {
240
                        return false;
241
                }
242

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

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

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

    
275
                        if (count.longValue() > 0) {
276
                                types.add(type);
277
                        }
278
                }
279

    
280
                return types;
281
        }
282

    
283
        public long getSize() throws DataException {
284
                return getSelectedCount();
285
        }
286

    
287
        public boolean isEmpty() throws DataException {
288
                return getSelectedCount() == 0;
289
        }
290

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

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

    
309
        /**
310
         * Returns the list of selected values, or the deselected ones if the
311
         * selection has been reversed.
312
         * 
313
         * WARN: not really a fast implementation.
314
         */
315
        public DisposableIterator fastIterator() {
316
                return fastIterator(0);
317
        }
318

    
319
        /**
320
         * Returns the list of selected values, or the deselected ones if the
321
         * selection has been reversed.
322
         * 
323
         * WARN: not really a fast implementation.
324
         */
325
        public DisposableIterator fastIterator(long index) {
326
                return iterator(index, true);
327
        }
328

    
329
        protected void clearFeatureReferences() {
330
                super.clearFeatureReferences();
331
                featureTypeCounts.clear();
332
        }
333

    
334
        /**
335
         * Creates an iterator for the Selection.
336
         */
337
        private DisposableIterator iterator(long index, boolean fastIterator) {
338
                if (isReversed()) {
339
                        DisposableIterator iter = new ReversedFeatureIteratorFacade(
340
                                        getData(), getFeatureStore(), fastIterator);
341
                        for (long l = 0; l < index && iter.hasNext(); l++) {
342
                                iter.next();
343
                        }
344
                        return iter;
345

    
346
                } else {
347
                        // TODO: maybe we could add a new referenceIterator(int index)
348
                        // method that could be implemented in a more performant way
349

    
350
                        java.util.Iterator iter = referenceIterator();
351
                        for (long l = 0; l < index && iter.hasNext(); l++) {
352
                                iter.next();
353
                        }
354
                        return new FeatureIteratorFacade(iter, getFeatureStore());
355
                }
356
        }
357

    
358
        private Long removeFeatureTypeCount(FeatureType featureType) {
359
                Long count = (Long) featureTypeCounts.get(featureType);
360
                if (count == null) {
361
                        count = new Long(-1);
362
                } else {
363
                        count = new Long(count.longValue() - 1);
364
                }
365
                featureTypeCounts.put(featureType, count);
366
                return count;
367
        }
368

    
369
        private Long addFeatureTypeCount(FeatureType featureType) {
370
                Long count = (Long) featureTypeCounts.get(featureType);
371
                if (count == null) {
372
                        count = new Long(1);
373
                } else {
374
                        count = new Long(count.longValue() + 1);
375
                }
376
                featureTypeCounts.put(featureType, count);
377
                return count;
378
        }
379

    
380
        /**
381
         * Facade over a Iterator of FeatureReferences, to return Features instead.
382
         * 
383
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
384
         */
385
        private class FeatureIteratorFacade implements DisposableIterator {
386

    
387
                private final Logger LOGGER = LoggerFactory
388
                                .getLogger(FeatureIteratorFacade.class);
389

    
390
                private java.util.Iterator refIterator;
391

    
392
                private FeatureStore featureStore;
393

    
394
                public FeatureIteratorFacade(java.util.Iterator iter,
395
                                FeatureStore featureStore) {
396
                        this.refIterator = iter;
397
                        this.featureStore = featureStore;
398
                }
399

    
400
                public boolean hasNext() {
401
                        return refIterator.hasNext();
402
                }
403

    
404
                public Object next() {
405
                        FeatureReference ref = nextFeatureReference();
406
                        try {
407
                                return featureStore.getFeatureByReference(ref);
408
                        } catch (DataException ex) {
409
                                LOGGER.error(
410
                                                "Error loading the Feature with FeatureReference: "
411
                                                                + ref, ex);
412
                                return null;
413
                        }
414
                }
415

    
416
                /**
417
                 * Returns the next FeatureReference.
418
                 * 
419
                 * @return the next FeatureReference
420
                 */
421
                public FeatureReference nextFeatureReference() {
422
                        return (FeatureReference) refIterator.next();
423
                }
424

    
425
                public void remove() {
426
                        refIterator.remove();
427
                }
428

    
429
                public void dispose() {
430
                        if (refIterator instanceof DisposableIterator) {
431
                                ((DisposableIterator) refIterator).dispose();
432
                        }
433
                        refIterator = null;
434
                        featureStore = null;
435
                }
436
        }
437

    
438
        /**
439
         * Facade over a Iterator of FeatureReferences, to return Features instead,
440
         * when the Selection is reversed
441
         * 
442
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
443
         */
444
        private class ReversedFeatureIteratorFacade implements DisposableIterator {
445

    
446
                private SelectionData selectionData;
447

    
448
                private DisposableIterator iterator;
449

    
450
                private Feature nextFeature = null;
451

    
452
                private FeatureSet featureSet;
453

    
454
                public ReversedFeatureIteratorFacade(SelectionData selectionData,
455
                                FeatureStore featureStore, boolean fastIterator) {
456
                        this.selectionData = selectionData;
457

    
458
                        // Load a Set with all the store features
459
                        try {
460
                                featureSet = featureStore.getFeatureSet();
461
                                //if (fastIterator) {
462
                                        iterator = featureSet.fastIterator();
463
//                                } else {
464
//                                        iterator = featureSet.iterator();
465
//                                }
466
                        } catch (DataException ex) {
467
                                throw new ReversedSelectionIteratorException(ex);
468
                        }
469

    
470
                        // Filter the features not selected and position in the next
471
                        // selected feature
472
                        positionInNextElement();
473
                }
474

    
475
                public boolean hasNext() {
476
                        return nextFeature != null;
477
                }
478

    
479
                public Object next() {
480
                        Feature tmp = nextFeature.getCopy();
481
                        positionInNextElement();
482
                        return tmp;
483
                }
484

    
485
                public void remove() {
486
                        iterator.remove();
487
                }
488

    
489
                private void positionInNextElement() {
490
                        nextFeature = null;
491
                        while (iterator.hasNext()) {
492
                                nextFeature = (Feature) iterator.next();
493
                                if (selectionData.contains(nextFeature.getReference())) {
494
                                        nextFeature = null;
495
                                } else {
496
                                        break;
497
                                }
498
                        }
499
                }
500

    
501
                public void dispose() {
502
                        this.featureSet.dispose();
503
                        this.iterator.dispose();
504
                        this.selectionData = null;
505
                        this.nextFeature = null;
506
                }
507
        }
508

    
509
        public void delete(Feature feature) throws DataException {
510
                throw new UnsupportedOperationException();
511
        }
512

    
513
        public void insert(EditableFeature feature) throws DataException {
514
                throw new UnsupportedOperationException();
515
        }
516

    
517
        public void update(EditableFeature feature) throws DataException {
518
                throw new UnsupportedOperationException();
519
        }
520

    
521
        /*
522
         * (non-Javadoc)
523
         * 
524
         * @seeorg.gvsig.fmap.dal.feature.impl.DefaultFeatureReferenceSelection#
525
         * loadFromState(org.gvsig.tools.persistence.PersistentState)
526
         */
527
        public void loadFromState(PersistentState state)
528
                        throws PersistenceException {
529
                super.loadFromState(state);
530

    
531
        }
532

    
533
        public void accept(Visitor visitor) throws BaseException {
534
                accept(visitor, 0);
535
        }
536

    
537
        public final void accept(Visitor visitor, long firstValueIndex)
538
                        throws BaseException {
539
                try {
540
                        doAccept(visitor, firstValueIndex);
541
                } catch (VisitCanceledException ex) {
542
                        // The visit has been cancelled by the visitor, so we finish here.
543
                        LOG.debug(
544
                                        "The visit, beggining on position {}, has been cancelled "
545
                                                        + "by the visitor: {}", new Long(firstValueIndex),
546
                                        visitor);
547
                }
548
        }
549

    
550
        private void doAccept(Visitor visitor, long firstValueIndex)
551
                        throws BaseException {
552
                DisposableIterator iterator = fastIterator(firstValueIndex);
553

    
554
                if (iterator != null) {
555
                        try {
556
                                while (iterator.hasNext()) {
557
                                        Feature feature = (Feature) iterator.next();
558
                                        visitor.visit(feature);
559
                                }
560
                        } finally {
561
                                iterator.dispose();
562
                        }
563
                }
564
        }
565

    
566
        protected void doDispose() throws BaseException {
567
                super.doDispose();
568
                featureTypeCounts.clear();
569
        }
570

    
571
        public static void registerPersistent() {
572
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
573
                DynStruct definition = manager.addDefinition(
574
                                DefaultFeatureSelection.class, "DefaultFeatureSelection",
575
                                "DefaultFeatureSelection Persistent definition", null, null);
576

    
577
                definition.extend(manager.getDefinition(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME));
578
                definition.addDynFieldMap("featureTypeCounts")
579
                                .setClassOfItems(Long.class).setMandatory(false);
580

    
581
        }
582

    
583
        public Object clone() throws CloneNotSupportedException {
584
                DefaultFeatureSelection clone = (DefaultFeatureSelection) super.clone();
585
                clone.featureTypeCounts = new HashMap(featureTypeCounts);
586
                return clone;
587
        }
588

    
589
    public DynObjectSet getDynObjectSet() {
590
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore());
591
    }
592

    
593
    public DynObjectSet getDynObjectSet(boolean fast) {
594
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore(), fast);
595
    }
596

    
597
}