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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
159
                        getCommands().startComplex("_selectionSelectFeatureSet");
160
                        inComplex = getCommands().inComplex();
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

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

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

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

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

    
282
                return types;
283
        }
284

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

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

    
293
        /**
294
         * Returns the list of selected values, or the deselected ones if the
295
         * selection has been reversed.
296
         */
297
        public DisposableIterator iterator() {
298
                return iterator(0);
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 very good performance implementation.
306
         */
307
        public DisposableIterator iterator(long index) {
308
                return iterator(index, false);
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() {
318
                return fastIterator(0);
319
        }
320

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

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

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

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

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

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

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

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

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

    
392
                private java.util.Iterator refIterator;
393

    
394
                private FeatureStore featureStore;
395
                private Feature currentFeature = null;
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
                            currentFeature = featureStore.getFeatureByReference(ref);
411
                                return currentFeature;
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
                    try {
431
                featureStore.delete(currentFeature);
432
                refIterator.remove();
433
            } catch (DataException e) {
434
                throw new RemoveFromFeatureSelectionException(e);
435
            }
436
                }
437

    
438
        public class RemoveFromFeatureSelectionException extends DataRuntimeException {
439

    
440
            /**
441
             *
442
             */
443
            private static final long serialVersionUID = 2636692469445838928L;
444
            private final static String MESSAGE_FORMAT = "Can't remove feature from selection.";
445
            private final static String MESSAGE_KEY = "_RemoveFromFeatureSelectionException";
446

    
447
            public RemoveFromFeatureSelectionException(Throwable cause) {
448
                super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
449
                //setValue("store", store);
450
            }
451
        }
452

    
453
                public void dispose() {
454
                        if (refIterator instanceof DisposableIterator) {
455
                                ((DisposableIterator) refIterator).dispose();
456
                        }
457
                        refIterator = null;
458
                        featureStore = null;
459
                }
460
        }
461

    
462
        /**
463
         * Facade over a Iterator of FeatureReferences, to return Features instead,
464
         * when the Selection is reversed
465
         *
466
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
467
         */
468
        private class ReversedFeatureIteratorFacade implements DisposableIterator {
469

    
470
                private SelectionData selectionData;
471

    
472
                private DisposableIterator iterator;
473

    
474
        private Feature nextFeature = null;
475
        private Feature currentFeature = null;
476

    
477
                private FeatureSet featureSet;
478

    
479
                public ReversedFeatureIteratorFacade(SelectionData selectionData,
480
                                FeatureStore featureStore, boolean fastIterator) {
481
                        this.selectionData = selectionData;
482

    
483
                        // Load a Set with all the store features
484
                        try {
485
                                featureSet = featureStore.getFeatureSet();
486
                                //if (fastIterator) {
487
                                        iterator = featureSet.fastIterator();
488
//                                } else {
489
//                                        iterator = featureSet.iterator();
490
//                                }
491
                        } catch (DataException ex) {
492
                                throw new ReversedSelectionIteratorException(ex);
493
                        }
494

    
495
                        // Filter the features not selected and position in the next
496
                        // selected feature
497
                        positionInNextElement();
498
                }
499

    
500
                public boolean hasNext() {
501
                        return nextFeature != null;
502
                }
503

    
504
                public Object next() {
505
            featureIterators.remove(currentFeature);
506
                    currentFeature = nextFeature.getCopy();
507
                    featureIterators.put(currentFeature, this);
508
                        positionInNextElement();
509
                        return currentFeature ;
510
                }
511

    
512
                public void remove() {
513
                        try {
514
                featureSet.delete(currentFeature);
515
            } catch (DataException e) {
516
                throw new RemoveFromFeatureSelectionException(e);
517

    
518
            }
519
                }
520

    
521
            public class RemoveFromFeatureSelectionException extends DataRuntimeException {
522

    
523
                /**
524
             *
525
             */
526
            private static final long serialVersionUID = 2636692469445838928L;
527
            private final static String MESSAGE_FORMAT = "Can't remove feature from reversed selection.";
528
                private final static String MESSAGE_KEY = "_RemoveFromFeatureSelectionException";
529

    
530
                public RemoveFromFeatureSelectionException(Throwable cause) {
531
                    super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
532
                    //setValue("store", store);
533
                }
534
            }
535

    
536

    
537
                private void positionInNextElement() {
538
                        nextFeature = null;
539
                        while (iterator.hasNext()) {
540
                                nextFeature = (Feature) iterator.next();
541
                                if (selectionData.contains(nextFeature.getReference())) {
542
                                        nextFeature = null;
543
                                } else {
544
                                        break;
545
                                }
546
                        }
547
                }
548

    
549
                public void dispose() {
550
                        this.featureSet.dispose();
551
                        this.iterator.dispose();
552
                        this.selectionData = null;
553
                        this.nextFeature = null;
554
                }
555
        }
