source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java

Last change on this file was 18972, checked in by taylor.smock, 3 months ago

See #23465: Add additional javadoc comments

This also fixes some sonarlint issues

  • Property svn:eol-style set to native
File size: 27.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.mapcss;
3
4import java.awt.Color;
5import java.lang.annotation.ElementType;
6import java.lang.annotation.Retention;
7import java.lang.annotation.RetentionPolicy;
8import java.lang.annotation.Target;
9import java.lang.reflect.Array;
10import java.util.Arrays;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Map;
16import java.util.Objects;
17import java.util.function.BiFunction;
18import java.util.function.DoubleBinaryOperator;
19import java.util.function.DoubleUnaryOperator;
20import java.util.function.Function;
21
22import org.openstreetmap.josm.data.osm.PrimitiveId;
23import org.openstreetmap.josm.gui.mappaint.Cascade;
24import org.openstreetmap.josm.gui.mappaint.Environment;
25import org.openstreetmap.josm.tools.SubclassFilteredCollection;
26import org.openstreetmap.josm.tools.Utils;
27
28/**
29 * Factory to generate {@link Expression}s.
30 * <p>
31 * See {@link #createFunctionExpression}.
32 */
33public final class ExpressionFactory {
34
35 /**
36 * Marks functions which should be executed also when one or more arguments are null.
37 */
38 @Target(ElementType.METHOD)
39 @Retention(RetentionPolicy.RUNTIME)
40 @interface NullableArguments {}
41
42 /**
43 * Represents a function that accepts three arguments and produces a result. This is a specialization of {@link Function}.
44 * This is a functional interface whose functional method is {@link #apply(Object, Object, Object)}.
45 * @param <T> The type of the first argument
46 * @param <U> The type of the second argument
47 * @param <V> The type of the third argument
48 * @param <R> The type of the result of the function
49 * @see Function
50 * @see BiFunction
51 */
52 @FunctionalInterface
53 public interface TriFunction<T, U, V, R> {
54 /**
55 * Call the function
56 * @param t The first argument
57 * @param u The second argument
58 * @param v The third argument
59 * @return The result of the function call
60 */
61 R apply(T t, U u, V v);
62 }
63
64 /**
65 * Represents a function that accepts four arguments and produces a result. This is a specialization of {@link Function}.
66 * This is a functional interface whose functional method is {@link #apply(Object, Object, Object, Object)}.
67 * @param <T> The type of the first argument
68 * @param <U> The type of the second argument
69 * @param <V> The type of the third argument
70 * @param <W> The type of the fourth argument
71 * @param <R> The type of the result of the function
72 * @see Function
73 * @see BiFunction
74 */
75 @FunctionalInterface
76 public interface QuadFunction<T, U, V, W, R> {
77 /**
78 * Call the function
79 * @param t The first argument
80 * @param u The second argument
81 * @param v The third argument
82 * @param w The fourth argument
83 * @return The result of the function call
84 */
85 R apply(T t, U u, V v, W w);
86 }
87
88 @FunctionalInterface
89 interface Factory {
90 Expression createExpression(List<Expression> args);
91
92 static Factory of(DoubleUnaryOperator operator) {
93 return of(Double.class, operator::applyAsDouble);
94 }
95
96 static Factory ofNumberVarArgs(double identity, DoubleUnaryOperator unaryOperator, DoubleBinaryOperator operator) {
97 return args -> env -> {
98 if (args.isEmpty()) {
99 return identity;
100 } else if (args.size() == 1) {
101 Double arg = Cascade.convertTo(args.get(0).evaluate(env), Double.class);
102 return arg == null ? null : unaryOperator.applyAsDouble(arg);
103 } else {
104 return args.stream()
105 .map(arg -> Cascade.convertTo(arg.evaluate(env), Double.class))
106 .filter(Objects::nonNull)
107 .reduce(operator::applyAsDouble).orElse(null);
108 }
109 };
110 }
111
112 static Factory ofStringVarargs(BiFunction<Environment, String[], ?> function) {
113 return args -> env -> function.apply(env, args.stream()
114 .map(arg -> Cascade.convertTo(arg.evaluate(env), String.class))
115 .toArray(String[]::new));
116 }
117
118 static Factory ofObjectVarargs(BiFunction<Environment, Object[], ?> function) {
119 return args -> env -> function.apply(env, args.stream()
120 .map(arg -> arg.evaluate(env))
121 .toArray(Object[]::new));
122 }
123
124 static <T> Factory of(Class<T> type, Function<T, ?> function) {
125 return args -> env -> {
126 T v = Cascade.convertTo(args.get(0).evaluate(env), type);
127 return v == null ? null : function.apply(v);
128 };
129 }
130
131 static <T, U> Factory of(Class<T> type1, Class<U> type2, BiFunction<T, U, ?> function) {
132 return args -> env -> {
133 T v1 = Cascade.convertTo(args.get(0).evaluate(env), type1);
134 U v2 = Cascade.convertTo(args.get(1).evaluate(env), type2);
135 return v1 == null || v2 == null ? null : function.apply(v1, v2);
136 };
137 }
138
139 static <T, U, V> Factory of(Class<T> type1, Class<U> type2, Class<V> type3,
140 BiFunction<T, U, ?> biFunction, TriFunction<T, U, V, ?> triFunction) {
141 return args -> env -> {
142 T v1 = !args.isEmpty() ? Cascade.convertTo(args.get(0).evaluate(env), type1) : null;
143 U v2 = args.size() >= 2 ? Cascade.convertTo(args.get(1).evaluate(env), type2) : null;
144 V v3 = args.size() >= 3 ? Cascade.convertTo(args.get(2).evaluate(env), type3) : null;
145 return v1 == null || v2 == null ? null : v3 == null ? biFunction.apply(v1, v2) : triFunction.apply(v1, v2, v3);
146 };
147 }
148
149 static <T, U, V, W> Factory of(Class<T> type1, Class<U> type2, Class<V> type3, Class<W> type4,
150 QuadFunction<T, U, V, W, ?> function) {
151 return args -> env -> {
152 T v1 = !args.isEmpty() ? Cascade.convertTo(args.get(0).evaluate(env), type1) : null;
153 U v2 = args.size() >= 2 ? Cascade.convertTo(args.get(1).evaluate(env), type2) : null;
154 V v3 = args.size() >= 3 ? Cascade.convertTo(args.get(2).evaluate(env), type3) : null;
155 W v4 = args.size() >= 4 ? Cascade.convertTo(args.get(3).evaluate(env), type4) : null;
156 return v1 == null || v2 == null || v3 == null || v4 == null ? null : function.apply(v1, v2, v3, v4);
157 };
158 }
159
160 /**
161 * Create a factory that accepts an iterable (array <i>or</i> generic iterable)
162 * @param type The expected type class
163 * @param function The function to apply the arguments to
164 * @param <T> The iterable type
165 * @return The result of the function call
166 */
167 @SuppressWarnings("unchecked")
168 static <T> Factory ofIterable(Class<T> type, Function<Iterable<T>, ?> function) {
169 return args -> env -> {
170 Object arg0 = args.get(0).evaluate(env);
171 if (args.size() == 1 && arg0 instanceof Iterable) {
172 return function.apply((Iterable<T>) arg0);
173 } else {
174 return function.apply(Arrays.asList(args.stream().map(arg -> Cascade.convertTo(arg, type))
175 .toArray(length -> (T[]) Array.newInstance(type, length))));
176 }
177 };
178 }
179
180 /**
181 * Create a {@link Factory} for a function
182 * @param function The function to use
183 * @return The result of the function
184 */
185 static Factory ofEnv(Function<Environment, ?> function) {
186 return args -> function::apply;
187 }
188
189 /**
190 * Create a {@link Factory} for a function that takes a parameter
191 * @param type The parameter type class
192 * @param function The function to use when one argument is available
193 * @param <T> the type of the input to the function
194 * @return The result of the function
195 */
196 static <T> Factory ofEnv(Class<T> type, BiFunction<Environment, T, ?> function) {
197 return args -> env -> {
198 T v = Cascade.convertTo(args.get(0).evaluate(env), type);
199 return v == null ? null : function.apply(env, v);
200 };
201 }
202
203 /**
204 * Create a {@link Factory} for an overloaded function
205 * @param type1 The first parameter type class
206 * @param type2 The second parameter type class
207 * @param biFunction The function to use when one argument is available
208 * @param triFunction The function to use when two arguments are available
209 * @param <T> the type of the input to the function
210 * @param <U> the type of the input to the function
211 * @return The result of one of the functions
212 */
213 static <T, U> Factory ofEnv(Class<T> type1, Class<U> type2,
214 BiFunction<Environment, T, ?> biFunction, TriFunction<Environment, T, U, ?> triFunction) {
215 return args -> env -> {
216 T v1 = !args.isEmpty() ? Cascade.convertTo(args.get(0).evaluate(env), type1) : null;
217 U v2 = args.size() >= 2 ? Cascade.convertTo(args.get(1).evaluate(env), type2) : null;
218 return v1 == null ? null : v2 == null ? biFunction.apply(env, v1) : triFunction.apply(env, v1, v2);
219 };
220 }
221
222 /**
223 * Create a {@link Factory} for an overloaded function
224 * @param type1 The first parameter type class
225 * @param type2 The second parameter type class
226 * @param function The function to use when no args are available
227 * @param biFunction The function to use when one argument is available
228 * @param triFunction The function to use when two arguments are available
229 * @param <T> the type of the input to the function
230 * @param <U> the type of the input to the function
231 * @return The result of one of the functions
232 */
233 static <T, U> Factory ofEnv(Class<T> type1, Class<U> type2, Function<Environment, ?> function,
234 BiFunction<Environment, T, ?> biFunction, TriFunction<Environment, T, U, ?> triFunction) {
235 return args -> env -> {
236 T v1 = !args.isEmpty() ? Cascade.convertTo(args.get(0).evaluate(env), type1) : null;
237 U v2 = args.size() >= 2 ? Cascade.convertTo(args.get(1).evaluate(env), type2) : null;
238 return v1 == null ? function.apply(env) : v2 == null ? biFunction.apply(env, v1) : triFunction.apply(env, v1, v2);
239 };
240 }
241 }
242
243 static final Map<String, Factory> FACTORY_MAP = new HashMap<>();
244
245 static {
246 initFactories();
247 }
248
249 @SuppressWarnings("unchecked")
250 private static void initFactories() {
251 FACTORY_MAP.put("CRC32_checksum", Factory.of(String.class, Functions::CRC32_checksum));
252 FACTORY_MAP.put("JOSM_pref", Factory.ofEnv(String.class, String.class, null, Functions::JOSM_pref));
253 FACTORY_MAP.put("JOSM_search", Factory.ofEnv(String.class, Functions::JOSM_search));
254 FACTORY_MAP.put("URL_decode", Factory.of(String.class, Functions::URL_decode));
255 FACTORY_MAP.put("URL_encode", Factory.of(String.class, Functions::URL_encode));
256 FACTORY_MAP.put("XML_encode", Factory.of(String.class, Functions::XML_encode));
257 FACTORY_MAP.put("abs", Factory.of(Math::acos));
258 FACTORY_MAP.put("acos", Factory.of(Math::acos));
259 FACTORY_MAP.put("alpha", Factory.of(Color.class, Functions::alpha));
260 FACTORY_MAP.put("any", Factory.ofObjectVarargs(Functions::any));
261 FACTORY_MAP.put("areasize", Factory.ofEnv(Functions::areasize));
262 FACTORY_MAP.put("asin", Factory.of(Math::asin));
263 FACTORY_MAP.put("at", Factory.ofEnv(double.class, double.class, null, Functions::at));
264 FACTORY_MAP.put("atan", Factory.of(Math::atan));
265 FACTORY_MAP.put("atan2", Factory.of(Double.class, Double.class, Math::atan2));
266 FACTORY_MAP.put("blue", Factory.of(Color.class, Functions::blue));
267 FACTORY_MAP.put("cardinal_to_radians", Factory.of(String.class, Functions::cardinal_to_radians));
268 FACTORY_MAP.put("ceil", Factory.of(Math::ceil));
269 FACTORY_MAP.put("center", Factory.ofEnv(Functions::center));
270 FACTORY_MAP.put("child_tag", Factory.ofEnv(String.class, Functions::child_tag));
271 FACTORY_MAP.put("color2html", Factory.of(Color.class, Functions::color2html));
272 FACTORY_MAP.put("concat", Factory.ofObjectVarargs(Functions::concat));
273 FACTORY_MAP.put("convert_primitive_to_string", Factory.of(PrimitiveId.class, Functions::convert_primitive_to_string));
274 FACTORY_MAP.put("convert_primitives_to_string", Factory.ofIterable(PrimitiveId.class, Functions::convert_primitives_to_string));
275 FACTORY_MAP.put("cos", Factory.of(Math::cos));
276 FACTORY_MAP.put("cosh", Factory.of(Math::cosh));
277 FACTORY_MAP.put("count", Factory.of(List.class, Functions::count));
278 FACTORY_MAP.put("count_roles", Factory.ofStringVarargs(Functions::count_roles));
279 FACTORY_MAP.put("degree_to_radians", Factory.of(Functions::degree_to_radians));
280 FACTORY_MAP.put("divided_by", Factory.ofNumberVarArgs(1.0, DoubleUnaryOperator.identity(), Functions::divided_by));
281 FACTORY_MAP.put("equal", Factory.of(Object.class, Object.class, Functions::equal));
282 FACTORY_MAP.put("eval", Factory.of(Object.class, Functions::eval));
283 FACTORY_MAP.put("exp", Factory.of(Math::exp));
284 FACTORY_MAP.put("floor", Factory.of(Math::floor));
285 FACTORY_MAP.put("get", Factory.of(List.class, float.class, Functions::get));
286 FACTORY_MAP.put("gpx_distance", Factory.ofEnv(Functions::gpx_distance));
287 FACTORY_MAP.put("greater", Factory.of(float.class, float.class, Functions::greater));
288 FACTORY_MAP.put("greater_equal", Factory.of(float.class, float.class, Functions::greater_equal));
289 FACTORY_MAP.put("green", Factory.of(Color.class, Functions::green));
290 FACTORY_MAP.put("has_tag_key", Factory.ofEnv(String.class, Functions::has_tag_key));
291 FACTORY_MAP.put("hsb_color", Factory.of(float.class, float.class, float.class, null, Functions::hsb_color));
292 FACTORY_MAP.put("html2color", Factory.of(String.class, Functions::html2color));
293 FACTORY_MAP.put("index", Factory.ofEnv(Functions::index));
294 FACTORY_MAP.put("inside", Factory.ofEnv(String.class, Functions::inside));
295 FACTORY_MAP.put("is_anticlockwise", Factory.ofEnv(Functions::is_anticlockwise));
296 FACTORY_MAP.put("is_clockwise", Factory.ofEnv(Functions::is_clockwise));
297 FACTORY_MAP.put("is_prop_set", Factory.ofEnv(String.class, String.class, Functions::is_prop_set, Functions::is_prop_set));
298 FACTORY_MAP.put("is_right_hand_traffic", Factory.ofEnv(Functions::is_right_hand_traffic));
299 FACTORY_MAP.put("is_similar", Factory.of(String.class, String.class, Functions::is_similar));
300 FACTORY_MAP.put("join", Factory.ofStringVarargs(Functions::join));
301 FACTORY_MAP.put("join_list", Factory.of(String.class, List.class, Functions::join_list));
302 FACTORY_MAP.put("less", Factory.of(float.class, float.class, Functions::less));
303 FACTORY_MAP.put("less_equal", Factory.of(float.class, float.class, Functions::less_equal));
304 FACTORY_MAP.put("list", Factory.ofObjectVarargs(Functions::list));
305 FACTORY_MAP.put("log", Factory.of(Math::log));
306 FACTORY_MAP.put("lower", Factory.of(String.class, Functions::lower));
307 FACTORY_MAP.put("minus", Factory.ofNumberVarArgs(0.0, v -> -v, Functions::minus));
308 FACTORY_MAP.put("mod", Factory.of(float.class, float.class, Functions::mod));
309 FACTORY_MAP.put("not", Factory.of(boolean.class, Functions::not));
310 FACTORY_MAP.put("not_equal", Factory.of(Object.class, Object.class, Functions::not_equal));
311 FACTORY_MAP.put("number_of_tags", Factory.ofEnv(Functions::number_of_tags));
312 FACTORY_MAP.put("osm_changeset_id", Factory.ofEnv(Functions::osm_changeset_id));
313 FACTORY_MAP.put("osm_id", Factory.ofEnv(Functions::osm_id));
314 FACTORY_MAP.put("osm_timestamp", Factory.ofEnv(Functions::osm_timestamp));
315 FACTORY_MAP.put("osm_user_id", Factory.ofEnv(Functions::osm_user_id));
316 FACTORY_MAP.put("osm_user_name", Factory.ofEnv(Functions::osm_user_name));
317 FACTORY_MAP.put("osm_version", Factory.ofEnv(Functions::osm_version));
318 FACTORY_MAP.put("outside", Factory.ofEnv(String.class, Functions::outside));
319 FACTORY_MAP.put("parent_osm_id", Factory.ofEnv(Functions::parent_osm_id));
320 FACTORY_MAP.put("parent_osm_primitives", Factory.ofEnv(String.class, String.class,
321 Functions::parent_osm_primitives, Functions::parent_osm_primitives, Functions::parent_osm_primitives));
322 FACTORY_MAP.put("parent_tag", Factory.ofEnv(String.class, Functions::parent_tag));
323 FACTORY_MAP.put("parent_tags", Factory.ofEnv(String.class, Functions::parent_tags));
324 FACTORY_MAP.put("parent_way_angle", Factory.ofEnv(Functions::parent_way_angle));
325 FACTORY_MAP.put("plus", Factory.ofNumberVarArgs(0.0, DoubleUnaryOperator.identity(), Functions::plus));
326 FACTORY_MAP.put("print", Factory.of(Object.class, Functions::print));
327 FACTORY_MAP.put("println", Factory.of(Object.class, Functions::println));
328 FACTORY_MAP.put("prop", Factory.ofEnv(String.class, String.class, Functions::prop, Functions::prop));
329 FACTORY_MAP.put("red", Factory.of(Color.class, Functions::red));
330 FACTORY_MAP.put("regexp_match", Factory.of(String.class, String.class, String.class, Functions::regexp_match, Functions::regexp_match));
331 FACTORY_MAP.put("regexp_test", Factory.of(String.class, String.class, String.class, Functions::regexp_test, Functions::regexp_test));
332 FACTORY_MAP.put("replace", Factory.of(String.class, String.class, String.class, null, Functions::replace));
333 FACTORY_MAP.put("rgb", Factory.of(float.class, float.class, float.class, null, Functions::rgb));
334 FACTORY_MAP.put("rgba", Factory.of(float.class, float.class, float.class, float.class, Functions::rgba));
335 FACTORY_MAP.put("role", Factory.ofEnv(Functions::role));
336 FACTORY_MAP.put("round", Factory.of(Math::round));
337 FACTORY_MAP.put("setting", Factory.ofEnv(String.class, Functions::setting));
338 FACTORY_MAP.put("signum", Factory.of(Math::signum));
339 FACTORY_MAP.put("sin", Factory.of(Math::sin));
340 FACTORY_MAP.put("sinh", Factory.of(Math::sinh));
341 FACTORY_MAP.put("sort", Factory.ofStringVarargs(Functions::sort));
342 FACTORY_MAP.put("sort_list", Factory.of(List.class, Functions::sort_list));
343 FACTORY_MAP.put("split", Factory.of(String.class, String.class, Functions::split));
344 FACTORY_MAP.put("sqrt", Factory.of(Math::sqrt));
345 FACTORY_MAP.put("substring", Factory.of(String.class, float.class, float.class, Functions::substring, Functions::substring));
346 FACTORY_MAP.put("tag", Factory.ofEnv(String.class, Functions::tag));
347 FACTORY_MAP.put("tag_regex", Factory.ofEnv(String.class, String.class, Functions::tag_regex, Functions::tag_regex));
348 FACTORY_MAP.put("tan", Factory.of(Math::tan));
349 FACTORY_MAP.put("tanh", Factory.of(Math::tanh));
350 FACTORY_MAP.put("times", Factory.ofNumberVarArgs(1.0, DoubleUnaryOperator.identity(), Functions::times));
351 FACTORY_MAP.put("title", Factory.of(String.class, Functions::title));
352 FACTORY_MAP.put("to_boolean", Factory.of(String.class, Functions::to_boolean));
353 FACTORY_MAP.put("to_byte", Factory.of(String.class, Functions::to_byte));
354 FACTORY_MAP.put("to_double", Factory.of(String.class, Functions::to_double));
355 FACTORY_MAP.put("to_float", Factory.of(String.class, Functions::to_float));
356 FACTORY_MAP.put("to_int", Factory.of(String.class, Functions::to_int));
357 FACTORY_MAP.put("to_long", Factory.of(String.class, Functions::to_long));
358 FACTORY_MAP.put("to_short", Factory.of(String.class, Functions::to_short));
359 FACTORY_MAP.put("tr", Factory.ofStringVarargs(Functions::tr));
360 FACTORY_MAP.put("trim", Factory.of(String.class, Functions::trim));
361 FACTORY_MAP.put("trim_list", Factory.of(List.class, Functions::trim_list));
362 FACTORY_MAP.put("uniq", Factory.ofStringVarargs(Functions::uniq));
363 FACTORY_MAP.put("uniq_list", Factory.of(List.class, Functions::uniq_list));
364 FACTORY_MAP.put("upper", Factory.of(String.class, Functions::upper));
365 FACTORY_MAP.put("waylength", Factory.ofEnv(Functions::waylength));
366 }
367
368 private ExpressionFactory() {
369 // Hide default constructor for utils classes
370 }
371
372 /**
373 * Main method to create an function-like expression.
374 *
375 * @param name the name of the function or operator
376 * @param args the list of arguments (as expressions)
377 * @return the generated Expression. If no suitable function can be found,
378 * returns {@link NullExpression#INSTANCE}.
379 */
380 public static Expression createFunctionExpression(String name, List<Expression> args) {
381 if ("cond".equals(name) && args.size() == 3)
382 return new CondOperator(args.get(0), args.get(1), args.get(2));
383 else if ("and".equals(name))
384 return new AndOperator(args);
385 else if ("or".equals(name))
386 return new OrOperator(args);
387 else if ("length".equals(name) && args.size() == 1)
388 return new LengthFunction(args.get(0));
389 else if ("max".equals(name) && !args.isEmpty())
390 return new MinMaxFunction(args, true);
391 else if ("min".equals(name) && !args.isEmpty())
392 return new MinMaxFunction(args, false);
393 else if ("inside".equals(name) && args.size() == 1)
394 return new IsInsideFunction(args.get(0));
395 else if ("random".equals(name))
396 return env -> Math.random();
397
398 Factory factory = FACTORY_MAP.get(name);
399 if (factory != null) {
400 return factory.createExpression(args);
401 }
402 return NullExpression.INSTANCE;
403 }
404
405 /**
406 * Expression that always evaluates to null.
407 */
408 public static class NullExpression implements Expression {
409
410 /**
411 * The unique instance.
412 */
413 public static final NullExpression INSTANCE = new NullExpression();
414
415 @Override
416 public Object evaluate(Environment env) {
417 return null;
418 }
419 }
420
421 /**
422 * Conditional operator.
423 */
424 public static class CondOperator implements Expression {
425
426 private final Expression condition, firstOption, secondOption;
427
428 /**
429 * Constructs a new {@code CondOperator}.
430 * @param condition condition
431 * @param firstOption first option
432 * @param secondOption second option
433 */
434 public CondOperator(Expression condition, Expression firstOption, Expression secondOption) {
435 this.condition = condition;
436 this.firstOption = firstOption;
437 this.secondOption = secondOption;
438 }
439
440 @Override
441 public Object evaluate(Environment env) {
442 Boolean b = Cascade.convertTo(condition.evaluate(env), boolean.class);
443 if (b != null && b)
444 return firstOption.evaluate(env);
445 else
446 return secondOption.evaluate(env);
447 }
448 }
449
450 /**
451 * "And" logical operator.
452 */
453 public static class AndOperator implements Expression {
454
455 private final List<Expression> args;
456
457 /**
458 * Constructs a new {@code AndOperator}.
459 * @param args arguments
460 */
461 public AndOperator(List<Expression> args) {
462 this.args = args;
463 }
464
465 @Override
466 public Object evaluate(Environment env) {
467 return args.stream()
468 .map(arg -> Cascade.convertTo(arg.evaluate(env), boolean.class))
469 .allMatch(Boolean.TRUE::equals);
470 }
471 }
472
473 /**
474 * "Or" logical operator.
475 */
476 public static class OrOperator implements Expression {
477
478 private final List<Expression> args;
479
480 /**
481 * Constructs a new {@code OrOperator}.
482 * @param args arguments
483 */
484 public OrOperator(List<Expression> args) {
485 this.args = args;
486 }
487
488 @Override
489 public Object evaluate(Environment env) {
490 return args.stream()
491 .map(arg -> Cascade.convertTo(arg.evaluate(env), boolean.class))
492 .anyMatch(Boolean.TRUE::equals);
493 }
494 }
495
496 /**
497 * Function to calculate the length of a string or list in a MapCSS eval expression.
498 * <p>
499 * Separate implementation to support overloading for different argument types.
500 * <p>
501 * The use for calculating the length of a list is deprecated, use
502 * {@link Functions#count(java.util.List)} instead (see #10061).
503 */
504 public static class LengthFunction implements Expression {
505
506 private final Expression arg;
507
508 /**
509 * Constructs a new {@code LengthFunction}.
510 * @param args arguments
511 */
512 public LengthFunction(Expression args) {
513 this.arg = args;
514 }
515
516 @Override
517 public Object evaluate(Environment env) {
518 List<?> l = Cascade.convertTo(arg.evaluate(env), List.class);
519 if (l != null)
520 return l.size();
521 String s = Cascade.convertTo(arg.evaluate(env), String.class);
522 if (s != null)
523 return s.length();
524 return null;
525 }
526 }
527
528 /**
529 * Computes the maximum/minimum value an arbitrary number of floats, or a list of floats.
530 */
531 public static class MinMaxFunction implements Expression {
532
533 private final List<Expression> args;
534 private final boolean computeMax;
535
536 /**
537 * Constructs a new {@code MinMaxFunction}.
538 * @param args arguments
539 * @param computeMax if {@code true}, compute max. If {@code false}, compute min
540 */
541 public MinMaxFunction(final List<Expression> args, final boolean computeMax) {
542 this.args = args;
543 this.computeMax = computeMax;
544 }
545
546 /**
547 * Compute the minimum / maximum over the list
548 * @param lst The list
549 * @return The minimum or maximum depending on {@link #computeMax}
550 */
551 public Float aggregateList(List<?> lst) {
552 final List<Float> floats = Utils.transform(lst, (Function<Object, Float>) x -> Cascade.convertTo(x, float.class));
553 final Collection<Float> nonNullList = SubclassFilteredCollection.filter(floats, Objects::nonNull);
554 return nonNullList.isEmpty() ? (Float) Float.NaN : computeMax ? Collections.max(nonNullList) : Collections.min(nonNullList);
555 }
556
557 @Override
558 public Object evaluate(final Environment env) {
559 List<?> l = Cascade.convertTo(args.get(0).evaluate(env), List.class);
560 if (args.size() != 1 || l == null)
561 l = Utils.transform(args, (Function<Expression, Object>) x -> x.evaluate(env));
562 return aggregateList(l);
563 }
564 }
565
566 /**
567 * {@code Functions#inside} implementation for use in {@link org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker}
568 *
569 * @see Functions#inside
570 */
571 public static class IsInsideFunction implements Expression {
572 private final Expression arg;
573
574 /**
575 * Constructs a new {@code IsInsideFunction}.
576 * @param arg argument
577 */
578 public IsInsideFunction(Expression arg) {
579 this.arg = arg;
580 }
581
582 /**
583 * Returns the argument
584 * @return the argument
585 */
586 public Expression getArg() {
587 return arg;
588 }
589
590 @Override
591 public Object evaluate(Environment env) {
592 String codes = Cascade.convertTo(arg.evaluate(env), String.class);
593 return Functions.inside(env, codes);
594 }
595 }
596}
Note: See TracBrowser for help on using the repository browser.