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