001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.osm;
003
004import static java.util.Comparator.comparing;
005import static java.util.Comparator.comparingInt;
006
007import java.util.Comparator;
008import java.util.HashMap;
009import java.util.Map;
010import java.util.function.Function;
011
012import org.openstreetmap.josm.tools.AlphanumComparator;
013
014/**
015 * Comparators for comparing {@link IPrimitive}.
016 * @since 13803
017 */
018public final class PrimitiveComparator {
019
020    /**
021     * Returns a comparator comparing primitives by their name using {@link DefaultNameFormatter}.
022     *
023     * {@linkplain DefaultNameFormatter#format(IPrimitive) Formatted names} are cached.
024     *
025     * @return a comparator comparing primitives by their name using {@link DefaultNameFormatter}
026     */
027    public static Comparator<IPrimitive> comparingNames() {
028        return doComparingNames();
029    }
030
031    static <T extends IPrimitive> Comparator<T> doComparingNames() {
032        final Comparator<String> digitsLast = comparing(str -> !str.isEmpty() && Character.isDigit(str.charAt(0)) ? 1 : 0);
033        return comparing(memoize(DefaultNameFormatter.getInstance()::format),
034                digitsLast.thenComparing(AlphanumComparator.getInstance()));
035    }
036
037    /**
038     * Returns a comparator comparing primitives by their {@linkplain IPrimitive#getUniqueId unique id}.
039     *
040     * @return a comparator comparing primitives by their {@linkplain IPrimitive#getUniqueId unique id}.
041     */
042    public static Comparator<IPrimitive> comparingUniqueId() {
043        return doComparingUniqueId();
044    }
045
046    static <T extends IPrimitive> Comparator<T> doComparingUniqueId() {
047        return comparing(IPrimitive::getUniqueId);
048    }
049
050    /**
051     * Returns a comparator ordering the primitives by type in the order NODE, WAY, RELATION
052     *
053     * @return a comparator ordering the primitives by type in the order NODE, WAY, RELATION
054     */
055    public static Comparator<IPrimitive> orderingNodesWaysRelations() {
056        return doOrderingNodesWaysRelations();
057    }
058
059    static <T extends IPrimitive> Comparator<T> doOrderingNodesWaysRelations() {
060        return comparingInt(osm -> osm.getType().ordinal());
061    }
062
063    /**
064     * Returns a comparator ordering the primitives by type in the order WAY, RELATION, NODE
065     *
066     * @return a comparator ordering the primitives by type in the order WAY, RELATION, NODE
067     */
068    public static Comparator<IPrimitive> orderingWaysRelationsNodes() {
069        return doOrderingWaysRelationsNodes();
070    }
071
072    static <T extends IPrimitive> Comparator<T> doOrderingWaysRelationsNodes() {
073        return comparingInt(osm -> {
074            switch (osm.getType()) {
075                case WAY:
076                    return 1;
077                case RELATION:
078                    return 2;
079                case NODE:
080                    return 3;
081                default:
082                    throw new IllegalStateException();
083            }
084        });
085    }
086
087    /**
088     * Returns a comparator ordering the primitives by type in the order RELATION, WAY, NODE
089     *
090     * @return a comparator ordering the primitives by type in the order RELATION, WAY, NODE
091     */
092    public static Comparator<IPrimitive> orderingRelationsWaysNodes() {
093        return doOrderingRelationsWaysNodes();
094    }
095
096    static <T extends IPrimitive> Comparator<T> doOrderingRelationsWaysNodes() {
097        return comparingInt(osm -> {
098            switch (osm.getType()) {
099                case RELATION:
100                    return 1;
101                case WAY:
102                    return 2;
103                case NODE:
104                    return 3;
105                default:
106                    throw new IllegalStateException();
107            }
108        });
109    }
110
111    private static <T, R> Function<T, R> memoize(Function<T, R> base) {
112        final Map<T, R> cache = new HashMap<>();
113        return t -> cache.computeIfAbsent(t, base);
114    }
115
116    private PrimitiveComparator() {
117    }
118}