Statistics
| Revision:

root / tags / v2_0_0_Build_2047 / libraries / libFMap_dal / src / org / gvsig / fmap / dal / feature / impl / DefaultFeatureReferenceSelection.java @ 38284

History | View | Annotate | Download (17.1 KB)

1 23928 cordinyana
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4 31544 cordinyana
 * of the Valencian Government (CIT)
5 24062 jjdelcerro
 *
6 23928 cordinyana
 * 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 24062 jjdelcerro
 *
11 23928 cordinyana
 * 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 24062 jjdelcerro
 *
16 23928 cordinyana
 * 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 24062 jjdelcerro
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 23928 cordinyana
 * MA  02110-1301, USA.
20 24062 jjdelcerro
 *
21 23928 cordinyana
 */
22
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 {DiSiD Technologies}  {Implement data selection}
26
 */
27 24496 jmvivo
package org.gvsig.fmap.dal.feature.impl;
28 23928 cordinyana
29 31284 cordinyana
import java.lang.ref.Reference;
30 28006 jmvivo
import java.util.Collections;
31
import java.util.HashSet;
32
import java.util.Iterator;
33
import java.util.Set;
34 23928 cordinyana
35 24496 jmvivo
import org.gvsig.fmap.dal.DataStore;
36
import org.gvsig.fmap.dal.DataStoreNotification;
37 24505 jmvivo
import org.gvsig.fmap.dal.exception.DataException;
38 28006 jmvivo
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 24961 cordinyana
import org.gvsig.fmap.dal.feature.impl.undo.FeatureCommandsStack;
43 30208 jmvivo
import org.gvsig.tools.ToolsLocator;
44 31284 cordinyana
import org.gvsig.tools.dispose.impl.AbstractDisposable;
45 32880 jjdelcerro
import org.gvsig.tools.dynobject.DynStruct;
46 23928 cordinyana
import org.gvsig.tools.exception.BaseException;
47 33378 cordinyana
import org.gvsig.tools.lang.Cloneable;
48 24794 jmvivo
import org.gvsig.tools.observer.Observable;
49 31284 cordinyana
import org.gvsig.tools.observer.Observer;
50 24268 jjdelcerro
import org.gvsig.tools.observer.impl.BaseWeakReferencingObservable;
51 31284 cordinyana
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
52 24062 jjdelcerro
import org.gvsig.tools.persistence.PersistentState;
53 32880 jjdelcerro
import org.gvsig.tools.persistence.exception.PersistenceException;
54 23928 cordinyana
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 28006 jmvivo
 *
61 23928 cordinyana
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
62
 */