556

    
557
        public void delete(Feature feature) throws DataException {
558
            Iterator it = this.featureIterators.get(feature);
559
            if( it!=null ) {
560
                it.remove();
561
                return;
562
            }
563
            feature.getStore().delete(feature);
564
        }
565

    
566
        public void insert(EditableFeature feature) throws DataException {
567
        feature.getStore().insert(feature);
568
    }
569

    
570
        public void update(EditableFeature feature) throws DataException {
571
        feature.getStore().update(feature);
572
    }
573

    
574
        /*
575
         * (non-Javadoc)
576
         *
577
         * @seeorg.gvsig.fmap.dal.feature.impl.DefaultFeatureReferenceSelection#
578
         * loadFromState(org.gvsig.tools.persistence.PersistentState)
579
         */
580
        public void loadFromState(PersistentState state)
581
                        throws PersistenceException {
582
                super.loadFromState(state);
583

    
584
        }
585

    
586
        public void accept(Visitor visitor) throws BaseException {
587
                accept(visitor, 0);
588
        }
589

    
590
        public final void accept(Visitor visitor, long firstValueIndex)
591
                        throws BaseException {
592
                try {
593
                        doAccept(visitor, firstValueIndex);
594
                } catch (VisitCanceledException ex) {
595
                        // The visit has been cancelled by the visitor, so we finish here.
596
                        LOG.debug(
597
                                        "The visit, beggining on position {}, has been cancelled "
598
                                                        + "by the visitor: {}", new Long(firstValueIndex),
599
                                        visitor);
600
                }
601
        }
602

    
603
        private void doAccept(Visitor visitor, long firstValueIndex)
604
                        throws BaseException {
605
                DisposableIterator iterator = fastIterator(firstValueIndex);
606

    
607
                if (iterator != null) {
608
                        try {
609
                                while (iterator.hasNext()) {
610
                                        Feature feature = (Feature) iterator.next();
611
                                        visitor.visit(feature);
612
                                }
613
                        } finally {
614
                                iterator.dispose();
615
                        }
616
                }
617
        }
618

    
619
        protected void doDispose() throws BaseException {
620
                super.doDispose();
621
                featureTypeCounts.clear();
622
        }
623

    
624
        public static void registerPersistent() {
625
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
626
                DynStruct definition = manager.addDefinition(
627
                                DefaultFeatureSelection.class, "DefaultFeatureSelection",
628
                                "DefaultFeatureSelection Persistent definition", null, null);
629

    
630
                definition.extend(manager.getDefinition(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME));
631
                definition.addDynFieldMap("featureTypeCounts")
632
                                .setClassOfItems(Long.class).setMandatory(false);
633

    
634
        }
635

    
636
        public Object clone() throws CloneNotSupportedException {
637
                DefaultFeatureSelection clone = (DefaultFeatureSelection) super.clone();
638
                clone.featureTypeCounts = new HashMap(featureTypeCounts);
639
                return clone;
640
        }
641

    
642
    public DynObjectSet getDynObjectSet() {
643
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore());
644
    }
645

    
646
    public DynObjectSet getDynObjectSet(boolean fast) {
647
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore(), fast);
648
    }
649

    
650
}