Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libFMap_dal / src / org / gvsig / fmap / dal / feature / impl / DefaultFeatureSelection.java @ 28076

History | View | Annotate | Download (15 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.*;
30
import java.util.Map.Entry;
31

    
32
import org.gvsig.fmap.dal.DataStoreNotification;
33
import org.gvsig.fmap.dal.exception.DataException;
34
import org.gvsig.fmap.dal.feature.*;
35
import org.gvsig.fmap.dal.feature.exception.ReversedSelectionIteratorException;
36
import org.gvsig.tools.persistence.PersistenceException;
37
import org.gvsig.tools.persistence.PersistentState;
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
40

    
41
/**
42
 * Default implementation of the FeatureSelection interface. Internally, only
43
 * FeatureReference values are stored.
44
 *
45
 * This implementation performs better if used with the selection related
46
 * methods: select, deselect and isSelected ones.
47
 *
48
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
49
 */
50
public class DefaultFeatureSelection extends DefaultFeatureReferenceSelection
51
        implements FeatureSelection {
52

    
53
    private static final Logger LOGGER = LoggerFactory
54
            .getLogger(DefaultFeatureSelection.class);
55

    
56
    private Map featureTypeCounts = new HashMap(1);
57

    
58
    /**
59
     * Creates a DefaultFeatureSelection, with a FeatureStore.
60
     *
61
     * @param featureStore
62
     *            the FeatureStore to load Features from
63
     * @throws DataException
64
     *             if there is an error while getting the total number of
65
     *             Features of the Store.
66
     * @see AbstractSetBasedDataSelection#DefaultSelection(int)
67
     */
68
    public DefaultFeatureSelection(DefaultFeatureStore featureStore)
69
            throws DataException {
70
        super(featureStore);
71
    }
72

    
73
    /**
74
     * Creates a new Selection with the total size of Features from which the
75
     * selection will be performed.
76
     * 
77
     * @param featureStore
78
     *            the FeatureStore of the selected FeatureReferences
79
     * @param helper
80
     *            to get some information of the Store
81
     * @throws DataException
82
     *             if there is an error while getting the total number of
83
     *             Features of the Store.
84
     */
85
    public DefaultFeatureSelection(FeatureStore featureStore,
86
            FeatureSelectionHelper helper) throws DataException {
87
        super(featureStore, helper);
88
    }
89

    
90
    public boolean select(Feature feature) {
91
        return select(feature, true);
92
    }
93

    
94
    /**
95
     * @see #select(Feature)
96
     * @param undoable
97
     *            if the action must be undoable
98
     */
99
    public boolean select(Feature feature, boolean undoable) {
100
        // TODO: should we check if the feature is from the same FeatureStore??
101
        if (feature == null) {
102
            return false;
103
        }
104

    
105
//        LOGGER.debug("Selected feature: {}", feature);
106

    
107
        if (isReversed()) {
108
            getFeatureTypeCount(feature.getType()).remove();
109
        } else {
110
            getFeatureTypeCount(feature.getType()).add();
111
        }
112
        return select(feature.getReference(), undoable);
113
    }
114

    
115
    public boolean select(FeatureSet features) throws DataException {
116
        return select(features, true);
117
    }
118

    
119
    /**
120
     * @see #select(FeatureSet)
121
     * @param undoable
122
     *            if the action must be undoable
123
     */
124
    public boolean select(FeatureSet features, boolean undoable)
125
            throws DataException {
126
        boolean change = false;
127
        boolean inComplex = false;
128
        if (undoable && getFeatureStore().isEditing()
129
                                && !getCommands().inComplex()) {
130
                        inComplex = getCommands().inComplex();
131
                        getCommands().startComplex("_selectionSelectFeatureSet");
132
        }
133

    
134
        disableNotifications();
135
        for (DisposableIterator iter = features.fastIterator(); iter.hasNext();) {
136
            change |= select((Feature) iter.next(), undoable);
137
        }
138
        enableNotifications();
139
        if (undoable && getFeatureStore().isEditing() && !inComplex) {
140
            getCommands().endComplex();
141
        }
142
        if (change) {
143
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
144
        }
145
        return change;
146
    }
147

    
148
    public boolean deselect(Feature feature) {
149
        return deselect(feature, true);
150
    }
151

    
152
    /**
153
     * @see #deselect(Feature)
154
     * @param undoable
155
     *            if the action must be undoable
156
     */
157
    public boolean deselect(Feature feature, boolean undoable) {
158
        if (feature == null) {
159
            return false;
160
        }
161

    
162
        LOGGER.debug("Deselected feature: {}", feature);
163

    
164
        if (isReversed()) {
165
            getFeatureTypeCount(feature.getType()).add();
166
        } else {
167
            getFeatureTypeCount(feature.getType()).remove();
168
        }
169
        return deselect(feature.getReference(), undoable);
170
    }
171

    
172
    public boolean deselect(FeatureSet features) throws DataException {
173
        return deselect(features, true);
174
    }
175

    
176
    /**
177
     * @see #deselect(FeatureSet)
178
     * @param undoable
179
     *            if the action must be undoable
180
     */
181
    public boolean deselect(FeatureSet features, boolean undoable)
182
            throws DataException {
183
        boolean change = false;
184
        if (undoable && getFeatureStore().isEditing()) {
185
            getCommands().startComplex("_selectionDeselectFeatureSet");
186
        }
187
        disableNotifications();
188
        for (DisposableIterator iter = features.fastIterator(); iter.hasNext();) {
189
            change |= deselect((Feature) iter.next(), undoable);
190
        }
191
        enableNotifications();
192
        if (undoable && getFeatureStore().isEditing()) {
193
            getCommands().endComplex();
194
        }
195
        if (change) {
196
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
197
        }
198
        return change;
199
    }
200

    
201
    public boolean isSelected(Feature feature) {
202
        if (feature == null) {
203
            return false;
204
        }
205
        return isSelected(feature.getReference());
206
    }
207

    
208
    public FeatureType getDefaultFeatureType() {
209
        try {
210
            return getFeatureStore().getDefaultFeatureType();
211
        } catch (DataException ex) {
212
            LOGGER.error("Error getting the default feature type "
213
                    + "of the FeatureStore: " + getFeatureStore(), ex);
214
        }
215
        return null;
216
    }
217

    
218
    public List getFeatureTypes() {
219
        // Go through the map of FeatureTypes, and return only the ones that
220
        // have at least a Feature.
221
        List types = new ArrayList();
222
        for (java.util.Iterator iterator = featureTypeCounts.entrySet()
223
                                .iterator(); iterator
224
                .hasNext();) {
225
            Map.Entry entry = (Entry) iterator.next();
226
            FeatureType type = (FeatureType) entry.getKey();
227
            FeatureTypeCount count = (FeatureTypeCount) entry.getValue();
228

    
229
            if (count.getCount() > 0) {
230
                types.add(type);
231
            }
232
        }
233

    
234
        return types;
235
    }
236

    
237
    public long getSize() throws DataException {
238
        return getSelectedCount();
239
    }
240

    
241
    public boolean isEmpty() throws DataException {
242
        return getSelectedCount() == 0;
243
    }
244

    
245
    /**
246
     * Returns the list of selected values, or the deselected ones if the
247
     * selection has been reversed.
248
     */
249
    public DisposableIterator iterator() {
250
        return iterator(0);
251
    }
252

    
253
    /**
254
     * Returns the list of selected values, or the deselected ones if the
255
     * selection has been reversed.
256
     *
257
     * WARN: not very good performance implementation.
258
     */
259
    public DisposableIterator iterator(long index) {
260
        return iterator(index, false);
261
    }
262

    
263
    /**
264
     * Returns the list of selected values, or the deselected ones if the
265
     * selection has been reversed.
266
     *
267
     * WARN: not really a fast implementation.
268
     */
269
    public DisposableIterator fastIterator() {
270
        return fastIterator(0);
271
    }
272

    
273
    /**
274
     * Returns the list of selected values, or the deselected ones if the
275
     * selection has been reversed.
276
     *
277
     * WARN: not really a fast implementation.
278
     */
279
    public DisposableIterator fastIterator(long index) {
280
        return iterator(index, true);
281
    }
282

    
283
    public void saveToState(PersistentState state) throws PersistenceException {
284
        super.saveToState(state);
285
        for (DisposableIterator iterator = fastIterator(); iterator.hasNext();) {
286
            Feature feature = (Feature) iterator.next();
287
            getFeatureTypeCount(feature.getType()).add();
288
        }
289
    }
290

    
291
    protected void clearFeatureReferences() {
292
        super.clearFeatureReferences();
293
        featureTypeCounts.clear();
294
    }
295

    
296
    /**
297
     * Creates an iterator for the Selection.
298
     */
299
    private DisposableIterator iterator(long index, boolean fastIterator) {
300
        if (isReversed()) {
301
            DisposableIterator iter = new ReversedFeatureIteratorFacade(getData(),
302
                    getFeatureStore(), fastIterator);
303
            for (long l = 0; l < index && iter.hasNext(); l++) {
304
                iter.next();
305
            }
306
            return iter;
307

    
308
        } else {
309
            // TODO: maybe we could add a new referenceIterator(int index)
310
            // method that could be implemented in a more performant way
311

    
312
            java.util.Iterator iter = referenceIterator();
313
            for (long l = 0; l < index && iter.hasNext(); l++) {
314
                iter.next();
315
            }
316
            return new FeatureIteratorFacade(iter, getFeatureStore());
317
        }
318
    }
319

    
320
    private FeatureTypeCount getFeatureTypeCount(FeatureType featureType) {
321
        FeatureTypeCount count = (FeatureTypeCount) featureTypeCounts
322
                .get(featureType);
323
        if (count == null) {
324
            count = new FeatureTypeCount();
325
            featureTypeCounts.put(featureType, count);
326
        }
327
        return count;
328
    }
329

    
330
    /**
331
     * Facade over a Iterator of FeatureReferences, to return Features instead.
332
     *
333
     * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
334
     */
335
    private class FeatureIteratorFacade implements DisposableIterator {
336

    
337
        final Logger logger = LoggerFactory
338
                .getLogger(FeatureIteratorFacade.class);
339

    
340
        private java.util.Iterator refIterator;
341

    
342
        private FeatureStore featureStore;
343

    
344
        public FeatureIteratorFacade(java.util.Iterator iter,
345
                FeatureStore featureStore) {
346
            this.refIterator = iter;
347
            this.featureStore = featureStore;
348
        }
349

    
350
        public boolean hasNext() {
351
            return refIterator.hasNext();
352
        }
353

    
354
        public Object next() {
355
            FeatureReference ref = nextFeatureReference();
356
            try {
357
                return featureStore.getFeatureByReference(ref);
358
            } catch (DataException ex) {
359
                logger.error(
360
                        "Error loading the Feature with FeatureReference: "
361
                                + ref, ex);
362
                return null;
363
            }
364
        }
365

    
366
        /**
367
         * Returns the next FeatureReference.
368
         *
369
         * @return the next FeatureReference
370
         */
371
        public FeatureReference nextFeatureReference() {
372
            return (FeatureReference) refIterator.next();
373
        }
374

    
375
        public void remove() {
376
            refIterator.remove();
377
        }
378

    
379
        /**
380
         * Returns a Feature for the FeatureReference.
381
         */
382
        protected Feature getFeature(FeatureReference ref) throws DataException {
383
            return ref.getFeature();
384
        }
385

    
386
                public void dispose() {
387
                        if (refIterator instanceof DisposableIterator) {
388
                                ((DisposableIterator) refIterator).dispose();
389
                        }
390
                        refIterator = null;
391
                        featureStore = null;
392
                }
393
    }
394

    
395
    /**
396
     * Facade over a Iterator of FeatureReferences, to return Features instead,
397
     * when the Selection is reversed
398
     *
399
     * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
400
     */
401
    private class ReversedFeatureIteratorFacade implements DisposableIterator {
402

    
403
        final Logger logger = LoggerFactory
404
                .getLogger(ReversedFeatureIteratorFacade.class);
405

    
406

    
407
        private SelectionData selectionData;
408

    
409
        private DisposableIterator iterator;
410

    
411
        private Feature nextFeature = null;
412

    
413
                private FeatureSet featureSet;
414

    
415
        public ReversedFeatureIteratorFacade(SelectionData selectionData,
416
                FeatureStore featureStore, boolean fastIterator) {
417
            this.selectionData = selectionData;
418

    
419
            // Load a Set with all the store features
420
            try {
421
                featureSet = featureStore.getFeatureSet();
422
                if (fastIterator) {
423
                    iterator = featureSet.fastIterator();
424
                } else {
425
                    iterator = featureSet.iterator();
426
                }
427
            } catch (DataException ex) {
428
                throw new ReversedSelectionIteratorException(ex);
429
            }
430

    
431
            // Filter the features not selected and position in the next
432
            // selected feature
433
            positionInNextElement();
434
        }
435

    
436
        public boolean hasNext() {
437
            return nextFeature != null;
438
        }
439

    
440
        public Object next() {
441
            Feature tmp = nextFeature;
442
            positionInNextElement();
443
            return tmp;
444
        }
445

    
446
        public void remove() {
447
            iterator.remove();
448
        }
449

    
450
        private void positionInNextElement() {
451
            nextFeature = null;
452
            while (iterator.hasNext()) {
453
                nextFeature = (Feature) iterator.next();
454
                if (selectionData.contains(nextFeature.getReference())) {
455
                    nextFeature = null;
456
                } else {
457
                    break;
458
                }
459
            }
460
        }
461

    
462
                public void dispose() {
463
                        this.featureSet.dispose();
464
                        this.iterator.dispose();
465
                        this.selectionData = null;
466
                        this.nextFeature = null;
467
                }
468
    }
469

    
470
    private class FeatureTypeCount {
471
        private long count = 0;
472

    
473
        public void add() {
474
            count++;
475
        }
476

    
477
        public void remove() {
478
            count--;
479
        }
480

    
481
        public long getCount() {
482
            return count;
483
        }
484
    }
485

    
486
    public void delete(Feature feature) throws DataException {
487
        throw new UnsupportedOperationException();
488
    }
489

    
490
    public void insert(EditableFeature feature) throws DataException {
491
        throw new UnsupportedOperationException();
492
    }
493

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