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 / DefaultFeatureReferenceSelection.java @ 44036

History | View | Annotate | Download (18.8 KB)

1 40559 jjdelcerro
/**
2
 * gvSIG. Desktop Geographic Information System.
3 40435 jjdelcerro
 *
4 40559 jjdelcerro
 * Copyright (C) 2007-2013 gvSIG Association.
5 40435 jjdelcerro
 *
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 40559 jjdelcerro
 * as published by the Free Software Foundation; either version 3
9 40435 jjdelcerro
 * 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 40559 jjdelcerro
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23 40435 jjdelcerro
 */
24
package org.gvsig.fmap.dal.feature.impl;
25
26
import java.lang.ref.Reference;
27
import java.util.Collections;
28
import java.util.HashSet;
29
import java.util.Iterator;
30
import java.util.Set;
31 43646 jjdelcerro
import java.util.logging.Level;
32
import java.util.logging.Logger;
33 40435 jjdelcerro
34
import org.gvsig.fmap.dal.DataStore;
35
import org.gvsig.fmap.dal.DataStoreNotification;
36
import org.gvsig.fmap.dal.exception.DataException;
37
import org.gvsig.fmap.dal.feature.FeatureReference;
38
import org.gvsig.fmap.dal.feature.FeatureReferenceSelection;
39
import org.gvsig.fmap.dal.feature.FeatureStore;
40
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
41
import org.gvsig.fmap.dal.feature.impl.undo.FeatureCommandsStack;
42
import org.gvsig.tools.ToolsLocator;
43
import org.gvsig.tools.dispose.impl.AbstractDisposable;
44
import org.gvsig.tools.dynobject.DynStruct;
45
import org.gvsig.tools.exception.BaseException;
46
import org.gvsig.tools.lang.Cloneable;
47
import org.gvsig.tools.observer.Observable;
48
import org.gvsig.tools.observer.Observer;
49
import org.gvsig.tools.observer.impl.BaseWeakReferencingObservable;
50
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
51
import org.gvsig.tools.persistence.PersistentState;
52
import org.gvsig.tools.persistence.exception.PersistenceException;
53
import org.gvsig.tools.visitor.Visitor;
54
55
/**
56
 * Default implementation of a FeatureReferenceSelection, based on the usage of
57
 * a java.util.Set to store individual selected or not selected
58
 * FeatureReferences, depending on the usage of the {@link #reverse()} method.
59
 *
60
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
61
 */
