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

History | View | Annotate | Download (17.6 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 {DiSiD Technologies}  {Implement data selection}
26
 */
27
package org.gvsig.fmap.dal.feature.impl;
28

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

    
35
import org.gvsig.fmap.dal.DataStore;
36
import org.gvsig.fmap.dal.DataStoreNotification;
37
import org.gvsig.fmap.dal.exception.DataException;
38
import org.gvsig.fmap.dal.feature.FeatureReference;
39
import org.gvsig.fmap.dal.feature.FeatureReferenceSelection;
40
import org.gvsig.fmap.dal.feature.FeatureStore;
41
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
42
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
    protected SelectionData selectionData = new SelectionData();
70

    
71
    private FeatureStore featureStore;
72

    
73
    private FeatureSelectionHelper helper;
74

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

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

    
96
    /**
97
     * Creates a new Selection with the total size of Features from which the
98
     * selection will be performed.
99
     *
100
     * @param featureStore
101
     *            the FeatureStore of the selected FeatureReferences
102
     * @param helper
103
     *            to get some information of the Store
104
     * @throws DataException
105
     *             if there is an error while getting the total number of
106
     *             Features of the Store.
107
     */
108
    public DefaultFeatureReferenceSelection(FeatureStore featureStore,
109
            FeatureSelectionHelper helper)
110
            throws DataException {
111
        super();
112
        this.featureStore = featureStore;
113
        this.helper = helper;
114
        selectionData.setTotalSize(featureStore.getFeatureCount());
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
        if (selectionData.isReversed()) {
151
            change = selectionData.remove(reference);
152
        } else {
153
            change = selectionData.add(reference);
154
        }
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
        if (selectionData.isReversed()) {
182
            change = selectionData.add(reference);
183
        } else {
184
            change = selectionData.remove(reference);
185
        }
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
        if (!selectionData.isReversed()) {
209
            selectionData.setReversed(true);
210
        }
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
        if (undoable && getFeatureStore().isEditing()) {
229
            getCommands().startComplex("_selectionDeselectAll");
230
            getCommands().deselectAll(this);
231
        }
232
        if (selectionData.isReversed()) {
233
            selectionData.setReversed(false);
234
        }
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
        if (selectionData.isReversed()) {
245
            return !selectionData.contains(reference);
246
        } else {
247
            return selectionData.contains(reference);
248
        }
249
    }
250

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
371
        private long totalSize;
372

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

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

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

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

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

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

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

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

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

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

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

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

    
447
    // *** Persistence ***
448

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

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

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

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

    
495
        }
496

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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