63 31284 cordinyana
public class DefaultFeatureReferenceSelection extends AbstractDisposable
64
                implements FeatureReferenceSelection {
65 24794 jmvivo
66 31284 cordinyana
        public static final String DYNCLASS_PERSISTENT_NAME =
67 32880 jjdelcerro
                        "DefaultFeatureReferenceSelection";
68 28006 jmvivo
69 26833 cordinyana
    protected SelectionData selectionData = new SelectionData();
70 28006 jmvivo
71 27716 cordinyana
    private FeatureStore featureStore;
72 23928 cordinyana
73 27716 cordinyana
    private FeatureSelectionHelper helper;
74
75 31284 cordinyana
        private DelegateWeakReferencingObservable delegateObservable =
76
                        new DelegateWeakReferencingObservable(this);
77
78 24794 jmvivo
        /**
79
         * Creates a new Selection with the total size of Features from which the
80
         * selection will be performed.
81 28006 jmvivo
         *
82 24794 jmvivo
         * @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 24269 cordinyana
    public DefaultFeatureReferenceSelection(DefaultFeatureStore featureStore)
89
            throws DataException {
90 23928 cordinyana
        super();
91 24269 cordinyana
        this.featureStore = featureStore;
92 27716 cordinyana
        this.helper = new DefaultFeatureSelectionHelper(featureStore);
93 28017 jmvivo
        selectionData.setTotalSize(featureStore.getFeatureCount());
94 23928 cordinyana
    }
95
96 27716 cordinyana
    /**
97
     * Creates a new Selection with the total size of Features from which the
98
     * selection will be performed.
99 28006 jmvivo
     *
100 27716 cordinyana
     * @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 28017 jmvivo
        selectionData.setTotalSize(featureStore.getFeatureCount());
115 27716 cordinyana
    }
116 28006 jmvivo
117 31284 cordinyana
        /**
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 23928 cordinyana
    public boolean select(FeatureReference reference) {
128 26317 cordinyana
        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 38241 jldominguez
138
        if (reference == null) {
139
            throw new IllegalArgumentException("reference");
140
        }
141
142 26833 cordinyana
        if (isSelected(reference)) {
143
            return false;
144
        }
145 28006 jmvivo
146 26317 cordinyana
        if (undoable && getFeatureStore().isEditing()) {
147 24346 cordinyana
            getCommands().select(this, reference);
148
        }
149 23928 cordinyana
        boolean change = false;
150 26833 cordinyana
        if (selectionData.isReversed()) {
151
            change = selectionData.remove(reference);
152 23928 cordinyana
        } else {
153 26833 cordinyana
            change = selectionData.add(reference);
154 23928 cordinyana
        }
155
156
        if (change) {
157 24178 cordinyana
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
158 23928 cordinyana
        }
159
160
        return change;
161
    }
162
163
    public boolean deselect(FeatureReference reference) {
164 26317 cordinyana
        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 26833 cordinyana
        if (!isSelected(reference)) {
174
            return false;
175
        }
176
177 26317 cordinyana
        if (undoable && getFeatureStore().isEditing()) {
178 24346 cordinyana
            getCommands().deselect(this, reference);
179
        }
180 23928 cordinyana
        boolean change = false;
181 26833 cordinyana
        if (selectionData.isReversed()) {
182
            change = selectionData.add(reference);
183 23928 cordinyana
        } else {
184 26833 cordinyana
            change = selectionData.remove(reference);
185 23928 cordinyana
        }
186 24269 cordinyana
187 23928 cordinyana
        if (change) {
188 24178 cordinyana
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
189 23928 cordinyana
        }
190
191
        return change;
192
    }
193
194 24346 cordinyana
    public void selectAll() throws DataException {
195 26317 cordinyana
        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 24346 cordinyana
            getCommands().startComplex("_selectionSelectAll");
206
            getCommands().selectAll(this);
207
        }
208 26833 cordinyana
        if (!selectionData.isReversed()) {
209
            selectionData.setReversed(true);
210 23928 cordinyana
        }
211 24526 cordinyana
        clearFeatureReferences();
212 26833 cordinyana
        if (undoable && getFeatureStore().isEditing()) {
213 24346 cordinyana
            getCommands().endComplex();
214
        }
215 24178 cordinyana
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
216 23928 cordinyana
    }
217
218 24346 cordinyana
    public void deselectAll() throws DataException {
219 26317 cordinyana
        deselectAll(true);
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 24346 cordinyana
            getCommands().startComplex("_selectionDeselectAll");
230
            getCommands().deselectAll(this);
231
        }
232 26833 cordinyana
        if (selectionData.isReversed()) {
233
            selectionData.setReversed(false);
234 23928 cordinyana
        }
235 24526 cordinyana
        clearFeatureReferences();
236 26833 cordinyana
        if (undoable && getFeatureStore().isEditing()) {
237 24346 cordinyana
            getCommands().endComplex();
238
        }
239 23928 cordinyana
240 24178 cordinyana
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
241 23928 cordinyana
    }
242
243
    public boolean isSelected(FeatureReference reference) {
244 26833 cordinyana
        if (selectionData.isReversed()) {
245
            return !selectionData.contains(reference);
246 23928 cordinyana
        } else {
247 26833 cordinyana
            return selectionData.contains(reference);
248 23928 cordinyana
        }
249
    }
250
251
    public void reverse() {
252 26317 cordinyana
        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 24346 cordinyana
            getCommands().selectionReverse(this);
263
        }
264 26833 cordinyana
        selectionData.setReversed(!selectionData.isReversed());
265 24178 cordinyana
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
266 23928 cordinyana
    }
267
268
    public long getSelectedCount() {
269 26833 cordinyana
        if (selectionData.isReversed()) {
270
                return selectionData.getTotalSize() - selectionData.getSize()
271 27716 cordinyana
                        + helper.getFeatureStoreDeltaSize();
272 23928 cordinyana
        } else {
273 26833 cordinyana
            return selectionData.getSize();
274 23928 cordinyana
        }
275
    }
276
277
    public Iterator referenceIterator() {
278 26833 cordinyana
        return Collections.unmodifiableSet(selectionData.getSelected())
279
                .iterator();
280 23928 cordinyana
    }
281
282 31284 cordinyana
        protected void doDispose() throws BaseException {
283
                delegateObservable.deleteObservers();
284
                deselectAll(false);
285 23928 cordinyana
    }
286
287
    public boolean isFromStore(DataStore store) {
288
        return featureStore.equals(store);
289
    }
290
291
    public void accept(Visitor visitor) throws BaseException {
292 26833 cordinyana
        for (Iterator iter = selectionData.getSelected().iterator(); iter
293
                .hasNext();) {
294 23928 cordinyana
            visitor.visit(iter.next());
295
        }
296
    }
297
298 24794 jmvivo
    public void update(Observable observable,
299
                        Object notification) {
300 23928 cordinyana
        // 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 26833 cordinyana
                selectionData.remove(storeNotif.getFeature().getReference());
306 23928 cordinyana
            }
307
        }
308
    }
309
310 26833 cordinyana
    public SelectionData getData() {
311
        return selectionData;
312
    }
313 23928 cordinyana
314 26833 cordinyana
    public void setData(SelectionData selectionData) {
315
        this.selectionData = selectionData;
316
        notifyObservers(DataStoreNotification.SELECTION_CHANGE);
317
    }
318
319 25800 cordinyana
    public String toString() {
320
        return getClass().getName() + ": " + getSelectedCount()
321 26833 cordinyana
                + " features selected, reversed = "
322
                + selectionData.isReversed() + ", featureIds contained: "
323
                + selectionData.getSelected();
324 25800 cordinyana
    }
325
326 26833 cordinyana
    protected boolean isReversed() {
327
        return selectionData.isReversed();
328
    }
329
330 24526 cordinyana
    /**
331
     * Removes all the stored FeatureRefence objects.
332
     */
333
    protected void clearFeatureReferences() {
334 26833 cordinyana
        selectionData.clear();
335 24269 cordinyana
    }
336
337 24794 jmvivo
        /**
338
         * Returns the FeatureStore of the selected FeatureReferences.
339 28006 jmvivo
         *
340 24794 jmvivo
         * @return the featureStore
341
         */
342 27716 cordinyana
    protected FeatureStore getFeatureStore() {
343 24526 cordinyana
        return featureStore;
344
    }
345
346 24794 jmvivo
        /**
347
         * Returns the reference to the commands record.
348 28006 jmvivo
         *
349 24794 jmvivo
         * @return the reference to the commands record
350
         */
351 24961 cordinyana
    protected FeatureCommandsStack getCommands() {
352 27716 cordinyana
        return helper.getFeatureStoreCommandsStack();
353 24346 cordinyana
    }
354 24194 jjdelcerro
355 33378 cordinyana
        public static class SelectionData implements Cloneable {
356 26833 cordinyana
        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 28006 jmvivo
434 26833 cordinyana
        public int getSize() {
435
            return selected.size();
436
        }
437 28006 jmvivo
438 26833 cordinyana
        public Object clone() throws CloneNotSupportedException {
439 33378 cordinyana
                        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 26833 cordinyana
            return clone;
444
        }
445
    }
446 30208 jmvivo
447
    // *** Persistence ***
448
449
        public void saveToState(PersistentState state) throws PersistenceException {
450 31272 jpiera
                state.set("store", featureStore);
451 30208 jmvivo
                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 31272 jpiera
                featureStore = (FeatureStore)state.get("store");
459 38192 cordinyana
                helper = new DefaultFeatureSelectionHelper((DefaultFeatureStore)featureStore);
460 30208 jmvivo
                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
        public static void registerPersistent() {
470 32880 jjdelcerro
                DynStruct definition = ToolsLocator.getPersistenceManager().addDefinition(
471
                                DefaultFeatureReferenceSelection.class,
472
                                DYNCLASS_PERSISTENT_NAME,
473
                                "DefaultFeatureReferenceSelection Persistent definition",
474
                                null,
475
                                null
476
                        );
477 30208 jmvivo
478 33281 jjdelcerro
                definition.addDynFieldObject("store").setClassOfValue(FeatureStore.class).setMandatory(true);
479 32880 jjdelcerro
                definition.addDynFieldBoolean("reversed").setMandatory(true);
480
                definition.addDynFieldLong("totalSize").setMandatory(true);
481 33281 jjdelcerro
                definition.addDynFieldList("selected").setClassOfItems(DefaultFeatureReference.class).setMandatory(true);
482 30208 jmvivo
483
        }
484 31284 cordinyana
485
        public void addObserver(Observer observer) {
486
                delegateObservable.addObserver(observer);
487
        }
488
489
        public void addObserver(Reference ref) {
490
                delegateObservable.addObserver(ref);
491
        }
492
493
        public void addObservers(BaseWeakReferencingObservable observable) {
494
                delegateObservable.addObservers(observable);
495
        }
496
497
        public void beginComplexNotification() {
498
                delegateObservable.beginComplexNotification();
499
        }
500
501
        public int countObservers() {
502
                return delegateObservable.countObservers();
503
        }
504
505
        public void deleteObserver(Observer observer) {
506
                delegateObservable.deleteObserver(observer);
507
        }
508
509
        public void deleteObserver(Reference ref) {
510
                delegateObservable.deleteObserver(ref);
511
        }
512
513
        public void deleteObservers() {
514
                delegateObservable.deleteObservers();
515
        }
516
517
        public void disableNotifications() {
518
                delegateObservable.disableNotifications();
519
        }
520
521
        public void enableNotifications() {
522
                delegateObservable.enableNotifications();
523
        }
524
525
        public void endComplexNotification() {
526 33382 cordinyana
                // We don't want to notify many times in a complex notification
527
                // scenario, so ignore notifications if in complex.
528
                // Only one notification will be sent when the complex notification
529
                // ends.
530
                delegateObservable
531
                                .notifyObservers(DataStoreNotification.SELECTION_CHANGE);
532 31284 cordinyana
                delegateObservable.endComplexNotification();
533
        }
534
535
        public boolean inComplex() {
536
                return delegateObservable.inComplex();
537
        }
538
539
        public boolean isEnabledNotifications() {
540
                return delegateObservable.isEnabledNotifications();
541
        }
542
543
        public void notifyObservers() {
544 33382 cordinyana
                // We don't want to notify many times in a complex notification
545
                // scenario, so ignore notifications if in complex.
546
                // Only one notification will be sent when the complex notification
547
                // ends.
548
                if (!delegateObservable.inComplex()) {
549
                        delegateObservable.notifyObservers();
550
                }
551 31284 cordinyana
        }
552
553
        public void notifyObservers(Object arg) {
554 33382 cordinyana
                if (!delegateObservable.inComplex()) {
555
                        delegateObservable.notifyObservers(arg);
556
                }
557 31284 cordinyana
        }
558 33378 cordinyana
559
        public Object clone() throws CloneNotSupportedException {
560
                DefaultFeatureReferenceSelection clone = (DefaultFeatureReferenceSelection) super
561
                                .clone();
562
                // Original observers aren't cloned
563
                clone.delegateObservable = new DelegateWeakReferencingObservable(clone);
564
                // Clone internal data
565
                clone.selectionData = (SelectionData) selectionData.clone();
566
                // featureStore and helper are already swallow cloned by our parent
567
                return clone;
568
        }
569 23928 cordinyana
}