62
public class DefaultFeatureReferenceSelection extends AbstractDisposable
63
                implements FeatureReferenceSelection {
64
65
        public static final String DYNCLASS_PERSISTENT_NAME =
66
                        "DefaultFeatureReferenceSelection";
67
68 43646 jjdelcerro
    protected SelectionData selectionData = null;
69 40435 jjdelcerro
70
    private FeatureStore featureStore;
71
72
    private FeatureSelectionHelper helper;
73
74
        private DelegateWeakReferencingObservable delegateObservable =
75
                        new DelegateWeakReferencingObservable(this);
76
77
        /**
78
         * Creates a new Selection with the total size of Features from which the
79
         * selection will be performed.
80
         *
81
         * @param featureStore
82
         *            the FeatureStore of the selected FeatureReferences
83
         * @throws DataException
84
         *             if there is an error while getting the total number of
85
         *             Features of the Store.
86
         */
87
    public DefaultFeatureReferenceSelection(DefaultFeatureStore featureStore)
88
            throws DataException {
89
        super();
90
        this.featureStore = featureStore;
91
        this.helper = new DefaultFeatureSelectionHelper(featureStore);
92
    }
93
94
    /**
95
     * Creates a new Selection with the total size of Features from which the
96
     * selection will be performed.
97
     *
98
     * @param featureStore
99
     *            the FeatureStore of the selected FeatureReferences
100
     * @param helper
101
     *            to get some information of the Store
102
     * @throws DataException
103
     *             if there is an error while getting the total number of
104
     *             Features of the Store.
105
     */
106
    public DefaultFeatureReferenceSelection(FeatureStore featureStore,
107
            FeatureSelectionHelper helper)
108
            throws DataException {
109
        super();
110
        this.featureStore = featureStore;
111
        this.helper = helper;
112
    }
113
114
        /**
115
         * Constructor used by the persistence manager. Don't use directly. After to
116
         * invoke this method, the persistence manager calls the the method
117
         * {@link #loadFromState(PersistentState)} to set the values of the internal
118
         * attributes that this class needs to work.
119
         */
120
        public DefaultFeatureReferenceSelection() {
121
                super();
122
        }
123
124
    public boolean select(FeatureReference reference) {
125
        return select(reference, true);
126
    }
127
128
    /**
129
     * @see #select(FeatureReference)
130
     * @param undoable
131
     *            if the action must be undoable
132
     */
133
    public boolean select(FeatureReference reference, boolean undoable) {
134
135
        if (reference == null) {
136
            throw new IllegalArgumentException("reference");
137
        }
138
139
        if (isSelected(reference)) {
140
            return false;
141
        }
142
143
        if (undoable && getFeatureStore().isEditing()) {
144
            getCommands().select(this, reference);
145
        }
146
        boolean change = false;
147 43646 jjdelcerro
        if ( this.getData().isReversed()) {
148
            change = this.getData().remove(reference);
149 40435 jjdelcerro
        } else {
150 43646 jjdelcerro
            change = this.getData().add(reference);
151 40435 jjdelcerro
        }
152
153
        if (change) {
154
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
155
        }
156
157
        return change;
158
    }
159
160
    public boolean deselect(FeatureReference reference) {
161
        return deselect(reference, true);
162
    }
163
164
    /**
165
     * @see #deselect(FeatureReference)
166
     * @param undoable
167
     *            if the action must be undoable
168
     */
169
    public boolean deselect(FeatureReference reference, boolean undoable) {
170
        if (!isSelected(reference)) {
171
            return false;
172
        }
173
174
        if (undoable && getFeatureStore().isEditing()) {
175
            getCommands().deselect(this, reference);
176
        }
177
        boolean change = false;
178 43646 jjdelcerro
        if (this.getData().isReversed()) {
179
            change = this.getData().add(reference);
180 40435 jjdelcerro
        } else {
181 43646 jjdelcerro
            change = this.getData().remove(reference);
182 40435 jjdelcerro
        }
183
184
        if (change) {
185
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
186
        }
187
188
        return change;
189
    }
190
191
    public void selectAll() throws DataException {
192
        selectAll(true);
193
    }
194
195
    /**
196
     * @see #selectAll()
197
     * @param undoable
198
     *            if the action must be undoable
199
     */
200
    public void selectAll(boolean undoable) throws DataException {
201
        if (undoable && getFeatureStore().isEditing()) {
202
            getCommands().startComplex("_selectionSelectAll");
203
            getCommands().selectAll(this);
204
        }
205 43646 jjdelcerro
        if (!this.getData().isReversed()) {
206
            this.getData().setReversed(true);
207 40435 jjdelcerro
        }
208
        clearFeatureReferences();
209
        if (undoable && getFeatureStore().isEditing()) {
210
            getCommands().endComplex();
211
        }
212
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
213
    }
214
215
    public void deselectAll() throws DataException {
216
        deselectAll(false);
217
    }
218
219
    /**
220
     * @see #deselectAll()
221
     * @param undoable
222
     *            if the action must be undoable
223
     */
224
    public void deselectAll(boolean undoable) throws DataException {
225 43646 jjdelcerro
        if( this.selectionData==null ) {
226
            return;
227
        }
228 40435 jjdelcerro
        if (undoable && getFeatureStore().isEditing()) {
229
            getCommands().startComplex("_selectionDeselectAll");
230
            getCommands().deselectAll(this);
231
        }
232 43646 jjdelcerro
        if (this.getData().isReversed()) {
233
            this.getData().setReversed(false);
234 40435 jjdelcerro
        }
235
        clearFeatureReferences();
236
        if (undoable && getFeatureStore().isEditing()) {
237
            getCommands().endComplex();
238
        }
239
240
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
241
    }
242
243
    public boolean isSelected(FeatureReference reference) {
244 43646 jjdelcerro
        if( this.selectionData==null ) {
245
            return false;
246
        }
247
        if (this.getData().isReversed()) {
248
            return !this.getData().contains(reference);
249 40435 jjdelcerro
        } else {
250 43646 jjdelcerro
            return this.getData().contains(reference);
251 40435 jjdelcerro
        }
252
    }
253
254
    public void reverse() {
255
        reverse(true);
256
    }
257
258
    /**
259
     * @see #reverse()
260
     * @param undoable
261
     *            if the action must be undoable
262
     */
263
    public void reverse(boolean undoable) {
264
        if (undoable && getFeatureStore().isEditing()) {
265
            getCommands().selectionReverse(this);
266
        }
267 43646 jjdelcerro
        this.getData().setReversed(!this.getData().isReversed());
268 40435 jjdelcerro
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
269
    }
270 43646 jjdelcerro
271
    public boolean isEmpty() {
272
        if( this.selectionData == null ) {
273
            return true;
274
        }
275
        return this.getSelectedCount()==0;
276
    }
277 40435 jjdelcerro
278
    public long getSelectedCount() {
279 43646 jjdelcerro
        if( this.selectionData == null ) {
280
            return 0;
281
        }
282
        if (this.getData().isReversed()) {
283
                return this.getData().getTotalSize() - this.getData().getSize()
284 40435 jjdelcerro
                        + helper.getFeatureStoreDeltaSize();
285
        } else {
286 43646 jjdelcerro
            return this.getData().getSize();
287 40435 jjdelcerro
        }
288
    }
289
290
    public Iterator referenceIterator() {
291 43646 jjdelcerro
        return Collections.unmodifiableSet(this.getData().getSelected())
292 40435 jjdelcerro
                .iterator();
293
    }
294
295
        protected void doDispose() throws BaseException {
296
                delegateObservable.deleteObservers();
297
                deselectAll(false);
298
    }
299
300
    public boolean isFromStore(DataStore store) {
301
        return featureStore.equals(store);
302
    }
303
304
    public void accept(Visitor visitor) throws BaseException {
305 43646 jjdelcerro
        if( this.selectionData==null ) {
306
            return;
307
        }
308
        for (Iterator iter = this.getData().getSelected().iterator(); iter
309 40435 jjdelcerro
                .hasNext();) {
310
            visitor.visit(iter.next());
311
        }
312
    }
313
314
    public void update(Observable observable,
315
                        Object notification) {
316
        // If a Feature is deleted, remove it from the selection Set.
317
        if (notification instanceof FeatureStoreNotification) {
318
            FeatureStoreNotification storeNotif = (FeatureStoreNotification) notification;
319
            if (FeatureStoreNotification.AFTER_DELETE
320
                    .equalsIgnoreCase(storeNotif.getType())) {
321 43646 jjdelcerro
                this.getData().remove(storeNotif.getFeature().getReference());
322 40435 jjdelcerro
            }
323
        }
324
    }
325
326
    public SelectionData getData() {
327 43646 jjdelcerro
        if( selectionData==null ) {
328
            selectionData = new SelectionData();
329
            try {
330
                selectionData.setTotalSize(featureStore.getFeatureCount());
331
            } catch (DataException ex) {
332
                throw new RuntimeException("Can't initialize SelectionData, don't get the feature count.",ex);
333
            }
334
        }
335 40435 jjdelcerro
        return selectionData;
336
    }
337
338
    public void setData(SelectionData selectionData) {
339
        this.selectionData = selectionData;
340
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
341
    }
342
343
    public String toString() {
344
        return getClass().getName() + ": " + getSelectedCount()
345
                + " features selected, reversed = "
346 43646 jjdelcerro
                + this.getData().isReversed() + ", featureIds contained: "
347
                + this.getData().getSelected();
348 40435 jjdelcerro
    }
349
350
    protected boolean isReversed() {
351 43646 jjdelcerro
        if( this.selectionData==null ) {
352
            return false;
353
        }
354
        return this.getData().isReversed();
355 40435 jjdelcerro
    }
356
357
    /**
358
     * Removes all the stored FeatureRefence objects.
359
     */
360
    protected void clearFeatureReferences() {
361 43646 jjdelcerro
        if( this.selectionData==null ) {
362
            return;
363
        }
364
        this.getData().clear();
365 40435 jjdelcerro
    }
366
367
        /**
368
         * Returns the FeatureStore of the selected FeatureReferences.
369
         *
370
         * @return the featureStore
371
         */
372
    protected FeatureStore getFeatureStore() {
373
        return featureStore;
374
    }
375
376
        /**
377
         * Returns the reference to the commands record.
378
         *
379
         * @return the reference to the commands record
380
         */
381
    protected FeatureCommandsStack getCommands() {
382
        return helper.getFeatureStoreCommandsStack();
383
    }
384
385
        public static class SelectionData implements Cloneable {
386
        private Set selected = new HashSet();
387
388
        /**
389
         * Sets how the Set of selected values has to be dealt.
390
         * <p>
391
         * If selected is FALSE, then values into the Set are the selected ones,
392
         * anything else is not selected.
393
         * </p>
394
         * <p>
395
         * If selected is TRUE, then values into the Set are values not
396
         * selected, anything else is selected.
397
         * </p>
398
         */
399
        private boolean reversed = false;
400
401
        private long totalSize;
402
403
        /**
404
         * @return the selected
405
         */
406
        public Set getSelected() {
407
            return selected;
408
        }
409
410
        /**
411
         * @param selected
412
         *            the selected to set
413
         */
414
        public void setSelected(Set selected) {
415
            this.selected = selected;
416
        }
417
418
        /**
419
         * @return the reversed
420
         */
421
        public boolean isReversed() {
422
            return reversed;
423
        }
424
425
        /**
426
         * @param reversed
427
         *            the reversed to set
428
         */
429
        public void setReversed(boolean reversed) {
430
            this.reversed = reversed;
431
        }
432
433
        /**
434
         * @return the totalSize
435
         */
436
        public long getTotalSize() {
437
            return totalSize;
438
        }
439
440
        /**
441
         * @param totalSize
442
         *            the totalSize to set
443
         */
444
        public void setTotalSize(long totalSize) {
445
            this.totalSize = totalSize;
446
        }
447
448
        public boolean add(FeatureReference reference) {
449
            return selected.add(reference);
450
        }
451
452
        public boolean remove(FeatureReference reference) {
453
            return selected.remove(reference);
454
        }
455
456
        public void clear() {
457
            selected.clear();
458
        }
459
460
        public boolean contains(FeatureReference reference) {
461
            return selected.contains(reference);
462
        }
463
464
        public int getSize() {
465
            return selected.size();
466
        }
467
468
        public Object clone() throws CloneNotSupportedException {
469
                        SelectionData clone = (SelectionData) super.clone();
470
                        // reversed and totalSize already cloned by parent.
471
                        // clone the selected Set
472
                        clone.selected = new HashSet(selected);
473
            return clone;
474
        }
475
    }
476
477
    // *** Persistence ***
478
479 43725 jjdelcerro
        @Override
480 40435 jjdelcerro
        public void saveToState(PersistentState state) throws PersistenceException {
481
                state.set("store", featureStore);
482 43646 jjdelcerro
                state.set("reversed", this.getData().isReversed());
483
                state.set("totalSize", this.getData().getTotalSize());
484
                state.set("selected", this.getData().getSelected().iterator());
485 40435 jjdelcerro
        }
486
487 43725 jjdelcerro
        @Override
488 40435 jjdelcerro
        public void loadFromState(PersistentState state)
489
                        throws PersistenceException {
490 43725 jjdelcerro
            SelectionData data = new SelectionData(); // Do not use this.getData()
491
            featureStore = (FeatureStore)state.get("store");
492
            helper = new DefaultFeatureSelectionHelper((DefaultFeatureStore)featureStore);
493
            data.setReversed(state.getBoolean("reversed"));
494
            data.setTotalSize(state.getLong("totalSize"));
495
            Iterator it = state.getIterator("selected");
496
            while (it.hasNext()) {
497
                    DefaultFeatureReference ref = (DefaultFeatureReference) it.next();
498
                    data.add(ref);
499
            }
500
501
            /*
502
             * If we do not do this, feature store will not listen
503
             * to changes in selection after instantiating a
504
             * persisted selection. For non-persisted instances,
505
             * this line corresponds to the line found in method:
506
             * getFeatureSelection() in DefaultFeatureStore.
507
             * This is not dangerous because "addObserver" only adds
508
             * if they were not already added, so future invocations
509
             * with same instances will have no effect.
510
             */
511
            this.addObserver((DefaultFeatureStore)featureStore);
512 40435 jjdelcerro
        }
513
514
        public static void registerPersistent() {
515
                DynStruct definition = ToolsLocator.getPersistenceManager().addDefinition(
516
                                DefaultFeatureReferenceSelection.class,
517
                                DYNCLASS_PERSISTENT_NAME,
518
                                "DefaultFeatureReferenceSelection Persistent definition",
519
                                null,
520
                                null
521
                        );
522
523
                definition.addDynFieldObject("store").setClassOfValue(FeatureStore.class).setMandatory(true);
524
                definition.addDynFieldBoolean("reversed").setMandatory(true);
525
                definition.addDynFieldLong("totalSize").setMandatory(true);
526
                definition.addDynFieldList("selected").setClassOfItems(DefaultFeatureReference.class).setMandatory(true);
527
528
        }
529
530
        public void addObserver(Observer observer) {
531
                delegateObservable.addObserver(observer);
532
        }
533
534
        public void addObserver(Reference ref) {
535
                delegateObservable.addObserver(ref);
536
        }
537
538
        public void addObservers(BaseWeakReferencingObservable observable) {
539
                delegateObservable.addObservers(observable);
540
        }
541
542
        public void beginComplexNotification() {
543
                delegateObservable.beginComplexNotification();
544
        }
545
546
        public int countObservers() {
547
                return delegateObservable.countObservers();
548
        }
549
550
        public void deleteObserver(Observer observer) {
551
                delegateObservable.deleteObserver(observer);
552
        }
553
554
        public void deleteObserver(Reference ref) {
555
                delegateObservable.deleteObserver(ref);
556
        }
557
558
        public void deleteObservers() {
559
                delegateObservable.deleteObservers();
560
        }
561
562
        public void disableNotifications() {
563
                delegateObservable.disableNotifications();
564
        }
565
566
        public void enableNotifications() {
567
                delegateObservable.enableNotifications();
568
        }
569
570
        public void endComplexNotification() {
571
                // We don't want to notify many times in a complex notification
572
                // scenario, so ignore notifications if in complex.
573
                // Only one notification will be sent when the complex notification
574
                // ends.
575
                delegateObservable
576
                                .notifyObservers(DataStoreNotification.SELECTION_CHANGE);
577
                delegateObservable.endComplexNotification();
578
        }
579
580
        public boolean inComplex() {
581
                return delegateObservable.inComplex();
582
        }
583
584
        public boolean isEnabledNotifications() {
585
                return delegateObservable.isEnabledNotifications();
586
        }
587
588
        public void notifyObservers() {
589
                // We don't want to notify many times in a complex notification
590
                // scenario, so ignore notifications if in complex.
591
                // Only one notification will be sent when the complex notification
592
                // ends.
593
                if (!delegateObservable.inComplex()) {
594
                        delegateObservable.notifyObservers();
595
                }
596
        }
597
598
        public void notifyObservers(Object arg) {
599
                if (!delegateObservable.inComplex()) {
600
                        delegateObservable.notifyObservers(arg);
601
                }
602
        }
603
604 43646 jjdelcerro
    @Override
605 40435 jjdelcerro
        public Object clone() throws CloneNotSupportedException {
606
                DefaultFeatureReferenceSelection clone = (DefaultFeatureReferenceSelection) super
607
                                .clone();
608
                // Original observers aren't cloned
609
                clone.delegateObservable = new DelegateWeakReferencingObservable(clone);
610
                // Clone internal data
611 43646 jjdelcerro
                clone.selectionData = (SelectionData) this.getData().clone();
612 40435 jjdelcerro
                // featureStore and helper are already swallow cloned by our parent
613
                return clone;
614
        }
615 40767 jjdelcerro
}