Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libFMap_dal / src / org / gvsig / fmap / dal / feature / impl / DefaultFeatureSelection.java @ 33657

History | View | Annotate | Download (15.9 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
                        inComplex = getCommands().inComplex();
160
                        getCommands().startComplex("_selectionSelectFeatureSet");
161
                }
162

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

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

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

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

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

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

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

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

    
247
        public FeatureType getDefaultFeatureType() {
248
                try {
249
                        return getFeatureStore().getDefaultFeatureType();
250
                } catch (DataException ex) {
251
                        LOG.error("Error getting the default feature type "
252
                                        + "of the FeatureStore: " + getFeatureStore(), ex);
253
                }
254
                return null;
255
        }
256

    
257
        public List getFeatureTypes() {
258
                // Go through the map of FeatureTypes, and return only the ones that
259
                // have at least a Feature.
260
                List types = new ArrayList();
261
                for (java.util.Iterator iterator = featureTypeCounts.entrySet()
262
                                .iterator(); iterator.hasNext();) {
263
                        Map.Entry entry = (Entry) iterator.next();
264
                        FeatureType type = (FeatureType) entry.getKey();
265
                        Long count = (Long) entry.getValue();
266

    
267
                        if (count.longValue() > 0) {
268
                                types.add(type);
269
                        }
270
                }
271

    
272
                return types;
273
        }
274

    
275
        public long getSize() throws DataException {
276
                return getSelectedCount();
277
        }
278

    
279
        public boolean isEmpty() throws DataException {
280
                return getSelectedCount() == 0;
281
        }
282

    
283
        /**
284
         * Returns the list of selected values, or the deselected ones if the
285
         * selection has been reversed.
286
         */
287
        public DisposableIterator iterator() {
288
                return iterator(0);
289
        }
290

    
291
        /**
292
         * Returns the list of selected values, or the deselected ones if the
293
         * selection has been reversed.
294
         * 
295
         * WARN: not very good performance implementation.
296
         */
297
        public DisposableIterator iterator(long index) {
298
                return iterator(index, false);
299
        }
300

    
301
        /**
302
         * Returns the list of selected values, or the deselected ones if the
303
         * selection has been reversed.
304
         * 
305
         * WARN: not really a fast implementation.
306
         */
307
        public DisposableIterator fastIterator() {
308
                return fastIterator(0);
309
        }
310

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

    
321
        protected void clearFeatureReferences() {
322
                super.clearFeatureReferences();
323
                featureTypeCounts.clear();
324
        }
325

    
326
        /**
327
         * Creates an iterator for the Selection.
328
         */
329
        private DisposableIterator iterator(long index, boolean fastIterator) {
330
                if (isReversed()) {
331
                        DisposableIterator iter = new ReversedFeatureIteratorFacade(
332
                                        getData(), getFeatureStore(), fastIterator);
333
                        for (long l = 0; l < index && iter.hasNext(); l++) {
334
                                iter.next();
335
                        }
336
                        return iter;
337

    
338
                } else {
339
                        // TODO: maybe we could add a new referenceIterator(int index)
340
                        // method that could be implemented in a more performant way
341

    
342
                        java.util.Iterator iter = referenceIterator();
343
                        for (long l = 0; l < index && iter.hasNext(); l++) {
344
                                iter.next();
345
                        }
346
                        return new FeatureIteratorFacade(iter, getFeatureStore());
347
                }
348
        }
349

    
350
        private Long removeFeatureTypeCount(FeatureType featureType) {
351
                Long count = (Long) featureTypeCounts.get(featureType);
352
                if (count == null) {
353
                        count = new Long(-1);
354
                } else {
355
                        count = new Long(count.longValue() - 1);
356
                }
357
                featureTypeCounts.put(featureType, count);
358
                return count;
359
        }
