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

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

see #9414 - convert some tagchecker tests to MapCSS, extend MapCSS by test for false values

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