source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Functions.java@ 17758

Last change on this file since 17758 was 17758, checked in by simon04, 3 years ago

fix #20744 - Evaluate MapCSS expression without array creation

  • Property svn:eol-style set to native
File size: 45.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.mapcss;
3
4import java.awt.Color;
5import java.nio.charset.StandardCharsets;
6import java.util.Arrays;
7import java.util.Collections;
8import java.util.List;
9import java.util.Locale;
10import java.util.Map.Entry;
11import java.util.Objects;
12import java.util.regex.Matcher;
13import java.util.regex.Pattern;
14import java.util.stream.Collectors;
15import java.util.zip.CRC32;
16
17import org.openstreetmap.josm.data.coor.LatLon;
18import org.openstreetmap.josm.data.gpx.GpxDistance;
19import org.openstreetmap.josm.data.osm.IPrimitive;
20import org.openstreetmap.josm.data.osm.Node;
21import org.openstreetmap.josm.data.osm.OsmPrimitive;
22import org.openstreetmap.josm.data.osm.Relation;
23import org.openstreetmap.josm.data.osm.Way;
24import org.openstreetmap.josm.data.osm.search.SearchCompiler;
25import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match;
26import org.openstreetmap.josm.data.osm.search.SearchParseError;
27import org.openstreetmap.josm.data.preferences.NamedColorProperty;
28import org.openstreetmap.josm.gui.MainApplication;
29import org.openstreetmap.josm.gui.mappaint.Cascade;
30import org.openstreetmap.josm.gui.mappaint.Environment;
31import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
32import org.openstreetmap.josm.gui.mappaint.mapcss.ExpressionFactory.NullableArguments;
33import org.openstreetmap.josm.io.XmlWriter;
34import org.openstreetmap.josm.tools.AlphanumComparator;
35import org.openstreetmap.josm.tools.ColorHelper;
36import org.openstreetmap.josm.tools.Geometry;
37import org.openstreetmap.josm.tools.Logging;
38import org.openstreetmap.josm.tools.RightAndLefthandTraffic;
39import org.openstreetmap.josm.tools.RotationAngle;
40import org.openstreetmap.josm.tools.StreamUtils;
41import org.openstreetmap.josm.tools.Territories;
42import org.openstreetmap.josm.tools.Utils;
43
44/**
45 * List of functions that can be used in MapCSS expressions.
46 *
47 * First parameter can be of type {@link Environment} (if needed). This is
48 * automatically filled in by JOSM and the user only sees the remaining arguments.
49 * When one of the user supplied arguments cannot be converted the
50 * expected type or is null, the function is not called and it returns null
51 * immediately. Add the annotation {@link NullableArguments} to allow null arguments.
52 * Every method must be static.
53 *
54 * @since 15245 (extracted from {@link ExpressionFactory})
55 */
56@SuppressWarnings("UnusedDeclaration")
57public final class Functions {
58
59 private Functions() {
60 // Hide implicit public constructor for utility classes
61 }
62
63 /**
64 * Identity function for compatibility with MapCSS specification.
65 * @param o any object
66 * @return {@code o} unchanged
67 */
68 public static Object eval(Object o) { // NO_UCD (unused code)
69 return o;
70 }
71
72 /**
73 * Function associated to the numeric "+" operator.
74 * @param a the first operand
75 * @param b the second operand
76 * @return Sum of arguments
77 * @see Float#sum
78 */
79 public static double plus(double a, double b) { // NO_UCD (unused code)
80 return a + b;
81 }
82
83 /**
84 * Function associated to the numeric "-" operator.
85 * @param a the first operand
86 * @param b the second operand
87 * @return Subtraction of arguments
88 */
89 public static double minus(double a, double b) { // NO_UCD (unused code)
90 return a - b;
91 }
92
93 /**
94 * Function associated to the numeric "*" operator.
95 * @param a the first operand
96 * @param b the second operand
97 * @return Multiplication of arguments
98 */
99 public static double times(double a, double b) { // NO_UCD (unused code)
100 return a * b;
101 }
102
103 /**
104 * Function associated to the numeric "/" operator.
105 * @param a the first operand
106 * @param b the second operand
107 * @return Division of arguments
108 */
109 public static double divided_by(double a, double b) { // NO_UCD (unused code)
110 return a / b;
111 }
112
113 /**
114 * Creates a list of values, e.g., for the {@code dashes} property.
115 * @param ignored The environment (ignored)
116 * @param args The values to put in a list
117 * @return list of values
118 * @see Arrays#asList(Object[])
119 */
120 public static List<Object> list(Environment ignored, Object... args) { // NO_UCD (unused code)
121 return Arrays.asList(args);
122 }
123
124 /**
125 * Returns the number of elements in a list.
126 * @param lst the list
127 * @return length of the list
128 */
129 public static Integer count(List<?> lst) { // NO_UCD (unused code)
130 return lst.size();
131 }
132
133 /**
134 * Returns the first non-null object.
135 * The name originates from <a href="http://wiki.openstreetmap.org/wiki/MapCSS/0.2/eval">MapCSS standard</a>.
136 * @param ignored The environment (ignored)
137 * @param args arguments
138 * @return the first non-null object
139 * @see Utils#firstNonNull(Object[])
140 */
141 @NullableArguments
142 public static Object any(Environment ignored, Object... args) { // NO_UCD (unused code)
143 return Utils.firstNonNull(args);
144 }
145
146 /**
147 * Get the {@code n}th element of the list {@code lst} (counting starts at 0).
148 * @param lst list
149 * @param n index
150 * @return {@code n}th element of the list, or {@code null} if index out of range
151 * @since 5699
152 */
153 public static Object get(List<?> lst, float n) { // NO_UCD (unused code)
154 int idx = Math.round(n);
155 if (idx >= 0 && idx < lst.size()) {
156 return lst.get(idx);
157 }
158 return null;
159 }
160
161 /**
162 * Splits string {@code toSplit} at occurrences of the separator string {@code sep} and returns a list of matches.
163 * @param sep separator string
164 * @param toSplit string to split
165 * @return list of matches
166 * @see String#split(String)
167 * @since 5699
168 */
169 public static List<String> split(String sep, String toSplit) { // NO_UCD (unused code)
170 return Arrays.asList(toSplit.split(Pattern.quote(sep), -1));
171 }
172
173 /**
174 * 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)
175 * @param r the red component
176 * @param g the green component
177 * @param b the blue component
178 * @return color matching the given components
179 * @see Color#Color(float, float, float)
180 */
181 public static Color rgb(float r, float g, float b) { // NO_UCD (unused code)
182 try {
183 return new Color(r, g, b);
184 } catch (IllegalArgumentException e) {
185 Logging.trace(e);
186 return null;
187 }
188 }
189
190 /**
191 * Creates a color value with the specified amounts of {@code r}ed, {@code g}reen, {@code b}lue, {@code alpha}
192 * (arguments from 0.0 to 1.0)
193 * @param r the red component
194 * @param g the green component
195 * @param b the blue component
196 * @param alpha the alpha component
197 * @return color matching the given components
198 * @see Color#Color(float, float, float, float)
199 */
200 public static Color rgba(float r, float g, float b, float alpha) { // NO_UCD (unused code)
201 try {
202 return new Color(r, g, b, alpha);
203 } catch (IllegalArgumentException e) {
204 Logging.trace(e);
205 return null;
206 }
207 }
208
209 /**
210 * Create color from hsb color model. (arguments form 0.0 to 1.0)
211 * @param h hue
212 * @param s saturation
213 * @param b brightness
214 * @return the corresponding color
215 */
216 public static Color hsb_color(float h, float s, float b) { // NO_UCD (unused code)
217 try {
218 return Color.getHSBColor(h, s, b);
219 } catch (IllegalArgumentException e) {
220 Logging.trace(e);
221 return null;
222 }
223 }
224
225 /**
226 * Creates a color value from an HTML notation, i.e., {@code #rrggbb}.
227 * @param html HTML notation
228 * @return color matching the given notation
229 */
230 public static Color html2color(String html) { // NO_UCD (unused code)
231 return ColorHelper.html2color(html);
232 }
233
234 /**
235 * Computes the HTML notation ({@code #rrggbb}) for a color value).
236 * @param c color
237 * @return HTML notation matching the given color
238 */
239 public static String color2html(Color c) { // NO_UCD (unused code)
240 return ColorHelper.color2html(c);
241 }
242
243 /**
244 * Get the value of the red color channel in the rgb color model
245 * @param c color
246 * @return the red color channel in the range [0;1]
247 * @see java.awt.Color#getRed()
248 */
249 public static float red(Color c) { // NO_UCD (unused code)
250 return ColorHelper.int2float(c.getRed());
251 }
252
253 /**
254 * Get the value of the green color channel in the rgb color model
255 * @param c color
256 * @return the green color channel in the range [0;1]
257 * @see java.awt.Color#getGreen()
258 */
259 public static float green(Color c) { // NO_UCD (unused code)
260 return ColorHelper.int2float(c.getGreen());
261 }
262
263 /**
264 * Get the value of the blue color channel in the rgb color model
265 * @param c color
266 * @return the blue color channel in the range [0;1]
267 * @see java.awt.Color#getBlue()
268 */
269 public static float blue(Color c) { // NO_UCD (unused code)
270 return ColorHelper.int2float(c.getBlue());
271 }
272
273 /**
274 * Get the value of the alpha channel in the rgba color model
275 * @param c color
276 * @return the alpha channel in the range [0;1]
277 * @see java.awt.Color#getAlpha()
278 */
279 public static float alpha(Color c) { // NO_UCD (unused code)
280 return ColorHelper.int2float(c.getAlpha());
281 }
282
283 /**
284 * Assembles the strings to one.
285 * @param ignored The environment (ignored)
286 * @param args arguments
287 * @return assembled string
288 * @see Collectors#joining
289 */
290 @NullableArguments
291 public static String concat(Environment ignored, Object... args) { // NO_UCD (unused code)
292 return Arrays.stream(args)
293 .filter(Objects::nonNull)
294 .map(String::valueOf)
295 .collect(Collectors.joining());
296 }
297
298 /**
299 * Assembles the strings to one, where the first entry is used as separator.
300 * @param ignored The environment (ignored)
301 * @param args arguments. First one is used as separator
302 * @return assembled string
303 * @see String#join
304 */
305 @NullableArguments
306 public static String join(Environment ignored, String... args) { // NO_UCD (unused code)
307 return String.join(args[0], Arrays.asList(args).subList(1, args.length));
308 }
309
310 /**
311 * Joins a list of {@code values} into a single string with fields separated by {@code separator}.
312 * @param separator the separator
313 * @param values collection of objects
314 * @return assembled string
315 * @see String#join
316 */
317 public static String join_list(final String separator, final List<String> values) { // NO_UCD (unused code)
318 return String.join(separator, values);
319 }
320
321 /**
322 * Returns the value of the property {@code key}, e.g., {@code prop("width")}.
323 * @param env the environment
324 * @param key the property key
325 * @return the property value
326 */
327 public static Object prop(final Environment env, String key) { // NO_UCD (unused code)
328 return prop(env, key, null);
329 }
330
331 /**
332 * Returns the value of the property {@code key} from layer {@code layer}.
333 * @param env the environment
334 * @param key the property key
335 * @param layer layer
336 * @return the property value
337 */
338 public static Object prop(final Environment env, String key, String layer) {
339 return env.getCascade(layer).get(key);
340 }
341
342 /**
343 * Determines whether property {@code key} is set.
344 * @param env the environment
345 * @param key the property key
346 * @return {@code true} if the property is set, {@code false} otherwise
347 */
348 public static Boolean is_prop_set(final Environment env, String key) { // NO_UCD (unused code)
349 return is_prop_set(env, key, null);
350 }
351
352 /**
353 * Determines whether property {@code key} is set on layer {@code layer}.
354 * @param env the environment
355 * @param key the property key
356 * @param layer layer
357 * @return {@code true} if the property is set, {@code false} otherwise
358 */
359 public static Boolean is_prop_set(final Environment env, String key, String layer) {
360 return env.getCascade(layer).containsKey(key);
361 }
362
363 /**
364 * Gets the value of the key {@code key} from the object in question.
365 * @param env the environment
366 * @param key the OSM key
367 * @return the value for given key
368 */
369 public static String tag(final Environment env, String key) { // NO_UCD (unused code)
370 return env.osm == null ? null : env.osm.get(key);
371 }
372
373 /**
374 * Get keys that follow a regex
375 * @param env the environment
376 * @param keyRegex the pattern that the key must match
377 * @return the values for the keys that match the pattern
378 * @see Functions#tag_regex(Environment, String, String)
379 * @since 15315
380 */
381 public static List<String> tag_regex(final Environment env, String keyRegex) { // NO_UCD (unused code)
382 return tag_regex(env, keyRegex, "");
383 }
384
385 /**
386 * Get keys that follow a regex
387 * @param env the environment
388 * @param keyRegex the pattern that the key must match
389 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all")
390 * @return the values for the keys that match the pattern
391 * @see Pattern#CASE_INSENSITIVE
392 * @see Pattern#DOTALL
393 * @see Pattern#MULTILINE
394 * @since 15315
395 */
396 public static List<String> tag_regex(final Environment env, String keyRegex, String flags) { // NO_UCD (unused code)
397 int f = parse_regex_flags(flags);
398 Pattern compiled = Pattern.compile(keyRegex, f);
399 return env.osm.getKeys().entrySet().stream()
400 .filter(object -> compiled.matcher(object.getKey()).find())
401 .map(Entry::getValue).collect(Collectors.toList());
402 }
403
404 /**
405 * Parse flags for regex usage. Shouldn't be used in mapcss
406 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all")
407 * @return An int that can be used by a {@link Pattern} object
408 * @see Pattern#CASE_INSENSITIVE
409 * @see Pattern#DOTALL
410 * @see Pattern#MULTILINE
411 */
412 private static int parse_regex_flags(String flags) {
413 int f = 0;
414 if (flags.contains("i")) {
415 f |= Pattern.CASE_INSENSITIVE;
416 }
417 if (flags.contains("s")) {
418 f |= Pattern.DOTALL;
419 }
420 if (flags.contains("m")) {
421 f |= Pattern.MULTILINE;
422 }
423 return f;
424 }
425
426 /**
427 * Gets the first non-null value of the key {@code key} from the object's parent(s).
428 * @param env the environment
429 * @param key the OSM key
430 * @return first non-null value of the key {@code key} from the object's parent(s)
431 */
432 public static String parent_tag(final Environment env, String key) { // NO_UCD (unused code)
433 if (env.parent == null) {
434 if (env.osm != null) {
435 // we don't have a matched parent, so just search all referrers
436 return env.osm.getReferrers().stream()
437 .map(parent -> parent.get(key))
438 .filter(Objects::nonNull)
439 .findFirst().orElse(null);
440 }
441 return null;
442 }
443 return env.parent.get(key);
444 }
445
446 /**
447 * Gets a list of all non-null values of the key {@code key} from the object's parent(s).
448 *
449 * The values are sorted according to {@link AlphanumComparator}.
450 * @param env the environment
451 * @param key the OSM key
452 * @return a list of non-null values of the key {@code key} from the object's parent(s)
453 */
454 public static List<String> parent_tags(final Environment env, String key) { // NO_UCD (unused code)
455 if (env.parent == null) {
456 if (env.osm != null) {
457 // we don't have a matched parent, so just search all referrers
458 return env.osm.getReferrers().stream().map(parent -> parent.get(key))
459 .filter(Objects::nonNull)
460 .distinct()
461 .sorted(AlphanumComparator.getInstance())
462 .collect(StreamUtils.toUnmodifiableList());
463 }
464 return Collections.emptyList();
465 }
466 return Collections.singletonList(env.parent.get(key));
467 }
468
469 /**
470 * Gets the value of the key {@code key} from the object's child.
471 * @param env the environment
472 * @param key the OSM key
473 * @return the value of the key {@code key} from the object's child, or {@code null} if there is no child
474 */
475 public static String child_tag(final Environment env, String key) { // NO_UCD (unused code)
476 return env.child == null ? null : env.child.get(key);
477 }
478
479 /**
480 * Returns the OSM id of the object's parent.
481 * <p>
482 * Parent must be matched by child selector.
483 * @param env the environment
484 * @return the OSM id of the object's parent, if available, or {@code null}
485 * @see IPrimitive#getUniqueId()
486 */
487 public static Long parent_osm_id(final Environment env) { // NO_UCD (unused code)
488 return env.parent == null ? null : env.parent.getUniqueId();
489 }
490
491 /**
492 * Returns the lowest distance between the OSM object and a GPX point
493 * <p>
494 * @param env the environment
495 * @return the distance between the object and the closest gpx point or {@code Double.MAX_VALUE}
496 * @since 14802
497 */
498 public static double gpx_distance(final Environment env) { // NO_UCD (unused code)
499 if (env.osm instanceof OsmPrimitive) {
500 return MainApplication.getLayerManager().getAllGpxData().stream()
501 .mapToDouble(gpx -> GpxDistance.getLowestDistance((OsmPrimitive) env.osm, gpx))
502 .min().orElse(Double.MAX_VALUE);
503 }
504 return Double.MAX_VALUE;
505 }
506
507 /**
508 * Determines whether the object has a tag with the given key.
509 * @param env the environment
510 * @param key the OSM key
511 * @return {@code true} if the object has a tag with the given key, {@code false} otherwise
512 */
513 public static boolean has_tag_key(final Environment env, String key) { // NO_UCD (unused code)
514 return env.osm.hasKey(key);
515 }
516
517 /**
518 * Returns the index of node in parent way or member in parent relation.
519 * @param env the environment
520 * @return the index as float. Starts at 1
521 */
522 public static Float index(final Environment env) { // NO_UCD (unused code)
523 if (env.index == null) {
524 return null;
525 }
526 return Float.valueOf(env.index + 1f);
527 }
528
529 /**
530 * Sort an array of strings
531 * @param ignored The environment (ignored)
532 * @param sortables The array to sort
533 * @return The sorted list
534 * @since 15279
535 */
536 public static List<String> sort(Environment ignored, String... sortables) { // NO_UCD (unused code)
537 Arrays.parallelSort(sortables);
538 return Arrays.asList(sortables);
539 }
540
541 /**
542 * Sort a list of strings
543 * @param sortables The list to sort
544 * @return The sorted list
545 * @since 15279
546 */
547 public static List<String> sort_list(List<String> sortables) { // NO_UCD (unused code)
548 Collections.sort(sortables);
549 return sortables;
550 }
551
552 /**
553 * Get unique values
554 * @param ignored The environment (ignored)
555 * @param values A list of values that may have duplicates
556 * @return A list with no duplicates
557 * @since 15323
558 */
559 public static List<String> uniq(Environment ignored, String... values) { // NO_UCD (unused code)
560 return uniq_list(Arrays.asList(values));
561 }
562
563 /**
564 * Get unique values
565 * @param values A list of values that may have duplicates
566 * @return A list with no duplicates
567 * @since 15323
568 */
569 public static List<String> uniq_list(List<String> values) {
570 return values.stream().distinct().collect(Collectors.toList());
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 rValue = (int) rel.getMembers().stream()
596 .filter(member -> roleList.contains(member.getRole()))
597 .count();
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 "&gt;=" 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 "&lt;=" 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 "&gt;" 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 "&lt;" 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 *
750 * If the default value can be {@linkplain Cascade#convertTo converted} to a {@link Color},
751 * the {@link NamedColorProperty} is retrieved as string.
752 *
753 * @param env the environment
754 * @param key Key in JOSM preference
755 * @param def Default value
756 * @return value for key, or default value if not found
757 */
758 public static String JOSM_pref(Environment env, String key, String def) { // NO_UCD (unused code)
759 return MapPaintStyles.getStyles().getPreferenceCached(env != null ? env.source : null, key, def);
760 }
761
762 /**
763 * Tests if string {@code target} matches pattern {@code pattern}
764 * @param pattern The regex expression
765 * @param target The character sequence to be matched
766 * @return {@code true} if, and only if, the entire region sequence matches the pattern
767 * @see Pattern#matches(String, CharSequence)
768 * @since 5699
769 */
770 public static boolean regexp_test(String pattern, String target) { // NO_UCD (unused code)
771 return Pattern.matches(pattern, target);
772 }
773
774 /**
775 * Tests if string {@code target} matches pattern {@code pattern}
776 * @param pattern The regex expression
777 * @param target The character sequence to be matched
778 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all")
779 * @return {@code true} if, and only if, the entire region sequence matches the pattern
780 * @see Pattern#CASE_INSENSITIVE
781 * @see Pattern#DOTALL
782 * @see Pattern#MULTILINE
783 * @since 5699
784 */
785 public static boolean regexp_test(String pattern, String target, String flags) { // NO_UCD (unused code)
786 int f = parse_regex_flags(flags);
787 return Pattern.compile(pattern, f).matcher(target).matches();
788 }
789
790 /**
791 * Tries to match string against pattern regexp and returns a list of capture groups in case of success.
792 * The first element (index 0) is the complete match (i.e. string).
793 * Further elements correspond to the bracketed parts of the regular expression.
794 * @param pattern The regex expression
795 * @param target The character sequence to be matched
796 * @param flags a string that may contain "i" (case insensitive), "m" (multiline) and "s" ("dot all")
797 * @return a list of capture groups if {@link Matcher#matches()}, or {@code null}.
798 * @see Pattern#CASE_INSENSITIVE
799 * @see Pattern#DOTALL
800 * @see Pattern#MULTILINE
801 * @since 5701
802 */
803 public static List<String> regexp_match(String pattern, String target, String flags) { // NO_UCD (unused code)
804 int f = parse_regex_flags(flags);
805 return Utils.getMatches(Pattern.compile(pattern, f).matcher(target));
806 }
807
808 /**
809 * Tries to match string against pattern regexp and returns a list of capture groups in case of success.
810 * The first element (index 0) is the complete match (i.e. string).
811 * Further elements correspond to the bracketed parts of the regular expression.
812 * @param pattern The regex expression
813 * @param target The character sequence to be matched
814 * @return a list of capture groups if {@link Matcher#matches()}, or {@code null}.
815 * @since 5701
816 */
817 public static List<String> regexp_match(String pattern, String target) { // NO_UCD (unused code)
818 return Utils.getMatches(Pattern.compile(pattern).matcher(target));
819 }
820
821 /**
822 * Returns the OSM id of the current object.
823 * @param env the environment
824 * @return the OSM id of the current object
825 * @see IPrimitive#getUniqueId()
826 */
827 public static long osm_id(final Environment env) { // NO_UCD (unused code)
828 return env.osm.getUniqueId();
829 }
830
831 /**
832 * Returns the OSM user name who last touched the current object.
833 * @param env the environment
834 * @return the OSM user name who last touched the current object
835 * @see IPrimitive#getUser
836 * @since 15246
837 */
838 public static String osm_user_name(final Environment env) { // NO_UCD (unused code)
839 return env.osm.getUser().getName();
840 }
841
842 /**
843 * Returns the OSM user id who last touched the current object.
844 * @param env the environment
845 * @return the OSM user id who last touched the current object
846 * @see IPrimitive#getUser
847 * @since 15246
848 */
849 public static long osm_user_id(final Environment env) { // NO_UCD (unused code)
850 return env.osm.getUser().getId();
851 }
852
853 /**
854 * Returns the version number of the current object.
855 * @param env the environment
856 * @return the version number of the current object
857 * @see IPrimitive#getVersion
858 * @since 15246
859 */
860 public static int osm_version(final Environment env) { // NO_UCD (unused code)
861 return env.osm.getVersion();
862 }
863
864 /**
865 * Returns the id of the changeset the current object was last uploaded to.
866 * @param env the environment
867 * @return the id of the changeset the current object was last uploaded to
868 * @see IPrimitive#getChangesetId
869 * @since 15246
870 */
871 public static int osm_changeset_id(final Environment env) { // NO_UCD (unused code)
872 return env.osm.getChangesetId();
873 }
874
875 /**
876 * Returns the time of last modification to the current object, as timestamp.
877 * @param env the environment
878 * @return the time of last modification to the current object, as timestamp
879 * @see IPrimitive#getRawTimestamp
880 * @since 15246
881 */
882 public static int osm_timestamp(final Environment env) { // NO_UCD (unused code)
883 return env.osm.getRawTimestamp();
884 }
885
886 /**
887 * Translates some text for the current locale. The first argument is the text to translate,
888 * and the subsequent arguments are parameters for the string indicated by <code>{0}</code>, <code>{1}</code>, …
889 * @param ignored The environment (ignored)
890 * @param args arguments
891 * @return the translated string
892 */
893 @NullableArguments
894 public static String tr(Environment ignored, String... args) { // NO_UCD (unused code)
895 final String text = args[0];
896 System.arraycopy(args, 1, args, 0, args.length - 1);
897 return org.openstreetmap.josm.tools.I18n.tr(text, (Object[]) args);
898 }
899
900 /**
901 * Returns the substring of {@code s} starting at index {@code begin} (inclusive, 0-indexed).
902 * @param s The base string
903 * @param begin The start index
904 * @return the substring
905 * @see String#substring(int)
906 */
907 public static String substring(String s, /* due to missing Cascade.convertTo for int*/ float begin) { // NO_UCD (unused code)
908 return s == null ? null : s.substring((int) begin);
909 }
910
911 /**
912 * Returns the substring of {@code s} starting at index {@code begin} (inclusive)
913 * and ending at index {@code end}, (exclusive, 0-indexed).
914 * @param s The base string
915 * @param begin The start index
916 * @param end The end index
917 * @return the substring
918 * @see String#substring(int, int)
919 */
920 public static String substring(String s, float begin, float end) { // NO_UCD (unused code)
921 return s == null ? null : s.substring((int) begin, (int) end);
922 }
923
924 /**
925 * Replaces in {@code s} every {@code} target} substring by {@code replacement}.
926 * @param s The source string
927 * @param target The sequence of char values to be replaced
928 * @param replacement The replacement sequence of char values
929 * @return The resulting string
930 * @see String#replace(CharSequence, CharSequence)
931 */
932 public static String replace(String s, String target, String replacement) { // NO_UCD (unused code)
933 return s == null ? null : s.replace(target, replacement);
934 }
935
936 /**
937 * Converts string {@code s} to uppercase.
938 * @param s The source string
939 * @return The resulting string
940 * @see String#toUpperCase(Locale)
941 * @since 11756
942 */
943 public static String upper(String s) {
944 return s == null ? null : s.toUpperCase(Locale.ENGLISH);
945 }
946
947 /**
948 * Converts string {@code s} to lowercase.
949 * @param s The source string
950 * @return The resulting string
951 * @see String#toLowerCase(Locale)
952 * @since 11756
953 */
954 public static String lower(String s) {
955 return s == null ? null : s.toLowerCase(Locale.ENGLISH);
956 }
957
958 /**
959 * Returns a title-cased version of the string where words start with an uppercase character and the remaining characters are lowercase
960 *
961 * Also known as "capitalize".
962 * @param str The source string
963 * @return The resulting string
964 * @see Character#toTitleCase(char)
965 * @since 17613
966 */
967 public static String title(String str) {
968 // adapted from org.apache.commons.lang3.text.WordUtils.capitalize
969 if (str == null) {
970 return null;
971 }
972 final char[] buffer = str.toCharArray();
973 boolean capitalizeNext = true;
974 for (int i = 0; i < buffer.length; i++) {
975 final char ch = buffer[i];
976 if (Character.isWhitespace(ch)) {
977 capitalizeNext = true;
978 } else if (capitalizeNext) {
979 buffer[i] = Character.toTitleCase(ch);
980 capitalizeNext = false;
981 } else {
982 buffer[i] = Character.toLowerCase(ch);
983 }
984 }
985 return new String(buffer);
986 }
987
988 /**
989 * Trim whitespaces from the string {@code s}.
990 * @param s The source string
991 * @return The resulting string
992 * @see Utils#strip
993 * @since 11756
994 */
995 public static String trim(String s) {
996 return Utils.strip(s);
997 }
998
999 /**
1000 * Trim whitespaces from the strings {@code strings}.
1001 *
1002 * @param strings The list of strings to strip
1003 * @return The resulting string
1004 * @see Utils#strip
1005 * @since 15591
1006 */
1007 public static List<String> trim_list(List<String> strings) {
1008 return strings.stream().map(Utils::strip).filter(str -> !str.isEmpty()).collect(Collectors.toList());
1009 }
1010
1011 /**
1012 * Check if two strings are similar, but not identical, i.e., have a Levenshtein distance of 1 or 2.
1013 * @param string1 first string to compare
1014 * @param string2 second string to compare
1015 * @return true if the normalized strings are different but only a "little bit"
1016 * @see Utils#isSimilar
1017 * @since 14371
1018 */
1019 public static boolean is_similar(String string1, String string2) {
1020 return Utils.isSimilar(string1, string2);
1021 }
1022
1023 /**
1024 * Percent-decode a string. (See https://en.wikipedia.org/wiki/Percent-encoding)
1025 * This is especially useful for wikipedia titles
1026 * @param s url-encoded string
1027 * @return the decoded string, or original in case of an error
1028 * @since 11756
1029 */
1030 public static String URL_decode(String s) {
1031 if (s == null) return null;
1032 try {
1033 return Utils.decodeUrl(s);
1034 } catch (IllegalStateException e) {
1035 Logging.debug(e);
1036 return s;
1037 }
1038 }
1039
1040 /**
1041 * Percent-encode a string. (See https://en.wikipedia.org/wiki/Percent-encoding)
1042 * This is especially useful for data urls, e.g.
1043 * <code>concat("data:image/svg+xml,", URL_encode("&lt;svg&gt;...&lt;/svg&gt;"));</code>
1044 * @param s arbitrary string
1045 * @return the encoded string
1046 */
1047 public static String URL_encode(String s) { // NO_UCD (unused code)
1048 return s == null ? null : Utils.encodeUrl(s);
1049 }
1050
1051 /**
1052 * XML-encode a string.
1053 *
1054 * Escapes special characters in xml. Alternative to using &lt;![CDATA[ ... ]]&gt; blocks.
1055 * @param s arbitrary string
1056 * @return the encoded string
1057 */
1058 public static String XML_encode(String s) { // NO_UCD (unused code)
1059 return s == null ? null : XmlWriter.encode(s);
1060 }
1061
1062 /**
1063 * Calculates the CRC32 checksum from a string (based on RFC 1952).
1064 * @param s the string
1065 * @return long value from 0 to 2^32-1
1066 */
1067 public static long CRC32_checksum(String s) { // NO_UCD (unused code)
1068 CRC32 cs = new CRC32();
1069 cs.update(s.getBytes(StandardCharsets.UTF_8));
1070 return cs.getValue();
1071 }
1072
1073 /**
1074 * check if there is right-hand traffic at the current location
1075 * @param env the environment
1076 * @return true if there is right-hand traffic
1077 * @since 7193
1078 */
1079 public static boolean is_right_hand_traffic(Environment env) {
1080 return RightAndLefthandTraffic.isRightHandTraffic(center(env));
1081 }
1082
1083 /**
1084 * Determines whether the way is {@link Geometry#isClockwise closed and oriented clockwise},
1085 * or non-closed and the {@link Geometry#angleIsClockwise 1st, 2nd and last node are in clockwise order}.
1086 *
1087 * @param env the environment
1088 * @return true if the way is closed and oriented clockwise
1089 */
1090 public static boolean is_clockwise(Environment env) {
1091 if (!(env.osm instanceof Way)) {
1092 return false;
1093 }
1094 final Way way = (Way) env.osm;
1095 return (way.isClosed() && Geometry.isClockwise(way))
1096 || (!way.isClosed() && way.getNodesCount() > 2 && Geometry.angleIsClockwise(way.getNode(0), way.getNode(1), way.lastNode()));
1097 }
1098
1099 /**
1100 * Determines whether the way is {@link Geometry#isClockwise closed and oriented anticlockwise},
1101 * or non-closed and the {@link Geometry#angleIsClockwise 1st, 2nd and last node are in anticlockwise order}.
1102 *
1103 * @param env the environment
1104 * @return true if the way is closed and oriented clockwise
1105 */
1106 public static boolean is_anticlockwise(Environment env) {
1107 if (!(env.osm instanceof Way)) {
1108 return false;
1109 }
1110 final Way way = (Way) env.osm;
1111 return (way.isClosed() && !Geometry.isClockwise(way))
1112 || (!way.isClosed() && way.getNodesCount() > 2 && !Geometry.angleIsClockwise(way.getNode(0), way.getNode(1), way.lastNode()));
1113 }
1114
1115 /**
1116 * Prints the object to the command line (for debugging purpose).
1117 * @param o the object
1118 * @return the same object, unchanged
1119 */
1120 @NullableArguments
1121 public static Object print(Object o) { // NO_UCD (unused code)
1122 System.out.print(o == null ? "none" : o.toString());
1123 return o;
1124 }
1125
1126 /**
1127 * Prints the object to the command line, with new line at the end
1128 * (for debugging purpose).
1129 * @param o the object
1130 * @return the same object, unchanged
1131 */
1132 @NullableArguments
1133 public static Object println(Object o) { // NO_UCD (unused code)
1134 System.out.println(o == null ? "none" : o.toString());
1135 return o;
1136 }
1137
1138 /**
1139 * Get the number of tags for the current primitive.
1140 * @param env the environment
1141 * @return number of tags
1142 */
1143 public static int number_of_tags(Environment env) { // NO_UCD (unused code)
1144 return env.osm.getNumKeys();
1145 }
1146
1147 /**
1148 * Get value of a setting.
1149 * @param env the environment
1150 * @param key setting key (given as layer identifier, e.g. setting::mykey {...})
1151 * @return the value of the setting (calculated when the style is loaded)
1152 */
1153 public static Object setting(Environment env, String key) { // NO_UCD (unused code)
1154 return env.source.settingValues.get(key);
1155 }
1156
1157 /**
1158 * Returns the center of the environment OSM primitive.
1159 * @param env the environment
1160 * @return the center of the environment OSM primitive
1161 * @since 11247
1162 */
1163 public static LatLon center(Environment env) { // NO_UCD (unused code)
1164 return env.osm instanceof Node ? ((Node) env.osm).getCoor() : env.osm.getBBox().getCenter();
1165 }
1166
1167 /**
1168 * Determines if the object is inside territories matching given ISO3166 codes.
1169 * @param env the environment
1170 * @param codes comma-separated list of ISO3166-1-alpha2 or ISO3166-2 country/subdivision codes
1171 * @return {@code true} if the object is inside territory matching given ISO3166 codes
1172 * @since 11247
1173 */
1174 public static boolean inside(Environment env, String codes) { // NO_UCD (unused code)
1175 return Arrays.stream(codes.toUpperCase(Locale.ENGLISH).split(",", -1))
1176 .anyMatch(code -> Territories.isIso3166Code(code.trim(), center(env)));
1177 }
1178
1179 /**
1180 * Determines if the object is outside territories matching given ISO3166 codes.
1181 * @param env the environment
1182 * @param codes comma-separated list of ISO3166-1-alpha2 or ISO3166-2 country/subdivision codes
1183 * @return {@code true} if the object is outside territory matching given ISO3166 codes
1184 * @since 11247
1185 */
1186 public static boolean outside(Environment env, String codes) { // NO_UCD (unused code)
1187 return !inside(env, codes);
1188 }
1189
1190 /**
1191 * Determines if the object centroid lies at given lat/lon coordinates.
1192 * @param env the environment
1193 * @param lat latitude, i.e., the north-south position in degrees
1194 * @param lon longitude, i.e., the east-west position in degrees
1195 * @return {@code true} if the object centroid lies at given lat/lon coordinates
1196 * @since 12514
1197 */
1198 public static boolean at(Environment env, double lat, double lon) { // NO_UCD (unused code)
1199 return new LatLon(lat, lon).equalsEpsilon(center(env));
1200 }
1201
1202 /**
1203 * Parses the string argument as a boolean.
1204 * @param value the {@code String} containing the boolean representation to be parsed
1205 * @return the boolean represented by the string argument
1206 * @see Boolean#parseBoolean
1207 * @since 16110
1208 */
1209 public static boolean to_boolean(String value) { // NO_UCD (unused code)
1210 return Boolean.parseBoolean(value);
1211 }
1212
1213 /**
1214 * Parses the string argument as a byte.
1215 * @param value the {@code String} containing the byte representation to be parsed
1216 * @return the byte represented by the string argument
1217 * @see Byte#parseByte
1218 * @since 16110
1219 */
1220 public static byte to_byte(String value) { // NO_UCD (unused code)
1221 return Byte.parseByte(value);
1222 }
1223
1224 /**
1225 * Parses the string argument as a short.
1226 * @param value the {@code String} containing the short representation to be parsed
1227 * @return the short represented by the string argument
1228 * @see Short#parseShort
1229 * @since 16110
1230 */
1231 public static short to_short(String value) { // NO_UCD (unused code)
1232 return Short.parseShort(value);
1233 }
1234
1235 /**
1236 * Parses the string argument as an int.
1237 * @param value the {@code String} containing the int representation to be parsed
1238 * @return the int represented by the string argument
1239 * @see Integer#parseInt
1240 * @since 16110
1241 */
1242 public static int to_int(String value) { // NO_UCD (unused code)
1243 return Integer.parseInt(value);
1244 }
1245
1246 /**
1247 * Parses the string argument as a long.
1248 * @param value the {@code String} containing the long representation to be parsed
1249 * @return the long represented by the string argument
1250 * @see Long#parseLong
1251 * @since 16110
1252 */
1253 public static long to_long(String value) { // NO_UCD (unused code)
1254 return Long.parseLong(value);
1255 }
1256
1257 /**
1258 * Parses the string argument as a float.
1259 * @param value the {@code String} containing the float representation to be parsed
1260 * @return the float represented by the string argument
1261 * @see Float#parseFloat
1262 * @since 16110
1263 */
1264 public static float to_float(String value) { // NO_UCD (unused code)
1265 return Float.parseFloat(value);
1266 }
1267
1268 /**
1269 * Parses the string argument as a double.
1270 * @param value the {@code String} containing the double representation to be parsed
1271 * @return the double represented by the string argument
1272 * @see Double#parseDouble
1273 * @since 16110
1274 */
1275 public static double to_double(String value) { // NO_UCD (unused code)
1276 return Double.parseDouble(value);
1277 }
1278}
Note: See TracBrowser for help on using the repository browser.