source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java@ 6248

Last change on this file since 6248 was 6248, checked in by Don-vip, 11 years ago

Rework console output:

  • new log level "error"
  • Replace nearly all calls to system.out and system.err to Main.(error|warn|info|debug)
  • Remove some unnecessary debug output
  • Some messages are modified (removal of "Info", "Warning", "Error" from the message itself -> notable i18n impact but limited to console error messages not seen by the majority of users, so that's ok)
  • Property svn:eol-style set to native
File size: 11.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.mapcss;
3
4import java.util.List;
5import java.util.regex.PatternSyntaxException;
6
7import org.openstreetmap.josm.Main;
8import org.openstreetmap.josm.data.osm.Node;
9import org.openstreetmap.josm.data.osm.OsmPrimitive;
10import org.openstreetmap.josm.data.osm.Relation;
11import org.openstreetmap.josm.data.osm.RelationMember;
12import org.openstreetmap.josm.data.osm.Way;
13import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
14import org.openstreetmap.josm.gui.mappaint.Environment;
15import org.openstreetmap.josm.gui.mappaint.Range;
16import org.openstreetmap.josm.tools.Pair;
17import org.openstreetmap.josm.tools.Utils;
18
19public interface Selector {
20
21 /**
22 * Apply the selector to the primitive and check if it matches.
23 *
24 * @param env the Environment. env.mc and env.layer are read-only when matching a selector.
25 * env.source is not needed. This method will set the matchingReferrers field of env as
26 * a side effect! Make sure to clear it before invoking this method.
27 * @return true, if the selector applies
28 */
29 public boolean matches(Environment env);
30
31 public String getSubpart();
32
33 public Range getRange();
34
35 /**
36 * <p>Represents a child selector or a parent selector.</p>
37 *
38 * <p>In addition to the standard CSS notation for child selectors, JOSM also supports
39 * an "inverse" notation:</p>
40 * <pre>
41 * selector_a > selector_b { ... } // the standard notation (child selector)
42 * relation[type=route] > way { ... } // example (all ways of a route)
43 *
44 * selector_a < selector_b { ... } // the inverse notation (parent selector)
45 * node[traffic_calming] < way { ... } // example (way that has a traffic calming node)
46 * </pre>
47 *
48 */
49 public static class ChildOrParentSelector implements Selector {
50 private final Selector left;
51 private final LinkSelector link;
52 private final Selector right;
53 /** true, if this represents a parent selector (otherwise it is a child selector)
54 */
55 private final boolean parentSelector;
56
57 /**
58 *
59 * @param a the first selector
60 * @param b the second selector
61 * @param parentSelector if true, this is a parent selector; otherwise a child selector
62 */
63 public ChildOrParentSelector(Selector a, LinkSelector link, Selector b, boolean parentSelector) {
64 this.left = a;
65 this.link = link;
66 this.right = b;
67 this.parentSelector = parentSelector;
68 }
69
70 /**
71 * <p>Finds the first referrer matching {@link #left}</p>
72 *
73 * <p>The visitor works on an environment and it saves the matching
74 * referrer in {@code e.parent} and its relative position in the
75 * list referrers "child list" in {@code e.index}.</p>
76 *
77 * <p>If after execution {@code e.parent} is null, no matching
78 * referrer was found.</p>
79 *
80 */
81 private class MatchingReferrerFinder extends AbstractVisitor{
82 private Environment e;
83
84 /**
85 * Constructor
86 * @param e the environment against which we match
87 */
88 public MatchingReferrerFinder(Environment e){
89 this.e = e;
90 }
91
92 @Override
93 public void visit(Node n) {
94 // node should never be a referrer
95 throw new AssertionError();
96 }
97
98 @Override
99 public void visit(Way w) {
100 /*
101 * If e.parent is already set to the first matching referrer. We skip any following
102 * referrer injected into the visitor.
103 */
104 if (e.parent != null) return;
105
106 if (!left.matches(e.withPrimitive(w)))
107 return;
108 for (int i=0; i<w.getNodesCount(); i++) {
109 Node n = w.getNode(i);
110 if (n.equals(e.osm)) {
111 if (link.matches(e.withParentAndIndexAndLinkContext(w, i))) {
112 e.parent = w;
113 e.index = i;
114 return;
115 }
116 }
117 }
118 }
119
120 @Override
121 public void visit(Relation r) {
122 /*
123 * If e.parent is already set to the first matching referrer. We skip any following
124 * referrer injected into the visitor.
125 */
126 if (e.parent != null) return;
127
128 if (!left.matches(e.withPrimitive(r)))
129 return;
130 for (int i=0; i < r.getMembersCount(); i++) {
131 RelationMember m = r.getMember(i);
132 if (m.getMember().equals(e.osm)) {
133 if (link.matches(e.withParentAndIndexAndLinkContext(r, i))) {
134 e.parent = r;
135 e.index = i;
136 return;
137 }
138 }
139 }
140 }
141 }
142
143 @Override
144 public boolean matches(Environment e) {
145 if (!right.matches(e))
146 return false;
147
148 if (!parentSelector) {
149 MatchingReferrerFinder collector = new MatchingReferrerFinder(e);
150 e.osm.visitReferrers(collector);
151 if (e.parent != null)
152 return true;
153 } else {
154 if (e.osm instanceof Way) {
155 List<Node> wayNodes = ((Way) e.osm).getNodes();
156 for (int i=0; i<wayNodes.size(); i++) {
157 Node n = wayNodes.get(i);
158 if (left.matches(e.withPrimitive(n))) {
159 if (link.matches(e.withChildAndIndexAndLinkContext(n, i))) {
160 e.child = n;
161 e.index = i;
162 return true;
163 }
164 }
165 }
166 }
167 else if (e.osm instanceof Relation) {
168 List<RelationMember> members = ((Relation) e.osm).getMembers();
169 for (int i=0; i<members.size(); i++) {
170 OsmPrimitive member = members.get(i).getMember();
171 if (left.matches(e.withPrimitive(member))) {
172 if (link.matches(e.withChildAndIndexAndLinkContext(member, i))) {
173 e.child = member;
174 e.index = i;
175 return true;
176 }
177 }
178 }
179 }
180 }
181 return false;
182 }
183
184 @Override
185 public String getSubpart() {
186 return right.getSubpart();
187 }
188
189 @Override
190 public Range getRange() {
191 return right.getRange();
192 }
193
194 @Override
195 public String toString() {
196 return left +" "+ (parentSelector? "<" : ">")+link+" " +right;
197 }
198 }
199
200 /**
201 * Super class of {@link GeneralSelector} and {@link LinkSelector}
202 * @since 5841
203 */
204 public static abstract class AbstractSelector implements Selector {
205
206 protected final List<Condition> conds;
207
208 protected AbstractSelector(List<Condition> conditions) {
209 if (conditions == null || conditions.isEmpty()) {
210 this.conds = null;
211 } else {
212 this.conds = conditions;
213 }
214 }
215
216 /**
217 * Determines if all conditions match the given environment.
218 * @param env The environment to check
219 * @return {@code true} if all conditions apply, false otherwise.
220 */
221 public final boolean matchesConditions(Environment env) {
222 if (conds == null) return true;
223 for (Condition c : conds) {
224 try {
225 if (!c.applies(env)) return false;
226 } catch (PatternSyntaxException e) {
227 Main.error("PatternSyntaxException while applying condition" + c +": "+e.getMessage());
228 return false;
229 }
230 }
231 return true;
232 }
233 }
234
235 public static class LinkSelector extends AbstractSelector {
236
237 public LinkSelector(List<Condition> conditions) {
238 super(conditions);
239 }
240
241 @Override
242 public boolean matches(Environment env) {
243 Utils.ensure(env.isLinkContext(), "Requires LINK context in environment, got ''{0}''", env.getContext());
244 return matchesConditions(env);
245 }
246
247 @Override
248 public String getSubpart() {
249 throw new UnsupportedOperationException("Not supported yet.");
250 }
251
252 @Override
253 public Range getRange() {
254 throw new UnsupportedOperationException("Not supported yet.");
255 }
256
257 @Override
258 public String toString() {
259 return "LinkSelector{" + "conditions=" + conds + '}';
260 }
261 }
262
263 public static class GeneralSelector extends AbstractSelector {
264 private String base;
265 public Range range;
266 private String subpart;
267
268 public GeneralSelector(String base, Pair<Integer, Integer> zoom, List<Condition> conds, String subpart) {
269 super(conds);
270 this.base = base;
271 if (zoom != null) {
272 int a = zoom.a == null ? 0 : zoom.a;
273 int b = zoom.b == null ? Integer.MAX_VALUE : zoom.b;
274 if (a <= b) {
275 range = fromLevel(a, b);
276 }
277 }
278 if (range == null) {
279 range = new Range();
280 }
281 this.subpart = subpart;
282 }
283
284 @Override
285 public String getSubpart() {
286 return subpart;
287 }
288 @Override
289 public Range getRange() {
290 return range;
291 }
292
293 public boolean matchesBase(Environment e){
294 if (e.osm instanceof Node) {
295 return base.equals("node") || base.equals("*");
296 } else if (e.osm instanceof Way) {
297 return base.equals("way") || base.equals("area") || base.equals("*");
298 } else if (e.osm instanceof Relation) {
299 if (base.equals("area")) {
300 return ((Relation) e.osm).isMultipolygon();
301 } else if (base.equals("relation")) {
302 return true;
303 } else if (base.equals("canvas")) {
304 return e.osm.get("#canvas") != null;
305 }
306 }
307 return false;
308 }
309
310 @Override
311 public boolean matches(Environment e) {
312 if (!matchesBase(e)) return false;
313 return matchesConditions(e);
314 }
315
316 public String getBase() {
317 return base;
318 }
319
320 public static Range fromLevel(int a, int b) {
321 if (a > b)
322 throw new AssertionError();
323 double lower = 0;
324 double upper = Double.POSITIVE_INFINITY;
325 if (b != Integer.MAX_VALUE) {
326 lower = level2scale(b + 1);
327 }
328 if (a != 0) {
329 upper = level2scale(a);
330 }
331 return new Range(lower, upper);
332 }
333
334 final static double R = 6378135;
335
336 public static double level2scale(int lvl) {
337 if (lvl < 0)
338 throw new IllegalArgumentException();
339 // preliminary formula - map such that mapnik imagery tiles of the same
340 // or similar level are displayed at the given scale
341 return 2.0 * Math.PI * R / Math.pow(2.0, lvl) / 2.56;
342 }
343
344 @Override
345 public String toString() {
346 return base + (range == null ? "" : range) + Utils.join("", conds) + (subpart != null ? ("::" + subpart) : "");
347 }
348 }
349}
Note: See TracBrowser for help on using the repository browser.