source: josm/trunk/src/org/openstreetmap/josm/data/osm/FilterModel.java@ 12494

Last change on this file since 12494 was 12414, checked in by Don-vip, 7 years ago

fix more SonarQube issues

File size: 13.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Graphics2D;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.HashSet;
11import java.util.LinkedList;
12import java.util.List;
13import java.util.Set;
14import java.util.Stack;
15
16import javax.swing.JOptionPane;
17
18import org.openstreetmap.josm.Main;
19import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
20import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry;
21import org.openstreetmap.josm.gui.layer.OsmDataLayer;
22import org.openstreetmap.josm.gui.widgets.OSDLabel;
23import org.openstreetmap.josm.tools.Utils;
24
25/**
26 * The model that is used both for auto and manual filters.
27 * @since 12400
28 */
29public class FilterModel {
30
31 /**
32 * number of primitives that are disabled but not hidden
33 */
34 private int disabledCount;
35 /**
36 * number of primitives that are disabled and hidden
37 */
38 private int disabledAndHiddenCount;
39 /**
40 * true, if the filter state (normal / disabled / hidden) of any primitive has changed in the process
41 */
42 private boolean changed;
43
44 private final List<Filter> filters = new LinkedList<>();
45 private final FilterMatcher filterMatcher = new FilterMatcher();
46
47 private void updateFilterMatcher() {
48 filterMatcher.reset();
49 for (Filter filter : filters) {
50 try {
51 filterMatcher.add(filter);
52 } catch (ParseError e) {
53 Main.error(e);
54 JOptionPane.showMessageDialog(
55 Main.parent,
56 tr("<html>Error in filter <code>{0}</code>:<br>{1}",
57 Utils.escapeReservedCharactersHTML(Utils.shortenString(filter.text, 80)),
58 Utils.escapeReservedCharactersHTML(e.getMessage())),
59 tr("Error in filter"),
60 JOptionPane.ERROR_MESSAGE);
61 filter.enable = false;
62 }
63 }
64 }
65
66 /**
67 * Initializes the model from preferences.
68 * @param prefEntry preference key
69 */
70 public void loadPrefs(String prefEntry) {
71 List<FilterPreferenceEntry> entries = Main.pref.getListOfStructs(prefEntry, null, FilterPreferenceEntry.class);
72 if (entries != null) {
73 for (FilterPreferenceEntry e : entries) {
74 filters.add(new Filter(e));
75 }
76 updateFilterMatcher();
77 }
78 }
79
80 /**
81 * Saves the model to preferences.
82 * @param prefEntry preferences key
83 */
84 public void savePrefs(String prefEntry) {
85 Collection<FilterPreferenceEntry> entries = new ArrayList<>();
86 for (Filter flt : filters) {
87 entries.add(flt.getPreferenceEntry());
88 }
89 Main.pref.putListOfStructs(prefEntry, entries, FilterPreferenceEntry.class);
90 }
91
92 /**
93 * Runs the filters on the current edit data set.
94 */
95 public void executeFilters() {
96 DataSet ds = Main.getLayerManager().getEditDataSet();
97 changed = false;
98 if (ds == null) {
99 disabledAndHiddenCount = 0;
100 disabledCount = 0;
101 changed = true;
102 } else {
103 final Collection<OsmPrimitive> deselect = new HashSet<>();
104
105 ds.beginUpdate();
106 try {
107
108 final Collection<OsmPrimitive> all = ds.allNonDeletedCompletePrimitives();
109
110 changed = FilterWorker.executeFilters(all, filterMatcher);
111
112 disabledCount = 0;
113 disabledAndHiddenCount = 0;
114 // collect disabled and selected the primitives
115 for (OsmPrimitive osm : all) {
116 if (osm.isDisabled()) {
117 disabledCount++;
118 if (osm.isSelected()) {
119 deselect.add(osm);
120 }
121 if (osm.isDisabledAndHidden()) {
122 disabledAndHiddenCount++;
123 }
124 }
125 }
126 disabledCount -= disabledAndHiddenCount;
127 } finally {
128 ds.endUpdate();
129 }
130
131 if (!deselect.isEmpty()) {
132 ds.clearSelection(deselect);
133 }
134 }
135 if (changed) {
136 updateMap();
137 }
138 }
139
140 /**
141 * Runs the filter on a list of primitives that are part of the edit data set.
142 * @param primitives The primitives
143 */
144 public void executeFilters(Collection<? extends OsmPrimitive> primitives) {
145 DataSet ds = Main.getLayerManager().getEditDataSet();
146 if (ds == null)
147 return;
148
149 changed = false;
150 List<OsmPrimitive> deselect = new ArrayList<>();
151
152 ds.beginUpdate();
153 try {
154 for (int i = 0; i < 2; i++) {
155 for (OsmPrimitive primitive: primitives) {
156
157 if (i == 0 && primitive instanceof Node) {
158 continue;
159 }
160
161 if (i == 1 && !(primitive instanceof Node)) {
162 continue;
163 }
164
165 if (primitive.isDisabled()) {
166 disabledCount--;
167 }
168 if (primitive.isDisabledAndHidden()) {
169 disabledAndHiddenCount--;
170 }
171 changed |= FilterWorker.executeFilters(primitive, filterMatcher);
172 if (primitive.isDisabled()) {
173 disabledCount++;
174 }
175 if (primitive.isDisabledAndHidden()) {
176 disabledAndHiddenCount++;
177 }
178
179 if (primitive.isSelected() && primitive.isDisabled()) {
180 deselect.add(primitive);
181 }
182 }
183 }
184 } finally {
185 ds.endUpdate();
186 }
187
188 if (!deselect.isEmpty()) {
189 ds.clearSelection(deselect);
190 }
191 if (changed) {
192 updateMap();
193 }
194 }
195
196 private static void updateMap() {
197 OsmDataLayer editLayer = Main.getLayerManager().getEditLayer();
198 if (editLayer != null) {
199 editLayer.invalidate();
200 }
201 }
202
203 /**
204 * Clears all filtered flags from all primitives in the dataset
205 */
206 public void clearFilterFlags() {
207 DataSet ds = Main.getLayerManager().getEditDataSet();
208 if (ds != null) {
209 FilterWorker.clearFilterFlags(ds.allPrimitives());
210 }
211 disabledCount = 0;
212 disabledAndHiddenCount = 0;
213 }
214
215 /**
216 * Removes all filters from this model.
217 */
218 public void clearFilters() {
219 filters.clear();
220 updateFilterMatcher();
221 }
222
223 /**
224 * Adds a new filter to the filter list.
225 * @param filter The new filter
226 * @return true (as specified by {@link Collection#add})
227 */
228 public boolean addFilter(Filter filter) {
229 filters.add(filter);
230 updateFilterMatcher();
231 return true;
232 }
233
234 /**
235 * Moves down the filter in the given row.
236 * @param rowIndex The filter row
237 * @return true if the filter has been moved down
238 */
239 public boolean moveDownFilter(int rowIndex) {
240 if (rowIndex >= filters.size() - 1)
241 return false;
242 filters.add(rowIndex + 1, filters.remove(rowIndex));
243 updateFilterMatcher();
244 return true;
245 }
246
247 /**
248 * Moves up the filter in the given row
249 * @param rowIndex The filter row
250 * @return true if the filter has been moved up
251 */
252 public boolean moveUpFilter(int rowIndex) {
253 if (rowIndex == 0)
254 return false;
255 filters.add(rowIndex - 1, filters.remove(rowIndex));
256 updateFilterMatcher();
257 return true;
258 }
259
260 /**
261 * Removes the filter that is displayed in the given row
262 * @param rowIndex The index of the filter to remove
263 * @return the filter previously at the specified position
264 */
265 public Filter removeFilter(int rowIndex) {
266 Filter result = filters.remove(rowIndex);
267 updateFilterMatcher();
268 return result;
269 }
270
271 /**
272 * Sets/replaces the filter for a given row.
273 * @param rowIndex The row index
274 * @param filter The filter that should be placed in that row
275 * @return the filter previously at the specified position
276 */
277 public Filter setFilter(int rowIndex, Filter filter) {
278 Filter result = filters.set(rowIndex, filter);
279 updateFilterMatcher();
280 return result;
281 }
282
283 /**
284 * Gets the filter by row index
285 * @param rowIndex The row index
286 * @return The filter in that row
287 */
288 public Filter getFilter(int rowIndex) {
289 return filters.get(rowIndex);
290 }
291
292 /**
293 * Draws a text on the map display that indicates that filters are active.
294 * @param g The graphics to draw that text on.
295 * @param lblOSD On Screen Display label
296 * @param header The title to display at the beginning of OSD
297 * @param footer The message to display at the bottom of OSD. Must end by {@code </html>}
298 */
299 public void drawOSDText(Graphics2D g, OSDLabel lblOSD, String header, String footer) {
300 if (disabledCount == 0 && disabledAndHiddenCount == 0)
301 return;
302
303 String message = "<html>" + header;
304
305 if (disabledAndHiddenCount != 0) {
306 /* for correct i18n of plural forms - see #9110 */
307 message += trn("<p><b>{0}</b> object hidden", "<p><b>{0}</b> objects hidden", disabledAndHiddenCount, disabledAndHiddenCount);
308 }
309
310 if (disabledAndHiddenCount != 0 && disabledCount != 0) {
311 message += "<br>";
312 }
313
314 if (disabledCount != 0) {
315 /* for correct i18n of plural forms - see #9110 */
316 message += trn("<b>{0}</b> object disabled", "<b>{0}</b> objects disabled", disabledCount, disabledCount);
317 }
318
319 message += footer;
320
321 lblOSD.setText(message);
322 lblOSD.setSize(lblOSD.getPreferredSize());
323
324 int dx = Main.map.mapView.getWidth() - lblOSD.getPreferredSize().width - 15;
325 int dy = 15;
326 g.translate(dx, dy);
327 lblOSD.paintComponent(g);
328 g.translate(-dx, -dy);
329 }
330
331 /**
332 * Returns the list of filters.
333 * @return the list of filters
334 */
335 public List<Filter> getFilters() {
336 return new ArrayList<>(filters);
337 }
338
339 /**
340 * Returns the number of filters.
341 * @return the number of filters
342 */
343 public int getFiltersCount() {
344 return filters.size();
345 }
346
347 /**
348 * Returns the number of primitives that are disabled but not hidden.
349 * @return the number of primitives that are disabled but not hidden
350 */
351 public int getDisabledCount() {
352 return disabledCount;
353 }
354
355 /**
356 * Returns the number of primitives that are disabled and hidden.
357 * @return the number of primitives that are disabled and hidden
358 */
359 public int getDisabledAndHiddenCount() {
360 return disabledAndHiddenCount;
361 }
362
363 /**
364 * Determines if the filter state (normal / disabled / hidden) of any primitive has changed in the process.
365 * @return true, if the filter state (normal / disabled / hidden) of any primitive has changed in the process
366 */
367 public boolean isChanged() {
368 return changed;
369 }
370
371 /**
372 * Returns the list of primitives whose filtering can be affected by change in primitive
373 * @param primitives list of primitives to check
374 * @return List of primitives whose filtering can be affected by change in source primitives
375 */
376 public static Collection<OsmPrimitive> getAffectedPrimitives(Collection<? extends OsmPrimitive> primitives) {
377 // Filters can use nested parent/child expression so complete tree is necessary
378 Set<OsmPrimitive> result = new HashSet<>();
379 Stack<OsmPrimitive> stack = new Stack<>();
380 stack.addAll(primitives);
381
382 while (!stack.isEmpty()) {
383 OsmPrimitive p = stack.pop();
384
385 if (result.contains(p)) {
386 continue;
387 }
388
389 result.add(p);
390
391 if (p instanceof Way) {
392 for (OsmPrimitive n: ((Way) p).getNodes()) {
393 stack.push(n);
394 }
395 } else if (p instanceof Relation) {
396 for (RelationMember rm: ((Relation) p).getMembers()) {
397 stack.push(rm.getMember());
398 }
399 }
400
401 for (OsmPrimitive ref: p.getReferrers()) {
402 stack.push(ref);
403 }
404 }
405
406 return result;
407 }
408}
Note: See TracBrowser for help on using the repository browser.