Ignore:
Timestamp:
2016-07-29T22:15:28+02:00 (8 years ago)
Author:
Don-vip
Message:

fix #13239, fix #13240 - Java 8: MapCSS Condition class (patches by michael2402) - gsoc-core

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java

    r10659 r10674  
    66import java.text.MessageFormat;
    77import java.util.Arrays;
    8 import java.util.Collection;
    98import java.util.EnumSet;
    109import java.util.Map;
    1110import java.util.Objects;
    1211import java.util.Set;
     12import java.util.function.BiFunction;
     13import java.util.function.IntFunction;
    1314import java.util.function.Predicate;
    1415import java.util.regex.Pattern;
     
    2829import org.openstreetmap.josm.tools.CheckParameterUtil;
    2930import org.openstreetmap.josm.tools.Predicates;
    30 import org.openstreetmap.josm.tools.SubclassFilteredCollection;
    3131import org.openstreetmap.josm.tools.Utils;
    3232
     33/**
     34 * This is a condition that needs to be fulfilled in order to apply a MapCSS style.
     35 */
    3336@FunctionalInterface
    3437public interface Condition {
    3538
     39    /**
     40     * Checks if the condition applies in the given MapCSS {@link Environment}.
     41     * @param e The environment to check. May not be <code>null</code>.
     42     * @return <code>true</code> if the condition applies.
     43     */
    3644    boolean applies(Environment e);
    3745
     46    /**
     47     * Create a new condition that checks the key and the value of the object.
     48     * @param k The key.
     49     * @param v The reference value
     50     * @param op The operation to use when comparing the value
     51     * @param context The type of context to use.
     52     * @param considerValAsKey whether to consider {@code v} as another key and compare the values of key {@code k} and key {@code v}.
     53     * @return The new condition.
     54     */
    3855    static Condition createKeyValueCondition(String k, String v, Op op, Context context, boolean considerValAsKey) {
    3956        switch (context) {
     
    5976    }
    6077
     78    /**
     79     * Create a condition in which the key and the value need to match a given regexp
     80     * @param k The key regexp
     81     * @param v The value regexp
     82     * @param op The operation to use when comparing the key and the value.
     83     * @return The new condition.
     84     */
    6185    static Condition createRegexpKeyRegexpValueCondition(String k, String v, Op op) {
    6286        return new RegexpKeyValueRegexpCondition(k, v, op);
    6387    }
    6488
     89    /**
     90     * Creates a condition that checks the given key.
     91     * @param k The key to test for
     92     * @param not <code>true</code> to invert the match
     93     * @param matchType The match type to check for.
     94     * @param context The context this rule is found in.
     95     * @return the new condition.
     96     */
    6597    static Condition createKeyCondition(String k, boolean not, KeyMatchType matchType, Context context) {
    6698        switch (context) {
     
    79111    }
    80112
     113    /**
     114     * Create a new pseudo class condition
     115     * @param id The id of the pseudo class
     116     * @param not <code>true</code> to invert the condition
     117     * @param context The context the class is found in.
     118     * @return The new condition
     119     */
    81120    static PseudoClassCondition createPseudoClassCondition(String id, boolean not, Context context) {
    82121        return PseudoClassCondition.createPseudoClassCondition(id, not, context);
    83122    }
    84123
     124    /**
     125     * Create a new class condition
     126     * @param id The id of the class to match
     127     * @param not <code>true</code> to invert the condition
     128     * @param context Ignored
     129     * @return The new condition
     130     */
    85131    static ClassCondition createClassCondition(String id, boolean not, Context context) {
    86132        return new ClassCondition(id, not);
    87133    }
    88134
     135    /**
     136     * Create a new condition that a expression needs to be fulfilled
     137     * @param e the expression to check
     138     * @param context Ignored
     139     * @return The new condition
     140     */
    89141    static ExpressionCondition createExpressionCondition(Expression e, Context context) {
    90142        return new ExpressionCondition(e);
     
    96148    enum Op {
    97149        /** The value equals the given reference. */
    98         EQ,
     150        EQ(Objects::equals),
    99151        /** The value does not equal the reference. */
    100         NEQ,
     152        NEQ(EQ),
    101153        /** The value is greater than or equal to the given reference value (as float). */
    102         GREATER_OR_EQUAL,
     154        GREATER_OR_EQUAL(comparisonResult -> comparisonResult >= 0),
    103155        /** The value is greater than the given reference value (as float). */
    104         GREATER,
     156        GREATER(comparisonResult -> comparisonResult > 0),
    105157        /** The value is less than or equal to the given reference value (as float). */
    106         LESS_OR_EQUAL,
     158        LESS_OR_EQUAL(comparisonResult -> comparisonResult <= 0),
    107159        /** The value is less than the given reference value (as float). */
    108         LESS,
     160        LESS(comparisonResult -> comparisonResult < 0),
    109161        /** The reference is treated as regular expression and the value needs to match it. */
    110         REGEX,
     162        REGEX((test, prototype) -> Pattern.compile(prototype).matcher(test).find()),
    111163        /** The reference is treated as regular expression and the value needs to not match it. */
    112         NREGEX,
     164        NREGEX(REGEX),
    113165        /** The reference is treated as a list separated by ';'. Spaces around the ; are ignored.
    114166         *  The value needs to be equal one of the list elements. */
    115         ONE_OF,
     167        ONE_OF((test, prototype) -> Arrays.asList(test.split("\\s*;\\s*")).contains(prototype)),
    116168        /** The value needs to begin with the reference string. */
    117         BEGINS_WITH,
     169        BEGINS_WITH((test, prototype) -> test.startsWith(prototype)),
    118170        /** The value needs to end with the reference string. */
    119         ENDS_WITH,
     171        ENDS_WITH((test, prototype) -> test.endsWith(prototype)),
    120172        /** The value needs to contain the reference string. */
    121         CONTAINS;
     173        CONTAINS((test, prototype) -> test.contains(prototype));
    122174
    123175        static final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX);
     176
     177        private final BiFunction<String, String, Boolean> function;
     178
     179        private final boolean negated;
     180
     181        /**
     182         * Create a new string operation.
     183         * @param func The function to apply during {@link #eval(String, String)}.
     184         */
     185        Op(BiFunction<String, String, Boolean> func) {
     186            this.function = func;
     187            negated = false;
     188        }
     189
     190        /**
     191         * Create a new float operation that compares two float values
     192         * @param comparatorResult A function to mapt the result of the comparison
     193         */
     194        Op(IntFunction<Boolean> comparatorResult) {
     195            this.function = (test, prototype) -> {
     196                float testFloat;
     197                try {
     198                    testFloat = Float.parseFloat(test);
     199                } catch (NumberFormatException e) {
     200                    return false;
     201                }
     202                float prototypeFloat = Float.parseFloat(prototype);
     203
     204                int res = Float.compare(testFloat, prototypeFloat);
     205                return comparatorResult.apply(res);
     206            };
     207            negated = false;
     208        }
     209
     210        /**
     211         * Create a new Op by negating an other op.
     212         * @param negate inverse operation
     213         */
     214        Op(Op negate) {
     215            this.function = (a, b) -> !negate.function.apply(a, b);
     216            negated = true;
     217        }
    124218
    125219        /**
     
    130224         */
    131225        public boolean eval(String testString, String prototypeString) {
    132             if (testString == null && !NEGATED_OPS.contains(this))
    133                 return false;
    134             switch (this) {
    135             case EQ:
    136                 return Objects.equals(testString, prototypeString);
    137             case NEQ:
    138                 return !Objects.equals(testString, prototypeString);
    139             case REGEX:
    140             case NREGEX:
    141                 final boolean contains = Pattern.compile(prototypeString).matcher(testString).find();
    142                 return REGEX.equals(this) ? contains : !contains;
    143             case ONE_OF:
    144                 return testString != null && Arrays.asList(testString.split("\\s*;\\s*")).contains(prototypeString);
    145             case BEGINS_WITH:
    146                 return testString != null && testString.startsWith(prototypeString);
    147             case ENDS_WITH:
    148                 return testString != null && testString.endsWith(prototypeString);
    149             case CONTAINS:
    150                 return testString != null && testString.contains(prototypeString);
    151             case GREATER_OR_EQUAL:
    152             case GREATER:
    153             case LESS_OR_EQUAL:
    154             case LESS:
    155                 // See below
    156                 break;
    157             default:
    158                 throw new AssertionError();
    159             }
    160 
    161             float testFloat;
    162             try {
    163                 testFloat = Float.parseFloat(testString);
    164             } catch (NumberFormatException e) {
    165                 return false;
    166             }
    167             float prototypeFloat = Float.parseFloat(prototypeString);
    168 
    169             switch (this) {
    170             case GREATER_OR_EQUAL:
    171                 return testFloat >= prototypeFloat;
    172             case GREATER:
    173                 return testFloat > prototypeFloat;
    174             case LESS_OR_EQUAL:
    175                 return testFloat <= prototypeFloat;
    176             case LESS:
    177                 return testFloat < prototypeFloat;
    178             default:
    179                 throw new AssertionError();
    180             }
     226            if (testString == null)
     227                return negated;
     228            else
     229                return function.apply(testString, prototypeString);
    181230        }
    182231    }
     
    202251     * Extra class for performance reasons.
    203252     */
    204     class SimpleKeyValueCondition implements Condition {
     253    class SimpleKeyValueCondition implements Condition, ToTagConvertable {
    205254        /**
    206255         * The key to search for.
     
    227276        }
    228277
    229         public Tag asTag() {
     278        @Override
     279        public Tag asTag(OsmPrimitive primitive) {
    230280            return new Tag(k, v);
    231281        }
     
    242292     *
    243293     */
    244     class KeyValueCondition implements Condition {
     294    class KeyValueCondition implements Condition, ToTagConvertable {
    245295        /**
    246296         * The key to search for.
     
    258308         * If this flag is set, {@link #v} is treated as a key and the value is the value set for that key.
    259309         */
    260         public boolean considerValAsKey;
     310        public final boolean considerValAsKey;
    261311
    262312        /**
     
    280330        }
    281331
    282         public Tag asTag() {
     332        @Override
     333        public Tag asTag(OsmPrimitive primitive) {
    283334            return new Tag(k, v);
    284335        }
     
    290341    }
    291342
     343    /**
     344     * This condition requires a fixed key to match a given regexp
     345     */
    292346    class KeyValueRegexpCondition extends KeyValueCondition {
    293 
    294         public final Pattern pattern;
    295347        protected static final Set<Op> SUPPORTED_OPS = EnumSet.of(Op.REGEX, Op.NREGEX);
     348
     349        final Pattern pattern;
    296350
    297351        public KeyValueRegexpCondition(String k, String v, Op op, boolean considerValAsKey) {
     
    319373    }
    320374
     375    /**
     376     * A condition that checks that a key with the matching pattern has a value with the matching pattern.
     377     */
    321378    class RegexpKeyValueRegexpCondition extends KeyValueRegexpCondition {
    322379
    323380        public final Pattern keyPattern;
    324381
     382        /**
     383         * Create a condition in which the key and the value need to match a given regexp
     384         * @param k The key regexp
     385         * @param v The value regexp
     386         * @param op The operation to use when comparing the key and the value.
     387         */
    325388        public RegexpKeyValueRegexpCondition(String k, String v, Op op) {
    326389            super(k, v, op, false);
     
    419482     * </pre>
    420483     */
    421     class KeyCondition implements Condition {
     484    class KeyCondition implements Condition, ToTagConvertable {
    422485
    423486        /**
     
    484547         * @return The tag.
    485548         */
     549        @Override
    486550        public Tag asTag(OsmPrimitive p) {
    487551            String key = label;
    488552            if (KeyMatchType.REGEX.equals(matchType)) {
    489                 final Collection<String> matchingKeys = SubclassFilteredCollection.filter(p.keySet(), containsPattern);
    490                 if (!matchingKeys.isEmpty()) {
    491                     key = matchingKeys.iterator().next();
    492                 }
     553                key = p.keySet().stream().filter(containsPattern).findAny().orElse(key);
    493554            }
    494555            return new Tag(key, p.get(key));
     
    513574        @Override
    514575        public boolean applies(Environment env) {
    515             return env != null && env.getCascade(env.layer) != null && (not ^ env.getCascade(env.layer).containsKey(id));
     576            Cascade cascade = env.getCascade(env.layer);
     577            return cascade != null && (not ^ cascade.containsKey(id));
    516578        }
    517579
     
    703765        }
    704766
     767        /**
     768         * Create a new pseudo class condition
     769         * @param id The id of the pseudo class
     770         * @param not <code>true</code> to invert the condition
     771         * @param context The context the class is found in.
     772         * @return The new condition
     773         */
    705774        public static PseudoClassCondition createPseudoClassCondition(String id, boolean not, Context context) {
    706775            CheckParameterUtil.ensureThat(!"sameTags".equals(id) || Context.LINK.equals(context), "sameTags only supported in LINK context");
     
    753822    }
    754823
     824    /**
     825     * A condition that is fulfilled whenever the expression is evaluated to be true.
     826     */
    755827    class ExpressionCondition implements Condition {
    756828
     
    776848        }
    777849    }
     850
     851    /**
     852     * This is a condition that can be converted to a tag
     853     * @author Michael Zangl
     854     * @since 10674
     855     */
     856    public interface ToTagConvertable {
     857        /**
     858         * Converts the current condition to a tag
     859         * @param primitive A primitive to use as context. May be ignored.
     860         * @return A tag with the key/value of this condition.
     861         */
     862        Tag asTag(OsmPrimitive primitive);
     863    }
    778864}
Note: See TracChangeset for help on using the changeset viewer.