| 1 | // License: GPL. For details, see LICENSE file. |
|---|
| 2 | package org.openstreetmap.josm.data.osm; |
|---|
| 3 | |
|---|
| 4 | import java.util.ArrayList; |
|---|
| 5 | import java.util.Collection; |
|---|
| 6 | import java.util.List; |
|---|
| 7 | |
|---|
| 8 | import org.openstreetmap.josm.actions.search.SearchCompiler; |
|---|
| 9 | import org.openstreetmap.josm.actions.search.SearchAction.SearchMode; |
|---|
| 10 | import org.openstreetmap.josm.actions.search.SearchCompiler.Match; |
|---|
| 11 | import org.openstreetmap.josm.actions.search.SearchCompiler.Not; |
|---|
| 12 | import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError; |
|---|
| 13 | |
|---|
| 14 | public class FilterMatcher { |
|---|
| 15 | |
|---|
| 16 | private static class FilterInfo { |
|---|
| 17 | final Match match; |
|---|
| 18 | final boolean isDelete; |
|---|
| 19 | final boolean isInverted; |
|---|
| 20 | |
|---|
| 21 | FilterInfo(Filter filter) throws ParseError { |
|---|
| 22 | if (filter.mode == SearchMode.remove || filter.mode == SearchMode.in_selection) { |
|---|
| 23 | isDelete = true; |
|---|
| 24 | } else { |
|---|
| 25 | isDelete = false; |
|---|
| 26 | } |
|---|
| 27 | |
|---|
| 28 | Match compiled = SearchCompiler.compile(filter.text, filter.caseSensitive, filter.regexSearch); |
|---|
| 29 | this.match = filter.inverted?new Not(compiled):compiled; |
|---|
| 30 | this.isInverted = filter.inverted; |
|---|
| 31 | } |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | private final List<FilterInfo> hiddenFilters = new ArrayList<FilterInfo>(); |
|---|
| 35 | private final List<FilterInfo> disabledFilters = new ArrayList<FilterInfo>(); |
|---|
| 36 | |
|---|
| 37 | public void update(Collection<Filter> filters) throws ParseError { |
|---|
| 38 | hiddenFilters.clear(); |
|---|
| 39 | disabledFilters.clear(); |
|---|
| 40 | |
|---|
| 41 | for (Filter filter: filters) { |
|---|
| 42 | |
|---|
| 43 | if (!filter.enable) { |
|---|
| 44 | continue; |
|---|
| 45 | } |
|---|
| 46 | |
|---|
| 47 | FilterInfo fi = new FilterInfo(filter); |
|---|
| 48 | if (fi.isDelete) { |
|---|
| 49 | if (filter.hiding) { |
|---|
| 50 | // Remove only hide flag |
|---|
| 51 | hiddenFilters.add(fi); |
|---|
| 52 | } else { |
|---|
| 53 | // Remove both flags |
|---|
| 54 | disabledFilters.add(fi); |
|---|
| 55 | hiddenFilters.add(fi); |
|---|
| 56 | } |
|---|
| 57 | } else { |
|---|
| 58 | if (filter.mode == SearchMode.replace) { |
|---|
| 59 | if (filter.hiding) { |
|---|
| 60 | hiddenFilters.clear(); |
|---|
| 61 | disabledFilters.clear(); |
|---|
| 62 | } |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | disabledFilters.add(fi); |
|---|
| 66 | if (filter.hiding) { |
|---|
| 67 | hiddenFilters.add(fi); |
|---|
| 68 | } |
|---|
| 69 | } |
|---|
| 70 | } |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | private boolean getState(OsmPrimitive primitive, boolean hidden) { |
|---|
| 74 | return hidden?primitive.isDisabledAndHidden():primitive.isDisabled(); |
|---|
| 75 | } |
|---|
| 76 | |
|---|
| 77 | private boolean allParentWaysFiltered(OsmPrimitive primitive, boolean hidden) { |
|---|
| 78 | List<OsmPrimitive> refs = primitive.getReferrers(); |
|---|
| 79 | boolean foundWay = false; |
|---|
| 80 | |
|---|
| 81 | for (OsmPrimitive p: refs) { |
|---|
| 82 | if (p instanceof Way) { |
|---|
| 83 | foundWay = true; |
|---|
| 84 | if (!getState(p, hidden)) |
|---|
| 85 | return false; |
|---|
| 86 | } |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | return foundWay; |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | private boolean oneParentWayNotFiltered(OsmPrimitive primitive, boolean hidden) { |
|---|
| 93 | List<OsmPrimitive> refs = primitive.getReferrers(); |
|---|
| 94 | for (OsmPrimitive p: refs) { |
|---|
| 95 | if (p instanceof Way && !getState(p, hidden)) |
|---|
| 96 | return true; |
|---|
| 97 | } |
|---|
| 98 | |
|---|
| 99 | return false; |
|---|
| 100 | } |
|---|
| 101 | |
|---|
| 102 | private boolean test(List<FilterInfo> filters, OsmPrimitive primitive, boolean hidden) { |
|---|
| 103 | |
|---|
| 104 | if (primitive.isIncomplete()) |
|---|
| 105 | return false; |
|---|
| 106 | |
|---|
| 107 | boolean selected = false; |
|---|
| 108 | boolean onlyInvertedFilters = true; |
|---|
| 109 | |
|---|
| 110 | for (FilterInfo fi: filters) { |
|---|
| 111 | if (fi.isDelete && selected && fi.match.match(primitive)) { |
|---|
| 112 | selected = false; |
|---|
| 113 | } else if (!fi.isDelete && (!selected || (onlyInvertedFilters && !fi.isInverted)) && fi.match.match(primitive)) { |
|---|
| 114 | selected = true; |
|---|
| 115 | onlyInvertedFilters = onlyInvertedFilters && fi.isInverted; |
|---|
| 116 | } |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | if (primitive instanceof Node) { |
|---|
| 120 | if (!selected) |
|---|
| 121 | return !primitive.isTagged() && allParentWaysFiltered(primitive, hidden); |
|---|
| 122 | if (onlyInvertedFilters) |
|---|
| 123 | return selected && !oneParentWayNotFiltered(primitive, hidden); |
|---|
| 124 | return true; |
|---|
| 125 | } else |
|---|
| 126 | return selected; |
|---|
| 127 | |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | public boolean isHidden(OsmPrimitive primitive) { |
|---|
| 131 | return test(hiddenFilters, primitive, true); |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | public boolean isDisabled(OsmPrimitive primitive) { |
|---|
| 135 | return test(disabledFilters, primitive, false); |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | } |
|---|