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

History | View | Annotate | Download (16.4 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}  {Implement data selection}
27
 */
28
package org.gvsig.fmap.dal.feature.impl;
29

    
30
import java.util.ArrayList;
31
import java.util.HashMap;
32
import java.util.List;
33
import java.util.Map;
34
import java.util.Map.Entry;
35

    
36
import org.slf4j.Logger;
37
import org.slf4j.LoggerFactory;
38

    
39
import org.gvsig.fmap.dal.DataStoreNotification;
40
import org.gvsig.fmap.dal.exception.DataException;
41
import org.gvsig.fmap.dal.feature.EditableFeature;
42
import org.gvsig.fmap.dal.feature.Feature;
43
import org.gvsig.fmap.dal.feature.FeatureReference;
44
import org.gvsig.fmap.dal.feature.FeatureSelection;
45
import org.gvsig.fmap.dal.feature.FeatureSet;
46
import org.gvsig.fmap.dal.feature.FeatureStore;
47
import org.gvsig.fmap.dal.feature.FeatureType;
48
import org.gvsig.fmap.dal.feature.exception.ReversedSelectionIteratorException;
49
import org.gvsig.fmap.dal.feature.impl.featureset.DynObjectSetFeatureSetFacade;
50
import org.gvsig.tools.ToolsLocator;
51
import org.gvsig.tools.dispose.DisposableIterator;
52
import org.gvsig.tools.dynobject.DynObjectSet;
53
import org.gvsig.tools.dynobject.DynStruct;
54
import org.gvsig.tools.exception.BaseException;
55
import org.gvsig.tools.persistence.PersistenceManager;
56
import org.gvsig.tools.persistence.PersistentState;
57
import org.gvsig.tools.persistence.exception.PersistenceException;
58
import org.gvsig.tools.visitor.VisitCanceledException;
59
import org.gvsig.tools.visitor.Visitor;
60

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

    
73
        private static final Logger LOG = LoggerFactory
74
                        .getLogger(DefaultFeatureSelection.class);
75

    
76
        private Map featureTypeCounts = new HashMap(1);
77

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
284
                return types;
285
        }
286

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

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

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

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

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

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

    
333
        protected void clearFeatureReferences() {
334
                super.clearFeatureReferences();
335
                featureTypeCounts.clear();
336
        }
337

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

    
350
                } else {
351
                        // TODO: maybe we could add a new referenceIterator(int index)
352
                        // method that could be implemented in a more performant way
353

    
354
                        java.util.Iterator iter = referenceIterator();
355
                        for (long l = 0; l < index && iter.hasNext(); l++) {
356
                                iter.next();
357
                        }
358
                        return new FeatureIteratorFacade(iter, getFeatureStore());
359
                }
360
        }
361

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

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

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

    
391
                private final Logger LOGGER = LoggerFactory
392
                                .getLogger(FeatureIteratorFacade.class);
393

    
394
                private java.util.Iterator refIterator;
395

    
396
                private FeatureStore featureStore;
397

    
398
                public FeatureIteratorFacade(java.util.Iterator iter,
399
                                FeatureStore featureStore) {
400
                        this.refIterator = iter;
401
                        this.featureStore = featureStore;
402
                }
403

    
404
                public boolean hasNext() {
405
                        return refIterator.hasNext();
406
                }
407

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

    
420
                /**
421
                 * Returns the next FeatureReference.
422
                 * 
423
                 * @return the next FeatureReference
424
                 */
425
                public FeatureReference nextFeatureReference() {
426
                        return (FeatureReference) refIterator.next();
427
                }
428

    
429
                public void remove() {
430
                        refIterator.remove();
431
                }
432

    
433
                public void dispose() {
434
                        if (refIterator instanceof DisposableIterator) {
435
                                ((DisposableIterator) refIterator).dispose();
436
                        }
437
                        refIterator = null;
438
                        featureStore = null;
439
                }
440
        }
441

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

    
450
                private SelectionData selectionData;
451

    
452
                private DisposableIterator iterator;
453

    
454
                private Feature nextFeature = null;
455

    
456
                private FeatureSet featureSet;
457

    
458
                public ReversedFeatureIteratorFacade(SelectionData selectionData,
459
                                FeatureStore featureStore, boolean fastIterator) {
460
                        this.selectionData = selectionData;
461

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

    
474
                        // Filter the features not selected and position in the next
475
                        // selected feature
476
                        positionInNextElement();
477
                }
478

    
479
                public boolean hasNext() {
480
                        return nextFeature != null;
481
                }
482

    
483
                public Object next() {
484
                        Feature tmp = nextFeature.getCopy();
485
                        positionInNextElement();
486
                        return tmp;
487
                }
488

    
489
                public void remove() {
490
                        iterator.remove();
491
                }
492

    
493
                private void positionInNextElement() {
494
                        nextFeature = null;
495
                        while (iterator.hasNext()) {
496
                                nextFeature = (Feature) iterator.next();
497
                                if (selectionData.contains(nextFeature.getReference())) {
498
                                        nextFeature = null;
499
                                } else {
500
                                        break;
501
                                }
502
                        }
503
                }
504

    
505
                public void dispose() {
506
                        this.featureSet.dispose();
507
                        this.iterator.dispose();
508
                        this.selectionData = null;
509
                        this.nextFeature = null;
510
                }
511
        }
512

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

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

    
521
        public void update(EditableFeature feature) throws DataException {
522
                throw new UnsupportedOperationException();
523
        }
524

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

    
535
        }
536

    
537
        public void accept(Visitor visitor) throws BaseException {
538
                accept(visitor, 0);
539
        }
540

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

    
554
        private void doAccept(Visitor visitor, long firstValueIndex)
555
                        throws BaseException {
556
                DisposableIterator iterator = fastIterator(firstValueIndex);
557

    
558
                if (iterator != null) {
559
                        try {
560
                                while (iterator.hasNext()) {
561
                                        Feature feature = (Feature) iterator.next();
562
                                        visitor.visit(feature);
563
                                }
564
                        } finally {
565
                                iterator.dispose();
566
                        }
567
                }
568
        }
569

    
570
        protected void doDispose() throws BaseException {
571
                super.doDispose();
572
                featureTypeCounts.clear();
573
        }
574

    
575
        public static void registerPersistent() {
576
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
577
                DynStruct definition = manager.addDefinition(
578
                                DefaultFeatureSelection.class, "DefaultFeatureSelection",
579
                                "DefaultFeatureSelection Persistent definition", null, null);
580

    
581
                definition.extend(manager.getDefinition(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME));
582
                definition.addDynFieldMap("featureTypeCounts")
583
                                .setClassOfItems(Long.class).setMandatory(false);
584

    
585
        }
586

    
587
        public Object clone() throws CloneNotSupportedException {
588
                DefaultFeatureSelection clone = (DefaultFeatureSelection) super.clone();
589
                clone.featureTypeCounts = new HashMap(featureTypeCounts);
590
                return clone;
591
        }
592

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

    
597
    public DynObjectSet getDynObjectSet(boolean fast) {
598
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore(), fast);
599
    }
600

    
601
}