360

    
361
        private Long addFeatureTypeCount(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
        /**
373
         * Facade over a Iterator of FeatureReferences, to return Features instead.
374
         * 
375
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
376
         */
377
        private class FeatureIteratorFacade implements DisposableIterator {
378

    
379
                private final Logger LOGGER = LoggerFactory
380
                                .getLogger(FeatureIteratorFacade.class);
381

    
382
                private java.util.Iterator refIterator;
383

    
384
                private FeatureStore featureStore;
385

    
386
                public FeatureIteratorFacade(java.util.Iterator iter,
387
                                FeatureStore featureStore) {
388
                        this.refIterator = iter;
389
                        this.featureStore = featureStore;
390
                }
391

    
392
                public boolean hasNext() {
393
                        return refIterator.hasNext();
394
                }
395

    
396
                public Object next() {
397
                        FeatureReference ref = nextFeatureReference();
398
                        try {
399
                                return featureStore.getFeatureByReference(ref);
400
                        } catch (DataException ex) {
401
                                LOGGER.error(
402
                                                "Error loading the Feature with FeatureReference: "
403
                                                                + ref, ex);
404
                                return null;
405
                        }
406
                }
407

    
408
                /**
409
                 * Returns the next FeatureReference.
410
                 * 
411
                 * @return the next FeatureReference
412
                 */
413
                public FeatureReference nextFeatureReference() {
414
                        return (FeatureReference) refIterator.next();
415
                }
416

    
417
                public void remove() {
418
                        refIterator.remove();
419
                }
420

    
421
                public void dispose() {
422
                        if (refIterator instanceof DisposableIterator) {
423
                                ((DisposableIterator) refIterator).dispose();
424
                        }
425
                        refIterator = null;
426
                        featureStore = null;
427
                }
428
        }
429

    
430
        /**
431
         * Facade over a Iterator of FeatureReferences, to return Features instead,
432
         * when the Selection is reversed
433
         * 
434
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
435
         */
436
        private class ReversedFeatureIteratorFacade implements DisposableIterator {
437

    
438
                private SelectionData selectionData;
439

    
440
                private DisposableIterator iterator;
441

    
442
                private Feature nextFeature = null;
443

    
444
                private FeatureSet featureSet;
445

    
446
                public ReversedFeatureIteratorFacade(SelectionData selectionData,
447
                                FeatureStore featureStore, boolean fastIterator) {
448
                        this.selectionData = selectionData;
449

    
450
                        // Load a Set with all the store features
451
                        try {
452
                                featureSet = featureStore.getFeatureSet();
453
                                if (fastIterator) {
454
                                        iterator = featureSet.fastIterator();
455
                                } else {
456
                                        iterator = featureSet.iterator();
457
                                }
458
                        } catch (DataException ex) {
459
                                throw new ReversedSelectionIteratorException(ex);
460
                        }
461

    
462
                        // Filter the features not selected and position in the next
463
                        // selected feature
464
                        positionInNextElement();
465
                }
466

    
467
                public boolean hasNext() {
468
                        return nextFeature != null;
469
                }
470

    
471
                public Object next() {
472
                        Feature tmp = nextFeature.getCopy();
473
                        positionInNextElement();
474
                        return tmp;
475
                }
476

    
477
                public void remove() {
478
                        iterator.remove();
479
                }
480

    
481
                private void positionInNextElement() {
482
                        nextFeature = null;
483
                        while (iterator.hasNext()) {
484
                                nextFeature = (Feature) iterator.next();
485
                                if (selectionData.contains(nextFeature.getReference())) {
486
                                        nextFeature = null;
487
                                } else {
488
                                        break;
489
                                }
490
                        }
491
                }
492

    
493
                public void dispose() {
494
                        this.featureSet.dispose();
495
                        this.iterator.dispose();
496
                        this.selectionData = null;
497
                        this.nextFeature = null;
498
                }
499
        }
500

    
501
        public void delete(Feature feature) throws DataException {
502
                throw new UnsupportedOperationException();
503
        }
504

    
505
        public void insert(EditableFeature feature) throws DataException {
506
                throw new UnsupportedOperationException();
507
        }
508

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

    
513
        /*
514
         * (non-Javadoc)
515
         * 
516
         * @seeorg.gvsig.fmap.dal.feature.impl.DefaultFeatureReferenceSelection#
517
         * loadFromState(org.gvsig.tools.persistence.PersistentState)
518
         */
519
        public void loadFromState(PersistentState state)
520
                        throws PersistenceException {
521
                super.loadFromState(state);
522

    
523
        }
524

    
525
        public void accept(Visitor visitor) throws BaseException {
526
                accept(visitor, 0);
527
        }
528

    
529
        public final void accept(Visitor visitor, long firstValueIndex)
530
                        throws BaseException {
531
                try {
532
                        doAccept(visitor, firstValueIndex);
533
                } catch (VisitCanceledException ex) {
534
                        // The visit has been cancelled by the visitor, so we finish here.
535
                        LOG.debug(
536
                                        "The visit, beggining on position {}, has been cancelled "
537
                                                        + "by the visitor: {}", new Long(firstValueIndex),
538
                                        visitor);
539
                }
540
        }
541

    
542
        private void doAccept(Visitor visitor, long firstValueIndex)
543
                        throws BaseException {
544
                DisposableIterator iterator = fastIterator(firstValueIndex);
545

    
546
                if (iterator != null) {
547
                        try {
548
                                while (iterator.hasNext()) {
549
                                        Feature feature = (Feature) iterator.next();
550
                                        visitor.visit(feature);
551
                                }
552
                        } finally {
553
                                iterator.dispose();
554
                        }
555
                }
556
        }
557

    
558
        protected void doDispose() throws BaseException {
559
                super.doDispose();
560
                featureTypeCounts.clear();
561
        }
562

    
563
        public static void registerPersistent() {
564
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
565
                DynStruct definition = manager.addDefinition(
566
                                DefaultFeatureSelection.class, "DefaultFeatureSelection",
567
                                "DefaultFeatureSelection Persistent definition", null, null);
568

    
569
                definition.extend(manager.getDefinition(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME));
570
                definition.addDynFieldMap("featureTypeCounts")
571
                                .setClassOfItems(Long.class).setMandatory(false);
572

    
573
        }
574

    
575
        public Object clone() throws CloneNotSupportedException {
576
                DefaultFeatureSelection clone = (DefaultFeatureSelection) super.clone();
577
                clone.featureTypeCounts = new HashMap(featureTypeCounts);
578
                return clone;
579
        }
580

    
581
    public DynObjectSet getDynObjectSet() {
582
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore());
583
    }
584

    
585
    public DynObjectSet getDynObjectSet(boolean fast) {
586
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore(), fast);
587
    }
588

    
589
}