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

History | View | Annotate | Download (17.6 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
32
import org.gvsig.fmap.dal.DataStore;
33
import org.gvsig.fmap.dal.DataStoreNotification;
34
import org.gvsig.fmap.dal.exception.DataException;
35
import org.gvsig.fmap.dal.feature.FeatureReference;
36
import org.gvsig.fmap.dal.feature.FeatureReferenceSelection;
37
import org.gvsig.fmap.dal.feature.FeatureStore;
38
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
39
import org.gvsig.fmap.dal.feature.impl.undo.FeatureCommandsStack;
40
import org.gvsig.tools.ToolsLocator;
41
import org.gvsig.tools.dispose.impl.AbstractDisposable;
42
import org.gvsig.tools.dynobject.DynStruct;
43
import org.gvsig.tools.exception.BaseException;
44
import org.gvsig.tools.lang.Cloneable;
45
import org.gvsig.tools.observer.Observable;
46
import org.gvsig.tools.observer.Observer;
47
import org.gvsig.tools.observer.impl.BaseWeakReferencingObservable;
48
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
49
import org.gvsig.tools.persistence.PersistentState;
50
import org.gvsig.tools.persistence.exception.PersistenceException;
51
import org.gvsig.tools.visitor.Visitor;
52
53
/**
54
 * Default implementation of a FeatureReferenceSelection, based on the usage of
55
 * a java.util.Set to store individual selected or not selected
56
 * FeatureReferences, depending on the usage of the {@link #reverse()} method.
57
 *
58
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
59
 */
60
public class DefaultFeatureReferenceSelection extends AbstractDisposable
61
                implements FeatureReferenceSelection {
62
63
        public static final String DYNCLASS_PERSISTENT_NAME =
64
                        "DefaultFeatureReferenceSelection";
65
66
    protected SelectionData selectionData = new SelectionData();
67
68
    private FeatureStore featureStore;
69
70
    private FeatureSelectionHelper helper;
71
72
        private DelegateWeakReferencingObservable delegateObservable =
73
                        new DelegateWeakReferencingObservable(this);
74
75
        /**
76
         * Creates a new Selection with the total size of Features from which the
77
         * selection will be performed.
78
         *
79
         * @param featureStore
80
         *            the FeatureStore of the selected FeatureReferences
81
         * @throws DataException
82
         *             if there is an error while getting the total number of
83
         *             Features of the Store.
84
         */
85
    public DefaultFeatureReferenceSelection(DefaultFeatureStore featureStore)
86
            throws DataException {
87
        super();
88
        this.featureStore = featureStore;
89
        this.helper = new DefaultFeatureSelectionHelper(featureStore);
90
        selectionData.setTotalSize(featureStore.getFeatureCount());
91
    }
92
93
    /**
94
     * Creates a new Selection with the total size of Features from which the
95
     * selection will be performed.
96
     *
97
     * @param featureStore
98
     *            the FeatureStore of the selected FeatureReferences
99
     * @param helper
100
     *            to get some information of the Store
101
     * @throws DataException
102
     *             if there is an error while getting the total number of
103
     *             Features of the Store.
104
     */
105
    public DefaultFeatureReferenceSelection(FeatureStore featureStore,
106
            FeatureSelectionHelper helper)
107
            throws DataException {
108
        super();
109
        this.featureStore = featureStore;
110
        this.helper = helper;
111
        selectionData.setTotalSize(featureStore.getFeatureCount());
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
        if (selectionData.isReversed()) {
148
            change = selectionData.remove(reference);
149
        } else {
150
            change = selectionData.add(reference);
151
        }
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
        if (selectionData.isReversed()) {
179
            change = selectionData.add(reference);
180
        } else {
181
            change = selectionData.remove(reference);
182
        }
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
        if (!selectionData.isReversed()) {
206
            selectionData.setReversed(true);
207
        }
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
        if (undoable && getFeatureStore().isEditing()) {
226
            getCommands().startComplex("_selectionDeselectAll");
227
            getCommands().deselectAll(this);
228
        }
229
        if (selectionData.isReversed()) {
230
            selectionData.setReversed(false);
231
        }
232
        clearFeatureReferences();
233
        if (undoable && getFeatureStore().isEditing()) {
234
            getCommands().endComplex();
235
        }
236
237
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
238
    }
239
240
    public boolean isSelected(FeatureReference reference) {
241
        if (selectionData.isReversed()) {
242
            return !selectionData.contains(reference);
243
        } else {
244
            return selectionData.contains(reference);
245
        }
246
    }
247
248
    public void reverse() {
249
        reverse(true);
250
    }
251
252
    /**
253
     * @see #reverse()
254
     * @param undoable
255
     *            if the action must be undoable
256
     */
257
    public void reverse(boolean undoable) {
258
        if (undoable && getFeatureStore().isEditing()) {
259
            getCommands().selectionReverse(this);
260
        }
261
        selectionData.setReversed(!selectionData.isReversed());
262
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
263
    }
264
265
    public long getSelectedCount() {
266
        if (selectionData.isReversed()) {
267
                return selectionData.getTotalSize() - selectionData.getSize()
268
                        + helper.getFeatureStoreDeltaSize();
269
        } else {
270
            return selectionData.getSize();
271
        }
272
    }
273
274
    public Iterator referenceIterator() {
275
        return Collections.unmodifiableSet(selectionData.getSelected())
276
                .iterator();
277
    }
278
279
        protected void doDispose() throws BaseException {
280
                delegateObservable.deleteObservers();
281
                deselectAll(false);
282
    }
283
284
    public boolean isFromStore(DataStore store) {
285
        return featureStore.equals(store);
286
    }
287
288
    public void accept(Visitor visitor) throws BaseException {
289
        for (Iterator iter = selectionData.getSelected().iterator(); iter
290
                .hasNext();) {
291
            visitor.visit(iter.next());
292
        }
293
    }
294
295
    public void update(Observable observable,
296
                        Object notification) {
297
        // If a Feature is deleted, remove it from the selection Set.
298
        if (notification instanceof FeatureStoreNotification) {
299
            FeatureStoreNotification storeNotif = (FeatureStoreNotification) notification;
300
            if (FeatureStoreNotification.AFTER_DELETE
301
                    .equalsIgnoreCase(storeNotif.getType())) {
302
                selectionData.remove(storeNotif.getFeature().getReference());
303
            }
304
        }
305
    }
306
307
    public SelectionData getData() {
308
        return selectionData;
309
    }
310
311
    public void setData(SelectionData selectionData) {
312
        this.selectionData = selectionData;
313
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
314
    }
315
316
    public String toString() {
317
        return getClass().getName() + ": " + getSelectedCount()
318
                + " features selected, reversed = "
319
                + selectionData.isReversed() + ", featureIds contained: "
320
                + selectionData.getSelected();
321
    }
322
323
    protected boolean isReversed() {
324
        return selectionData.isReversed();
325
    }
326
327
    /**
328
     * Removes all the stored FeatureRefence objects.
329
     */
330
    protected void clearFeatureReferences() {
331
        selectionData.clear();
332
    }
333
334
        /**
335
         * Returns the FeatureStore of the selected FeatureReferences.
336
         *
337
         * @return the featureStore
338
         */
339
    protected FeatureStore getFeatureStore() {
340
        return featureStore;
341
    }
342
343
        /**
344
         * Returns the reference to the commands record.
345
         *
346
         * @return the reference to the commands record
347
         */
348
    protected FeatureCommandsStack getCommands() {
349
        return helper.getFeatureStoreCommandsStack();
350
    }
351
352
        public static class SelectionData implements Cloneable {
353
        private Set selected = new HashSet();
354
355
        /**
356
         * Sets how the Set of selected values has to be dealt.
357
         * <p>
358
         * If selected is FALSE, then values into the Set are the selected ones,
359
         * anything else is not selected.
360
         * </p>
361
         * <p>
362
         * If selected is TRUE, then values into the Set are values not
363
         * selected, anything else is selected.
364
         * </p>
365
         */
366
        private boolean reversed = false;
367
368
        private long totalSize;
369
370
        /**
371
         * @return the selected
372
         */
373
        public Set getSelected() {
374
            return selected;
375
        }
376
377
        /**
378
         * @param selected
379
         *            the selected to set
380
         */
381
        public void setSelected(Set selected) {
382
            this.selected = selected;
383
        }
384
385
        /**
386
         * @return the reversed
387
         */
388
        public boolean isReversed() {
389
            return reversed;
390
        }
391
392
        /**
393
         * @param reversed
394
         *            the reversed to set
395
         */
396
        public void setReversed(boolean reversed) {
397
            this.reversed = reversed;
398
        }
399
400
        /**
401
         * @return the totalSize
402
         */
403
        public long getTotalSize() {
404
            return totalSize;
405
        }
406
407
        /**
408
         * @param totalSize
409
         *            the totalSize to set
410
         */
411
        public void setTotalSize(long totalSize) {
412
            this.totalSize = totalSize;
413
        }
414
415
        public boolean add(FeatureReference reference) {
416
            return selected.add(reference);
417
        }
418
419
        public boolean remove(FeatureReference reference) {
420
            return selected.remove(reference);
421
        }
422
423
        public void clear() {
424
            selected.clear();
425
        }
426
427
        public boolean contains(FeatureReference reference) {
428
            return selected.contains(reference);
429
        }
430
431
        public int getSize() {
432
            return selected.size();
433
        }
434
435
        public Object clone() throws CloneNotSupportedException {
436
                        SelectionData clone = (SelectionData) super.clone();
437
                        // reversed and totalSize already cloned by parent.
438
                        // clone the selected Set
439
                        clone.selected = new HashSet(selected);
440
            return clone;
441
        }
442
    }
443
444
    // *** Persistence ***
445
446
        public void saveToState(PersistentState state) throws PersistenceException {
447
                state.set("store", featureStore);
448
                state.set("reversed", selectionData.isReversed());
449
                state.set("totalSize", selectionData.getTotalSize());
450
                state.set("selected", selectionData.getSelected().iterator());
451
        }
452
453
        public void loadFromState(PersistentState state)
454
                        throws PersistenceException {
455
                featureStore = (FeatureStore)state.get("store");
456
                helper = new DefaultFeatureSelectionHelper((DefaultFeatureStore)featureStore);
457
                selectionData.setReversed(state.getBoolean("reversed"));
458
                selectionData.setTotalSize(state.getLong("totalSize"));
459
                Iterator it = state.getIterator("selected");
460
                while (it.hasNext()) {
461
                        DefaultFeatureReference ref = (DefaultFeatureReference) it.next();
462
                        selectionData.add(ref);
463
                }
464
465
                /*
466
                 * If we do not do this, feature store will not listen
467
                 * to changes in selection after instantiating a
468
                 * persisted selection. For non-persisted instances,
469
                 * this line corresponds to the line found in method:
470
                 * getFeatureSelection() in DefaultFeatureStore.
471
                 * This is not dangerous because "addObserver" only adds
472
                 * if they were not already added, so future invocations
473
                 * with same instances will have no effect.
474
                 */
475
                this.addObserver((DefaultFeatureStore)featureStore);
476
        }
477
478
        public static void registerPersistent() {
479
                DynStruct definition = ToolsLocator.getPersistenceManager().addDefinition(
480
                                DefaultFeatureReferenceSelection.class,
481
                                DYNCLASS_PERSISTENT_NAME,
482
                                "DefaultFeatureReferenceSelection Persistent definition",
483
                                null,
484
                                null
485
                        );
486
487
                definition.addDynFieldObject("store").setClassOfValue(FeatureStore.class).setMandatory(true);
488
                definition.addDynFieldBoolean("reversed").setMandatory(true);
489
                definition.addDynFieldLong("totalSize").setMandatory(true);
490
                definition.addDynFieldList("selected").setClassOfItems(DefaultFeatureReference.class).setMandatory(true);
491
492
        }
493
494
        public void addObserver(Observer observer) {
495
                delegateObservable.addObserver(observer);
496
        }
497
498
        public void addObserver(Reference ref) {
499
                delegateObservable.addObserver(ref);
500
        }
501
502
        public void addObservers(BaseWeakReferencingObservable observable) {
503
                delegateObservable.addObservers(observable);
504
        }
505
506
        public void beginComplexNotification() {
507
                delegateObservable.beginComplexNotification();
508
        }
509
510
        public int countObservers() {
511
                return delegateObservable.countObservers();
512
        }
513
514
        public void deleteObserver(Observer observer) {
515
                delegateObservable.deleteObserver(observer);
516
        }
517
518
        public void deleteObserver(Reference ref) {
519
                delegateObservable.deleteObserver(ref);
520
        }
521
522
        public void deleteObservers() {
523
                delegateObservable.deleteObservers();
524
        }
525
526
        public void disableNotifications() {
527
                delegateObservable.disableNotifications();
528
        }
529
530
        public void enableNotifications() {
531
                delegateObservable.enableNotifications();
532
        }
533
534
        public void endComplexNotification() {
535
                // We don't want to notify many times in a complex notification
536
                // scenario, so ignore notifications if in complex.
537
                // Only one notification will be sent when the complex notification
538
                // ends.
539
                delegateObservable
540
                                .notifyObservers(DataStoreNotification.SELECTION_CHANGE);
541
                delegateObservable.endComplexNotification();
542
        }
543
544
        public boolean inComplex() {
545
                return delegateObservable.inComplex();
546
        }
547
548
        public boolean isEnabledNotifications() {
549
                return delegateObservable.isEnabledNotifications();
550
        }
551
552
        public void notifyObservers() {
553
                // We don't want to notify many times in a complex notification
554
                // scenario, so ignore notifications if in complex.
555
                // Only one notification will be sent when the complex notification
556
                // ends.
557
                if (!delegateObservable.inComplex()) {
558
                        delegateObservable.notifyObservers();
559
                }
560
        }
561
562
        public void notifyObservers(Object arg) {
563
                if (!delegateObservable.inComplex()) {
564
                        delegateObservable.notifyObservers(arg);
565
                }
566
        }
567
568
        public Object clone() throws CloneNotSupportedException {
569
                DefaultFeatureReferenceSelection clone = (DefaultFeatureReferenceSelection) super
570
                                .clone();
571
                // Original observers aren't cloned
572
                clone.delegateObservable = new DelegateWeakReferencingObservable(clone);
573
                // Clone internal data
574
                clone.selectionData = (SelectionData) selectionData.clone();
575
                // featureStore and helper are already swallow cloned by our parent
576
                return clone;
577
        }
578 40767 jjdelcerro
}