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

History | View | Annotate | Download (17.7 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
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 3
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
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
/*
25
 * AUTHORS (In addition to CIT):
26
 * 2008 {DiSiD Technologies}  {Implement data selection}
27
 */
28
package org.gvsig.fmap.dal.feature.impl;
29

    
30
import java.lang.ref.Reference;
31
import java.util.Collections;
32
import java.util.HashSet;
33
import java.util.Iterator;
34
import java.util.Set;
35

    
36
import org.gvsig.fmap.dal.DataStore;
37
import org.gvsig.fmap.dal.DataStoreNotification;
38
import org.gvsig.fmap.dal.exception.DataException;
39
import org.gvsig.fmap.dal.feature.FeatureReference;
40
import org.gvsig.fmap.dal.feature.FeatureReferenceSelection;
41
import org.gvsig.fmap.dal.feature.FeatureStore;
42
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
43
import org.gvsig.fmap.dal.feature.impl.undo.FeatureCommandsStack;
44
import org.gvsig.tools.ToolsLocator;
45
import org.gvsig.tools.dispose.impl.AbstractDisposable;
46
import org.gvsig.tools.dynobject.DynStruct;
47
import org.gvsig.tools.exception.BaseException;
48
import org.gvsig.tools.lang.Cloneable;
49
import org.gvsig.tools.observer.Observable;
50
import org.gvsig.tools.observer.Observer;
51
import org.gvsig.tools.observer.impl.BaseWeakReferencingObservable;
52
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
53
import org.gvsig.tools.persistence.PersistentState;
54
import org.gvsig.tools.persistence.exception.PersistenceException;
55
import org.gvsig.tools.visitor.Visitor;
56

    
57
/**
58
 * Default implementation of a FeatureReferenceSelection, based on the usage of
59
 * a java.util.Set to store individual selected or not selected
60
 * FeatureReferences, depending on the usage of the {@link #reverse()} method.
61
 *
62
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
63
 */
64
public class DefaultFeatureReferenceSelection extends AbstractDisposable
65
                implements FeatureReferenceSelection {
66

    
67
        public static final String DYNCLASS_PERSISTENT_NAME =
68
                        "DefaultFeatureReferenceSelection";
69

    
70
    protected SelectionData selectionData = new SelectionData();
71

    
72
    private FeatureStore featureStore;
73

    
74
    private FeatureSelectionHelper helper;
75

    
76
        private DelegateWeakReferencingObservable delegateObservable =
77
                        new DelegateWeakReferencingObservable(this);
78

    
79
        /**
80
         * Creates a new Selection with the total size of Features from which the
81
         * selection will be performed.
82
         *
83
         * @param featureStore
84
         *            the FeatureStore of the selected FeatureReferences
85
         * @throws DataException
86
         *             if there is an error while getting the total number of
87
         *             Features of the Store.
88
         */
89
    public DefaultFeatureReferenceSelection(DefaultFeatureStore featureStore)
90
            throws DataException {
91
        super();
92
        this.featureStore = featureStore;
93
        this.helper = new DefaultFeatureSelectionHelper(featureStore);
94
        selectionData.setTotalSize(featureStore.getFeatureCount());
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
        selectionData.setTotalSize(featureStore.getFeatureCount());
116
    }
117

    
118
        /**
119
         * Constructor used by the persistence manager. Don't use directly. After to
120
         * invoke this method, the persistence manager calls the the method
121
         * {@link #loadFromState(PersistentState)} to set the values of the internal
122
         * attributes that this class needs to work.
123
         */
124
        public DefaultFeatureReferenceSelection() {
125
                super();
126
        }
127

    
128
    public boolean select(FeatureReference reference) {
129
        return select(reference, true);
130
    }
131

    
132
    /**
133
     * @see #select(FeatureReference)
134
     * @param undoable
135
     *            if the action must be undoable
136
     */
137
    public boolean select(FeatureReference reference, boolean undoable) {
138
        
139
        if (reference == null) {
140
            throw new IllegalArgumentException("reference");
141
        }
142
        
143
        if (isSelected(reference)) {
144
            return false;
145
        }
146

    
147
        if (undoable && getFeatureStore().isEditing()) {
148
            getCommands().select(this, reference);
149
        }
150
        boolean change = false;
151
        if (selectionData.isReversed()) {
152
            change = selectionData.remove(reference);
153
        } else {
154
            change = selectionData.add(reference);
155
        }
156

    
157
        if (change) {
158
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
159
        }
160

    
161
        return change;
162
    }
163

    
164
    public boolean deselect(FeatureReference reference) {
165
        return deselect(reference, true);
166
    }
167

    
168
    /**
169
     * @see #deselect(FeatureReference)
170
     * @param undoable
171
     *            if the action must be undoable
172
     */
173
    public boolean deselect(FeatureReference reference, boolean undoable) {
174
        if (!isSelected(reference)) {
175
            return false;
176
        }
177

    
178
        if (undoable && getFeatureStore().isEditing()) {
179
            getCommands().deselect(this, reference);
180
        }
181
        boolean change = false;
182
        if (selectionData.isReversed()) {
183
            change = selectionData.add(reference);
184
        } else {
185
            change = selectionData.remove(reference);
186
        }
187

    
188
        if (change) {
189
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
190
        }
191

    
192
        return change;
193
    }
194

    
195
    public void selectAll() throws DataException {
196
        selectAll(true);
197
    }
198

    
199
    /**
200
     * @see #selectAll()
201
     * @param undoable
202
     *            if the action must be undoable
203
     */
204
    public void selectAll(boolean undoable) throws DataException {
205
        if (undoable && getFeatureStore().isEditing()) {
206
            getCommands().startComplex("_selectionSelectAll");
207
            getCommands().selectAll(this);
208
        }
209
        if (!selectionData.isReversed()) {
210
            selectionData.setReversed(true);
211
        }
212
        clearFeatureReferences();
213
        if (undoable && getFeatureStore().isEditing()) {
214
            getCommands().endComplex();
215
        }
216
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
217
    }
218

    
219
    public void deselectAll() throws DataException {
220
        deselectAll(false);
221
    }
222

    
223
    /**
224
     * @see #deselectAll()
225
     * @param undoable
226
     *            if the action must be undoable
227
     */
228
    public void deselectAll(boolean undoable) throws DataException {
229
        if (undoable && getFeatureStore().isEditing()) {
230
            getCommands().startComplex("_selectionDeselectAll");
231
            getCommands().deselectAll(this);
232
        }
233
        if (selectionData.isReversed()) {
234
            selectionData.setReversed(false);
235
        }
236
        clearFeatureReferences();
237
        if (undoable && getFeatureStore().isEditing()) {
238
            getCommands().endComplex();
239
        }
240

    
241
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
242
    }
243

    
244
    public boolean isSelected(FeatureReference reference) {
245
        if (selectionData.isReversed()) {
246
            return !selectionData.contains(reference);
247
        } else {
248
            return selectionData.contains(reference);
249
        }
250
    }
251

    
252
    public void reverse() {
253
        reverse(true);
254
    }
255

    
256
    /**
257
     * @see #reverse()
258
     * @param undoable
259
     *            if the action must be undoable
260
     */
261
    public void reverse(boolean undoable) {
262
        if (undoable && getFeatureStore().isEditing()) {
263
            getCommands().selectionReverse(this);
264
        }
265
        selectionData.setReversed(!selectionData.isReversed());
266
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
267
    }
268

    
269
    public long getSelectedCount() {
270
        if (selectionData.isReversed()) {
271
                return selectionData.getTotalSize() - selectionData.getSize()
272
                        + helper.getFeatureStoreDeltaSize();
273
        } else {
274
            return selectionData.getSize();
275
        }
276
    }
277

    
278
    public Iterator referenceIterator() {
279
        return Collections.unmodifiableSet(selectionData.getSelected())
280
                .iterator();
281
    }
282

    
283
        protected void doDispose() throws BaseException {
284
                delegateObservable.deleteObservers();
285
                deselectAll(false);
286
    }
287

    
288
    public boolean isFromStore(DataStore store) {
289
        return featureStore.equals(store);
290
    }
291

    
292
    public void accept(Visitor visitor) throws BaseException {
293
        for (Iterator iter = selectionData.getSelected().iterator(); iter
294
                .hasNext();) {
295
            visitor.visit(iter.next());
296
        }
297
    }
298

    
299
    public void update(Observable observable,
300
                        Object notification) {
301
        // If a Feature is deleted, remove it from the selection Set.
302
        if (notification instanceof FeatureStoreNotification) {
303
            FeatureStoreNotification storeNotif = (FeatureStoreNotification) notification;
304
            if (FeatureStoreNotification.AFTER_DELETE
305
                    .equalsIgnoreCase(storeNotif.getType())) {
306
                selectionData.remove(storeNotif.getFeature().getReference());
307
            }
308
        }
309
    }
310

    
311
    public SelectionData getData() {
312
        return selectionData;
313
    }
314

    
315
    public void setData(SelectionData selectionData) {
316
        this.selectionData = selectionData;
317
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
318
    }
319

    
320
    public String toString() {
321
        return getClass().getName() + ": " + getSelectedCount()
322
                + " features selected, reversed = "
323
                + selectionData.isReversed() + ", featureIds contained: "
324
                + selectionData.getSelected();
325
    }
326

    
327
    protected boolean isReversed() {
328
        return selectionData.isReversed();
329
    }
330

    
331
    /**
332
     * Removes all the stored FeatureRefence objects.
333
     */
334
    protected void clearFeatureReferences() {
335
        selectionData.clear();
336
    }
337

    
338
        /**
339
         * Returns the FeatureStore of the selected FeatureReferences.
340
         *
341
         * @return the featureStore
342
         */
343
    protected FeatureStore getFeatureStore() {
344
        return featureStore;
345
    }
346

    
347
        /**
348
         * Returns the reference to the commands record.
349
         *
350
         * @return the reference to the commands record
351
         */
352
    protected FeatureCommandsStack getCommands() {
353
        return helper.getFeatureStoreCommandsStack();
354
    }
355

    
356
        public static class SelectionData implements Cloneable {
357
        private Set selected = new HashSet();
358

    
359
        /**
360
         * Sets how the Set of selected values has to be dealt.
361
         * <p>
362
         * If selected is FALSE, then values into the Set are the selected ones,
363
         * anything else is not selected.
364
         * </p>
365
         * <p>
366
         * If selected is TRUE, then values into the Set are values not
367
         * selected, anything else is selected.
368
         * </p>
369
         */
370
        private boolean reversed = false;
371

    
372
        private long totalSize;
373

    
374
        /**
375
         * @return the selected
376
         */
377
        public Set getSelected() {
378
            return selected;
379
        }
380

    
381
        /**
382
         * @param selected
383
         *            the selected to set
384
         */
385
        public void setSelected(Set selected) {
386
            this.selected = selected;
387
        }
388

    
389
        /**
390
         * @return the reversed
391
         */
392
        public boolean isReversed() {
393
            return reversed;
394
        }
395

    
396
        /**
397
         * @param reversed
398
         *            the reversed to set
399
         */
400
        public void setReversed(boolean reversed) {
401
            this.reversed = reversed;
402
        }
403

    
404
        /**
405
         * @return the totalSize
406
         */
407
        public long getTotalSize() {
408
            return totalSize;
409
        }
410

    
411
        /**
412
         * @param totalSize
413
         *            the totalSize to set
414
         */
415
        public void setTotalSize(long totalSize) {
416
            this.totalSize = totalSize;
417
        }
418

    
419
        public boolean add(FeatureReference reference) {
420
            return selected.add(reference);
421
        }
422

    
423
        public boolean remove(FeatureReference reference) {
424
            return selected.remove(reference);
425
        }
426

    
427
        public void clear() {
428
            selected.clear();
429
        }
430

    
431
        public boolean contains(FeatureReference reference) {
432
            return selected.contains(reference);
433
        }
434

    
435
        public int getSize() {
436
            return selected.size();
437
        }
438

    
439
        public Object clone() throws CloneNotSupportedException {
440
                        SelectionData clone = (SelectionData) super.clone();
441
                        // reversed and totalSize already cloned by parent.
442
                        // clone the selected Set
443
                        clone.selected = new HashSet(selected);
444
            return clone;
445
        }
446
    }
447

    
448
    // *** Persistence ***
449

    
450
        public void saveToState(PersistentState state) throws PersistenceException {
451
                state.set("store", featureStore);
452
                state.set("reversed", selectionData.isReversed());
453
                state.set("totalSize", selectionData.getTotalSize());
454
                state.set("selected", selectionData.getSelected().iterator());
455
        }
456

    
457
        public void loadFromState(PersistentState state)
458
                        throws PersistenceException {
459
                featureStore = (FeatureStore)state.get("store");
460
                helper = new DefaultFeatureSelectionHelper((DefaultFeatureStore)featureStore);
461
                selectionData.setReversed(state.getBoolean("reversed"));
462
                selectionData.setTotalSize(state.getLong("totalSize"));
463
                Iterator it = state.getIterator("selected");
464
                while (it.hasNext()) {
465
                        DefaultFeatureReference ref = (DefaultFeatureReference) it.next();
466
                        selectionData.add(ref);
467
                }
468
                
469
                /*
470
                 * If we do not do this, feature store will not listen
471
                 * to changes in selection after instantiating a
472
                 * persisted selection. For non-persisted instances,
473
                 * this line corresponds to the line found in method:
474
                 * getFeatureSelection() in DefaultFeatureStore.
475
                 * This is not dangerous because "addObserver" only adds
476
                 * if they were not already added, so future invocations
477
                 * with same instances will have no effect.
478
                 */
479
                this.addObserver((DefaultFeatureStore)featureStore);
480
        }
481

    
482
        public static void registerPersistent() {
483
                DynStruct definition = ToolsLocator.getPersistenceManager().addDefinition(
484
                                DefaultFeatureReferenceSelection.class, 
485
                                DYNCLASS_PERSISTENT_NAME, 
486
                                "DefaultFeatureReferenceSelection Persistent definition",
487
                                null, 
488
                                null
489
                        );
490

    
491
                definition.addDynFieldObject("store").setClassOfValue(FeatureStore.class).setMandatory(true);
492
                definition.addDynFieldBoolean("reversed").setMandatory(true);
493
                definition.addDynFieldLong("totalSize").setMandatory(true);
494
                definition.addDynFieldList("selected").setClassOfItems(DefaultFeatureReference.class).setMandatory(true);
495

    
496
        }
497

    
498
        public void addObserver(Observer observer) {
499
                delegateObservable.addObserver(observer);
500
        }
501

    
502
        public void addObserver(Reference ref) {
503
                delegateObservable.addObserver(ref);
504
        }
505

    
506
        public void addObservers(BaseWeakReferencingObservable observable) {
507
                delegateObservable.addObservers(observable);
508
        }
509

    
510
        public void beginComplexNotification() {
511
                delegateObservable.beginComplexNotification();
512
        }
513

    
514
        public int countObservers() {
515
                return delegateObservable.countObservers();
516
        }
517

    
518
        public void deleteObserver(Observer observer) {
519
                delegateObservable.deleteObserver(observer);
520
        }
521

    
522
        public void deleteObserver(Reference ref) {
523
                delegateObservable.deleteObserver(ref);
524
        }
525

    
526
        public void deleteObservers() {
527
                delegateObservable.deleteObservers();
528
        }
529

    
530
        public void disableNotifications() {
531
                delegateObservable.disableNotifications();
532
        }
533

    
534
        public void enableNotifications() {
535
                delegateObservable.enableNotifications();
536
        }
537

    
538
        public void endComplexNotification() {
539
                // We don't want to notify many times in a complex notification
540
                // scenario, so ignore notifications if in complex.
541
                // Only one notification will be sent when the complex notification
542
                // ends.
543
                delegateObservable
544
                                .notifyObservers(DataStoreNotification.SELECTION_CHANGE);
545
                delegateObservable.endComplexNotification();
546
        }
547

    
548
        public boolean inComplex() {
549
                return delegateObservable.inComplex();
550
        }
551

    
552
        public boolean isEnabledNotifications() {
553
                return delegateObservable.isEnabledNotifications();
554
        }
555

    
556
        public void notifyObservers() {
557
                // We don't want to notify many times in a complex notification
558
                // scenario, so ignore notifications if in complex.
559
                // Only one notification will be sent when the complex notification
560
                // ends.
561
                if (!delegateObservable.inComplex()) {
562
                        delegateObservable.notifyObservers();
563
                }
564
        }
565

    
566
        public void notifyObservers(Object arg) {
567
                if (!delegateObservable.inComplex()) {
568
                        delegateObservable.notifyObservers(arg);
569
                }
570
        }
571

    
572
        public Object clone() throws CloneNotSupportedException {
573
                DefaultFeatureReferenceSelection clone = (DefaultFeatureReferenceSelection) super
574
                                .clone();
575
                // Original observers aren't cloned
576
                clone.delegateObservable = new DelegateWeakReferencingObservable(clone);
577
                // Clone internal data
578
                clone.selectionData = (SelectionData) selectionData.clone();
579
                // featureStore and helper are already swallow cloned by our parent
580
                return clone;
581
        }
582
}