001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.mappaint.mapcss; 003 004import java.awt.Color; 005import java.nio.charset.StandardCharsets; 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.Collection; 009import java.util.Collections; 010import java.util.List; 011import java.util.Locale; 012import java.util.Map.Entry; 013import java.util.Objects; 014import java.util.TreeSet; 015import java.util.regex.Matcher; 016import java.util.regex.Pattern; 017import java.util.stream.Collectors; 018import java.util.zip.CRC32; 019 020import org.openstreetmap.josm.data.coor.LatLon; 021import org.openstreetmap.josm.data.gpx.GpxDistance; 022import org.openstreetmap.josm.data.osm.IPrimitive; 023import org.openstreetmap.josm.data.osm.Node; 024import org.openstreetmap.josm.data.osm.OsmPrimitive; 025import org.openstreetmap.josm.data.osm.Relation; 026import org.openstreetmap.josm.data.osm.RelationMember; 027import org.openstreetmap.josm.data.osm.Way; 028import org.openstreetmap.josm.data.osm.search.SearchCompiler; 029import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match; 030import org.openstreetmap.josm.data.osm.search.SearchParseError; 031import org.openstreetmap.josm.gui.MainApplication; 032import org.openstreetmap.josm.gui.mappaint.Cascade; 033import org.openstreetmap.josm.gui.mappaint.Environment; 034import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 035import org.openstreetmap.josm.gui.mappaint.mapcss.ExpressionFactory.NullableArguments; 036import org.openstreetmap.josm.io.XmlWriter; 037import org.openstreetmap.josm.tools.AlphanumComparator; 038import org.openstreetmap.josm.tools.ColorHelper; 039import org.openstreetmap.josm.tools.Geometry; 040import org.openstreetmap.josm.tools.Logging; 041import org.openstreetmap.josm.tools.RightAndLefthandTraffic; 042import org.openstreetmap.josm.tools.RotationAngle; 043import org.openstreetmap.josm.tools.Territories; 044import org.openstreetmap.josm.tools.Utils; 045 046/** 047 * List of functions that can be used in MapCSS expressions. 048 * 049 * First parameter can be of type {@link Environment} (if needed). This is 050 * automatically filled in by JOSM and the user only sees the remaining arguments. 051 * When one of the user supplied arguments cannot be converted the 052 * expected type or is null, the function is not called and it returns null 053 * immediately. Add the annotation {@link NullableArguments} to allow null arguments. 054 * Every method must be static. 055 * 056 * @since 15245 (extracted from {@link ExpressionFactory}) 057 */ 058@SuppressWarnings("UnusedDeclaration") 059public final class Functions { 060 061 private Functions() { 062 // Hide implicit public constructor for utility classes 063 } 064 065 /** 066 * Identity function for compatibility with MapCSS specification. 067 * @param o any object 068 * @return {@code o} unchanged 069 */ 070 public static Object eval(Object o) { // NO_UCD (unused code) 071 return o; 072 } 073 074 /** 075 * Function associated to the numeric "+" operator. 076 * @param args arguments 077 * @return Sum of arguments 078 */ 079 public static float plus(float... args) { // NO_UCD (unused code) 080 float res = 0; 081 for (float f : args) { 082 res += f; 083 } 084 return res; 085 } 086 087 /** 088 * Function associated to the numeric "-" operator. 089 * @param args arguments 090 * @return Substraction of arguments 091 */ 092 public static Float minus(float... args) { // NO_UCD (unused code) 093 if (args.length == 0) { 094 return 0.0F; 095 } 096 if (args.length == 1) { 097 return -args[0]; 098 } 099 float res = args[0]; 100 for (int i = 1; i < args.length; ++i) { 101 res -= args[i]; 102 } 103 return res; 104 } 105 106 /** 107 * Function associated to the numeric "*" operator. 108 * @param args arguments 109 * @return Multiplication of arguments 110 */ 111 public static float times(float... args) { // NO_UCD (unused code) 112 float res = 1; 113 for (float f : args) { 114 res *= f; 115 } 116 return res; 117 } 118 119 /** 120 * Function associated to the numeric "/" operator. 121 * @param args arguments 122 * @return Division of arguments 123 */ 124 public static Float divided_by(float... args) { // NO_UCD (unused code) 125 if (args.length == 0) { 126 return 1.0F; 127 } 128 float res = args[0]; 129 for (int i = 1; i < args.length; ++i) { 130 if (args[i] == 0) { 131 return null; 132 } 133 res /= args[i]; 134 } 135 return res; 136 } 137 138 /** 139 * Creates a list of values, e.g., for the {@code dashes} property. 140 * @param args The values to put in a list 141 * @return list of values 142 * @see Arrays#asList(Object[]) 143 */ 144 public static List<Object> list(Object... args) { // NO_UCD (unused code) 145 return Arrays.asList(args); 146 } 147 148 /** 149 * Returns the number of elements in a list. 150 * @param lst the list 151 * @return length of the list 152 */ 153 public static Integer count(List<?> lst) { // NO_UCD (unused code) 154 return lst.size(); 155 } 156 157 /** 158 * Returns the first non-null object. 159 * The name originates from <a href="http://wiki.openstreetmap.org/wiki/MapCSS/0.2/eval">MapCSS standard</a>. 160 * @param args arguments 161 * @return the first non-null object 162 * @see Utils#firstNonNull(Object[]) 163 */ 164 @NullableArguments 165 public static Object any(Object... args) { // NO_UCD (unused code) 166 return Utils.firstNonNull(args); 167 } 168 169 /** 170 * Get the {@code n}th element of the list {@code lst} (counting starts at 0). 171 * @param lst list 172 * @param n index 173 * @return {@code n}th element of the list, or {@code null} if index out of range 174 * @since 5699 175 */ 176 public static Object get(List<?> lst, float n) { // NO_UCD (unused code) 177 int idx = Math.round(n); 178 if (idx >= 0 && idx < lst.size()) { 179 return lst.get(idx); 180 } 181 return null; 182 } 183 184 /** 185 * Splits string {@code toSplit} at occurrences of the separator string {@code sep} and returns a list of matches. 186 * @param sep separator string 187 * @param toSplit string to split 188 * @return list of matches 189 * @see String#split(String) 190 * @since 5699 191 */ 192 public static List<String> split(String sep, String toSplit) { // NO_UCD (unused code) 193 return Arrays.asList(toSplit.split(Pattern.quote(sep), -1)); 194 } 195 196 /** 197 * Creates a color value with the specified amounts of {@code r}ed, {@code g}reen, {@code b}lue (arguments from 0.0 to 1.0) 198 * @param r the red component 199 * @param g the green component 200 * @param b the blue component 201 * @return color matching the given components 202 * @see Color#Color(float, float, float) 203 */ 204 public static Color rgb(float r, float g, float b) { // NO_UCD (unused code) 205 try { 206 return new Color(r, g, b); 207 } catch (IllegalArgumentException e) { 208 Logging.trace(e); 209 return null; 210 } 211 } 212 213 /** 214 * Creates a color value with the specified amounts of {@code r}ed, {@code g}reen, {@code b}lue, {@code alpha} 215 * (arguments from 0.0 to 1.0) 216 * @param r the red component 217 * @param g the green component 218 * @param b the blue component 219 * @param alpha the alpha component 220 * @return color matching the given components 221 * @see Color#Color(float, float, float, float) 222 */ 223 public static Color rgba(float r, float g, float b, float alpha) { // NO_UCD (unused code) 224 try { 225 return new Color(r, g, b, alpha); 226 } catch (IllegalArgumentException e) { 227 Logging.trace(e); 228 return null; 229 } 230 } 231 232 /** 233 * Create color from hsb color model. (arguments form 0.0 to 1.0) 234 * @param h hue 235 * @param s saturation 236 * @param b brightness 237 * @return the corresponding color 238 */ 239 public static Color hsb_color(float h, float s, float b) { // NO_UCD (unused code) 240 try { 241 return Color.getHSBColor(h, s, b); 242 } catch (IllegalArgumentException e) { 243 Logging.trace(e); 244 return null; 245 } 246 } 247 248 /** 249 * Creates a color value from an HTML notation, i.e., {@code #rrggbb}. 250 * @param html HTML notation 251 * @return color matching the given notation 252 */ 253 public static Color html2color(String html) { // NO_UCD (unused code) 254 return ColorHelper.html2color(html); 255 } 256 257 /** 258 * Computes the HTML notation ({@code #rrggbb}) for a color value). 259 * @param c color 260 * @return HTML notation matching the given color 261 */ 262 public static String color2html(Color c) { // NO_UCD (unused code) 263 return ColorHelper.color2html(c); 264 } 265 266 /** 267 * Get the value of the red color channel in the rgb color model 268 * @param c color 269 * @return the red color channel in the range [0;1] 270 * @see java.awt.Color#getRed() 271 */ 272 public static float red(Color c) { // NO_UCD (unused code) 273 return Utils.colorInt2float(c.getRed()); 274 } 275 276 /** 277 * Get the value of the green color channel in the rgb color model 278 * @param c color 279 * @return the green color channel in the range [0;1] 280 * @see java.awt.Color#getGreen() 281 */ 282 public static float green(Color c) { // NO_UCD (unused code) 283 return Utils.colorInt2float(c.getGreen()); 284 } 285 286 /** 287 * Get the value of the blue color channel in the rgb color model 288 * @param c color 289 * @return the blue color channel in the range [0;1] 290 * @see java.awt.Color#getBlue() 291 */ 292 public static float blue(Color c) { // NO_UCD (unused code) 293 return Utils.colorInt2float(c.getBlue()); 294 } 295 296 /** 297 * Get the value of the alpha channel in the rgba color model 298 * @param c color 299 * @return the alpha channel in the range [0;1] 300 * @see java.awt.Color#getAlpha() 301 */ 302 public static float alpha(Color c) { // NO_UCD (unused code) 303 return Utils.colorInt2float(c.getAlpha()); 304 } 305 306 /** 307 * Assembles the strings to one. 308 * @param args arguments 309 * @return assembled string 310 * @see Collectors#joining 311 */ 312 @NullableArguments 313 public static String concat(Object... args) { // NO_UCD (unused code) 314 return Arrays.stream(args) 315 .filter(Objects::nonNull) 316 .map(String::valueOf) 317 .collect(Collectors.joining()); 318 } 319 320 /** 321 * Assembles the strings to one, where the first entry is used as separator. 322 * @param args arguments. First one is used as separator 323 * @return assembled string 324 * @see String#join 325 */ 326 @NullableArguments 327 public static String join(String... args) { // NO_UCD (unused code) 328 return String.join(args[0], Arrays.asList(args).subList(1, args.length)); 329 } 330 331 /** 332 * Joins a list of {@code values} into a single string with fields separated by {@code separator}. 333 * @param separator the separator 334 * @param values collection of objects 335 * @return assembled string 336 * @see String#join 337 */ 338 public static String join_list(final String separator, final List<String> values) { // NO_UCD (unused code) 339 return String.join(separator, values); 340 } 341 342 /** 343 * Returns the value of the property {@code key}, e.g., {@code prop("width")}. 344 * @param env the environment 345 * @param key the property key 346 * @return the property value 347 */ 348 public static Object prop(final Environment env, String key) { // NO_UCD (unused code) 349 return prop(env, key, null); 350 } 351 352 /** 353 * Returns the value of the property {@code key} from layer {@code layer}. 354 * @param env the environment 355 * @param key the property key 356 * @param layer layer 357 * @return the property value 358 */ 359 public static Object prop(final Environment env, String key, String layer) { 360 return env.getCascade(layer).get(key); 361 } 362 363 /** 364 * Determines whether property {@code key} is set. 365 * @param env the environment 366 * @param key the property key 367 * @return {@code true} if the property is set, {@code false} otherwise 368 */ 369 public static Boolean is_prop_set(final Environment env, String key) { // NO_UCD (unused code) 370 return is_prop_set(env, key, null); 371 } 372 373 /** 374 * Determines whether property {@code key} is set on layer {@code layer}. 375 * @param env the environment 376 * @param key the property key 377 * @param layer layer 378 * @return {@code true} if the property is set, {@code false} otherwise 379 */ 380 public static Boolean is_prop_set(final Environment env, String key, String layer) { 381 return env.getCascade(layer).containsKey(key); 382 } 383 384 /** 385 * Gets the value of the key {@code key} from the object in question. 386 * @param env the environment 387 * @param key the OSM key 388 * @return the value for given key 389 */ 390 public static String tag(final Environment env, String key) { // NO_UCD (unused code) 391 return env.osm == null ? null : env.osm.get(key); 392 } 393 394 /** 395 * Get keys that follow a regex 396 * @param env the environment 397 * @param keyRegex the pattern that the key must match 398 * @return the values for the keys that match the pattern 399 * @see Functions#tag_regex(Environment, String, String) 400 * @since 15315 401 */ 402 public static List<String> tag_regex(final Environment env, String keyRegex) { // NO_UCD (unused code) 403 return tag_regex(env, keyRegex, ""); 404 } 405 406 /** 407 * Get keys that follow a regex 408 * @param env the environment 409 * @param keyRegex the pattern that the key must match 410 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all") 411 * @return the values for the keys that match the pattern 412 * @see Pattern#CASE_INSENSITIVE 413 * @see Pattern#DOTALL 414 * @see Pattern#MULTILINE 415 * @since 15315 416 */ 417 public static List<String> tag_regex(final Environment env, String keyRegex, String flags) { // NO_UCD (unused code) 418 int f = parse_regex_flags(flags); 419 Pattern compiled = Pattern.compile(keyRegex, f); 420 return env.osm.getKeys().entrySet().stream() 421 .filter(object -> compiled.matcher(object.getKey()).find()) 422 .map(Entry::getValue).collect(Collectors.toList()); 423 } 424 425 /** 426 * Parse flags for regex usage. Shouldn't be used in mapcss 427 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all") 428 * @return An int that can be used by a {@link Pattern} object 429 * @see Pattern#CASE_INSENSITIVE 430 * @see Pattern#DOTALL 431 * @see Pattern#MULTILINE 432 */ 433 private static int parse_regex_flags(String flags) { 434 int f = 0; 435 if (flags.contains("i")) { 436 f |= Pattern.CASE_INSENSITIVE; 437 } 438 if (flags.contains("s")) { 439 f |= Pattern.DOTALL; 440 } 441 if (flags.contains("m")) { 442 f |= Pattern.MULTILINE; 443 } 444 return f; 445 } 446 447 /** 448 * Gets the first non-null value of the key {@code key} from the object's parent(s). 449 * @param env the environment 450 * @param key the OSM key 451 * @return first non-null value of the key {@code key} from the object's parent(s) 452 */ 453 public static String parent_tag(final Environment env, String key) { // NO_UCD (unused code) 454 if (env.parent == null) { 455 if (env.osm != null) { 456 // we don't have a matched parent, so just search all referrers 457 for (IPrimitive parent : env.osm.getReferrers()) { 458 String value = parent.get(key); 459 if (value != null) { 460 return value; 461 } 462 } 463 } 464 return null; 465 } 466 return env.parent.get(key); 467 } 468 469 /** 470 * Gets a list of all non-null values of the key {@code key} from the object's parent(s). 471 * 472 * The values are sorted according to {@link AlphanumComparator}. 473 * @param env the environment 474 * @param key the OSM key 475 * @return a list of non-null values of the key {@code key} from the object's parent(s) 476 */ 477 public static List<String> parent_tags(final Environment env, String key) { // NO_UCD (unused code) 478 if (env.parent == null) { 479 if (env.osm != null) { 480 final Collection<String> tags = new TreeSet<>(AlphanumComparator.getInstance()); 481 // we don't have a matched parent, so just search all referrers 482 for (IPrimitive parent : env.osm.getReferrers()) { 483 String value = parent.get(key); 484 if (value != null) { 485 tags.add(value); 486 } 487 } 488 return new ArrayList<>(tags); 489 } 490 return Collections.emptyList(); 491 } 492 return Collections.singletonList(env.parent.get(key)); 493 } 494 495 /** 496 * Gets the value of the key {@code key} from the object's child. 497 * @param env the environment 498 * @param key the OSM key 499 * @return the value of the key {@code key} from the object's child, or {@code null} if there is no child 500 */ 501 public static String child_tag(final Environment env, String key) { // NO_UCD (unused code) 502 return env.child == null ? null : env.child.get(key); 503 } 504 505 /** 506 * Returns the OSM id of the object's parent. 507 * <p> 508 * Parent must be matched by child selector. 509 * @param env the environment 510 * @return the OSM id of the object's parent, if available, or {@code null} 511 * @see IPrimitive#getUniqueId() 512 */ 513 public static Long parent_osm_id(final Environment env) { // NO_UCD (unused code) 514 return env.parent == null ? null : env.parent.getUniqueId(); 515 } 516 517 /** 518 * Returns the lowest distance between the OSM object and a GPX point 519 * <p> 520 * @param env the environment 521 * @return the distance between the object and the closest gpx point or {@code Double.MAX_VALUE} 522 * @since 14802 523 */ 524 public static double gpx_distance(final Environment env) { // NO_UCD (unused code) 525 if (env.osm instanceof OsmPrimitive) { 526 return MainApplication.getLayerManager().getAllGpxData().stream() 527 .mapToDouble(gpx -> GpxDistance.getLowestDistance((OsmPrimitive) env.osm, gpx)) 528 .min().orElse(Double.MAX_VALUE); 529 } 530 return Double.MAX_VALUE; 531 } 532 533 /** 534 * Determines whether the object has a tag with the given key. 535 * @param env the environment 536 * @param key the OSM key 537 * @return {@code true} if the object has a tag with the given key, {@code false} otherwise 538 */ 539 public static boolean has_tag_key(final Environment env, String key) { // NO_UCD (unused code) 540 return env.osm.hasKey(key); 541 } 542 543 /** 544 * Returns the index of node in parent way or member in parent relation. 545 * @param env the environment 546 * @return the index as float. Starts at 1 547 */ 548 public static Float index(final Environment env) { // NO_UCD (unused code) 549 if (env.index == null) { 550 return null; 551 } 552 return Float.valueOf(env.index + 1f); 553 } 554 555 /** 556 * Sort an array of strings 557 * @param sortables The array to sort 558 * @return The sorted list 559 * @since 15279 560 */ 561 public static List<String> sort(String... sortables) { // NO_UCD (unused code) 562 Arrays.parallelSort(sortables); 563 return Arrays.asList(sortables); 564 } 565 566 /** 567 * Sort a list of strings 568 * @param sortables The list to sort 569 * @return The sorted list 570 * @since 15279 571 */ 572 public static List<String> sort_list(List<String> sortables) { // NO_UCD (unused code) 573 Collections.sort(sortables); 574 return sortables; 575 } 576 577 /** 578 * Get unique values 579 * @param values A list of values that may have duplicates 580 * @return A list with no duplicates 581 * @since 15323 582 */ 583 public static List<String> uniq(String... values) { // NO_UCD (unused code) 584 return uniq_list(Arrays.asList(values)); 585 } 586 587 /** 588 * Get unique values 589 * @param values A list of values that may have duplicates 590 * @return A list with no duplicates 591 * @since 15323 592 */ 593 public static List<String> uniq_list(List<String> values) { 594 return values.stream().distinct().collect(Collectors.toList()); 595 } 596 597 /** 598 * Returns the role of current object in parent relation, or role of child if current object is a relation. 599 * @param env the environment 600 * @return role of current object in parent relation, or role of child if current object is a relation 601 * @see Environment#getRole() 602 */ 603 public static String role(final Environment env) { // NO_UCD (unused code) 604 return env.getRole(); 605 } 606 607 /** 608 * Returns the number of primitives in a relation with the specified roles. 609 * @param env the environment 610 * @param roles The roles to count in the relation 611 * @return The number of relation members with the specified role 612 * @since 15196 613 */ 614 public static int count_roles(final Environment env, String... roles) { // NO_UCD (unused code) 615 int rValue = 0; 616 if (env.osm instanceof Relation) { 617 List<String> roleList = Arrays.asList(roles); 618 Relation rel = (Relation) env.osm; 619 for (RelationMember member : rel.getMembers()) { 620 if (roleList.contains(member.getRole())) rValue++; 621 } 622 } 623 return rValue; 624 } 625 626 /** 627 * Returns the area of a closed way or multipolygon in square meters or {@code null}. 628 * @param env the environment 629 * @return the area of a closed way or multipolygon in square meters or {@code null} 630 * @see Geometry#computeArea(IPrimitive) 631 */ 632 public static Float areasize(final Environment env) { // NO_UCD (unused code) 633 final Double area = Geometry.computeArea(env.osm); 634 return area == null ? null : area.floatValue(); 635 } 636 637 /** 638 * Returns the length of the way in metres or {@code null}. 639 * @param env the environment 640 * @return the length of the way in metres or {@code null}. 641 * @see Way#getLength() 642 */ 643 public static Float waylength(final Environment env) { // NO_UCD (unused code) 644 if (env.osm instanceof Way) { 645 return (float) ((Way) env.osm).getLength(); 646 } else { 647 return null; 648 } 649 } 650 651 /** 652 * Function associated to the logical "!" operator. 653 * @param b boolean value 654 * @return {@code true} if {@code !b} 655 */ 656 public static boolean not(boolean b) { // NO_UCD (unused code) 657 return !b; 658 } 659 660 /** 661 * Function associated to the logical ">=" operator. 662 * @param a first value 663 * @param b second value 664 * @return {@code true} if {@code a >= b} 665 */ 666 public static boolean greater_equal(float a, float b) { // NO_UCD (unused code) 667 return a >= b; 668 } 669 670 /** 671 * Function associated to the logical "<=" operator. 672 * @param a first value 673 * @param b second value 674 * @return {@code true} if {@code a <= b} 675 */ 676 public static boolean less_equal(float a, float b) { // NO_UCD (unused code) 677 return a <= b; 678 } 679 680 /** 681 * Function associated to the logical ">" operator. 682 * @param a first value 683 * @param b second value 684 * @return {@code true} if {@code a > b} 685 */ 686 public static boolean greater(float a, float b) { // NO_UCD (unused code) 687 return a > b; 688 } 689 690 /** 691 * Function associated to the logical "<" operator. 692 * @param a first value 693 * @param b second value 694 * @return {@code true} if {@code a < b} 695 */ 696 public static boolean less(float a, float b) { // NO_UCD (unused code) 697 return a < b; 698 } 699 700 /** 701 * Converts an angle in degrees to radians. 702 * @param degree the angle in degrees 703 * @return the angle in radians 704 * @see Math#toRadians(double) 705 */ 706 public static double degree_to_radians(double degree) { // NO_UCD (unused code) 707 return Utils.toRadians(degree); 708 } 709 710 /** 711 * Converts an angle diven in cardinal directions to radians. 712 * The following values are supported: {@code n}, {@code north}, {@code ne}, {@code northeast}, 713 * {@code e}, {@code east}, {@code se}, {@code southeast}, {@code s}, {@code south}, 714 * {@code sw}, {@code southwest}, {@code w}, {@code west}, {@code nw}, {@code northwest}. 715 * @param cardinal the angle in cardinal directions. 716 * @return the angle in radians 717 * @see RotationAngle#parseCardinalRotation(String) 718 */ 719 public static Double cardinal_to_radians(String cardinal) { // NO_UCD (unused code) 720 try { 721 return RotationAngle.parseCardinalRotation(cardinal); 722 } catch (IllegalArgumentException ignore) { 723 Logging.trace(ignore); 724 return null; 725 } 726 } 727 728 /** 729 * Determines if the objects {@code a} and {@code b} are equal. 730 * @param a First object 731 * @param b Second object 732 * @return {@code true} if objects are equal, {@code false} otherwise 733 * @see Object#equals(Object) 734 */ 735 public static boolean equal(Object a, Object b) { 736 if (a.getClass() == b.getClass()) return a.equals(b); 737 if (a.equals(Cascade.convertTo(b, a.getClass()))) return true; 738 return b.equals(Cascade.convertTo(a, b.getClass())); 739 } 740 741 /** 742 * Determines if the objects {@code a} and {@code b} are not equal. 743 * @param a First object 744 * @param b Second object 745 * @return {@code false} if objects are equal, {@code true} otherwise 746 * @see Object#equals(Object) 747 */ 748 public static boolean not_equal(Object a, Object b) { // NO_UCD (unused code) 749 return !equal(a, b); 750 } 751 752 /** 753 * Determines whether the JOSM search with {@code searchStr} applies to the object. 754 * @param env the environment 755 * @param searchStr the search string 756 * @return {@code true} if the JOSM search with {@code searchStr} applies to the object 757 * @see SearchCompiler 758 */ 759 public static Boolean JOSM_search(final Environment env, String searchStr) { // NO_UCD (unused code) 760 Match m; 761 try { 762 m = SearchCompiler.compile(searchStr); 763 } catch (SearchParseError ex) { 764 Logging.trace(ex); 765 return null; 766 } 767 return m.match(env.osm); 768 } 769 770 /** 771 * Obtains the JOSM'key {@link org.openstreetmap.josm.data.Preferences} string for key {@code key}, 772 * and defaults to {@code def} if that is null. 773 * @param env the environment 774 * @param key Key in JOSM preference 775 * @param def Default value 776 * @return value for key, or default value if not found 777 */ 778 public static String JOSM_pref(Environment env, String key, String def) { // NO_UCD (unused code) 779 return MapPaintStyles.getStyles().getPreferenceCached(key, def); 780 } 781 782 /** 783 * Tests if string {@code target} matches pattern {@code pattern} 784 * @param pattern The regex expression 785 * @param target The character sequence to be matched 786 * @return {@code true} if, and only if, the entire region sequence matches the pattern 787 * @see Pattern#matches(String, CharSequence) 788 * @since 5699 789 */ 790 public static boolean regexp_test(String pattern, String target) { // NO_UCD (unused code) 791 return Pattern.matches(pattern, target); 792 } 793 794 /** 795 * Tests if string {@code target} matches pattern {@code pattern} 796 * @param pattern The regex expression 797 * @param target The character sequence to be matched 798 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all") 799 * @return {@code true} if, and only if, the entire region sequence matches the pattern 800 * @see Pattern#CASE_INSENSITIVE 801 * @see Pattern#DOTALL 802 * @see Pattern#MULTILINE 803 * @since 5699 804 */ 805 public static boolean regexp_test(String pattern, String target, String flags) { // NO_UCD (unused code) 806 int f = parse_regex_flags(flags); 807 return Pattern.compile(pattern, f).matcher(target).matches(); 808 } 809 810 /** 811 * Tries to match string against pattern regexp and returns a list of capture groups in case of success. 812 * The first element (index 0) is the complete match (i.e. string). 813 * Further elements correspond to the bracketed parts of the regular expression. 814 * @param pattern The regex expression 815 * @param target The character sequence to be matched 816 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all") 817 * @return a list of capture groups if {@link Matcher#matches()}, or {@code null}. 818 * @see Pattern#CASE_INSENSITIVE 819 * @see Pattern#DOTALL 820 * @see Pattern#MULTILINE 821 * @since 5701 822 */ 823 public static List<String> regexp_match(String pattern, String target, String flags) { // NO_UCD (unused code) 824 int f = parse_regex_flags(flags); 825 return Utils.getMatches(Pattern.compile(pattern, f).matcher(target)); 826 } 827 828 /** 829 * Tries to match string against pattern regexp and returns a list of capture groups in case of success. 830 * The first element (index 0) is the complete match (i.e. string). 831 * Further elements correspond to the bracketed parts of the regular expression. 832 * @param pattern The regex expression 833 * @param target The character sequence to be matched 834 * @return a list of capture groups if {@link Matcher#matches()}, or {@code null}. 835 * @since 5701 836 */ 837 public static List<String> regexp_match(String pattern, String target) { // NO_UCD (unused code) 838 return Utils.getMatches(Pattern.compile(pattern).matcher(target)); 839 } 840 841 /** 842 * Returns the OSM id of the current object. 843 * @param env the environment 844 * @return the OSM id of the current object 845 * @see IPrimitive#getUniqueId() 846 */ 847 public static long osm_id(final Environment env) { // NO_UCD (unused code) 848 return env.osm.getUniqueId(); 849 } 850 851 /** 852 * Returns the OSM user name who last touched the current object. 853 * @param env the environment 854 * @return the OSM user name who last touched the current object 855 * @see IPrimitive#getUser 856 * @since 15246 857 */ 858 public static String osm_user_name(final Environment env) { // NO_UCD (unused code) 859 return env.osm.getUser().getName(); 860 } 861 862 /** 863 * Returns the OSM user id who last touched the current object. 864 * @param env the environment 865 * @return the OSM user id who last touched the current object 866 * @see IPrimitive#getUser 867 * @since 15246 868 */ 869 public static long osm_user_id(final Environment env) { // NO_UCD (unused code) 870 return env.osm.getUser().getId(); 871 } 872 873 /** 874 * Returns the version number of the current object. 875 * @param env the environment 876 * @return the version number of the current object 877 * @see IPrimitive#getVersion 878 * @since 15246 879 */ 880 public static int osm_version(final Environment env) { // NO_UCD (unused code) 881 return env.osm.getVersion(); 882 } 883 884 /** 885 * Returns the id of the changeset the current object was last uploaded to. 886 * @param env the environment 887 * @return the id of the changeset the current object was last uploaded to 888 * @see IPrimitive#getChangesetId 889 * @since 15246 890 */ 891 public static int osm_changeset_id(final Environment env) { // NO_UCD (unused code) 892 return env.osm.getChangesetId(); 893 } 894 895 /** 896 * Returns the time of last modification to the current object, as timestamp. 897 * @param env the environment 898 * @return the time of last modification to the current object, as timestamp 899 * @see IPrimitive#getRawTimestamp 900 * @since 15246 901 */ 902 public static int osm_timestamp(final Environment env) { // NO_UCD (unused code) 903 return env.osm.getRawTimestamp(); 904 } 905 906 /** 907 * Translates some text for the current locale. The first argument is the text to translate, 908 * and the subsequent arguments are parameters for the string indicated by <code>{0}</code>, <code>{1}</code>, … 909 * @param args arguments 910 * @return the translated string 911 */ 912 @NullableArguments 913 public static String tr(String... args) { // NO_UCD (unused code) 914 final String text = args[0]; 915 System.arraycopy(args, 1, args, 0, args.length - 1); 916 return org.openstreetmap.josm.tools.I18n.tr(text, (Object[]) args); 917 } 918 919 /** 920 * Returns the substring of {@code s} starting at index {@code begin} (inclusive, 0-indexed). 921 * @param s The base string 922 * @param begin The start index 923 * @return the substring 924 * @see String#substring(int) 925 */ 926 public static String substring(String s, /* due to missing Cascade.convertTo for int*/ float begin) { // NO_UCD (unused code) 927 return s == null ? null : s.substring((int) begin); 928 } 929 930 /** 931 * Returns the substring of {@code s} starting at index {@code begin} (inclusive) 932 * and ending at index {@code end}, (exclusive, 0-indexed). 933 * @param s The base string 934 * @param begin The start index 935 * @param end The end index 936 * @return the substring 937 * @see String#substring(int, int) 938 */ 939 public static String substring(String s, float begin, float end) { // NO_UCD (unused code) 940 return s == null ? null : s.substring((int) begin, (int) end); 941 } 942 943 /** 944 * Replaces in {@code s} every {@code} target} substring by {@code replacement}. 945 * @param s The source string 946 * @param target The sequence of char values to be replaced 947 * @param replacement The replacement sequence of char values 948 * @return The resulting string 949 * @see String#replace(CharSequence, CharSequence) 950 */ 951 public static String replace(String s, String target, String replacement) { // NO_UCD (unused code) 952 return s == null ? null : s.replace(target, replacement); 953 } 954 955 /** 956 * Converts string {@code s} to uppercase. 957 * @param s The source string 958 * @return The resulting string 959 * @see String#toUpperCase(Locale) 960 * @since 11756 961 */ 962 public static String upper(String s) { 963 return s == null ? null : s.toUpperCase(Locale.ENGLISH); 964 } 965 966 /** 967 * Converts string {@code s} to lowercase. 968 * @param s The source string 969 * @return The resulting string 970 * @see String#toLowerCase(Locale) 971 * @since 11756 972 */ 973 public static String lower(String s) { 974 return s == null ? null : s.toLowerCase(Locale.ENGLISH); 975 } 976 977 /** 978 * Trim whitespaces from the string {@code s}. 979 * @param s The source string 980 * @return The resulting string 981 * @see Utils#strip 982 * @since 11756 983 */ 984 public static String trim(String s) { 985 return Utils.strip(s); 986 } 987 988 /** 989 * Trim whitespaces from the strings {@code strings}. 990 * 991 * @param strings The list of strings to strip 992 * @return The resulting string 993 * @see Utils#strip 994 * @since 15591 995 */ 996 public static List<String> trim_list(List<String> strings) { 997 return strings.stream().map(Utils::strip).filter(str -> !str.isEmpty()).collect(Collectors.toList()); 998 } 999 1000 /** 1001 * Check if two strings are similar, but not identical, i.e., have a Levenshtein distance of 1 or 2. 1002 * @param string1 first string to compare 1003 * @param string2 second string to compare 1004 * @return true if the normalized strings are different but only a "little bit" 1005 * @see Utils#isSimilar 1006 * @since 14371 1007 */ 1008 public static boolean is_similar(String string1, String string2) { 1009 return Utils.isSimilar(string1, string2); 1010 } 1011 1012 /** 1013 * Percent-decode a string. (See https://en.wikipedia.org/wiki/Percent-encoding) 1014 * This is especially useful for wikipedia titles 1015 * @param s url-encoded string 1016 * @return the decoded string, or original in case of an error 1017 * @since 11756 1018 */ 1019 public static String URL_decode(String s) { 1020 if (s == null) return null; 1021 try { 1022 return Utils.decodeUrl(s); 1023 } catch (IllegalStateException e) { 1024 Logging.debug(e); 1025 return s; 1026 } 1027 } 1028 1029 /** 1030 * Percent-encode a string. (See https://en.wikipedia.org/wiki/Percent-encoding) 1031 * This is especially useful for data urls, e.g. 1032 * <code>concat("data:image/svg+xml,", URL_encode("<svg>...</svg>"));</code> 1033 * @param s arbitrary string 1034 * @return the encoded string 1035 */ 1036 public static String URL_encode(String s) { // NO_UCD (unused code) 1037 return s == null ? null : Utils.encodeUrl(s); 1038 } 1039 1040 /** 1041 * XML-encode a string. 1042 * 1043 * Escapes special characters in xml. Alternative to using <![CDATA[ ... ]]> blocks. 1044 * @param s arbitrary string 1045 * @return the encoded string 1046 */ 1047 public static String XML_encode(String s) { // NO_UCD (unused code) 1048 return s == null ? null : XmlWriter.encode(s); 1049 } 1050 1051 /** 1052 * Calculates the CRC32 checksum from a string (based on RFC 1952). 1053 * @param s the string 1054 * @return long value from 0 to 2^32-1 1055 */ 1056 public static long CRC32_checksum(String s) { // NO_UCD (unused code) 1057 CRC32 cs = new CRC32(); 1058 cs.update(s.getBytes(StandardCharsets.UTF_8)); 1059 return cs.getValue(); 1060 } 1061 1062 /** 1063 * check if there is right-hand traffic at the current location 1064 * @param env the environment 1065 * @return true if there is right-hand traffic 1066 * @since 7193 1067 */ 1068 public static boolean is_right_hand_traffic(Environment env) { 1069 return RightAndLefthandTraffic.isRightHandTraffic(center(env)); 1070 } 1071 1072 /** 1073 * Determines whether the way is {@link Geometry#isClockwise closed and oriented clockwise}, 1074 * or non-closed and the {@link Geometry#angleIsClockwise 1st, 2nd and last node are in clockwise order}. 1075 * 1076 * @param env the environment 1077 * @return true if the way is closed and oriented clockwise 1078 */ 1079 public static boolean is_clockwise(Environment env) { 1080 if (!(env.osm instanceof Way)) { 1081 return false; 1082 } 1083 final Way way = (Way) env.osm; 1084 return (way.isClosed() && Geometry.isClockwise(way)) 1085 || (!way.isClosed() && way.getNodesCount() > 2 && Geometry.angleIsClockwise(way.getNode(0), way.getNode(1), way.lastNode())); 1086 } 1087 1088 /** 1089 * Determines whether the way is {@link Geometry#isClockwise closed and oriented anticlockwise}, 1090 * or non-closed and the {@link Geometry#angleIsClockwise 1st, 2nd and last node are in anticlockwise order}. 1091 * 1092 * @param env the environment 1093 * @return true if the way is closed and oriented clockwise 1094 */ 1095 public static boolean is_anticlockwise(Environment env) { 1096 if (!(env.osm instanceof Way)) { 1097 return false; 1098 } 1099 final Way way = (Way) env.osm; 1100 return (way.isClosed() && !Geometry.isClockwise(way)) 1101 || (!way.isClosed() && way.getNodesCount() > 2 && !Geometry.angleIsClockwise(way.getNode(0), way.getNode(1), way.lastNode())); 1102 } 1103 1104 /** 1105 * Prints the object to the command line (for debugging purpose). 1106 * @param o the object 1107 * @return the same object, unchanged 1108 */ 1109 @NullableArguments 1110 public static Object print(Object o) { // NO_UCD (unused code) 1111 System.out.print(o == null ? "none" : o.toString()); 1112 return o; 1113 } 1114 1115 /** 1116 * Prints the object to the command line, with new line at the end 1117 * (for debugging purpose). 1118 * @param o the object 1119 * @return the same object, unchanged 1120 */ 1121 @NullableArguments 1122 public static Object println(Object o) { // NO_UCD (unused code) 1123 System.out.println(o == null ? "none" : o.toString()); 1124 return o; 1125 } 1126 1127 /** 1128 * Get the number of tags for the current primitive. 1129 * @param env the environment 1130 * @return number of tags 1131 */ 1132 public static int number_of_tags(Environment env) { // NO_UCD (unused code) 1133 return env.osm.getNumKeys(); 1134 } 1135 1136 /** 1137 * Get value of a setting. 1138 * @param env the environment 1139 * @param key setting key (given as layer identifier, e.g. setting::mykey {...}) 1140 * @return the value of the setting (calculated when the style is loaded) 1141 */ 1142 public static Object setting(Environment env, String key) { // NO_UCD (unused code) 1143 return env.source.settingValues.get(key); 1144 } 1145 1146 /** 1147 * Returns the center of the environment OSM primitive. 1148 * @param env the environment 1149 * @return the center of the environment OSM primitive 1150 * @since 11247 1151 */ 1152 public static LatLon center(Environment env) { // NO_UCD (unused code) 1153 return env.osm instanceof Node ? ((Node) env.osm).getCoor() : env.osm.getBBox().getCenter(); 1154 } 1155 1156 /** 1157 * Determines if the object is inside territories matching given ISO3166 codes. 1158 * @param env the environment 1159 * @param codes comma-separated list of ISO3166-1-alpha2 or ISO3166-2 country/subdivision codes 1160 * @return {@code true} if the object is inside territory matching given ISO3166 codes 1161 * @since 11247 1162 */ 1163 public static boolean inside(Environment env, String codes) { // NO_UCD (unused code) 1164 for (String code : codes.toUpperCase(Locale.ENGLISH).split(",")) { 1165 if (Territories.isIso3166Code(code.trim(), center(env))) { 1166 return true; 1167 } 1168 } 1169 return false; 1170 } 1171 1172 /** 1173 * Determines if the object is outside territories matching given ISO3166 codes. 1174 * @param env the environment 1175 * @param codes comma-separated list of ISO3166-1-alpha2 or ISO3166-2 country/subdivision codes 1176 * @return {@code true} if the object is outside territory matching given ISO3166 codes 1177 * @since 11247 1178 */ 1179 public static boolean outside(Environment env, String codes) { // NO_UCD (unused code) 1180 return !inside(env, codes); 1181 } 1182 1183 /** 1184 * Determines if the object centroid lies at given lat/lon coordinates. 1185 * @param env the environment 1186 * @param lat latitude, i.e., the north-south position in degrees 1187 * @param lon longitude, i.e., the east-west position in degrees 1188 * @return {@code true} if the object centroid lies at given lat/lon coordinates 1189 * @since 12514 1190 */ 1191 public static boolean at(Environment env, double lat, double lon) { // NO_UCD (unused code) 1192 return new LatLon(lat, lon).equalsEpsilon(center(env)); 1193 } 1194}