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

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