Statistics
| Revision:

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

History | View | Annotate | Download (16.7 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.gvsig.fmap.dal.DataStoreNotification;
36
import org.gvsig.fmap.dal.exception.DataException;
37
import org.gvsig.fmap.dal.feature.EditableFeature;
38
import org.gvsig.fmap.dal.feature.Feature;
39
import org.gvsig.fmap.dal.feature.FeatureReference;
40
import org.gvsig.fmap.dal.feature.FeatureSelection;
41
import org.gvsig.fmap.dal.feature.FeatureSet;
42
import org.gvsig.fmap.dal.feature.FeatureStore;
43
import org.gvsig.fmap.dal.feature.FeatureType;
44
import org.gvsig.fmap.dal.feature.exception.ReversedSelectionIteratorException;
45
import org.gvsig.tools.ToolsLocator;
46
import org.gvsig.tools.dispose.DisposableIterator;
47
import org.gvsig.tools.dynobject.DynStruct;
48
import org.gvsig.tools.exception.BaseException;
49
import org.gvsig.tools.persistence.PersistenceManager;
50
import org.gvsig.tools.persistence.PersistentState;
51
import org.gvsig.tools.persistence.exception.PersistenceException;
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. After to
107
         * invoke this method, the persistence manager calls the the method
108
         * {@link #loadFromState(PersistentState)} to set the values of the internal
109
         * 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
                DisposableIterator iter = null;
161
                try {
162
                        for (iter = features.fastIterator(); iter.hasNext();) {
163
                                change |= select((Feature) iter.next(), undoable);
164
                        }
165
                } finally {
166
                        dispose(iter);
167
        }
168
        enableNotifications();
169
        if (undoable && getFeatureStore().isEditing() && !inComplex) {
170
            getCommands().endComplex();
171
        }
172
        if (change) {
173
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
174
        }
175
        return change;
176
    }
177

    
178
    public boolean deselect(Feature feature) {
179
        return deselect(feature, true);
180
    }
181

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

    
192
        LOGGER.debug("Deselected feature: {}", feature);
193

    
194
        if (isReversed()) {
195
            addFeatureTypeCount(feature.getType());
196
        } else {
197
            removeFeatureTypeCount(feature.getType());
198
        }
199
        return deselect(feature.getReference(), undoable);
200
    }
201

    
202
    public boolean deselect(FeatureSet features) throws DataException {
203
        return deselect(features, true);
204
    }
205

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

    
236
    public boolean isSelected(Feature feature) {
237
        if (feature == null) {
238
            return false;
239
        }
240
        return isSelected(feature.getReference());
241
    }
242

    
243
    public FeatureType getDefaultFeatureType() {
244
        try {
245
            return getFeatureStore().getDefaultFeatureType();
246
        } catch (DataException ex) {
247
            LOGGER.error("Error getting the default feature type "
248
                    + "of the FeatureStore: " + getFeatureStore(), ex);
249
        }
250
        return null;
251
    }
252

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

    
264
            if (count.longValue() > 0) {
265
                types.add(type);
266
            }
267
        }
268

    
269
        return types;
270
    }
271

    
272
    public long getSize() throws DataException {
273
        return getSelectedCount();
274
    }
275

    
276
    public boolean isEmpty() throws DataException {
277
        return getSelectedCount() == 0;
278
    }
279

    
280
    /**
281
     * Returns the list of selected values, or the deselected ones if the
282
     * selection has been reversed.
283
     */
284
    public DisposableIterator iterator() {
285
        return iterator(0);
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 very good performance implementation.
293
     */
294
    public DisposableIterator iterator(long index) {
295
        return iterator(index, false);
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() {
305
        return fastIterator(0);
306
    }
307

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

    
318

    
319
    protected void clearFeatureReferences() {
320
        super.clearFeatureReferences();
321
        featureTypeCounts.clear();
322
    }
323

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

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

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

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

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

    
370
    /**
371
     * Facade over a Iterator of FeatureReferences, to return Features instead.
372
     *
373
     * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
374
     */
375
    private class FeatureIteratorFacade implements DisposableIterator {
376

    
377
        final Logger logger = LoggerFactory
378
                .getLogger(FeatureIteratorFacade.class);
379

    
380
        private java.util.Iterator refIterator;
381

    
382
        private FeatureStore featureStore;
383

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

    
390
        public boolean hasNext() {
391
            return refIterator.hasNext();
392
        }
393

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

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

    
415
        public void remove() {
416
            refIterator.remove();
417
        }
418

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

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

    
436
        private SelectionData selectionData;
437

    
438
        private DisposableIterator iterator;
439

    
440
        private Feature nextFeature = null;
441

    
442
                private FeatureSet featureSet;
443

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

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

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

    
465
        public boolean hasNext() {
466
            return nextFeature != null;
467
        }
468

    
469
        public Object next() {
470
            Feature tmp = nextFeature;
471
            positionInNextElement();
472
            return tmp;
473
        }
474

    
475
        public void remove() {
476
            iterator.remove();
477
        }
478

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

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

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

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

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

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

    
521

    
522
        }
523

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

    
528
        public void accept(Visitor visitor, long firstValueIndex)
529
                        throws BaseException {
530
                DisposableIterator iterator = fastIterator(firstValueIndex);
531

    
532
                if (iterator != null) {
533
                        try {
534
                                while (iterator.hasNext()) {
535
                                        Feature feature = (Feature) iterator.next();
536
                                        visitor.visit(feature);
537
                                }
538
                        } finally {
539
                                iterator.dispose();
540
                        }
541
                }
542
        }
543

    
544
        protected void doDispose() throws BaseException {
545
                super.doDispose();
546
                featureTypeCounts.clear();
547
        }
548

    
549
        public static void registerPersistent() {
550
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
551
                DynStruct definition = manager.addDefinition(
552
                                DefaultFeatureSelection.class, 
553
                                "DefaultFeatureSelection", 
554
                                "DefaultFeatureSelection Persistent definition",
555
                                null, 
556
                                null
557
                        );
558

    
559
                definition.extend(
560
                        manager.getDefinition(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME)
561
                );
562
                definition.addDynFieldMap("featureTypeCounts").setClassOfItems(Long.class).setMandatory(true);
563

    
564
        }
565
}