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

Last change on this file since 16252 was 16252, checked in by simon04, 4 years ago

fix #18961 - ColorHelper: harmonize color functions

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