001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.mappaint.mapcss; 003 004import java.awt.Color; 005import java.lang.annotation.ElementType; 006import java.lang.annotation.Retention; 007import java.lang.annotation.RetentionPolicy; 008import java.lang.annotation.Target; 009import java.util.Collection; 010import java.util.Collections; 011import java.util.HashMap; 012import java.util.List; 013import java.util.Map; 014import java.util.Objects; 015import java.util.function.BiFunction; 016import java.util.function.DoubleBinaryOperator; 017import java.util.function.DoubleUnaryOperator; 018import java.util.function.Function; 019 020import org.openstreetmap.josm.gui.mappaint.Cascade; 021import org.openstreetmap.josm.gui.mappaint.Environment; 022import org.openstreetmap.josm.tools.SubclassFilteredCollection; 023import org.openstreetmap.josm.tools.Utils; 024 025/** 026 * Factory to generate {@link Expression}s. 027 * <p> 028 * See {@link #createFunctionExpression}. 029 */ 030public final class ExpressionFactory { 031 032 /** 033 * Marks functions which should be executed also when one or more arguments are null. 034 */ 035 @Target(ElementType.METHOD) 036 @Retention(RetentionPolicy.RUNTIME) 037 @interface NullableArguments {} 038 039 @FunctionalInterface 040 public interface TriFunction<T, U, V, R> { 041 R apply(T t, U u, V v); 042 } 043 044 @FunctionalInterface 045 public interface QuadFunction<T, U, V, W, R> { 046 R apply(T t, U u, V v, W w); 047 } 048 049 @FunctionalInterface 050 interface Factory { 051 Expression createExpression(List<Expression> args); 052 053 static Factory of(DoubleUnaryOperator operator) { 054 return of(Double.class, operator::applyAsDouble); 055 } 056 057 static Factory ofNumberVarArgs(double identity, DoubleUnaryOperator unaryOperator, DoubleBinaryOperator operator) { 058 return args -> env -> { 059 if (args.isEmpty()) { 060 return identity; 061 } else if (args.size() == 1) { 062 Double arg = Cascade.convertTo(args.get(0).evaluate(env), Double.class); 063 return arg == null ? null : unaryOperator.applyAsDouble(arg); 064 } else { 065 return args.stream() 066 .map(arg -> Cascade.convertTo(arg.evaluate(env), Double.class)) 067 .filter(Objects::nonNull) 068 .reduce(operator::applyAsDouble).orElse(null); 069 } 070 }; 071 } 072 073 static Factory ofStringVarargs(BiFunction<Environment, String[], ?> function) { 074 return args -> env -> function.apply(env, args.stream() 075 .map(arg -> Cascade.convertTo(arg.evaluate(env), String.class)) 076 .toArray(String[]::new)); 077 } 078 079 static Factory ofObjectVarargs(BiFunction<Environment, Object[], ?> function) { 080 return args -> env -> function.apply(env, args.stream() 081 .map(arg -> arg.evaluate(env)) 082 .toArray(Object[]::new)); 083 } 084 085 static <T> Factory of(Class<T> type, Function<T, ?> function) { 086 return args -> env -> { 087 T v = Cascade.convertTo(args.get(0).evaluate(env), type); 088 return v == null ? null : function.apply(v); 089 }; 090 } 091 092 static <T, U> Factory of(Class<T> type1, Class<U> type2, BiFunction<T, U, ?> function) { 093 return args -> env -> { 094 T v1 = Cascade.convertTo(args.get(0).evaluate(env), type1); 095 U v2 = Cascade.convertTo(args.get(1).evaluate(env), type2); 096 return v1 == null || v2 == null ? null : function.apply(v1, v2); 097 }; 098 } 099 100 static <T, U, V> Factory of(Class<T> type1, Class<U> type2, Class<V> type3, 101 BiFunction<T, U, ?> biFunction, TriFunction<T, U, V, ?> triFunction) { 102 return args -> env -> { 103 T v1 = args.size() >= 1 ? Cascade.convertTo(args.get(0).evaluate(env), type1) : null; 104 U v2 = args.size() >= 2 ? Cascade.convertTo(args.get(1).evaluate(env), type2) : null; 105 V v3 = args.size() >= 3 ? Cascade.convertTo(args.get(2).evaluate(env), type3) : null; 106 return v1 == null || v2 == null ? null : v3 == null ? biFunction.apply(v1, v2) : triFunction.apply(v1, v2, v3); 107 }; 108 } 109 110 static <T, U, V, W> Factory of(Class<T> type1, Class<U> type2, Class<V> type3, Class<W> type4, 111 QuadFunction<T, U, V, W, ?> function) { 112 return args -> env -> { 113 T v1 = args.size() >= 1 ? Cascade.convertTo(args.get(0).evaluate(env), type1) : null; 114 U v2 = args.size() >= 2 ? Cascade.convertTo(args.get(1).evaluate(env), type2) : null; 115 V v3 = args.size() >= 3 ? Cascade.convertTo(args.get(2).evaluate(env), type3) : null; 116 W v4 = args.size() >= 4 ? Cascade.convertTo(args.get(3).evaluate(env), type4) : null; 117 return v1 == null || v2 == null || v3 == null || v4 == null ? null : function.apply(v1, v2, v3, v4); 118 }; 119 } 120 121 static <T> Factory ofEnv(Function<Environment, ?> function) { 122 return args -> function::apply; 123 } 124 125 static <T> Factory ofEnv(Class<T> type, BiFunction<Environment, T, ?> function) { 126 return args -> env -> { 127 T v = Cascade.convertTo(args.get(0).evaluate(env), type); 128 return v == null ? null : function.apply(env, v); 129 }; 130 } 131 132 static <T, U> Factory ofEnv(Class<T> type1, Class<U> type2, 133 BiFunction<Environment, T, ?> biFunction, TriFunction<Environment, T, U, ?> triFunction) { 134 return args -> env -> { 135 T v1 = args.size() >= 1 ? Cascade.convertTo(args.get(0).evaluate(env), type1) : null; 136 U v2 = args.size() >= 2 ? Cascade.convertTo(args.get(1).evaluate(env), type2) : null; 137 return v1 == null ? null : v2 == null ? biFunction.apply(env, v1) : triFunction.apply(env, v1, v2); 138 }; 139 } 140 } 141 142 static final Map<String, Factory> FACTORY_MAP = new HashMap<>(); 143 144 static { 145 initFactories(); 146 } 147 148 @SuppressWarnings("unchecked") 149 private static void initFactories() { 150 FACTORY_MAP.put("CRC32_checksum", Factory.of(String.class, Functions::CRC32_checksum)); 151 FACTORY_MAP.put("JOSM_pref", Factory.ofEnv(String.class, String.class, null, Functions::JOSM_pref)); 152 FACTORY_MAP.put("JOSM_search", Factory.ofEnv(String.class, Functions::JOSM_search)); 153 FACTORY_MAP.put("URL_decode", Factory.of(String.class, Functions::URL_decode)); 154 FACTORY_MAP.put("URL_encode", Factory.of(String.class, Functions::URL_encode)); 155 FACTORY_MAP.put("XML_encode", Factory.of(String.class, Functions::XML_encode)); 156 FACTORY_MAP.put("abs", Factory.of(Math::acos)); 157 FACTORY_MAP.put("acos", Factory.of(Math::acos)); 158 FACTORY_MAP.put("alpha", Factory.of(Color.class, Functions::alpha)); 159 FACTORY_MAP.put("any", Factory.ofObjectVarargs(Functions::any)); 160 FACTORY_MAP.put("areasize", Factory.ofEnv(Functions::areasize)); 161 FACTORY_MAP.put("asin", Factory.of(Math::asin)); 162 FACTORY_MAP.put("at", Factory.ofEnv(double.class, double.class, null, Functions::at)); 163 FACTORY_MAP.put("atan", Factory.of(Math::atan)); 164 FACTORY_MAP.put("atan2", Factory.of(Double.class, Double.class, Math::atan2)); 165 FACTORY_MAP.put("blue", Factory.of(Color.class, Functions::blue)); 166 FACTORY_MAP.put("cardinal_to_radians", Factory.of(String.class, Functions::cardinal_to_radians)); 167 FACTORY_MAP.put("ceil", Factory.of(Math::ceil)); 168 FACTORY_MAP.put("center", Factory.ofEnv(Functions::center)); 169 FACTORY_MAP.put("child_tag", Factory.ofEnv(String.class, Functions::child_tag)); 170 FACTORY_MAP.put("color2html", Factory.of(Color.class, Functions::color2html)); 171 FACTORY_MAP.put("concat", Factory.ofObjectVarargs(Functions::concat)); 172 FACTORY_MAP.put("cos", Factory.of(Math::cos)); 173 FACTORY_MAP.put("cosh", Factory.of(Math::cosh)); 174 FACTORY_MAP.put("count", Factory.of(List.class, Functions::count)); 175 FACTORY_MAP.put("count_roles", Factory.ofStringVarargs(Functions::count_roles)); 176 FACTORY_MAP.put("degree_to_radians", Factory.of(Functions::degree_to_radians)); 177 FACTORY_MAP.put("divided_by", Factory.ofNumberVarArgs(1.0, DoubleUnaryOperator.identity(), Functions::divided_by)); 178 FACTORY_MAP.put("equal", Factory.of(Object.class, Object.class, Functions::equal)); 179 FACTORY_MAP.put("eval", Factory.of(Object.class, Functions::eval)); 180 FACTORY_MAP.put("exp", Factory.of(Math::exp)); 181 FACTORY_MAP.put("floor", Factory.of(Math::floor)); 182 FACTORY_MAP.put("get", Factory.of(List.class, float.class, Functions::get)); 183 FACTORY_MAP.put("gpx_distance", Factory.ofEnv(Functions::gpx_distance)); 184 FACTORY_MAP.put("greater", Factory.of(float.class, float.class, Functions::greater)); 185 FACTORY_MAP.put("greater_equal", Factory.of(float.class, float.class, Functions::greater_equal)); 186 FACTORY_MAP.put("green", Factory.of(Color.class, Functions::green)); 187 FACTORY_MAP.put("has_tag_key", Factory.ofEnv(String.class, Functions::has_tag_key)); 188 FACTORY_MAP.put("hsb_color", Factory.of(float.class, float.class, float.class, null, Functions::hsb_color)); 189 FACTORY_MAP.put("html2color", Factory.of(String.class, Functions::html2color)); 190 FACTORY_MAP.put("index", Factory.ofEnv(Functions::index)); 191 FACTORY_MAP.put("inside", Factory.ofEnv(String.class, Functions::inside)); 192 FACTORY_MAP.put("is_anticlockwise", Factory.ofEnv(Functions::is_anticlockwise)); 193 FACTORY_MAP.put("is_clockwise", Factory.ofEnv(Functions::is_clockwise)); 194 FACTORY_MAP.put("is_prop_set", Factory.ofEnv(String.class, String.class, Functions::is_prop_set, Functions::is_prop_set)); 195 FACTORY_MAP.put("is_right_hand_traffic", Factory.ofEnv(Functions::is_right_hand_traffic)); 196 FACTORY_MAP.put("is_similar", Factory.of(String.class, String.class, Functions::is_similar)); 197 FACTORY_MAP.put("join", Factory.ofStringVarargs(Functions::join)); 198 FACTORY_MAP.put("join_list", Factory.of(String.class, List.class, Functions::join_list)); 199 FACTORY_MAP.put("less", Factory.of(float.class, float.class, Functions::less)); 200 FACTORY_MAP.put("less_equal", Factory.of(float.class, float.class, Functions::less_equal)); 201 FACTORY_MAP.put("list", Factory.ofObjectVarargs(Functions::list)); 202 FACTORY_MAP.put("log", Factory.of(Math::log)); 203 FACTORY_MAP.put("lower", Factory.of(String.class, Functions::lower)); 204 FACTORY_MAP.put("minus", Factory.ofNumberVarArgs(0.0, v -> -v, Functions::minus)); 205 FACTORY_MAP.put("mod", Factory.of(float.class, float.class, Functions::mod)); 206 FACTORY_MAP.put("not", Factory.of(boolean.class, Functions::not)); 207 FACTORY_MAP.put("not_equal", Factory.of(Object.class, Object.class, Functions::not_equal)); 208 FACTORY_MAP.put("number_of_tags", Factory.ofEnv(Functions::number_of_tags)); 209 FACTORY_MAP.put("osm_changeset_id", Factory.ofEnv(Functions::osm_changeset_id)); 210 FACTORY_MAP.put("osm_id", Factory.ofEnv(Functions::osm_id)); 211 FACTORY_MAP.put("osm_timestamp", Factory.ofEnv(Functions::osm_timestamp)); 212 FACTORY_MAP.put("osm_user_id", Factory.ofEnv(Functions::osm_user_id)); 213 FACTORY_MAP.put("osm_user_name", Factory.ofEnv(Functions::osm_user_name)); 214 FACTORY_MAP.put("osm_version", Factory.ofEnv(Functions::osm_version)); 215 FACTORY_MAP.put("outside", Factory.ofEnv(String.class, Functions::outside)); 216 FACTORY_MAP.put("parent_osm_id", Factory.ofEnv(Functions::parent_osm_id)); 217 FACTORY_MAP.put("parent_tag", Factory.ofEnv(String.class, Functions::parent_tag)); 218 FACTORY_MAP.put("parent_tags", Factory.ofEnv(String.class, Functions::parent_tags)); 219 FACTORY_MAP.put("plus", Factory.ofNumberVarArgs(0.0, DoubleUnaryOperator.identity(), Functions::plus)); 220 FACTORY_MAP.put("print", Factory.of(Object.class, Functions::print)); 221 FACTORY_MAP.put("println", Factory.of(Object.class, Functions::println)); 222 FACTORY_MAP.put("prop", Factory.ofEnv(String.class, String.class, Functions::prop, Functions::prop)); 223 FACTORY_MAP.put("red", Factory.of(Color.class, Functions::red)); 224 FACTORY_MAP.put("regexp_match", Factory.of(String.class, String.class, String.class, Functions::regexp_match, Functions::regexp_match)); 225 FACTORY_MAP.put("regexp_test", Factory.of(String.class, String.class, String.class, Functions::regexp_test, Functions::regexp_test)); 226 FACTORY_MAP.put("replace", Factory.of(String.class, String.class, String.class, null, Functions::replace)); 227 FACTORY_MAP.put("rgb", Factory.of(float.class, float.class, float.class, null, Functions::rgb)); 228 FACTORY_MAP.put("rgba", Factory.of(float.class, float.class, float.class, float.class, Functions::rgba)); 229 FACTORY_MAP.put("role", Factory.ofEnv(Functions::role)); 230 FACTORY_MAP.put("round", Factory.of(Math::round)); 231 FACTORY_MAP.put("setting", Factory.ofEnv(String.class, Functions::setting)); 232 FACTORY_MAP.put("signum", Factory.of(Math::signum)); 233 FACTORY_MAP.put("sin", Factory.of(Math::sin)); 234 FACTORY_MAP.put("sinh", Factory.of(Math::sinh)); 235 FACTORY_MAP.put("sort", Factory.ofStringVarargs(Functions::sort)); 236 FACTORY_MAP.put("sort_list", Factory.of(List.class, Functions::sort_list)); 237 FACTORY_MAP.put("split", Factory.of(String.class, String.class, Functions::split)); 238 FACTORY_MAP.put("sqrt", Factory.of(Math::sqrt)); 239 FACTORY_MAP.put("substring", Factory.of(String.class, float.class, float.class, Functions::substring, Functions::substring)); 240 FACTORY_MAP.put("tag", Factory.ofEnv(String.class, Functions::tag)); 241 FACTORY_MAP.put("tag_regex", Factory.ofEnv(String.class, String.class, Functions::tag_regex, Functions::tag_regex)); 242 FACTORY_MAP.put("tan", Factory.of(Math::tan)); 243 FACTORY_MAP.put("tanh", Factory.of(Math::tanh)); 244 FACTORY_MAP.put("times", Factory.ofNumberVarArgs(1.0, DoubleUnaryOperator.identity(), Functions::times)); 245 FACTORY_MAP.put("title", Factory.of(String.class, Functions::title)); 246 FACTORY_MAP.put("to_boolean", Factory.of(String.class, Functions::to_boolean)); 247 FACTORY_MAP.put("to_byte", Factory.of(String.class, Functions::to_byte)); 248 FACTORY_MAP.put("to_double", Factory.of(String.class, Functions::to_double)); 249 FACTORY_MAP.put("to_float", Factory.of(String.class, Functions::to_float)); 250 FACTORY_MAP.put("to_int", Factory.of(String.class, Functions::to_int)); 251 FACTORY_MAP.put("to_long", Factory.of(String.class, Functions::to_long)); 252 FACTORY_MAP.put("to_short", Factory.of(String.class, Functions::to_short)); 253 FACTORY_MAP.put("tr", Factory.ofStringVarargs(Functions::tr)); 254 FACTORY_MAP.put("trim", Factory.of(String.class, Functions::trim)); 255 FACTORY_MAP.put("trim_list", Factory.of(List.class, Functions::trim_list)); 256 FACTORY_MAP.put("uniq", Factory.ofStringVarargs(Functions::uniq)); 257 FACTORY_MAP.put("uniq_list", Factory.of(List.class, Functions::uniq_list)); 258 FACTORY_MAP.put("upper", Factory.of(String.class, Functions::upper)); 259 FACTORY_MAP.put("waylength", Factory.ofEnv(Functions::waylength)); 260 } 261 262 private ExpressionFactory() { 263 // Hide default constructor for utils classes 264 } 265 266 /** 267 * Main method to create an function-like expression. 268 * 269 * @param name the name of the function or operator 270 * @param args the list of arguments (as expressions) 271 * @return the generated Expression. If no suitable function can be found, 272 * returns {@link NullExpression#INSTANCE}. 273 */ 274 public static Expression createFunctionExpression(String name, List<Expression> args) { 275 if ("cond".equals(name) && args.size() == 3) 276 return new CondOperator(args.get(0), args.get(1), args.get(2)); 277 else if ("and".equals(name)) 278 return new AndOperator(args); 279 else if ("or".equals(name)) 280 return new OrOperator(args); 281 else if ("length".equals(name) && args.size() == 1) 282 return new LengthFunction(args.get(0)); 283 else if ("max".equals(name) && !args.isEmpty()) 284 return new MinMaxFunction(args, true); 285 else if ("min".equals(name) && !args.isEmpty()) 286 return new MinMaxFunction(args, false); 287 else if ("inside".equals(name) && args.size() == 1) 288 return new IsInsideFunction(args.get(0)); 289 else if ("random".equals(name)) 290 return env -> Math.random(); 291 292 Factory factory = FACTORY_MAP.get(name); 293 if (factory != null) { 294 return factory.createExpression(args); 295 } 296 return NullExpression.INSTANCE; 297 } 298 299 /** 300 * Expression that always evaluates to null. 301 */ 302 public static class NullExpression implements Expression { 303 304 /** 305 * The unique instance. 306 */ 307 public static final NullExpression INSTANCE = new NullExpression(); 308 309 @Override 310 public Object evaluate(Environment env) { 311 return null; 312 } 313 } 314 315 /** 316 * Conditional operator. 317 */ 318 public static class CondOperator implements Expression { 319 320 private final Expression condition, firstOption, secondOption; 321 322 /** 323 * Constructs a new {@code CondOperator}. 324 * @param condition condition 325 * @param firstOption first option 326 * @param secondOption second option 327 */ 328 public CondOperator(Expression condition, Expression firstOption, Expression secondOption) { 329 this.condition = condition; 330 this.firstOption = firstOption; 331 this.secondOption = secondOption; 332 } 333 334 @Override 335 public Object evaluate(Environment env) { 336 Boolean b = Cascade.convertTo(condition.evaluate(env), boolean.class); 337 if (b != null && b) 338 return firstOption.evaluate(env); 339 else 340 return secondOption.evaluate(env); 341 } 342 } 343 344 /** 345 * "And" logical operator. 346 */ 347 public static class AndOperator implements Expression { 348 349 private final List<Expression> args; 350 351 /** 352 * Constructs a new {@code AndOperator}. 353 * @param args arguments 354 */ 355 public AndOperator(List<Expression> args) { 356 this.args = args; 357 } 358 359 @Override 360 public Object evaluate(Environment env) { 361 return args.stream() 362 .map(arg -> Cascade.convertTo(arg.evaluate(env), boolean.class)) 363 .allMatch(Boolean.TRUE::equals); 364 } 365 } 366 367 /** 368 * "Or" logical operator. 369 */ 370 public static class OrOperator implements Expression { 371 372 private final List<Expression> args; 373 374 /** 375 * Constructs a new {@code OrOperator}. 376 * @param args arguments 377 */ 378 public OrOperator(List<Expression> args) { 379 this.args = args; 380 } 381 382 @Override 383 public Object evaluate(Environment env) { 384 return args.stream() 385 .map(arg -> Cascade.convertTo(arg.evaluate(env), boolean.class)) 386 .anyMatch(Boolean.TRUE::equals); 387 } 388 } 389 390 /** 391 * Function to calculate the length of a string or list in a MapCSS eval expression. 392 * 393 * Separate implementation to support overloading for different argument types. 394 * 395 * The use for calculating the length of a list is deprecated, use 396 * {@link Functions#count(java.util.List)} instead (see #10061). 397 */ 398 public static class LengthFunction implements Expression { 399 400 private final Expression arg; 401 402 /** 403 * Constructs a new {@code LengthFunction}. 404 * @param args arguments 405 */ 406 public LengthFunction(Expression args) { 407 this.arg = args; 408 } 409 410 @Override 411 public Object evaluate(Environment env) { 412 List<?> l = Cascade.convertTo(arg.evaluate(env), List.class); 413 if (l != null) 414 return l.size(); 415 String s = Cascade.convertTo(arg.evaluate(env), String.class); 416 if (s != null) 417 return s.length(); 418 return null; 419 } 420 } 421 422 /** 423 * Computes the maximum/minimum value an arbitrary number of floats, or a list of floats. 424 */ 425 public static class MinMaxFunction implements Expression { 426 427 private final List<Expression> args; 428 private final boolean computeMax; 429 430 /** 431 * Constructs a new {@code MinMaxFunction}. 432 * @param args arguments 433 * @param computeMax if {@code true}, compute max. If {@code false}, compute min 434 */ 435 public MinMaxFunction(final List<Expression> args, final boolean computeMax) { 436 this.args = args; 437 this.computeMax = computeMax; 438 } 439 440 /** 441 * Compute the minimum / maximum over the list 442 * @param lst The list 443 * @return The minimum or maximum depending on {@link #computeMax} 444 */ 445 public Float aggregateList(List<?> lst) { 446 final List<Float> floats = Utils.transform(lst, (Function<Object, Float>) x -> Cascade.convertTo(x, float.class)); 447 final Collection<Float> nonNullList = SubclassFilteredCollection.filter(floats, Objects::nonNull); 448 return nonNullList.isEmpty() ? (Float) Float.NaN : computeMax ? Collections.max(nonNullList) : Collections.min(nonNullList); 449 } 450 451 @Override 452 public Object evaluate(final Environment env) { 453 List<?> l = Cascade.convertTo(args.get(0).evaluate(env), List.class); 454 if (args.size() != 1 || l == null) 455 l = Utils.transform(args, (Function<Expression, Object>) x -> x.evaluate(env)); 456 return aggregateList(l); 457 } 458 } 459 460 /** 461 * {@code Functions#inside} implementation for use in {@link org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker} 462 * 463 * @see Functions#inside 464 */ 465 public static class IsInsideFunction implements Expression { 466 private final Expression arg; 467 468 /** 469 * Constructs a new {@code IsInsideFunction}. 470 * @param arg argument 471 */ 472 public IsInsideFunction(Expression arg) { 473 this.arg = arg; 474 } 475 476 /** 477 * Returns the argument 478 * @return the argument 479 */ 480 public Expression getArg() { 481 return arg; 482 } 483 484 @Override 485 public Object evaluate(Environment env) { 486 String codes = Cascade.convertTo(arg.evaluate(env), String.class); 487 return Functions.inside(env, codes); 488 } 489 } 490}