Revision 43089 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/DefaultFeatureSelection.java

View differences:

DefaultFeatureSelection.java
3 3
 *
4 4
 * Copyright (C) 2007-2013 gvSIG Association.
5 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.
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10 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.
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15 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.
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 19
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
23 22
 */
24 23
package org.gvsig.fmap.dal.feature.impl;
25 24

  
......
41 40
import org.gvsig.fmap.dal.feature.FeatureStore;
42 41
import org.gvsig.fmap.dal.feature.FeatureType;
43 42
import org.gvsig.fmap.dal.feature.exception.ReversedSelectionIteratorException;
44
import org.gvsig.fmap.dal.feature.impl.dynobjectutils.DynObjectSetFeatureSetFacade;
43
import org.gvsig.fmap.dal.feature.impl.DefaultFeatureReferenceSelection.SelectionData;
44
import org.gvsig.fmap.dal.feature.impl.featureset.AbstractFeatureSet;
45
import org.gvsig.fmap.dal.feature.impl.undo.FeatureCommandsStack;
45 46
import org.gvsig.tools.ToolsLocator;
46 47
import org.gvsig.tools.dispose.DisposableIterator;
47 48
import org.gvsig.tools.dispose.DisposeUtils;
48
import org.gvsig.tools.dynobject.DynObjectSet;
49 49
import org.gvsig.tools.dynobject.DynStruct;
50 50
import org.gvsig.tools.exception.BaseException;
51
import org.gvsig.tools.observer.Observable;
52
import org.gvsig.tools.observer.Observer;
51 53
import org.gvsig.tools.persistence.PersistenceManager;
52 54
import org.gvsig.tools.persistence.PersistentState;
53 55
import org.gvsig.tools.persistence.exception.PersistenceException;
54
import org.gvsig.tools.visitor.VisitCanceledException;
55
import org.gvsig.tools.visitor.Visitor;
56 56
import org.slf4j.Logger;
57 57
import org.slf4j.LoggerFactory;
58 58

  
......
65 65
 *
66 66
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
67 67
 */
