Changeset 8265 in josm


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

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

Location:
trunk
Files:
2 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) {
  • trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java

    r7937 r8265  
    88
    99import java.io.StringReader;
     10import java.util.Iterator;
    1011import java.util.LinkedHashSet;
    1112import java.util.List;
     
    1718import org.openstreetmap.josm.Main;
    1819import org.openstreetmap.josm.command.ChangePropertyCommand;
     20import org.openstreetmap.josm.command.ChangePropertyKeyCommand;
     21import org.openstreetmap.josm.command.Command;
     22import org.openstreetmap.josm.command.PseudoCommand;
     23import org.openstreetmap.josm.command.SequenceCommand;
    1924import org.openstreetmap.josm.data.osm.Node;
    20 import org.openstreetmap.josm.data.osm.Tag;
     25import org.openstreetmap.josm.data.osm.OsmPrimitive;
     26import org.openstreetmap.josm.data.osm.OsmUtils;
    2127import org.openstreetmap.josm.data.validation.Severity;
    2228import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.TagCheck;
     
    4955        assertThat(check, notNullValue());
    5056        assertThat(check.getDescription(null), is("{0.key}=null is deprecated"));
    51         assertThat(check.change.get(0).apply(null), is(new Tag("{0.key}")));
    52         assertThat(check.change.get(1).apply(null), is(new Tag("natural", "wetland")));
    53         assertThat(check.change.get(2).apply(null), is(new Tag("wetland", "marsh")));
     57        assertThat(check.fixCommands.get(0).toString(), is("fixRemove: {0.key}"));
     58        assertThat(check.fixCommands.get(1).toString(), is("fixAdd: natural=wetland"));
     59        assertThat(check.fixCommands.get(2).toString(), is("fixAdd: wetland=marsh"));
    5460        final Node n1 = new Node();
    5561        n1.put("natural", "marsh");
     
    6571        assertThat(MapCSSTagChecker.TagCheck.insertArguments(check.rule.selectors.get(0), "The key is {0.key} and the value is {0.value}"),
    6672                is("The key is natural and the value is marsh"));
     73    }
     74
     75    @Test
     76    public void test10913() throws Exception {
     77        final OsmPrimitive p = OsmUtils.createPrimitive("way highway=tertiary construction=yes");
     78        final TagCheck check = TagCheck.readMapCSS(new StringReader("way {" +
     79                "throwError: \"error\";" +
     80                "fixChangeKey: \"highway => construction\";\n" +
     81                "fixAdd: \"highway=construction\";\n" +
     82                "}")).get(0);
     83        final Command command = check.fixPrimitive(p);
     84        assertThat(command instanceof SequenceCommand, is(true));
     85        final Iterator<PseudoCommand> it = command.getChildren().iterator();
     86        assertThat(it.next() instanceof ChangePropertyKeyCommand, is(true));
     87        assertThat(it.next() instanceof ChangePropertyCommand, is(true));
    6788    }
    6889
Note: See TracChangeset for help on using the changeset viewer.