source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java@ 6579

Last change on this file since 6579 was 6579, checked in by simon04, 10 years ago

fix #4280 - Validator, crossing ways: do not warn if layer is missing for simple, unambiguous cases (one way is bridge/tunnel and the other one is not)

  • Property svn:eol-style set to native
File size: 11.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.mapcss;
3
4import static org.openstreetmap.josm.tools.Utils.equal;
5
6import java.text.MessageFormat;
7import java.util.EnumSet;
8import java.util.regex.Pattern;
9
10import org.openstreetmap.josm.data.osm.Node;
11import org.openstreetmap.josm.data.osm.OsmUtils;
12import org.openstreetmap.josm.data.osm.Relation;
13import org.openstreetmap.josm.data.osm.Tag;
14import org.openstreetmap.josm.data.osm.Way;
15import org.openstreetmap.josm.gui.mappaint.Cascade;
16import org.openstreetmap.josm.gui.mappaint.Environment;
17import org.openstreetmap.josm.tools.Predicates;
18import org.openstreetmap.josm.tools.Utils;
19
20abstract public class Condition {
21
22 abstract public boolean applies(Environment e);
23
24 public static Condition createKeyValueCondition(String k, String v, Op op, Context context, boolean considerValAsKey) {
25 switch (context) {
26 case PRIMITIVE:
27 return new KeyValueCondition(k, v, op, considerValAsKey);
28 case LINK:
29 if (considerValAsKey)
30 throw new MapCSSException("''considerValAsKey'' not supported in LINK context");
31 if ("role".equalsIgnoreCase(k))
32 return new RoleCondition(v, op);
33 else if ("index".equalsIgnoreCase(k))
34 return new IndexCondition(v, op);
35 else
36 throw new MapCSSException(
37 MessageFormat.format("Expected key ''role'' or ''index'' in link context. Got ''{0}''.", k));
38
39 default: throw new AssertionError();
40 }
41 }
42
43 public static Condition createKeyCondition(String k, boolean not, KeyMatchType matchType, Context context) {
44 switch (context) {
45 case PRIMITIVE:
46 return new KeyCondition(k, not, matchType);
47 case LINK:
48 if (matchType != null)
49 throw new MapCSSException("Question mark operator ''?'' and regexp match not supported in LINK context");
50 if (not)
51 return new RoleCondition(k, Op.NEQ);
52 else
53 return new RoleCondition(k, Op.EQ);
54
55 default: throw new AssertionError();
56 }
57 }
58
59 public static Condition createPseudoClassCondition(String id, boolean not, Context context) {
60 return new PseudoClassCondition(id, not);
61 }
62
63 public static Condition createClassCondition(String id, boolean not, Context context) {
64 return new ClassCondition(id, not);
65 }
66
67 public static Condition createExpressionCondition(Expression e, Context context) {
68 return new ExpressionCondition(e);
69 }
70
71 public static enum Op {
72 EQ, NEQ, GREATER_OR_EQUAL, GREATER, LESS_OR_EQUAL, LESS,
73 REGEX, NREGEX, ONE_OF, BEGINS_WITH, ENDS_WITH, CONTAINS;
74
75 public boolean eval(String testString, String prototypeString) {
76 if (testString == null && this != NEQ)
77 return false;
78 switch (this) {
79 case EQ:
80 return equal(testString, prototypeString);
81 case NEQ:
82 return !equal(testString, prototypeString);
83 case REGEX:
84 case NREGEX:
85 final boolean contains = Pattern.compile(prototypeString).matcher(testString).find();
86 return REGEX.equals(this) ? contains : !contains;
87 case ONE_OF:
88 String[] parts = testString.split(";");
89 for (String part : parts) {
90 if (equal(prototypeString, part.trim()))
91 return true;
92 }
93 return false;
94 case BEGINS_WITH:
95 return testString.startsWith(prototypeString);
96 case ENDS_WITH:
97 return testString.endsWith(prototypeString);
98 case CONTAINS:
99 return testString.contains(prototypeString);
100 }
101
102 float test_float;
103 try {
104 test_float = Float.parseFloat(testString);
105 } catch (NumberFormatException e) {
106 return false;
107 }
108 float prototype_float = Float.parseFloat(prototypeString);
109
110 switch (this) {
111 case GREATER_OR_EQUAL:
112 return test_float >= prototype_float;
113 case GREATER:
114 return test_float > prototype_float;
115 case LESS_OR_EQUAL:
116 return test_float <= prototype_float;
117 case LESS:
118 return test_float < prototype_float;
119 default:
120 throw new AssertionError();
121 }
122 }
123 }
124
125 /**
126 * context, where the condition applies
127 */
128 public static enum Context {
129 /**
130 * normal primitive selector, e.g. way[highway=residential]
131 */
132 PRIMITIVE,
133
134 /**
135 * link between primitives, e.g. relation >[role=outer] way
136 */
137 LINK
138 }
139
140 public final static EnumSet<Op> COMPARISON_OPERATERS =
141 EnumSet.of(Op.GREATER_OR_EQUAL, Op.GREATER, Op.LESS_OR_EQUAL, Op.LESS);
142
143 /**
144 * <p>Represents a key/value condition which is either applied to a primitive.</p>
145 *
146 */
147 public static class KeyValueCondition extends Condition {
148
149 public final String k;
150 public final String v;
151 public final Op op;
152 public boolean considerValAsKey;
153
154 /**
155 * <p>Creates a key/value-condition.</p>
156 *
157 * @param k the key
158 * @param v the value
159 * @param op the operation
160 * @param considerValAsKey whether to consider {@code v} as another key and compare the values of key {@code k} and key {@code v}.
161 */
162 public KeyValueCondition(String k, String v, Op op, boolean considerValAsKey) {
163 this.k = k;
164 this.v = v;
165 this.op = op;
166 this.considerValAsKey = considerValAsKey;
167 }
168
169 @Override
170 public boolean applies(Environment env) {
171 return op.eval(env.osm.get(k), considerValAsKey ? env.osm.get(v) : v);
172 }
173
174 public Tag asTag() {
175 return new Tag(k, v);
176 }
177
178 @Override
179 public String toString() {
180 return "[" + k + "'" + op + "'" + v + "]";
181 }
182 }
183
184 public static class RoleCondition extends Condition {
185 public final String role;
186 public final Op op;
187
188 public RoleCondition(String role, Op op) {
189 this.role = role;
190 this.op = op;
191 }
192
193 @Override
194 public boolean applies(Environment env) {
195 String testRole = env.getRole();
196 if (testRole == null) return false;
197 return op.eval(testRole, role);
198 }
199 }
200
201 public static class IndexCondition extends Condition {
202 public final String index;
203 public final Op op;
204
205 public IndexCondition(String index, Op op) {
206 this.index = index;
207 this.op = op;
208 }
209
210 @Override
211 public boolean applies(Environment env) {
212 if (env.index == null) return false;
213 return op.eval(Integer.toString(env.index + 1), index);
214 }
215 }
216
217 public static enum KeyMatchType {
218 EQ, TRUE, FALSE, REGEX
219 }
220
221 /**
222 * <p>KeyCondition represent one of the following conditions in either the link or the
223 * primitive context:</p>
224 * <pre>
225 * ["a label"] PRIMITIVE: the primitive has a tag "a label"
226 * LINK: the parent is a relation and it has at least one member with the role
227 * "a label" referring to the child
228 *
229 * [!"a label"] PRIMITIVE: the primitive doesn't have a tag "a label"
230 * LINK: the parent is a relation but doesn't have a member with the role
231 * "a label" referring to the child
232 *
233 * ["a label"?] PRIMITIVE: the primitive has a tag "a label" whose value evaluates to a true-value
234 * LINK: not supported
235 *
236 * ["a label"?!] PRIMITIVE: the primitive has a tag "a label" whose value evaluates to a false-value
237 * LINK: not supported
238 * </pre>
239 */
240 public static class KeyCondition extends Condition {
241
242 public final String label;
243 public final boolean negateResult;
244 public final KeyMatchType matchType;
245
246 public KeyCondition(String label, boolean negateResult, KeyMatchType matchType){
247 this.label = label;
248 this.negateResult = negateResult;
249 this.matchType = matchType;
250 }
251
252 @Override
253 public boolean applies(Environment e) {
254 switch(e.getContext()) {
255 case PRIMITIVE:
256 if (KeyMatchType.TRUE.equals(matchType))
257 return e.osm.isKeyTrue(label) ^ negateResult;
258 else if (KeyMatchType.FALSE.equals(matchType))
259 return e.osm.isKeyFalse(label) ^ negateResult;
260 else if (KeyMatchType.REGEX.equals(matchType))
261 return Utils.exists(e.osm.keySet(), Predicates.stringContainsPattern(Pattern.compile(label))) ^ negateResult;
262 else
263 return e.osm.hasKey(label) ^ negateResult;
264 case LINK:
265 Utils.ensure(false, "Illegal state: KeyCondition not supported in LINK context");
266 return false;
267 default: throw new AssertionError();
268 }
269 }
270
271 public Tag asTag() {
272 return new Tag(label);
273 }
274
275 @Override
276 public String toString() {
277 return "[" + (negateResult ? "!" : "") + label + "]";
278 }
279 }
280
281 public static class ClassCondition extends Condition {
282
283 public final String id;
284 public final boolean not;
285
286 public ClassCondition(String id, boolean not) {
287 this.id = id;
288 this.not = not;
289 }
290
291 @Override
292 public boolean applies(Environment env) {
293 return not ^ env.mc.getCascade(env.layer).containsKey(id);
294 }
295
296 @Override
297 public String toString() {
298 return (not ? "!" : "") + "." + id;
299 }
300 }
301
302 public static class PseudoClassCondition extends Condition {
303
304 public final String id;
305 public final boolean not;
306
307 public PseudoClassCondition(String id, boolean not) {
308 this.id = id;
309 this.not = not;
310 }
311
312 @Override
313 public boolean applies(Environment e) {
314 return not ^ appliesImpl(e);
315 }
316
317 public boolean appliesImpl(Environment e) {
318 if (equal(id, "closed")) {
319 if (e.osm instanceof Way && ((Way) e.osm).isClosed())
320 return true;
321 if (e.osm instanceof Relation && ((Relation) e.osm).isMultipolygon())
322 return true;
323 return false;
324 } else if (equal(id, "modified"))
325 return e.osm.isModified() || e.osm.isNewOrUndeleted();
326 else if (equal(id, "new"))
327 return e.osm.isNew();
328 else if (equal(id, "connection") && (e.osm instanceof Node))
329 return ((Node) e.osm).isConnectionNode();
330 else if (equal(id, "tagged"))
331 return e.osm.isTagged();
332 return true;
333 }
334
335 @Override
336 public String toString() {
337 return ":" + (not ? "!" : "") + id;
338 }
339 }
340
341 public static class ExpressionCondition extends Condition {
342
343 private Expression e;
344
345 public ExpressionCondition(Expression e) {
346 this.e = e;
347 }
348
349 @Override
350 public boolean applies(Environment env) {
351 Boolean b = Cascade.convertTo(e.evaluate(env), Boolean.class);
352 return b != null && b;
353 }
354
355 @Override
356 public String toString() {
357 return "[" + e + "]";
358 }
359 }
360}
Note: See TracBrowser for help on using the repository browser.