Ignore:
Timestamp:
2015-04-25T18:33:40+02:00 (9 years ago)
Author:
simon04
Message:

fix #10913 - MapCSS validator: retain sequence of fixing commands

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

    r8130 r8265  
    141141    }
    142142
     143    /**
     144     * Represents a fix to a validation test. The fixing {@link Command} can be obtained by {@link #createCommand(OsmPrimitive, Selector)}.
     145     */
     146    static abstract class FixCommand {
     147        /**
     148         * Creates the fixing {@link Command} for the given primitive. The {@code matchingSelector} is used to
     149         * evaluate placeholders (cf. {@link org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.TagCheck#insertArguments(Selector, String)}).
     150         */
     151        abstract Command createCommand(final OsmPrimitive p, final Selector matchingSelector);
     152
     153        private static void checkObject(final Object obj) {
     154            CheckParameterUtil.ensureThat(obj instanceof Expression || obj instanceof String, "instance of Exception or String expected, but got " + obj);
     155        }
     156
     157        /**
     158         * Evaluates given object as {@link Expression} or {@link String} on the matched {@link OsmPrimitive} and {@code matchingSelector}.
     159         */
     160        private static String evaluateObject(final Object obj, final OsmPrimitive p, final Selector matchingSelector) {
     161            final String s;
     162            if (obj instanceof Expression) {
     163                s = (String) ((Expression) obj).evaluate(new Environment().withPrimitive(p));
     164            } else if (obj instanceof String) {
     165                s = (String) obj;
     166            } else {
     167                return null;
     168            }
     169            return TagCheck.insertArguments(matchingSelector, s);
     170        }
     171
     172        /**
     173         * Creates a fixing command which executes a {@link ChangePropertyCommand} on the specified tag.
     174         */
     175        static FixCommand fixAdd(final Object obj) {
     176            checkObject(obj);
     177            return new FixCommand() {
     178                @Override
     179                Command createCommand(OsmPrimitive p, Selector matchingSelector) {
     180                    final Tag tag = Tag.ofString(evaluateObject(obj, p, matchingSelector));
     181                    return new ChangePropertyCommand(p, tag.getKey(), tag.getValue());
     182                }
     183
     184                @Override
     185                public String toString() {
     186                    return "fixAdd: " + obj;
     187                }
     188            };
     189
     190        }
     191
     192        /**
     193         * Creates a fixing command which executes a {@link ChangePropertyCommand} to delete the specified key.
     194         */
     195        static FixCommand fixRemove(final Object obj) {
     196            checkObject(obj);
     197            return new FixCommand() {
     198                @Override
     199                Command createCommand(OsmPrimitive p, Selector matchingSelector) {
     200                    final String key = evaluateObject(obj, p, matchingSelector);
     201                    return new ChangePropertyCommand(p, key, "");
     202                }
     203
     204                @Override
     205                public String toString() {
     206                    return "fixRemove: " + obj;
     207                }
     208            };
     209        }
     210
     211        /**
     212         * Creates a fixing command which executes a {@link ChangePropertyKeyCommand} on the specified keys.
     213         */
     214        static FixCommand fixChangeKey(final String oldKey, final String newKey) {
     215            return new FixCommand() {
     216                @Override
     217                Command createCommand(OsmPrimitive p, Selector matchingSelector) {
     218                    return new ChangePropertyKeyCommand(p, oldKey, newKey);
     219                }
     220
     221                @Override
     222                public String toString() {
     223                    return "fixChangeKey: " + oldKey + " => " + newKey;
     224                }
     225            };
     226        }
     227    }
     228
    143229    final MultiMap<String, TagCheck> checks = new MultiMap<>();
    144230
    145231    static class TagCheck implements Predicate<OsmPrimitive> {
    146232        protected final GroupedMapCSSRule rule;
    147         protected final List<PrimitiveToTag> change = new ArrayList<>();
    148         protected final Map<String, String> keyChange = new LinkedHashMap<>();
     233        protected final List<FixCommand> fixCommands = new ArrayList<>();
    149234        protected final List<String> alternatives = new ArrayList<>();
    150235        protected final Map<Instruction.AssignmentInstruction, Severity> errors = new HashMap<>();
     
    155240        TagCheck(GroupedMapCSSRule rule) {
    156241            this.rule = rule;
    157         }
    158 
    159         /**
    160          * A function mapping the matched {@link OsmPrimitive} to a {@link Tag}.
    161          */
    162         abstract static class PrimitiveToTag implements Utils.Function<OsmPrimitive, Tag> {
    163 
    164             private PrimitiveToTag() {
    165                 // Hide implicit public constructor for utility class
    166             }
    167 
    168             /**
    169              * Creates a new mapping from an {@code MapCSS} object.
    170              * In case of an {@link Expression}, that is evaluated on the matched {@link OsmPrimitive}.
    171              * In case of a {@link String}, that is "compiled" to a {@link Tag} instance.
    172              */
    173             static PrimitiveToTag ofMapCSSObject(final Object obj, final boolean keyOnly) {
    174                 if (obj instanceof Expression) {
    175                     return new PrimitiveToTag() {
    176                         @Override
    177                         public Tag apply(OsmPrimitive p) {
    178                             final String s = (String) ((Expression) obj).evaluate(new Environment().withPrimitive(p));
    179                             return keyOnly? new Tag(s) : Tag.ofString(s);
    180                         }
    181                     };
    182                 } else if (obj instanceof String) {
    183                     final Tag tag = keyOnly ? new Tag((String) obj) : Tag.ofString((String) obj);
    184                     return new PrimitiveToTag() {
    185                         @Override
    186                         public Tag apply(OsmPrimitive ignore) {
    187                             return tag;
    188                         }
    189                     };
    190                 } else {
    191                     return null;
    192                 }
    193             }
    194242        }
    195243
     
    233281                        }
    234282                    } else if ("fixAdd".equals(ai.key)) {
    235                         final PrimitiveToTag toTag = PrimitiveToTag.ofMapCSSObject(ai.val, false);
    236                         if (toTag != null) {
    237                             check.change.add(toTag);
    238                         } else {
    239                             Main.warn("Invalid value for "+ai.key+": "+ai.val);
    240                         }
     283                        check.fixCommands.add(FixCommand.fixAdd(ai.val));
    241284                    } else if ("fixRemove".equals(ai.key)) {
    242285                        CheckParameterUtil.ensureThat(!(ai.val instanceof String) || !(val != null && val.contains("=")),
    243286                                "Unexpected '='. Please only specify the key to remove!");
    244                         final PrimitiveToTag toTag = PrimitiveToTag.ofMapCSSObject(ai.val, true);
    245                         if (toTag != null) {
    246                             check.change.add(toTag);
    247                         } else {
    248                             Main.warn("Invalid value for "+ai.key+": "+ai.val);
    249                         }
     287                        check.fixCommands.add(FixCommand.fixRemove(ai.val));
    250288                    } else if ("fixChangeKey".equals(ai.key) && val != null) {
    251289                        CheckParameterUtil.ensureThat(val.contains("=>"), "Separate old from new key by '=>'!");
    252290                        final String[] x = val.split("=>", 2);
    253                         check.keyChange.put(Tag.removeWhiteSpaces(x[0]), Tag.removeWhiteSpaces(x[1]));
     291                        check.fixCommands.add(FixCommand.fixChangeKey(Tag.removeWhiteSpaces(x[0]), Tag.removeWhiteSpaces(x[1])));
    254292                    } else if ("fixDeleteObject".equals(ai.key) && val != null) {
    255293                        CheckParameterUtil.ensureThat(val.equals("this"), "fixDeleteObject must be followed by 'this'");
     
    408446         */
    409447        Command fixPrimitive(OsmPrimitive p) {
    410             if (change.isEmpty() && keyChange.isEmpty() && !deletion) {
     448            if (fixCommands.isEmpty() && !deletion) {
    411449                return null;
    412450            }
    413451            final Selector matchingSelector = whichSelectorMatchesPrimitive(p);
    414452            Collection<Command> cmds = new LinkedList<>();
    415             for (PrimitiveToTag toTag : change) {
    416                 final Tag tag = toTag.apply(p);
    417                 final String key = insertArguments(matchingSelector, tag.getKey());
    418                 final String value = insertArguments(matchingSelector, tag.getValue());
    419                 cmds.add(new ChangePropertyCommand(p, key, value));
    420             }
    421             for (Map.Entry<String, String> i : keyChange.entrySet()) {
    422                 final String oldKey = insertArguments(matchingSelector, i.getKey());
    423                 final String newKey = insertArguments(matchingSelector, i.getValue());
    424                 cmds.add(new ChangePropertyKeyCommand(p, oldKey, newKey));
     453            for (FixCommand fixCommand : fixCommands) {
     454                cmds.add(fixCommand.createCommand(p, matchingSelector));
    425455            }
    426456            if (deletion) {
Note: See TracChangeset for help on using the changeset viewer.