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) { 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) { 569 Collections.sort(sortables); 570 return sortables; 571 } 572 573 /** 574 * Returns the role of current object in parent relation, or role of child if current object is a relation. 575 * @param env the environment 576 * @return role of current object in parent relation, or role of child if current object is a relation 577 * @see Environment#getRole() 578 */ 579 public static String role(final Environment env) { // NO_UCD (unused code) 580 return env.getRole(); 581 } 582 583 /** 584 * Returns the number of primitives in a relation with the specified roles. 585 * @param env the environment 586 * @param roles The roles to count in the relation 587 * @return The number of relation members with the specified role 588 * @since 15196 589 */ 590 public static int count_roles(final Environment env, String... roles) { // NO_UCD (unused code) 591 int rValue = 0; 592 if (env.osm instanceof Relation) { 593 List<String> roleList = Arrays.asList(roles); 594 Relation rel = (Relation) env.osm; 595 for (RelationMember member : rel.getMembers()) { 596 if (roleList.contains(member.getRole())) rValue++; 597 } 598 } 599 return rValue; 600 } 601 602 /** 603 * Returns the area of a closed way or multipolygon in square meters or {@code null}. 604 * @param env the environment 605 * @return the area of a closed way or multipolygon in square meters or {@code null} 606 * @see Geometry#computeArea(IPrimitive) 607 */ 608 public static Float areasize(final Environment env) { // NO_UCD (unused code) 609 final Double area = Geometry.computeArea(env.osm); 610 return area == null ? null : area.floatValue(); 611 } 612 613 /** 614 * Returns the length of the way in metres or {@code null}. 615 * @param env the environment 616 * @return the length of the way in metres or {@code null}. 617 * @see Way#getLength() 618 */ 619 public static Float waylength(final Environment env) { // NO_UCD (unused code) 620 if (env.osm instanceof Way) { 621 return (float) ((Way) env.osm).getLength(); 622 } else { 623 return null; 624 } 625 } 626 627 /** 628 * Function associated to the logical "!" operator. 629 * @param b boolean value 630 * @return {@code true} if {@code !b} 631 */ 632 public static boolean not(boolean b) { // NO_UCD (unused code) 633 return !b; 634 } 635 636 /** 637 * Function associated to the logical ">=" operator. 638 * @param a first value 639 * @param b second value 640 * @return {@code true} if {@code a >= b} 641 */ 642 public static boolean greater_equal(float a, float b) { // NO_UCD (unused code) 643 return a >= b; 644 } 645 646 /** 647 * Function associated to the logical "<=" operator. 648 * @param a first value 649 * @param b second value 650 * @return {@code true} if {@code a <= b} 651 */ 652 public static boolean less_equal(float a, float b) { // NO_UCD (unused code) 653 return a <= 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(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(float a, float b) { // NO_UCD (unused code) 673 return a < b; 674 } 675 676 /** 677 * Converts an angle in degrees to radians. 678 * @param degree the angle in degrees 679 * @return the angle in radians 680 * @see Math#toRadians(double) 681 */ 682 public static double degree_to_radians(double degree) { // NO_UCD (unused code) 683 return Utils.toRadians(degree); 684 } 685 686 /** 687 * Converts an angle diven in cardinal directions to radians. 688 * The following values are supported: {@code n}, {@code north}, {@code ne}, {@code northeast}, 689 * {@code e}, {@code east}, {@code se}, {@code southeast}, {@code s}, {@code south}, 690 * {@code sw}, {@code southwest}, {@code w}, {@code west}, {@code nw}, {@code northwest}. 691 * @param cardinal the angle in cardinal directions. 692 * @return the angle in radians 693 * @see RotationAngle#parseCardinalRotation(String) 694 */ 695 public static Double cardinal_to_radians(String cardinal) { // NO_UCD (unused code) 696 try { 697 return RotationAngle.parseCardinalRotation(cardinal); 698 } catch (IllegalArgumentException ignore) { 699 Logging.trace(ignore); 700 return null; 701 } 702 } 703 704 /** 705 * Determines if the objects {@code a} and {@code b} are equal. 706 * @param a First object 707 * @param b Second object 708 * @return {@code true} if objects are equal, {@code false} otherwise 709 * @see Object#equals(Object) 710 */ 711 public static boolean equal(Object a, Object b) { 712 if (a.getClass() == b.getClass()) return a.equals(b); 713 if (a.equals(Cascade.convertTo(b, a.getClass()))) return true; 714 return b.equals(Cascade.convertTo(a, b.getClass())); 715 } 716 717 /** 718 * Determines if the objects {@code a} and {@code b} are not equal. 719 * @param a First object 720 * @param b Second object 721 * @return {@code false} if objects are equal, {@code true} otherwise 722 * @see Object#equals(Object) 723 */ 724 public static boolean not_equal(Object a, Object b) { // NO_UCD (unused code) 725 return !equal(a, b); 726 } 727 728 /** 729 * Determines whether the JOSM search with {@code searchStr} applies to the object. 730 * @param env the environment 731 * @param searchStr the search string 732 * @return {@code true} if the JOSM search with {@code searchStr} applies to the object 733 * @see SearchCompiler 734 */ 735 public static Boolean JOSM_search(final Environment env, String searchStr) { // NO_UCD (unused code) 736 Match m; 737 try { 738 m = SearchCompiler.compile(searchStr); 739 } catch (SearchParseError ex) { 740 Logging.trace(ex); 741 return null; 742 } 743 return m.match(env.osm); 744 } 745 746 /** 747 * Obtains the JOSM'key {@link org.openstreetmap.josm.data.Preferences} string for key {@code key}, 748 * and defaults to {@code def} if that is null. 749 * @param env the environment 750 * @param key Key in JOSM preference 751 * @param def Default value 752 * @return value for key, or default value if not found 753 */ 754 public static String JOSM_pref(Environment env, String key, String def) { // NO_UCD (unused code) 755 return MapPaintStyles.getStyles().getPreferenceCached(key, def); 756 } 757 758 /** 759 * Tests if string {@code target} matches pattern {@code pattern} 760 * @param pattern The regex expression 761 * @param target The character sequence to be matched 762 * @return {@code true} if, and only if, the entire region sequence matches the pattern 763 * @see Pattern#matches(String, CharSequence) 764 * @since 5699 765 */ 766 public static boolean regexp_test(String pattern, String target) { // NO_UCD (unused code) 767 return Pattern.matches(pattern, target); 768 } 769 770 /** 771 * Tests if string {@code target} matches pattern {@code pattern} 772 * @param pattern The regex expression 773 * @param target The character sequence to be matched 774 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all") 775 * @return {@code true} if, and only if, the entire region sequence matches the pattern 776 * @see Pattern#CASE_INSENSITIVE 777 * @see Pattern#DOTALL 778 * @see Pattern#MULTILINE 779 * @since 5699 780 */ 781 public static boolean regexp_test(String pattern, String target, String flags) { // NO_UCD (unused code) 782 int f = parse_regex_flags(flags); 783 return Pattern.compile(pattern, f).matcher(target).matches(); 784 } 785 786 /** 787 * Tries to match string against pattern regexp and returns a list of capture groups in case of success. 788 * The first element (index 0) is the complete match (i.e. string). 789 * Further elements correspond to the bracketed parts of the regular expression. 790 * @param pattern The regex expression 791 * @param target The character sequence to be matched 792 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all") 793 * @return a list of capture groups if {@link Matcher#matches()}, or {@code null}. 794 * @see Pattern#CASE_INSENSITIVE 795 * @see Pattern#DOTALL 796 * @see Pattern#MULTILINE 797 * @since 5701 798 */ 799 public static List<String> regexp_match(String pattern, String target, String flags) { // NO_UCD (unused code) 800 int f = parse_regex_flags(flags); 801 return Utils.getMatches(Pattern.compile(pattern, f).matcher(target)); 802 } 803 804 /** 805 * Tries to match string against pattern regexp and returns a list of capture groups in case of success. 806 * The first element (index 0) is the complete match (i.e. string). 807 * Further elements correspond to the bracketed parts of the regular expression. 808 * @param pattern The regex expression 809 * @param target The character sequence to be matched 810 * @return a list of capture groups if {@link Matcher#matches()}, or {@code null}. 811 * @since 5701 812 */ 813 public static List<String> regexp_match(String pattern, String target) { // NO_UCD (unused code) 814 return Utils.getMatches(Pattern.compile(pattern).matcher(target)); 815 } 816 817 /** 818 * Returns the OSM id of the current object. 819 * @param env the environment 820 * @return the OSM id of the current object 821 * @see IPrimitive#getUniqueId() 822 */ 823 public static long osm_id(final Environment env) { // NO_UCD (unused code) 824 return env.osm.getUniqueId(); 825 } 826 827 /** 828 * Returns the OSM user name who last touched the current object. 829 * @param env the environment 830 * @return the OSM user name who last touched the current object 831 * @see IPrimitive#getUser 832 * @since 15246 833 */ 834 public static String osm_user_name(final Environment env) { // NO_UCD (unused code) 835 return env.osm.getUser().getName(); 836 } 837 838 /** 839 * Returns the OSM user id who last touched the current object. 840 * @param env the environment 841 * @return the OSM user id who last touched the current object 842 * @see IPrimitive#getUser 843 * @since 15246 844 */ 845 public static long osm_user_id(final Environment env) { // NO_UCD (unused code) 846 return env.osm.getUser().getId(); 847 } 848 849 /** 850 * Returns the version number of the current object. 851 * @param env the environment 852 * @return the version number of the current object 853 * @see IPrimitive#getVersion 854 * @since 15246 855 */ 856 public static int osm_version(final Environment env) { // NO_UCD (unused code) 857 return env.osm.getVersion(); 858 } 859 860 /** 861 * Returns the id of the changeset the current object was last uploaded to. 862 * @param env the environment 863 * @return the id of the changeset the current object was last uploaded to 864 * @see IPrimitive#getChangesetId 865 * @since 15246 866 */ 867 public static int osm_changeset_id(final Environment env) { // NO_UCD (unused code) 868 return env.osm.getChangesetId(); 869 } 870 871 /** 872 * Returns the time of last modification to the current object, as timestamp. 873 * @param env the environment 874 * @return the time of last modification to the current object, as timestamp 875 * @see IPrimitive#getRawTimestamp 876 * @since 15246 877 */ 878 public static int osm_timestamp(final Environment env) { // NO_UCD (unused code) 879 return env.osm.getRawTimestamp(); 880 } 881 882 /** 883 * Translates some text for the current locale. The first argument is the text to translate, 884 * and the subsequent arguments are parameters for the string indicated by <code>{0}</code>, <code>{1}</code>, … 885 * @param args arguments 886 * @return the translated string 887 */ 888 @NullableArguments 889 public static String tr(String... args) { // NO_UCD (unused code) 890 final String text = args[0]; 891 System.arraycopy(args, 1, args, 0, args.length - 1); 892 return org.openstreetmap.josm.tools.I18n.tr(text, (Object[]) args); 893 } 894 895 /** 896 * Returns the substring of {@code s} starting at index {@code begin} (inclusive, 0-indexed). 897 * @param s The base string 898 * @param begin The start index 899 * @return the substring 900 * @see String#substring(int) 901 */ 902 public static String substring(String s, /* due to missing Cascade.convertTo for int*/ float begin) { // NO_UCD (unused code) 903 return s == null ? null : s.substring((int) begin); 904 } 905 906 /** 907 * Returns the substring of {@code s} starting at index {@code begin} (inclusive) 908 * and ending at index {@code end}, (exclusive, 0-indexed). 909 * @param s The base string 910 * @param begin The start index 911 * @param end The end index 912 * @return the substring 913 * @see String#substring(int, int) 914 */ 915 public static String substring(String s, float begin, float end) { // NO_UCD (unused code) 916 return s == null ? null : s.substring((int) begin, (int) end); 917 } 918 919 /** 920 * Replaces in {@code s} every {@code} target} substring by {@code replacement}. 921 * @param s The source string 922 * @param target The sequence of char values to be replaced 923 * @param replacement The replacement sequence of char values 924 * @return The resulting string 925 * @see String#replace(CharSequence, CharSequence) 926 */ 927 public static String replace(String s, String target, String replacement) { // NO_UCD (unused code) 928 return s == null ? null : s.replace(target, replacement); 929 } 930 931 /** 932 * Converts string {@code s} to uppercase. 933 * @param s The source string 934 * @return The resulting string 935 * @see String#toUpperCase(Locale) 936 * @since 11756 937 */ 938 public static String upper(String s) { 939 return s == null ? null : s.toUpperCase(Locale.ENGLISH); 940 } 941 942 /** 943 * Converts string {@code s} to lowercase. 944 * @param s The source string 945 * @return The resulting string 946 * @see String#toLowerCase(Locale) 947 * @since 11756 948 */ 949 public static String lower(String s) { 950 return s == null ? null : s.toLowerCase(Locale.ENGLISH); 951 } 952 953 /** 954 * Trim whitespaces from the string {@code s}. 955 * @param s The source string 956 * @return The resulting string 957 * @see Utils#strip 958 * @since 11756 959 */ 960 public static String trim(String s) { 961 return Utils.strip(s); 962 } 963 964 /** 965 * Check if two strings are similar, but not identical, i.e., have a Levenshtein distance of 1 or 2. 966 * @param string1 first string to compare 967 * @param string2 second string to compare 968 * @return true if the normalized strings are different but only a "little bit" 969 * @see Utils#isSimilar 970 * @since 14371 971 */ 972 public static boolean is_similar(String string1, String string2) { 973 return Utils.isSimilar(string1, string2); 974 } 975 976 /** 977 * Percent-decode a string. (See https://en.wikipedia.org/wiki/Percent-encoding) 978 * This is especially useful for wikipedia titles 979 * @param s url-encoded string 980 * @return the decoded string, or original in case of an error 981 * @since 11756 982 */ 983 public static String URL_decode(String s) { 984 if (s == null) return null; 985 try { 986 return Utils.decodeUrl(s); 987 } catch (IllegalStateException e) { 988 Logging.debug(e); 989 return s; 990 } 991 } 992 993 /** 994 * Percent-encode a string. (See https://en.wikipedia.org/wiki/Percent-encoding) 995 * This is especially useful for data urls, e.g. 996 * <code>concat("data:image/svg+xml,", URL_encode("<svg>...</svg>"));</code> 997 * @param s arbitrary string 998 * @return the encoded string 999 */ 1000 public static String URL_encode(String s) { // NO_UCD (unused code) 1001 return s == null ? null : Utils.encodeUrl(s); 1002 } 1003 1004 /** 1005 * XML-encode a string. 1006 * 1007 * Escapes special characters in xml. Alternative to using <![CDATA[ ... ]]> blocks. 1008 * @param s arbitrary string 1009 * @return the encoded string 1010 */ 1011 public static String XML_encode(String s) { // NO_UCD (unused code) 1012 return s == null ? null : XmlWriter.encode(s); 1013 } 1014 1015 /** 1016 * Calculates the CRC32 checksum from a string (based on RFC 1952). 1017 * @param s the string 1018 * @return long value from 0 to 2^32-1 1019 */ 1020 public static long CRC32_checksum(String s) { // NO_UCD (unused code) 1021 CRC32 cs = new CRC32(); 1022 cs.update(s.getBytes(StandardCharsets.UTF_8)); 1023 return cs.getValue(); 1024 } 1025 1026 /** 1027 * check if there is right-hand traffic at the current location 1028 * @param env the environment 1029 * @return true if there is right-hand traffic 1030 * @since 7193 1031 */ 1032 public static boolean is_right_hand_traffic(Environment env) { 1033 return RightAndLefthandTraffic.isRightHandTraffic(center(env)); 1034 } 1035 1036 /** 1037 * Determines whether the way is {@link Geometry#isClockwise closed and oriented clockwise}, 1038 * or non-closed and the {@link Geometry#angleIsClockwise 1st, 2nd and last node are in clockwise order}. 1039 * 1040 * @param env the environment 1041 * @return true if the way is closed and oriented clockwise 1042 */ 1043 public static boolean is_clockwise(Environment env) { 1044 if (!(env.osm instanceof Way)) { 1045 return false; 1046 } 1047 final Way way = (Way) env.osm; 1048 return (way.isClosed() && Geometry.isClockwise(way)) 1049 || (!way.isClosed() && way.getNodesCount() > 2 && Geometry.angleIsClockwise(way.getNode(0), way.getNode(1), way.lastNode())); 1050 } 1051 1052 /** 1053 * Determines whether the way is {@link Geometry#isClockwise closed and oriented anticlockwise}, 1054 * or non-closed and the {@link Geometry#angleIsClockwise 1st, 2nd and last node are in anticlockwise order}. 1055 * 1056 * @param env the environment 1057 * @return true if the way is closed and oriented clockwise 1058 */ 1059 public static boolean is_anticlockwise(Environment env) { 1060 if (!(env.osm instanceof Way)) { 1061 return false; 1062 } 1063 final Way way = (Way) env.osm; 1064 return (way.isClosed() && !Geometry.isClockwise(way)) 1065 || (!way.isClosed() && way.getNodesCount() > 2 && !Geometry.angleIsClockwise(way.getNode(0), way.getNode(1), way.lastNode())); 1066 } 1067 1068 /** 1069 * Prints the object to the command line (for debugging purpose). 1070 * @param o the object 1071 * @return the same object, unchanged 1072 */ 1073 @NullableArguments 1074 public static Object print(Object o) { // NO_UCD (unused code) 1075 System.out.print(o == null ? "none" : o.toString()); 1076 return o; 1077 } 1078 1079 /** 1080 * Prints the object to the command line, with new line at the end 1081 * (for debugging purpose). 1082 * @param o the object 1083 * @return the same object, unchanged 1084 */ 1085 @NullableArguments 1086 public static Object println(Object o) { // NO_UCD (unused code) 1087 System.out.println(o == null ? "none" : o.toString()); 1088 return o; 1089 } 1090 1091 /** 1092 * Get the number of tags for the current primitive. 1093 * @param env the environment 1094 * @return number of tags 1095 */ 1096 public static int number_of_tags(Environment env) { // NO_UCD (unused code) 1097 return env.osm.getNumKeys(); 1098 } 1099 1100 /** 1101 * Get value of a setting. 1102 * @param env the environment 1103 * @param key setting key (given as layer identifier, e.g. setting::mykey {...}) 1104 * @return the value of the setting (calculated when the style is loaded) 1105 */ 1106 public static Object setting(Environment env, String key) { // NO_UCD (unused code) 1107 return env.source.settingValues.get(key); 1108 } 1109 1110 /** 1111 * Returns the center of the environment OSM primitive. 1112 * @param env the environment 1113 * @return the center of the environment OSM primitive 1114 * @since 11247 1115 */ 1116 public static LatLon center(Environment env) { // NO_UCD (unused code) 1117 return env.osm instanceof Node ? ((Node) env.osm).getCoor() : env.osm.getBBox().getCenter(); 1118 } 1119 1120 /** 1121 * Determines if the object is inside territories matching given ISO3166 codes. 1122 * @param env the environment 1123 * @param codes comma-separated list of ISO3166-1-alpha2 or ISO3166-2 country/subdivision codes 1124 * @return {@code true} if the object is inside territory matching given ISO3166 codes 1125 * @since 11247 1126 */ 1127 public static boolean inside(Environment env, String codes) { // NO_UCD (unused code) 1128 for (String code : codes.toUpperCase(Locale.ENGLISH).split(",")) { 1129 if (Territories.isIso3166Code(code.trim(), center(env))) { 1130 return true; 1131 } 1132 } 1133 return false; 1134 } 1135 1136 /** 1137 * Determines if the object is outside territories matching given ISO3166 codes. 1138 * @param env the environment 1139 * @param codes comma-separated list of ISO3166-1-alpha2 or ISO3166-2 country/subdivision codes 1140 * @return {@code true} if the object is outside territory matching given ISO3166 codes 1141 * @since 11247 1142 */ 1143 public static boolean outside(Environment env, String codes) { // NO_UCD (unused code) 1144 return !inside(env, codes); 1145 } 1146 1147 /** 1148 * Determines if the object centroid lies at given lat/lon coordinates. 1149 * @param env the environment 1150 * @param lat latitude, i.e., the north-south position in degrees 1151 * @param lon longitude, i.e., the east-west position in degrees 1152 * @return {@code true} if the object centroid lies at given lat/lon coordinates 1153 * @since 12514 1154 */ 1155 public static boolean at(Environment env, double lat, double lon) { // NO_UCD (unused code) 1156 return new LatLon(lat, lon).equalsEpsilon(center(env)); 1157 } 1158}