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

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

fix some Sonar issues

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