Statistics
| Revision:

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

History | View | Annotate | Download (14.6 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Gobernment (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.gvsig.fmap.dal.DataStoreNotification;
36
import org.gvsig.fmap.dal.exception.DataException;
37
import org.gvsig.fmap.dal.feature.DisposableIterator;
38
import org.gvsig.fmap.dal.feature.EditableFeature;
39
import org.gvsig.fmap.dal.feature.Feature;
40
import org.gvsig.fmap.dal.feature.FeatureReference;
41
import org.gvsig.fmap.dal.feature.FeatureSelection;
42
import org.gvsig.fmap.dal.feature.FeatureSet;
43
import org.gvsig.fmap.dal.feature.FeatureStore;
44
import org.gvsig.fmap.dal.feature.FeatureType;
45
import org.gvsig.fmap.dal.feature.exception.ReversedSelectionIteratorException;
46
import org.gvsig.tools.ToolsLocator;
47
import org.gvsig.tools.dynobject.DynClass;
48
import org.gvsig.tools.dynobject.DynObjectManager;
49
import org.gvsig.tools.exception.BaseException;
50
import org.gvsig.tools.persistence.PersistenceException;
51
import org.gvsig.tools.persistence.PersistentState;
52
import org.gvsig.tools.visitor.Visitor;
53
import org.slf4j.Logger;
54
import org.slf4j.LoggerFactory;
55

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

    
68
        private static final Logger LOGGER = LoggerFactory
69
        .getLogger(DefaultFeatureSelection.class);
70

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

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

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

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

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

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

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

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

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

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

    
159
                disableNotifications();
160
                for (DisposableIterator iter = features.fastIterator(); iter.hasNext();) {
161
                        change |= select((Feature) iter.next(), undoable);
162
                }
163
                enableNotifications();
164
                if (undoable && getFeatureStore().isEditing() && !inComplex) {
165
                        getCommands().endComplex();
166
                }
167
                if (change) {
168
                        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
169
                }
170
                return change;
171
        }
172

    
173
        public boolean deselect(Feature feature) {
174
                return deselect(feature, true);
175
        }
176

    
177
        /**
178
         * @see #deselect(Feature)
179
         * @param undoable
180
         *            if the action must be undoable
181
         */
182
        public boolean deselect(Feature feature, boolean undoable) {
183
                if (feature == null) {
184
                        return false;
185
                }
186

    
187
                LOGGER.debug("Deselected feature: {}", feature);
188

    
189
                if (isReversed()) {
190
                        addFeatureTypeCount(feature.getType());
191
                } else {
192
                        removeFeatureTypeCount(feature.getType());
193
                }
194
                return deselect(feature.getReference(), undoable);
195
        }
196

    
197
        public boolean deselect(FeatureSet features) throws DataException {
198
                return deselect(features, true);
199
        }
200

    
201
        /**
202
         * @see #deselect(FeatureSet)
203
         * @param undoable
204
         *            if the action must be undoable
205
         */
206
        public boolean deselect(FeatureSet features, boolean undoable)
207
        throws DataException {
208
                boolean change = false;
209
                if (undoable && getFeatureStore().isEditing()) {
210
                        getCommands().startComplex("_selectionDeselectFeatureSet");
211
                }
212
                disableNotifications();
213
                for (DisposableIterator iter = features.fastIterator(); iter.hasNext();) {
214
                        change |= deselect((Feature) iter.next(), undoable);
215
                }
216
                enableNotifications();
217
                if (undoable && getFeatureStore().isEditing()) {
218
                        getCommands().endComplex();
219
                }
220
                if (change) {
221
                        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
222
                }
223
                return change;
224
        }
225

    
226
        public boolean isSelected(Feature feature) {
227
                if (feature == null) {
228
                        return false;
229
                }
230
                return isSelected(feature.getReference());
231
        }
232

    
233
        public FeatureType getDefaultFeatureType() {
234
                try {
235
                        return getFeatureStore().getDefaultFeatureType();
236
                } catch (DataException ex) {
237
                        LOGGER.error("Error getting the default feature type "
238
                                        + "of the FeatureStore: " + getFeatureStore(), ex);
239
                }
240
                return null;
241
        }
242

    
243
        public List getFeatureTypes() {
244
                // Go through the map of FeatureTypes, and return only the ones that
245
                // have at least a Feature.
246
                List types = new ArrayList();
247
                for (java.util.Iterator iterator = featureTypeCounts.entrySet()
248
                                .iterator(); iterator
249
                                .hasNext();) {
250
                        Map.Entry entry = (Entry) iterator.next();
251
                        FeatureType type = (FeatureType) entry.getKey();
252
                        Long count = (Long) entry.getValue();
253

    
254
                        if (count.longValue() > 0) {
255
                                types.add(type);
256
                        }
257
                }
258

    
259
                return types;
260
        }
261

    
262
        public long getSize() throws DataException {
263
                return getSelectedCount();
264
        }
265

    
266
        public boolean isEmpty() throws DataException {
267
                return getSelectedCount() == 0;
268
        }
269

    
270
        /**
271
         * Returns the list of selected values, or the deselected ones if the
272
         * selection has been reversed.
273
         */
274
        public DisposableIterator iterator() {
275
                return iterator(0);
276
        }
277

    
278
        /**
279
         * Returns the list of selected values, or the deselected ones if the
280
         * selection has been reversed.
281
         *
282
         * WARN: not very good performance implementation.
283
         */
284
        public DisposableIterator iterator(long index) {
285
                return iterator(index, false);
286
        }
287

    
288
        /**
289
         * Returns the list of selected values, or the deselected ones if the
290
         * selection has been reversed.
291
         *
292
         * WARN: not really a fast implementation.
293
         */
294
        public DisposableIterator fastIterator() {
295
                return fastIterator(0);
296
        }
297

    
298
        /**
299
         * Returns the list of selected values, or the deselected ones if the
300
         * selection has been reversed.
301
         *
302
         * WARN: not really a fast implementation.
303
         */
304
        public DisposableIterator fastIterator(long index) {
305
                return iterator(index, true);
306
        }
307

    
308

    
309
        protected void clearFeatureReferences() {
310
                super.clearFeatureReferences();
311
                featureTypeCounts.clear();
312
        }
313

    
314
        /**
315
         * Creates an iterator for the Selection.
316
         */
317
        private DisposableIterator iterator(long index, boolean fastIterator) {
318
                if (isReversed()) {
319
                        DisposableIterator iter = new ReversedFeatureIteratorFacade(getData(),
320
                                        getFeatureStore(), fastIterator);
321
                        for (long l = 0; l < index && iter.hasNext(); l++) {
322
                                iter.next();
323
                        }
324
                        return iter;
325

    
326
                } else {
327
                        // TODO: maybe we could add a new referenceIterator(int index)
328
                // method that could be implemented in a more performant way
329

    
330
                        java.util.Iterator iter = referenceIterator();
331
                        for (long l = 0; l < index && iter.hasNext(); l++) {
332
                                iter.next();
333
                        }
334
                        return new FeatureIteratorFacade(iter, getFeatureStore());
335
                }
336
        }
337

    
338
        private Long removeFeatureTypeCount(FeatureType featureType) {
339
                Long count = (Long) featureTypeCounts.get(featureType);
340
                if (count == null) {
341
                        count = new Long(-1);
342
                } else {
343
                        count = new Long(count.longValue() - 1);
344
                }
345
                featureTypeCounts.put(featureType, count);
346
                return count;
347
        }
348

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

    
360
        /**
361
         * Facade over a Iterator of FeatureReferences, to return Features instead.
362
         *
363
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
364
         */
365
        private class FeatureIteratorFacade implements DisposableIterator {
366

    
367
                final Logger logger = LoggerFactory
368
                .getLogger(FeatureIteratorFacade.class);
369

    
370
                private java.util.Iterator refIterator;
371

    
372
                private FeatureStore featureStore;
373

    
374
                public FeatureIteratorFacade(java.util.Iterator iter,
375
                                FeatureStore featureStore) {
376
                        this.refIterator = iter;
377
                        this.featureStore = featureStore;
378
                }
379

    
380
                public boolean hasNext() {
381
                        return refIterator.hasNext();
382
                }
383

    
384
                public Object next() {
385
                        FeatureReference ref = nextFeatureReference();
386
                        try {
387
                                return featureStore.getFeatureByReference(ref);
388
                        } catch (DataException ex) {
389
                                logger.error(
390
                                                "Error loading the Feature with FeatureReference: "
391
                                                + ref, ex);
392
                                return null;
393
                        }
394
                }
395

    
396
                /**
397
                 * Returns the next FeatureReference.
398
                 *
399
                 * @return the next FeatureReference
400
                 */
401
                public FeatureReference nextFeatureReference() {
402
                        return (FeatureReference) refIterator.next();
403
                }
404

    
405
                public void remove() {
406
                        refIterator.remove();
407
                }
408

    
409
                public void dispose() {
410
                        if (refIterator instanceof DisposableIterator) {
411
                                ((DisposableIterator) refIterator).dispose();
412
                        }
413
                        refIterator = null;
414
                        featureStore = null;
415
                }
416
        }
417

    
418
        /**
419
         * Facade over a Iterator of FeatureReferences, to return Features instead,
420
         * when the Selection is reversed
421
         *
422
         * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
423
         */
424
        private class ReversedFeatureIteratorFacade implements DisposableIterator {
425

    
426
                private SelectionData selectionData;
427

    
428
                private DisposableIterator iterator;
429

    
430
                private Feature nextFeature = null;
431

    
432
                private FeatureSet featureSet;
433

    
434
                public ReversedFeatureIteratorFacade(SelectionData selectionData,
435
                                FeatureStore featureStore, boolean fastIterator) {
436
                        this.selectionData = selectionData;
437

    
438
                        // Load a Set with all the store features
439
                        try {
440
                                featureSet = featureStore.getFeatureSet();
441
                                if (fastIterator) {
442
                                        iterator = featureSet.fastIterator();
443
                                } else {
444
                                        iterator = featureSet.iterator();
445
                                }
446
                        } catch (DataException ex) {
447
                                throw new ReversedSelectionIteratorException(ex);
448
                        }
449

    
450
                        // Filter the features not selected and position in the next
451
                        // selected feature
452
                        positionInNextElement();
453
                }
454

    
455
                public boolean hasNext() {
456
                        return nextFeature != null;
457
                }
458

    
459
                public Object next() {
460
                        Feature tmp = nextFeature;
461
                        positionInNextElement();
462
                        return tmp;
463
                }
464

    
465
                public void remove() {
466
                        iterator.remove();
467
                }
468

    
469
                private void positionInNextElement() {
470
                        nextFeature = null;
471
                        while (iterator.hasNext()) {
472
                                nextFeature = (Feature) iterator.next();
473
                                if (selectionData.contains(nextFeature.getReference())) {
474
                                        nextFeature = null;
475
                                } else {
476
                                        break;
477
                                }
478
                        }
479
                }
480

    
481
                public void dispose() {
482
                        this.featureSet.dispose();
483
                        this.iterator.dispose();
484
                        this.selectionData = null;
485
                        this.nextFeature = null;
486
                }
487
        }
488

    
489
        public void delete(Feature feature) throws DataException {
490
                throw new UnsupportedOperationException();
491
        }
492

    
493
        public void insert(EditableFeature feature) throws DataException {
494
                throw new UnsupportedOperationException();
495
        }
496

    
497
        public void update(EditableFeature feature) throws DataException {
498
                throw new UnsupportedOperationException();
499
        }
500

    
501
        /*
502
         * (non-Javadoc)
503
         *
504
         * @seeorg.gvsig.fmap.dal.feature.impl.DefaultFeatureReferenceSelection#
505
         * loadFromState(org.gvsig.tools.persistence.PersistentState)
506
         */
507
        public void loadFromState(PersistentState state)
508
        throws PersistenceException {
509
                super.loadFromState(state);
510

    
511

    
512
        }
513

    
514
        public void accept(Visitor visitor) throws BaseException {
515
                accept(visitor, 0);
516
        }
517

    
518
        public void accept(Visitor visitor, long firstValueIndex)
519
        throws BaseException {
520
                DisposableIterator iterator = fastIterator(firstValueIndex);
521

    
522
                if (iterator != null) {
523
                        try {
524
                                while (iterator.hasNext()) {
525
                                        Feature feature = (Feature) iterator.next();
526
                                        visitor.visit(feature);
527
                                }
528
                        } finally {
529
                                iterator.dispose();
530
                        }
531
                }
532
        }
533

    
534
        public static void registerPersistent() {
535
                DynObjectManager dynMan = ToolsLocator.getDynObjectManager();
536
                DynClass dynClass = dynMan.add(
537
                                "DefaultFeatureSelection_Persistent",
538
                "DefaultFeatureSelection Persistent definition");
539

    
540
                dynClass
541
                .extend(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME);
542
                dynClass.addDynFieldMap("featureTypeCounts").setMandatory(true);
543
                ToolsLocator.getPersistenceManager().registerClass(
544
                                DefaultFeatureSelection.class, dynClass);
545

    
546
        }
547
}