source: josm/trunk/src/org/openstreetmap/josm/data/osm/DataSelectionListener.java@ 12189

Last change on this file since 12189 was 12113, checked in by michael2402, 7 years ago

See #13467: Preserve order of removed / added fields for selection events.

File size: 10.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import java.util.Collections;
5import java.util.HashSet;
6import java.util.LinkedHashSet;
7import java.util.Set;
8import java.util.stream.Collectors;
9import java.util.stream.Stream;
10
11import org.openstreetmap.josm.tools.CheckParameterUtil;
12
13/**
14 * This is a listener that listens to selection change events in the data set.
15 * @author Michael Zangl
16 * @since 12048
17 */
18@FunctionalInterface
19public interface DataSelectionListener {
20
21 /**
22 * Called whenever the selection is changed.
23 *
24 * You get notified about the new selection, the elements that were added and removed and the layer that triggered the event.
25 * @param event The selection change event.
26 * @see SelectionChangeEvent
27 */
28 void selectionChanged(SelectionChangeEvent event);
29
30 /**
31 * The event that is fired when the selection changed.
32 * @author Michael Zangl
33 * @since 12048
34 */
35 interface SelectionChangeEvent {
36 /**
37 * Gets the previous selection
38 * <p>
39 * This collection cannot be modified and will not change.
40 * @return The old selection
41 */
42 Set<OsmPrimitive> getOldSelection();
43
44 /**
45 * Gets the new selection. New elements are added to the end of the collection.
46 * <p>
47 * This collection cannot be modified and will not change.
48 * @return The new selection
49 */
50 Set<OsmPrimitive> getSelection();
51
52 /**
53 * Gets the primitives that have been removed from the selection.
54 * <p>
55 * Those are the primitives contained in {@link #getOldSelection()} but not in {@link #getSelection()}
56 * <p>
57 * This collection cannot be modified and will not change.
58 * @return The primitives that were removed
59 */
60 Set<OsmPrimitive> getRemoved();
61
62 /**
63 * Gets the primitives that have been added to the selection.
64 * <p>
65 * Those are the primitives contained in {@link #getSelection()} but not in {@link #getOldSelection()}
66 * <p>
67 * This collection cannot be modified and will not change.
68 * @return The primitives that were added
69 */
70 Set<OsmPrimitive> getAdded();
71
72 /**
73 * Gets the data set that triggered this selection event.
74 * @return The data set.
75 */
76 DataSet getSource();
77
78 /**
79 * Test if this event did not change anything.
80 * <p>
81 * This will return <code>false</code> for all events that are sent to listeners, so you don't need to test it.
82 * @return <code>true</code> if this did not change the selection.
83 */
84 default boolean isNop() {
85 return getAdded().isEmpty() && getRemoved().isEmpty();
86 }
87 }
88
89 /**
90 * The base class for selection events
91 * @author Michael Zangl
92 * @since 12048
93 */
94 abstract class AbstractSelectionEvent implements SelectionChangeEvent {
95 private final DataSet source;
96 private final Set<OsmPrimitive> old;
97
98 public AbstractSelectionEvent(DataSet source, Set<OsmPrimitive> old) {
99 CheckParameterUtil.ensureParameterNotNull(source, "source");
100 CheckParameterUtil.ensureParameterNotNull(old, "old");
101 this.source = source;
102 this.old = Collections.unmodifiableSet(old);
103 }
104
105 @Override
106 public Set<OsmPrimitive> getOldSelection() {
107 return old;
108 }
109
110 @Override
111 public DataSet getSource() {
112 return source;
113 }
114 }
115
116
117 /**
118 * The selection is replaced by a new selection
119 * @author Michael Zangl
120 * @since 12048
121 */
122 class SelectionReplaceEvent extends AbstractSelectionEvent {
123 private final Set<OsmPrimitive> current;
124 private Set<OsmPrimitive> removed;
125 private Set<OsmPrimitive> added;
126
127 /**
128 * Create a {@link SelectionReplaceEvent}
129 * @param source The source dataset
130 * @param old The old primitves that were previously selected. The caller needs to ensure that this set is not modifed.
131 * @param newSelection The primitives of the new selection.
132 */
133 public SelectionReplaceEvent(DataSet source, Set<OsmPrimitive> old, Stream<OsmPrimitive> newSelection) {
134 super(source, old);
135 this.current = newSelection.collect(Collectors.toCollection(LinkedHashSet::new));
136 }
137
138 @Override
139 public Set<OsmPrimitive> getSelection() {
140 return current;
141 }
142
143 @Override
144 public synchronized Set<OsmPrimitive> getRemoved() {
145 if (removed == null) {
146 removed = getOldSelection().stream()
147 .filter(p -> !current.contains(p))
148 .collect(Collectors.toCollection(LinkedHashSet::new));
149 }
150 return removed;
151 }
152
153 @Override
154 public synchronized Set<OsmPrimitive> getAdded() {
155 if (added == null) {
156 added = current.stream()
157 .filter(p -> !getOldSelection().contains(p)).collect(Collectors.toCollection(LinkedHashSet::new));
158 }
159 return added;
160 }
161 }
162
163 /**
164 * Primitives are added to the selection
165 * @author Michael Zangl
166 * @since 12048
167 */
168 class SelectionAddEvent extends AbstractSelectionEvent {
169 private final Set<OsmPrimitive> add;
170 private final Set<OsmPrimitive> current;
171
172 /**
173 * Create a {@link SelectionAddEvent}
174 * @param source The source dataset
175 * @param old The old primitves that were previously selected. The caller needs to ensure that this set is not modifed.
176 * @param toAdd The primitives to add.
177 */
178 public SelectionAddEvent(DataSet source, Set<OsmPrimitive> old, Stream<OsmPrimitive> toAdd) {
179 super(source, old);
180 this.add = toAdd
181 .filter(p -> !old.contains(p))
182 .collect(Collectors.toCollection(LinkedHashSet::new));
183 if (this.add.isEmpty()) {
184 this.current = this.getOldSelection();
185 } else {
186 this.current = new LinkedHashSet<>(old);
187 this.current.addAll(add);
188 }
189 }
190
191 @Override
192 public Set<OsmPrimitive> getSelection() {
193 return Collections.unmodifiableSet(current);
194 }
195
196 @Override
197 public Set<OsmPrimitive> getRemoved() {
198 return Collections.emptySet();
199 }
200
201 @Override
202 public Set<OsmPrimitive> getAdded() {
203 return Collections.unmodifiableSet(add);
204 }
205 }
206
207 /**
208 * Primitives are removed from the selection
209 * @author Michael Zangl
210 * @since 12048
211 */
212 class SelectionRemoveEvent extends AbstractSelectionEvent {
213 private final Set<OsmPrimitive> remove;
214 private final Set<OsmPrimitive> current;
215
216 /**
217 * Create a {@link SelectionRemoveEvent}
218 * @param source The source dataset
219 * @param old The old primitves that were previously selected. The caller needs to ensure that this set is not modifed.
220 * @param toRemove The primitives to remove.
221 */
222 public SelectionRemoveEvent(DataSet source, Set<OsmPrimitive> old, Stream<OsmPrimitive> toRemove) {
223 super(source, old);
224 this.remove = toRemove
225 .filter(old::contains)
226 .collect(Collectors.toCollection(LinkedHashSet::new));
227 if (this.remove.isEmpty()) {
228 this.current = this.getOldSelection();
229 } else {
230 HashSet<OsmPrimitive> currentSet = new LinkedHashSet<>(old);
231 currentSet.removeAll(remove);
232 current = Collections.unmodifiableSet(currentSet);
233 }
234 }
235
236 @Override
237 public Set<OsmPrimitive> getSelection() {
238 return Collections.unmodifiableSet(current);
239 }
240
241 @Override
242 public Set<OsmPrimitive> getRemoved() {
243 return Collections.unmodifiableSet(remove);
244 }
245
246 @Override
247 public Set<OsmPrimitive> getAdded() {
248 return Collections.emptySet();
249 }
250 }
251
252 /**
253 * Toggle the selected state of a primitive
254 * @author Michael Zangl
255 * @since 12048
256 */
257 class SelectionToggleEvent extends AbstractSelectionEvent {
258 private final Set<OsmPrimitive> current;
259 private final Set<OsmPrimitive> remove;
260 private final Set<OsmPrimitive> add;
261
262 /**
263 * Create a {@link SelectionToggleEvent}
264 * @param source The source dataset
265 * @param old The old primitves that were previously selected. The caller needs to ensure that this set is not modifed.
266 * @param toToggle The primitives to toggle.
267 */
268 public SelectionToggleEvent(DataSet source, Set<OsmPrimitive> old, Stream<OsmPrimitive> toToggle) {
269 super(source, old);
270 HashSet<OsmPrimitive> currentSet = new LinkedHashSet<>(old);
271 HashSet<OsmPrimitive> removeSet = new LinkedHashSet<>();
272 HashSet<OsmPrimitive> addSet = new LinkedHashSet<>();
273 toToggle.forEach(p -> {
274 if (currentSet.remove(p)) {
275 removeSet.add(p);
276 } else {
277 addSet.add(p);
278 currentSet.add(p);
279 }
280 });
281 this.current = Collections.unmodifiableSet(currentSet);
282 this.remove = Collections.unmodifiableSet(removeSet);
283 this.add = Collections.unmodifiableSet(addSet);
284 }
285
286 @Override
287 public Set<OsmPrimitive> getSelection() {
288 return Collections.unmodifiableSet(current);
289 }
290
291 @Override
292 public Set<OsmPrimitive> getRemoved() {
293 return Collections.unmodifiableSet(remove);
294 }
295
296 @Override
297 public Set<OsmPrimitive> getAdded() {
298 return Collections.unmodifiableSet(add);
299 }
300 }
301}
Note: See TracBrowser for help on using the repository browser.