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

Last change on this file since 18829 was 18829, checked in by taylor.smock, 9 months ago

Fix #16998: Add parent_osm_primitives and convert_primitives_to_string

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