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

History | View | Annotate | Download (16.4 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
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 2
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
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 {DiSiD Technologies}  {Implement data selection}
26
 */
27
package org.gvsig.fmap.dal.feature.impl;
28

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

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

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

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

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

    
75
        private Map featureTypeCounts = new HashMap(1);
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
        /**
323
         * Returns the list of selected values, or the deselected ones if the
324
         * selection has been reversed.
325
         * 
326
         * WARN: not really a fast implementation.
327
         */
328
        public DisposableIterator fastIterator(long index) {
329
                return iterator(index, true);
330
        }
331

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

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

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

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

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

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

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

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

    
393
                private java.util.Iterator refIterator;
394

    
395
                private FeatureStore featureStore;
396

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

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

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

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

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

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

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

    
449
                private SelectionData selectionData;
450

    
451
                private DisposableIterator iterator;
452

    
453
                private Feature nextFeature = null;
454

    
455
                private FeatureSet featureSet;
456

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

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

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

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

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

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

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

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

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

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

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

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

    
534
        }
535

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

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

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

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

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

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

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

    
584
        }
585

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

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

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

    
600
}