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