68
public class DefaultFeatureSelection extends DefaultFeatureReferenceSelection
69
		implements FeatureSelection {
68
public class DefaultFeatureSelection extends AbstractFeatureSet
69
        implements FeatureSelection {
70 70

  
71
	private static final Logger LOG = LoggerFactory
72
			.getLogger(DefaultFeatureSelection.class);
71
    private static final Logger LOG = LoggerFactory
72
            .getLogger(DefaultFeatureSelection.class);
73 73

  
74
	private Map featureTypeCounts = new HashMap(1);
75
	private Map<Feature,Iterator> featureIterators = new HashMap<>();
74
    public class RemoveFromFeatureSelectionException extends DataRuntimeException {
76 75

  
77
	/**
78
	 * Creates a DefaultFeatureSelection, with a FeatureStore.
79
	 *
80
	 * @param featureStore
81
	 *            the FeatureStore to load Features from
82
	 * @throws DataException
83
	 *             if there is an error while getting the total number of
84
	 *             Features of the Store.
85
	 * @see AbstractSetBasedDataSelection#DefaultSelection(int)
86
	 */
87
	public DefaultFeatureSelection(DefaultFeatureStore featureStore)
88
			throws DataException {
89
		super(featureStore);
90
	}
76
        /**
77
         *
78
         */
79
        private static final long serialVersionUID = 2636692469445838928L;
80
        private final static String MESSAGE_FORMAT = "Can't remove feature from reversed selection.";
81
        private final static String MESSAGE_KEY = "_RemoveFromFeatureSelectionException";
91 82

  
92
	/**
93
	 * Creates a new Selection with the total size of Features from which the
94
	 * selection will be performed.
95
	 *
96
	 * @param featureStore
97
	 *            the FeatureStore of the selected FeatureReferences
98
	 * @param helper
99
	 *            to get some information of the Store
100
	 * @throws DataException
101
	 *             if there is an error while getting the total number of
102
	 *             Features of the Store.
103
	 */
104
	public DefaultFeatureSelection(FeatureStore featureStore,
105
			FeatureSelectionHelper helper) throws DataException {
106
		super(featureStore, helper);
107
	}
83
        public RemoveFromFeatureSelectionException(Throwable cause) {
84
            super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
85
            //setValue("store", store);
86
        }
87
    }
108 88

  
109
	/**
110
	 * Constructor used by the persistence manager. Don't use directly. After to
111
	 * invoke this method, the persistence manager calls the the method
112
	 * {@link #loadFromState(PersistentState)} to set the values of the internal
113
	 * attributes that this class needs to work.
114
	 */
115
	public DefaultFeatureSelection() {
116
		super();
117
	}
89
    /**
90
     * Facade over a Iterator of FeatureReferences, to return Features instead.
91
     *
92
     * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
93
     */
94
    private class FeatureIteratorFacade implements DisposableIterator {
118 95

  
119
	public boolean select(Feature feature) {
120
		return select(feature, true);
121
	}
96
        private final Logger LOGGER = LoggerFactory
97
                .getLogger(FeatureIteratorFacade.class);
122 98

  
123
	/**
124
	 * @see #select(Feature)
125
	 * @param undoable
126
	 *            if the action must be undoable
127
	 */
128
	public boolean select(Feature feature, boolean undoable) {
129
		// TODO: should we check if the feature is from the same FeatureStore??
130
		if (feature == null) {
131
			return false;
132
		}
99
        private java.util.Iterator refIterator;
133 100

  
134
		// LOGGER.debug("Selected feature: {}", feature);
101
        private FeatureStore featureStore;
102
        private Feature currentFeature = null;
135 103

  
136
		if (isReversed()) {
137
			removeFeatureTypeCount(feature.getType());
138
		} else {
139
			addFeatureTypeCount(feature.getType());
140
		}
141
		return select(feature.getReference(), undoable);
142
	}
104
        public FeatureIteratorFacade(java.util.Iterator iter,
105
                FeatureStore featureStore) {
106
            this.refIterator = iter;
107
            this.featureStore = featureStore;
108
        }
143 109

  
144
	public boolean select(FeatureSet features) throws DataException {
145
		return select(features, true);
146
	}
110
        @Override
111
        public boolean hasNext() {
112
            return refIterator.hasNext();
113
        }
147 114

  
148
	/**
149
	 * @see #select(FeatureSet)
150
	 * @param undoable
151
	 *            if the action must be undoable
152
	 */
153
	public boolean select(FeatureSet features, boolean undoable)
154
			throws DataException {
155
		boolean change = false;
156
		boolean inComplex = false;
157
		if (undoable && getFeatureStore().isEditing()
158
				&& !getCommands().inComplex()) {
115
        @Override
116
        public Object next() {
117
            FeatureReference ref = nextFeatureReference();
118
            try {
119
                currentFeature = featureStore.getFeatureByReference(ref);
120
                return currentFeature;
121
            } catch (DataException ex) {
122
                LOGGER.error(
123
                        "Error loading the Feature with FeatureReference: "
124
                        + ref, ex);
125
                return null;
126
            }
127
        }
159 128

  
160
			getCommands().startComplex("_selectionSelectFeatureSet");
161
			inComplex = getCommands().inComplex();
162
		}
129
        /**
130
         * Returns the next FeatureReference.
131
         *
132
         * @return the next FeatureReference
133
         */
134
        public FeatureReference nextFeatureReference() {
135
            return (FeatureReference) refIterator.next();
136
        }
163 137

  
164
		disableNotifications();
165
		DisposableIterator iter = null;
166
		try {
167
			for (iter = features.fastIterator(); iter.hasNext();) {
168
				change |= select((Feature) iter.next(), undoable);
169
			}
170
		} finally {
171
			dispose(iter);
172
		}
173
		enableNotifications();
174
		if (undoable && getFeatureStore().isEditing() && inComplex) {
175
			getCommands().endComplex();
176
		}
177
		if (change) {
178
			notifyObservers(DataStoreNotification.SELECTION_CHANGE);
179
		}
180
		return change;
181
	}
138
        @Override
139
        public void remove() {
140
            try {
141
                featureStore.delete(currentFeature);
142
                refIterator.remove();
143
            } catch (DataException e) {
144
                throw new RemoveFromFeatureSelectionException(e);
145
            }
146
        }
182 147

  
183
	public boolean deselect(Feature feature) {
184
		return deselect(feature, true);
185
	}
148
        public class RemoveFromFeatureSelectionException extends DataRuntimeException {
186 149

  
187
	/**
188
	 * @see #deselect(Feature)
189
	 * @param undoable
190
	 *            if the action must be undoable
191
	 */
192
	public boolean deselect(Feature feature, boolean undoable) {
193
		if (feature == null) {
194
			return false;
195
		}
150
            /**
151
             *
152
             */
153
            private static final long serialVersionUID = 2636692469445838928L;
154
            private final static String MESSAGE_FORMAT = "Can't remove feature from selection.";
155
            private final static String MESSAGE_KEY = "_RemoveFromFeatureSelectionException";
196 156

  
197
		LOG.debug("Deselected feature: {}", feature);
157
            public RemoveFromFeatureSelectionException(Throwable cause) {
158
                super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
159
                //setValue("store", store);
160
            }
161
        }
198 162

  
199
		if (isReversed()) {
200
			addFeatureTypeCount(feature.getType());
201
		} else {
202
			removeFeatureTypeCount(feature.getType());
203
		}
204
		return deselect(feature.getReference(), undoable);
205
	}
163
        @Override
164
        public void dispose() {
165
            if (refIterator instanceof DisposableIterator) {
166
                ((DisposableIterator) refIterator).dispose();
167
            }
168
            refIterator = null;
169
            featureStore = null;
170
        }
171
    }
206 172

  
207
	public boolean deselect(FeatureSet features) throws DataException {
208
		return deselect(features, true);
209
	}
173
    /**
174
     * Facade over a Iterator of FeatureReferences, to return Features instead,
175
     * when the Selection is reversed
176
     *
177
     * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
178
     */
179
    private class ReversedFeatureIteratorFacade implements DisposableIterator {
210 180

  
211
	/**
212
	 * @see #deselect(FeatureSet)
213
	 * @param undoable
214
	 *            if the action must be undoable
215
	 */
216
	public boolean deselect(FeatureSet features, boolean undoable)
217
			throws DataException {
218
		boolean change = false;
219
		if (undoable && getFeatureStore().isEditing()) {
220
			getCommands().startComplex("_selectionDeselectFeatureSet");
221
		}
222
		disableNotifications();
223
		DisposableIterator iter = null;
224
		try {
225
			for (iter = features.fastIterator(); iter.hasNext();) {
226
				change |= deselect((Feature) iter.next(), undoable);
227
			}
228
		} finally {
229
			dispose(iter);
230
		}
231
		enableNotifications();
232
		if (undoable && getFeatureStore().isEditing()) {
233
			getCommands().endComplex();
234
		}
235
		if (change) {
236
			notifyObservers(DataStoreNotification.SELECTION_CHANGE);
237
		}
238
		return change;
239
	}
181
        private SelectionData selectionData;
240 182

  
241
	public boolean isSelected(Feature feature) {
242
		if (feature == null) {
243
			return false;
244
		}
183
        private DisposableIterator iterator;
245 184

  
246
        // Use the selection data size as a small optimization for the most
247
        // common case, when nothing is selected and every feature is checked
248
        // while drawing or painting the table document.
249
        if (selectionData.isReversed()) {
250
            return selectionData.getSize() == 0
251
                || !selectionData.contains(feature.getReference());
252
        } else {
253
            return selectionData.getSize() > 0
254
                && selectionData.contains(feature.getReference());
255
        }
256
	}
185
        private Feature nextFeature = null;
186
        private Feature currentFeature = null;
257 187

  
258
	public FeatureType getDefaultFeatureType() {
259
		try {
260
			return getFeatureStore().getDefaultFeatureType();
261
		} catch (DataException ex) {
262
			LOG.error("Error getting the default feature type "
263
					+ "of the FeatureStore: " + getFeatureStore(), ex);
264
		}
265
		return null;
266
	}
188
        private FeatureSet featureSet;
267 189

  
268
	public List getFeatureTypes() {
269
		// Go through the map of FeatureTypes, and return only the ones that
270
		// have at least a Feature.
271
		List types = new ArrayList();
272
		for (java.util.Iterator iterator = featureTypeCounts.entrySet()
273
				.iterator(); iterator.hasNext();) {
274
			Map.Entry entry = (Entry) iterator.next();
275
			FeatureType type = (FeatureType) entry.getKey();
276
			Long count = (Long) entry.getValue();
190
        public ReversedFeatureIteratorFacade(SelectionData selectionData,
191
                FeatureStore featureStore, boolean fastIterator) {
192
            this.selectionData = selectionData;
277 193

  
278
			if (count.longValue() > 0) {
279
				types.add(type);
280
			}
281
		}
194
            // Load a Set with all the store features
195
            try {
196
                featureSet = featureStore.getFeatureSet();
197
                //if (fastIterator) {
198
                iterator = featureSet.fastIterator();
199
//				} else {
200
//					iterator = featureSet.iterator();
201
//				}
202
            } catch (DataException ex) {
203
                throw new ReversedSelectionIteratorException(ex);
204
            }
282 205

  
283
		return types;
284
	}
206
            // Filter the features not selected and position in the next
207
            // selected feature
208
            positionInNextElement();
209
        }
285 210

  
286
	public long getSize() throws DataException {
287
		return getSelectedCount();
288
	}
211
        @Override
212
        public boolean hasNext() {
213
            return nextFeature != null;
214
        }
289 215

  
290
	public boolean isEmpty() throws DataException {
291
		return getSelectedCount() == 0;
292
	}
216
        @Override
217
        public Object next() {
218
            featureIterators.remove(currentFeature);
219
            currentFeature = nextFeature.getCopy();
220
            featureIterators.put(currentFeature, this);
221
            positionInNextElement();
222
            return currentFeature;
223
        }
293 224

  
294
	/**
295
	 * Returns the list of selected values, or the deselected ones if the
296
	 * selection has been reversed.
297
	 */
298
	public DisposableIterator iterator() {
299
		return iterator(0);
300
	}
225
        @Override
226
        public void remove() {
227
            try {
228
                featureSet.delete(currentFeature);
229
            } catch (DataException e) {
230
                throw new RemoveFromFeatureSelectionException(e);
301 231

  
302
	/**
303
	 * Returns the list of selected values, or the deselected ones if the
304
	 * selection has been reversed.
305
	 *
306
	 * WARN: not very good performance implementation.
307
	 */
308
	public DisposableIterator iterator(long index) {
309
		return iterator(index, false);
310
	}
232
            }
233
        }
311 234

  
312
	/**
313
	 * Returns the list of selected values, or the deselected ones if the
314
	 * selection has been reversed.
315
	 *
316
	 * WARN: not really a fast implementation.
317
	 */
318
	public DisposableIterator fastIterator() {
319
		return fastIterator(0);
320
	}
321
        
322
        @Override
323
        public Feature first() {
324
            DisposableIterator it = null;
325
            try {
326
                it = this.iterator();
327
                if( it == null ) {
328
                    return null;
235
        private void positionInNextElement() {
236
            nextFeature = null;
237
            while (iterator.hasNext()) {
238
                nextFeature = (Feature) iterator.next();
239
                if (selectionData.contains(nextFeature.getReference())) {
240
                    nextFeature = null;
241
                } else {
242
                    break;
329 243
                }
330
                Feature f = (Feature) it.next();
331
                return f;
332
            } finally {
333
                DisposeUtils.disposeQuietly(it);
334 244
            }
335 245
        }
336 246

  
337
	/**
338
	 * Returns the list of selected values, or the deselected ones if the
339
	 * selection has been reversed.
340
	 *
341
	 * WARN: not really a fast implementation.
342
	 */
343
	public DisposableIterator fastIterator(long index) {
344
		return iterator(index, true);
345
	}
247
        @Override
248
        public void dispose() {
249
            this.featureSet.dispose();
250
            this.iterator.dispose();
251
            this.selectionData = null;
252
            this.nextFeature = null;
253
        }
254
    }
346 255

  
347
	protected void clearFeatureReferences() {
348
		super.clearFeatureReferences();
349
		featureTypeCounts.clear();
350
	}
256
    private Map featureTypeCounts = new HashMap(1);
257
    private final Map<Feature, Iterator> featureIterators = new HashMap<>();
258
    private final DefaultFeatureReferenceSelection featureReferenceSelection;
351 259

  
352
	/**
353
	 * Creates an iterator for the Selection.
354
	 */
355
	private DisposableIterator iterator(long index, boolean fastIterator) {
356
		if (isReversed()) {
357
			DisposableIterator iter = new ReversedFeatureIteratorFacade(
358
					getData(), getFeatureStore(), fastIterator);
359
			for (long l = 0; l < index && iter.hasNext(); l++) {
360
				iter.next();
361
			}
362
			return iter;
260
    /**
261
     * Creates a DefaultFeatureSelection, with a FeatureStore.
262
     *
263
     * @param featureStore the FeatureStore to load Features from
264
     * @throws DataException if there is an error while getting the total number
265
     * of Features of the Store.
266
     * @see AbstractSetBasedDataSelection#DefaultSelection(int)
267
     */
268
    public DefaultFeatureSelection(DefaultFeatureStore featureStore)
269
            throws DataException {
270
        this.featureReferenceSelection = new DefaultFeatureReferenceSelection(featureStore);
271
    }
363 272

  
364
		} else {
365
			// TODO: maybe we could add a new referenceIterator(int index)
366
			// method that could be implemented in a more performant way
273
    /**
274
     * Creates a new Selection with the total size of Features from which the
275
     * selection will be performed.
276
     *
277
     * @param featureStore the FeatureStore of the selected FeatureReferences
278
     * @param helper to get some information of the Store
279
     * @throws DataException if there is an error while getting the total number
280
     * of Features of the Store.
281
     */
282
    public DefaultFeatureSelection(FeatureStore featureStore,
283
            FeatureSelectionHelper helper) throws DataException {
284
        this.featureReferenceSelection = new DefaultFeatureReferenceSelection(featureStore, helper);
285
    }
367 286

  
368
			java.util.Iterator iter = selectionData.getSelected().iterator();
369
			for (long l = 0; l < index && iter.hasNext(); l++) {
370
				iter.next();
371
			}
372
			return new FeatureIteratorFacade(iter, getFeatureStore());
373
		}
374
	}
287
    /**
288
     * Constructor used by the persistence manager. Don't use directly. After to
289
     * invoke this method, the persistence manager calls the the method
290
     * {@link #loadFromState(PersistentState)} to set the values of the internal
291
     * attributes that this class needs to work.
292
     */
293
    public DefaultFeatureSelection() {
294
        this.featureReferenceSelection = new DefaultFeatureReferenceSelection();
295
    }
375 296

  
376
	private Long removeFeatureTypeCount(FeatureType featureType) {
377
		Long count = (Long) featureTypeCounts.get(featureType);
378
		if (count == null) {
379
			count = new Long(-1);
380
		} else {
381
			count = new Long(count.longValue() - 1);
382
		}
383
		featureTypeCounts.put(featureType, count);
384
		return count;
385
	}
297
    @Override
298
    public FeatureStore getFeatureStore() {
299
        return this.featureReferenceSelection.getFeatureStore();
300
    }
386 301

  
387
	private Long addFeatureTypeCount(FeatureType featureType) {
388
		Long count = (Long) featureTypeCounts.get(featureType);
389
		if (count == null) {
390
			count = new Long(1);
391
		} else {
392
			count = new Long(count.longValue() + 1);
393
		}
394
		featureTypeCounts.put(featureType, count);
395
		return count;
396
	}
302
    private void notifyObservers(String notificationType) {
303
        this.featureReferenceSelection.notifyObservers(notificationType);
304
    }
397 305

  
398
	/**
399
	 * Facade over a Iterator of FeatureReferences, to return Features instead.
400
	 *
401
	 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
402
	 */
403
	private class FeatureIteratorFacade implements DisposableIterator {
306
    public FeatureCommandsStack getCommands() {
307
        return this.featureReferenceSelection.getCommands();
308
    }
404 309

  
405
		private final Logger LOGGER = LoggerFactory
406
				.getLogger(FeatureIteratorFacade.class);
310
    @Override
311
    public void enableNotifications() {
312
        this.featureReferenceSelection.enableNotifications();
313
    }
407 314

  
408
		private java.util.Iterator refIterator;
315
    @Override
316
    public void disableNotifications() {
317
        this.featureReferenceSelection.disableNotifications();
318
    }
409 319

  
410
		private FeatureStore featureStore;
411
		private Feature currentFeature = null;
320
    public boolean isReversed() {
321
        return this.featureReferenceSelection.isReversed();
322
    }
412 323

  
413
		public FeatureIteratorFacade(java.util.Iterator iter,
414
				FeatureStore featureStore) {
415
			this.refIterator = iter;
416
			this.featureStore = featureStore;
417
		}
324
    @Override
325
    public long getSelectedCount() {
326
        return this.featureReferenceSelection.getSelectedCount();
327
    }
418 328

  
419
		public boolean hasNext() {
420
			return refIterator.hasNext();
421
		}
329
    public DefaultFeatureReferenceSelection.SelectionData getData() {
330
        return this.featureReferenceSelection.getData();
331
    }
422 332

  
423
		public Object next() {
424
			FeatureReference ref = nextFeatureReference();
425
			try {
426
			    currentFeature = featureStore.getFeatureByReference(ref);
427
				return currentFeature;
428
			} catch (DataException ex) {
429
				LOGGER.error(
430
						"Error loading the Feature with FeatureReference: "
431
								+ ref, ex);
432
				return null;
433
			}
434
		}
333
    @Override
334
    public boolean select(FeatureReference reference) {
335
        return this.featureReferenceSelection.select(reference);
336
    }
435 337

  
436
		/**
437
		 * Returns the next FeatureReference.
438
		 *
439
		 * @return the next FeatureReference
440
		 */
441
		public FeatureReference nextFeatureReference() {
442
			return (FeatureReference) refIterator.next();
443
		}
338
    public boolean select(FeatureReference reference, boolean undoable) {
339
        return this.featureReferenceSelection.select(reference, undoable);
340
    }
444 341

  
445
		public void remove() {
446
		    try {
447
                featureStore.delete(currentFeature);
448
                refIterator.remove();
449
            } catch (DataException e) {
450
                throw new RemoveFromFeatureSelectionException(e);
451
            }
452
		}
342
    @Override
343
    public boolean deselect(FeatureReference reference) {
344
        return this.featureReferenceSelection.deselect(reference);
345
    }
453 346

  
454
        public class RemoveFromFeatureSelectionException extends DataRuntimeException {
347
    public boolean deselect(FeatureReference reference, boolean undoable) {
348
        return this.featureReferenceSelection.deselect(reference, undoable);
349
    }
455 350

  
456
            /**
457
             *
458
             */
459
            private static final long serialVersionUID = 2636692469445838928L;
460
            private final static String MESSAGE_FORMAT = "Can't remove feature from selection.";
461
            private final static String MESSAGE_KEY = "_RemoveFromFeatureSelectionException";
351
    @Override
352
    public Iterator referenceIterator() {
353
        return this.featureReferenceSelection.referenceIterator();
354
    }
462 355

  
463
            public RemoveFromFeatureSelectionException(Throwable cause) {
464
                super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
465
                //setValue("store", store);
466
            }
467
        }
356
    @Override
357
    public void selectAll() throws DataException {
358
        this.featureReferenceSelection.selectAll();
359
    }
468 360

  
469
		public void dispose() {
470
			if (refIterator instanceof DisposableIterator) {
471
				((DisposableIterator) refIterator).dispose();
472
			}
473
			refIterator = null;
474
			featureStore = null;
475
		}
476
	}
361
    @Override
362
    public void deselectAll() throws DataException {
363
        this.featureReferenceSelection.deselectAll();
364
    }
365
    
366
    public void deselectAll(boolean undoable) throws DataException {
367
        this.featureReferenceSelection.deselectAll(undoable);
368
    }
369
    
370
    @Override
371
    public boolean isSelected(FeatureReference reference) {
372
        return this.featureReferenceSelection.isSelected(reference);
373
    }
477 374

  
478
	/**
479
	 * Facade over a Iterator of FeatureReferences, to return Features instead,
480
	 * when the Selection is reversed
481
	 *
482
	 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
483
	 */
484
	private class ReversedFeatureIteratorFacade implements DisposableIterator {
375
    @Override
376
    public void reverse() {
377
        this.featureReferenceSelection.reverse();
378
    }
485 379

  
486
		private SelectionData selectionData;
380
    @Override
381
    public void dispose() {
382
        this.featureReferenceSelection.dispose();
383
    }
487 384

  
488
		private DisposableIterator iterator;
385
    @Override
386
    public void update(Observable o, Object o1) {
387
        this.featureReferenceSelection.update(o, o1);
388
    }
489 389

  
490
        private Feature nextFeature = null;
491
        private Feature currentFeature = null;
390
    @Override
391
    public void addObserver(Observer obsrvr) {
392
        this.featureReferenceSelection.addObserver(obsrvr);
393
    }
492 394

  
493
		private FeatureSet featureSet;
395
    @Override
396
    public void deleteObserver(Observer obsrvr) {
397
        this.featureReferenceSelection.deleteObserver(obsrvr);
398
    }
494 399

  
495
		public ReversedFeatureIteratorFacade(SelectionData selectionData,
496
				FeatureStore featureStore, boolean fastIterator) {
497
			this.selectionData = selectionData;
400
    @Override
401
    public void deleteObservers() {
402
        this.featureReferenceSelection.deleteObservers();
403
    }
498 404

  
499
			// Load a Set with all the store features
500
			try {
501
				featureSet = featureStore.getFeatureSet();
502
				//if (fastIterator) {
503
					iterator = featureSet.fastIterator();
504
//				} else {
505
//					iterator = featureSet.iterator();
506
//				}
507
			} catch (DataException ex) {
508
				throw new ReversedSelectionIteratorException(ex);
509
			}
405
    @Override
406
    public void beginComplexNotification() {
407
        this.featureReferenceSelection.beginComplexNotification();
408
    }
510 409

  
511
			// Filter the features not selected and position in the next
512
			// selected feature
513
			positionInNextElement();
514
		}
410
    @Override
411
    public void endComplexNotification() {
412
        this.featureReferenceSelection.endComplexNotification();
413
    }
515 414

  
516
		public boolean hasNext() {
517
			return nextFeature != null;
518
		}
415
    @Override
416
    public void saveToState(PersistentState ps) throws PersistenceException {
417
        this.featureReferenceSelection.saveToState(ps);
418
    }
519 419

  
520
		public Object next() {
521
            featureIterators.remove(currentFeature);
522
		    currentFeature = nextFeature.getCopy();
523
		    featureIterators.put(currentFeature, this);
524
			positionInNextElement();
525
			return currentFeature ;
526
		}
420
    @Override
421
    public boolean select(Feature feature) {
422
        return select(feature, true);
423
    }
527 424

  
528
		public void remove() {
529
			try {
530
                featureSet.delete(currentFeature);
531
            } catch (DataException e) {
532
                throw new RemoveFromFeatureSelectionException(e);
425
    /**
426
     * @param feature
427
     * @return
428
     * @see #select(Feature)
429
     * @param undoable if the action must be undoable
430
     */
431
    public boolean select(Feature feature, boolean undoable) {
432
        // TODO: should we check if the feature is from the same FeatureStore??
433
        if (feature == null) {
434
            return false;
435
        }
533 436

  
437
        // LOGGER.debug("Selected feature: {}", feature);
438
        if (isReversed()) {
439
            removeFeatureTypeCount(feature.getType());
440
        } else {
441
            addFeatureTypeCount(feature.getType());
442
        }
443
        return select(feature.getReference(), undoable);
444
    }
445

  
446
    @Override
447
    public boolean select(FeatureSet features) throws DataException {
448
        return select(features, true);
449
    }
450

  
451
    /**
452
     * @param features
453
     * @return
454
     * @throws org.gvsig.fmap.dal.exception.DataException
455
     * @see #select(FeatureSet)
456
     * @param undoable if the action must be undoable
457
     */
458
    public boolean select(FeatureSet features, boolean undoable)
459
            throws DataException {
460
        boolean change = false;
461
        boolean inComplex = false;
462
        if (undoable && this.featureReferenceSelection.getFeatureStore().isEditing()
463
                && !this.featureReferenceSelection.getCommands().inComplex()) {
464

  
465
            this.featureReferenceSelection.getCommands().startComplex("_selectionSelectFeatureSet");
466
            inComplex = this.featureReferenceSelection.getCommands().inComplex();
467
        }
468

  
469
        disableNotifications();
470
        DisposableIterator iter = null;
471
        try {
472
            for (iter = features.fastIterator(); iter.hasNext();) {
473
                change |= select((Feature) iter.next(), undoable);
534 474
            }
535
		}
475
        } finally {
476
            DisposeUtils.disposeQuietly(iter);
477
        }
478
        enableNotifications();
479
        if (undoable && getFeatureStore().isEditing() && inComplex) {
480
            getCommands().endComplex();
481
        }
482
        if (change) {
483
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
484
        }
485
        return change;
486
    }
536 487

  
537
    	public class RemoveFromFeatureSelectionException extends DataRuntimeException {
488
    @Override
489
    public boolean deselect(Feature feature) {
490
        return deselect(feature, true);
491
    }
538 492

  
539
    	    /**
540
             *
541
             */
542
            private static final long serialVersionUID = 2636692469445838928L;
543
            private final static String MESSAGE_FORMAT = "Can't remove feature from reversed selection.";
544
    	    private final static String MESSAGE_KEY = "_RemoveFromFeatureSelectionException";
493
    /**
494
     * @param feature
495
     * @return
496
     * @see #deselect(Feature)
497
     * @param undoable if the action must be undoable
498
     */
499
    public boolean deselect(Feature feature, boolean undoable) {
500
        if (feature == null) {
501
            return false;
502
        }
545 503

  
546
    	    public RemoveFromFeatureSelectionException(Throwable cause) {
547
    	        super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
548
    	        //setValue("store", store);
549
    	    }
550
    	}
504
        LOG.debug("Deselected feature: {}", feature);
551 505

  
506
        if (isReversed()) {
507
            addFeatureTypeCount(feature.getType());
508
        } else {
509
            removeFeatureTypeCount(feature.getType());
510
        }
511
        return deselect(feature.getReference(), undoable);
512
    }
552 513

  
553
		private void positionInNextElement() {
554
			nextFeature = null;
555
			while (iterator.hasNext()) {
556
				nextFeature = (Feature) iterator.next();
557
				if (selectionData.contains(nextFeature.getReference())) {
558
					nextFeature = null;
559
				} else {
560
					break;
561
				}
562
			}
563
		}
514
    @Override
515
    public boolean deselect(FeatureSet features) throws DataException {
516
        return deselect(features, true);
517
    }
564 518

  
565
		public void dispose() {
566
			this.featureSet.dispose();
567
			this.iterator.dispose();
568
			this.selectionData = null;
569
			this.nextFeature = null;
570
		}
571
	}
519
    /**
520
     * @param features
521
     * @return
522
     * @throws org.gvsig.fmap.dal.exception.DataException
523
     * @see #deselect(FeatureSet)
524
     * @param undoable if the action must be undoable
525
     */
526
    public boolean deselect(FeatureSet features, boolean undoable)
527
            throws DataException {
528
        boolean change = false;
529
        if (undoable && getFeatureStore().isEditing()) {
530
            getCommands().startComplex("_selectionDeselectFeatureSet");
531
        }
532
        disableNotifications();
533
        DisposableIterator iter = null;
534
        try {
535
            for (iter = features.fastIterator(); iter.hasNext();) {
536
                change |= deselect((Feature) iter.next(), undoable);
537
            }
538
        } finally {
539
            DisposeUtils.disposeQuietly(iter);
540
        }
541
        enableNotifications();
542
        if (undoable && getFeatureStore().isEditing()) {
543
            getCommands().endComplex();
544
        }
545
        if (change) {
546
            notifyObservers(DataStoreNotification.SELECTION_CHANGE);
547
        }
548
        return change;
549
    }
572 550

  
573
	public void delete(Feature feature) throws DataException {
574
	    Iterator it = this.featureIterators.get(feature);
575
	    if( it!=null ) {
576
	        it.remove();
577
	        return;
578
	    }
579
	    feature.getStore().delete(feature);
580
	}
551
    @Override
552
    public boolean isSelected(Feature feature) {
553
        if (feature == null) {
554
            return false;
555
        }
581 556

  
582
	public void insert(EditableFeature feature) throws DataException {
583
        feature.getStore().insert(feature);
557
        // Use the selection data size as a small optimization for the most
558
        // common case, when nothing is selected and every feature is checked
559
        // while drawing or painting the table document.
560
        if (getData().isReversed()) {
561
            return getData().getSize() == 0
562
                    || !getData().contains(feature.getReference());
563
        } else {
564
            return getData().getSize() > 0
565
                    && getData().contains(feature.getReference());
566
        }
584 567
    }
585 568

  
586
	public void update(EditableFeature feature) throws DataException {
587
        feature.getStore().update(feature);
569
    @Override
570
    public FeatureType getDefaultFeatureType() {
571
        try {
572
            return getFeatureStore().getDefaultFeatureType();
573
        } catch (DataException ex) {
574
            LOG.error("Error getting the default feature type "
575
                    + "of the FeatureStore: " + getFeatureStore(), ex);
576
        }
577
        return null;
588 578
    }
589 579

  
590
	/*
591
	 * (non-Javadoc)
592
	 *
593
	 * @seeorg.gvsig.fmap.dal.feature.impl.DefaultFeatureReferenceSelection#
594
	 * loadFromState(org.gvsig.tools.persistence.PersistentState)
595
	 */
596
	public void loadFromState(PersistentState state)
597
			throws PersistenceException {
598
		super.loadFromState(state);
580
    @Override
581
    public List getFeatureTypes() {
582
        // Go through the map of FeatureTypes, and return only the ones that
583
        // have at least a Feature.
584
        List types = new ArrayList();
585
        for (java.util.Iterator iterator = featureTypeCounts.entrySet()
586
                .iterator(); iterator.hasNext();) {
587
            Map.Entry entry = (Entry) iterator.next();
588
            FeatureType type = (FeatureType) entry.getKey();
589
            Long count = (Long) entry.getValue();
599 590

  
600
	}
591
            if (count > 0) {
592
                types.add(type);
593
            }
594
        }
601 595

  
602
	public void accept(Visitor visitor) throws BaseException {
603
		accept(visitor, 0);
604
	}
596
        return types;
597
    }
605 598

  
606
	public final void accept(Visitor visitor, long firstValueIndex)
607
			throws BaseException {
608
		try {
609
			doAccept(visitor, firstValueIndex);
610
		} catch (VisitCanceledException ex) {
611
			// The visit has been cancelled by the visitor, so we finish here.
612
			LOG.debug(
613
					"The visit, beggining on position {}, has been cancelled "
614
							+ "by the visitor: {}", new Long(firstValueIndex),
615
					visitor);
616
		}
617
	}
599
    @Override
600
    public long getSize() throws DataException {
601
        return getSelectedCount();
602
    }
618 603

  
619
	private void doAccept(Visitor visitor, long firstValueIndex)
620
			throws BaseException {
621
		DisposableIterator iterator = fastIterator(firstValueIndex);
604
    /**
605
     * Returns the list of selected values, or the deselected ones if the
606
     * selection has been reversed.
607
     *
608
     * WARN: not very good performance implementation.
609
     */
610
    @Override
611
    public DisposableIterator iterator(long index) {
612
        return iterator(index, false);
613
    }
622 614

  
623
		if (iterator != null) {
624
			try {
625
				while (iterator.hasNext()) {
626
					Feature feature = (Feature) iterator.next();
627
					visitor.visit(feature);
628
				}
629
			} finally {
630
				iterator.dispose();
631
			}
632
		}
633
	}
615
    /**
616
     * Returns the list of selected values, or the deselected ones if the
617
     * selection has been reversed.
618
     *
619
     * WARN: not really a fast implementation.
620
     */
621
    @Override
622
    public DisposableIterator fastIterator(long index) {
623
        return iterator(index, true);
624
    }
634 625

  
635
	protected void doDispose() throws BaseException {
636
		super.doDispose();
637
		featureTypeCounts.clear();
638
	}
626
    protected void clearFeatureReferences() {
627
        this.featureReferenceSelection.clearFeatureReferences();
628
        featureTypeCounts.clear();
629
    }
639 630

  
640
	public static void registerPersistent() {
641
		PersistenceManager manager = ToolsLocator.getPersistenceManager();
642
		DynStruct definition = manager.addDefinition(
643
				DefaultFeatureSelection.class, "DefaultFeatureSelection",
644
				"DefaultFeatureSelection Persistent definition", null, null);
631
    /**
632
     * Creates an iterator for the Selection.
633
     */
634
    private DisposableIterator iterator(long index, boolean fastIterator) {
635
        if (isReversed()) {
636
            DisposableIterator iter = new ReversedFeatureIteratorFacade(
637
                    getData(), getFeatureStore(), fastIterator);
638
            for (long l = 0; l < index && iter.hasNext(); l++) {
639
                iter.next();
640
            }
641
            return iter;
645 642

  
646
		definition.extend(manager.getDefinition(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME));
647
		definition.addDynFieldMap("featureTypeCounts")
648
				.setClassOfItems(Long.class).setMandatory(false);
643
        } else {
644
            // TODO: maybe we could add a new referenceIterator(int index)
645
            // method that could be implemented in a more performant way
649 646

  
650
	}
647
            java.util.Iterator iter = getData().getSelected().iterator();
648
            for (long l = 0; l < index && iter.hasNext(); l++) {
649
                iter.next();
650
            }
651
            return new FeatureIteratorFacade(iter, getFeatureStore());
652
        }
653
    }
651 654

  
652
	public Object clone() throws CloneNotSupportedException {
653
		DefaultFeatureSelection clone = (DefaultFeatureSelection) super.clone();
654
		clone.featureTypeCounts = new HashMap(featureTypeCounts);
655
		return clone;
656
	}
655
    private Long removeFeatureTypeCount(FeatureType featureType) {
656
        Long count = (Long) featureTypeCounts.get(featureType);
657
        if (count == null) {
658
            count = new Long(-1);
659
        } else {
660
            count = count - 1;
661
        }
662
        featureTypeCounts.put(featureType, count);
663
        return count;
664
    }
657 665

  
658
    public DynObjectSet getDynObjectSet() {
659
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore());
666
    private Long addFeatureTypeCount(FeatureType featureType) {
667
        Long count = (Long) featureTypeCounts.get(featureType);
668
        if (count == null) {
669
            count = new Long(1);
670
        } else {
671
            count = count + 1;
672
        }
673
        featureTypeCounts.put(featureType, count);
674
        return count;
660 675
    }
661 676

  
662
    public DynObjectSet getDynObjectSet(boolean fast) {
663
        return new DynObjectSetFeatureSetFacade(this, getFeatureStore(), fast);
677
    @Override
678
    public void delete(Feature feature) throws DataException {
679
        Iterator it = this.featureIterators.get(feature);
680
        if (it != null) {
681
            it.remove();
682
            return;
683
        }
684
        feature.getStore().delete(feature);
664 685
    }
665 686

  
687
    @Override
688
    public void insert(EditableFeature feature) throws DataException {
689
        feature.getStore().insert(feature);
690
    }
691

  
692
    @Override
693
    public void update(EditableFeature feature) throws DataException {
694
        feature.getStore().update(feature);
695
    }
696

  
697
    /*
698
     * (non-Javadoc)
699
     *
700
     * @seeorg.gvsig.fmap.dal.feature.impl.DefaultFeatureReferenceSelection#
701
     * loadFromState(org.gvsig.tools.persistence.PersistentState)
702
     */
703
    @Override
704
    public void loadFromState(PersistentState state)
705
            throws PersistenceException {
706
        this.featureReferenceSelection.loadFromState(state);
707
    }
708

  
709
    protected void doDispose() throws BaseException {
710
        this.featureReferenceSelection.doDispose();
711
        featureTypeCounts.clear();
712
    }
713

  
714
    public static void registerPersistent() {
715
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
716
        DynStruct definition = manager.addDefinition(
717
                DefaultFeatureSelection.class, "DefaultFeatureSelection",
718
                "DefaultFeatureSelection Persistent definition", null, null);
719

  
720
        definition.extend(manager.getDefinition(DefaultFeatureReferenceSelection.DYNCLASS_PERSISTENT_NAME));
721
        definition.addDynFieldMap("featureTypeCounts")
722
                .setClassOfItems(Long.class).setMandatory(false);
723

  
724
    }
725

  
726
    @Override
727
    public Object clone() throws CloneNotSupportedException {
728
        DefaultFeatureSelection clone = (DefaultFeatureSelection) super.clone();
729
        clone.featureTypeCounts = new HashMap(featureTypeCounts);
730
        return clone;
731
    }
732

  
666 733
}

Also available in: Unified diff