Changeset 6538 in josm


Ignore:
Timestamp:
2013-12-26T15:19:31+01:00 (10 years ago)
Author:
simon04
Message:

see #9414 - MapCSS-based tagchecker: provide {i.key}, {i.value}, {i.tag} variables to messages/fixes which refer to the corresponding match condition

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/data/validator/relation.mapcss

    r6537 r6538  
    88
    99/* from http://wiki.openstreetmap.org/wiki/Types_of_relation */
    10 relation[type=route][!route] {
    11   throwWarning: tr("{0} relation without {0} tag", "route");
     10/* see also #9071 */
     11relation[type=route][!route],
     12relation[type=route_master][!route_master],
     13relation[type=restriction][!restriction],
     14relation[type=boundary][!boundary],
     15relation[type=site][!site],
     16relation[type=public_transport][!public_transport],
     17relation[type=waterway][!waterway],
     18relation[type=enforcement][!enforcement] {
     19  throwWarning: tr("{0} relation without {0} tag", "{1.key}");
    1220  assertMatch: "relation type=route";
    1321  assertNoMatch: "relation type=route route=train";
    14 }
    15 
    16 relation[type=route_master][!route_master] {
    17   /* see #9071 */
    18   throwWarning: tr("{0} relation without {0} tag", "route_master");
    1922  assertMatch: "relation type=route_master";
    2023  assertNoMatch: "relation type=route_master route_master=train";
    21 }
    22 
    23 relation[type=restriction][!restriction] {
    24   throwWarning: tr("{0} relation without {0} tag", "restriction");
    2524  assertMatch: "relation type=restriction";
    2625  assertNoMatch: "relation type=restriction restriction=no_left_turn";
    27 }
    28 
    29 relation[type=boundary][!boundary] {
    30   throwWarning: tr("{0} relation without {0} tag", "boundary");
    3126  assertMatch: "relation type=boundary";
    3227  assertNoMatch: "relation type=boundary boundary=administrative";
    33 }
    34 
    35 relation[type=site][!site] {
    36   throwWarning: tr("{0} relation without {0} tag", "site");
    3728  assertMatch: "relation type=site";
    3829  assertNoMatch: "relation type=site site=administrative";
    39 }
    40 
    41 relation[type=public_transport][!public_transport] {
    42   throwWarning: tr("{0} relation without {0} tag", "public_transport");
    4330  assertMatch: "relation type=public_transport";
    4431  assertNoMatch: "relation type=public_transport public_transport=stop_area";
    45 }
    46 
    47 relation[type=waterway][!waterway] {
    48   throwWarning: tr("{0} relation without {0} tag", "waterway");
    4932  assertMatch: "relation type=waterway";
    5033  assertNoMatch: "relation type=waterway waterway=river";
    51 }
    52 
    53 relation[type=enforcement][!enforcement] {
    54   throwWarning: tr("{0} relation without {0} tag", "enforcement");
    5534  assertMatch: "relation type=enforcement";
    5635  assertNoMatch: "relation type=enforcement enforcement=maxspeed";
  • trunk/src/org/openstreetmap/josm/command/ChangePropertyCommand.java

    r6507 r6538  
    221221        return children;
    222222    }
     223
     224    public Map<String, String> getTags() {
     225        return Collections.unmodifiableMap(tags);
     226    }
    223227}
  • trunk/src/org/openstreetmap/josm/command/Command.java

    r6380 r6538  
    8686     */
    8787    public Command() {
    88         this.layer = Main.main.getEditLayer();
     88        this.layer = Main.main == null ? null : Main.main.getEditLayer();
    8989    }
    9090
  • trunk/src/org/openstreetmap/josm/data/validation/TestError.java

    r6378 r6538  
    219219    }
    220220
     221    public void setTester(Test tester) {
     222        this.tester = tester;
     223    }
     224
    221225    /**
    222226     * Gets the code
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

    r6537 r6538  
    1414import java.util.List;
    1515import java.util.Map;
     16import java.util.regex.Matcher;
     17import java.util.regex.Pattern;
    1618
    1719import org.openstreetmap.josm.command.ChangePropertyCommand;
     
    2931import org.openstreetmap.josm.data.validation.TestError;
    3032import org.openstreetmap.josm.gui.mappaint.Environment;
     33import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
    3134import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
    3235import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
     
    173176         */
    174177        boolean matchesPrimitive(OsmPrimitive primitive) {
     178            return whichSelectorMatchesPrimitive(primitive) != null;
     179        }
     180
     181        Selector whichSelectorMatchesPrimitive(OsmPrimitive primitive) {
    175182            final Environment env = new Environment().withPrimitive(primitive);
    176183            for (Selector i : selector) {
    177184                if (i.matches(env)) {
    178                     return true;
    179                 }
    180             }
    181             return false;
     185                    return i;
     186                }
     187            }
     188            return null;
     189        }
     190
     191        /**
     192         * Determines the {@code index}-th key/value/tag (depending on {@code type}) of the {@link Selector.GeneralSelector}.
     193         */
     194        static String determineArgument(Selector.GeneralSelector matchingSelector, int index, String type) {
     195            try {
     196                final Condition c = matchingSelector.getConditions().get(index);
     197                final Tag tag = c instanceof Condition.KeyCondition
     198                        ? ((Condition.KeyCondition) c).asTag()
     199                        : c instanceof Condition.KeyValueCondition
     200                        ? ((Condition.KeyValueCondition) c).asTag()
     201                        : null;
     202                if (tag == null) {
     203                    return null;
     204                } else if ("key".equals(type)) {
     205                    return tag.getKey();
     206                } else if ("value".equals(type)) {
     207                    return tag.getValue();
     208                } else if ("tag".equals(type)) {
     209                    return tag.toString();
     210                }
     211            } catch (IndexOutOfBoundsException ignore) {
     212            }
     213            return null;
     214        }
     215
     216        /**
     217         * Replaces occurrences of {@code {i.key}}, {@code {i.value}}, {@code {i.tag}} in {@code s} by the corresponding
     218         * key/value/tag of the {@code index}-th {@link Condition} of {@code matchingSelector}.
     219         */
     220        static String insertArguments(Selector matchingSelector, String s) {
     221            if (!(matchingSelector instanceof Selector.GeneralSelector) || s == null) {
     222                return s;
     223            }
     224            final Matcher m = Pattern.compile("\\{(\\d+)\\.(key|value|tag)\\}").matcher(s);
     225            final StringBuffer sb = new StringBuffer();
     226            while (m.find()) {
     227                m.appendReplacement(sb, determineArgument((Selector.GeneralSelector) matchingSelector, Integer.parseInt(m.group(1)), m.group(2)));
     228            }
     229            m.appendTail(sb);
     230            return sb.toString();
    182231        }
    183232
     
    193242                return null;
    194243            }
     244            final Selector matchingSelector = whichSelectorMatchesPrimitive(p);
    195245            Collection<Command> cmds = new LinkedList<Command>();
    196246            for (PrimitiveToTag toTag : change) {
    197247                final Tag tag = toTag.apply(p);
    198                 cmds.add(new ChangePropertyCommand(p, tag.getKey(), tag.getValue()));
     248                final String key = insertArguments(matchingSelector, tag.getKey());
     249                final String value = insertArguments(matchingSelector, tag.getValue());
     250                cmds.add(new ChangePropertyCommand(p, key, value));
    199251            }
    200252            for (Map.Entry<String, String> i : keyChange.entrySet()) {
    201                 cmds.add(new ChangePropertyKeyCommand(p, i.getKey(), i.getValue()));
     253                final String oldKey = insertArguments(matchingSelector, i.getKey());
     254                final String newKey = insertArguments(matchingSelector, i.getValue());
     255                cmds.add(new ChangePropertyKeyCommand(p, oldKey, newKey));
    202256            }
    203257            return new SequenceCommand(tr("Fix of {0}", getDescription()), cmds);
     
    231285        }
    232286
     287        /**
     288         * Constructs a {@link TestError} for the given primitive, or returns null if the primitive does not give rise to an error.
     289         *
     290         * @param p the primitive to construct the error for
     291         * @return an instance of {@link TestError}, or returns null if the primitive does not give rise to an error.
     292         */
     293        TestError getErrorForPrimitive(OsmPrimitive p) {
     294            final Selector matchingSelector = whichSelectorMatchesPrimitive(p);
     295            if (matchingSelector != null) {
     296                final Command fix = fixPrimitive(p);
     297                final String description = TagCheck.insertArguments(matchingSelector, getDescription());
     298                if (fix != null) {
     299                    return new FixableTestError(null, getSeverity(), description, 3000, p, fix);
     300                } else {
     301                    return new TestError(null, getSeverity(), description, 3000, p);
     302                }
     303            } else {
     304                return null;
     305            }
     306        }
    233307    }
    234308
     
    240314    public void visit(OsmPrimitive p) {
    241315        for (TagCheck check : checks) {
    242             if (check.matchesPrimitive(p)) {
    243                 final Command fix = check.fixPrimitive(p);
    244                 if (fix != null) {
    245                     errors.add(new FixableTestError(this, check.getSeverity(), check.getDescription(), 3000, p, fix));
    246                 } else {
    247                     errors.add(new TestError(this, check.getSeverity(), check.getDescription(), 3000, p));
    248                 }
     316            final TestError error = check.getErrorForPrimitive(p);
     317            if (error != null) {
     318                error.setTester(this);
     319                errors.add(error);
    249320            }
    250321        }
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java

    r6513 r6538  
    1212import org.openstreetmap.josm.data.osm.OsmUtils;
    1313import org.openstreetmap.josm.data.osm.Relation;
     14import org.openstreetmap.josm.data.osm.Tag;
    1415import org.openstreetmap.josm.data.osm.Way;
    1516import org.openstreetmap.josm.gui.mappaint.Cascade;
     
    161162        public boolean applies(Environment env) {
    162163            return op.eval(env.osm.get(k), v);
     164        }
     165
     166        public Tag asTag() {
     167            return new Tag(k, v);
    163168        }
    164169
     
    252257        }
    253258
     259        public Tag asTag() {
     260            return new Tag(label);
     261        }
     262
    254263        @Override
    255264        public String toString() {
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java

    r6535 r6538  
    431431            }
    432432            Matcher m = Pattern.compile(pattern, f).matcher(target);
    433             if (m.matches()) {
    434                 List<String> result = new ArrayList<String>(m.groupCount() + 1);
    435                 for (int i = 0; i <= m.groupCount(); i++) {
    436                     result.add(m.group(i));
    437                 }
    438                 return result;
    439             } else {
    440                 return null;
    441             }
     433            return Utils.getMatches(m);
    442434        }
    443435
     
    450442        public static List<String> regexp_match(String pattern, String target) {
    451443            Matcher m = Pattern.compile(pattern).matcher(target);
    452             if (m.matches()) {
    453                 List<String> result = new ArrayList<String>(m.groupCount() + 1);
    454                 for (int i = 0; i <= m.groupCount(); i++) {
    455                     result.add(m.group(i));
    456                 }
    457                 return result;
    458             } else {
    459                 return null;
    460             }
     444            return Utils.getMatches(m);
    461445        }
    462446
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java

    r6248 r6538  
    22package org.openstreetmap.josm.gui.mappaint.mapcss;
    33
     4import java.util.Collections;
    45import java.util.List;
    56import java.util.regex.PatternSyntaxException;
     
    231232            return true;
    232233        }
     234
     235        public List<Condition> getConditions() {
     236            return Collections.unmodifiableList(conds);
     237        }
    233238    }
    234239
     
    310315        @Override
    311316        public boolean matches(Environment e) {
    312             if (!matchesBase(e)) return false;
    313             return matchesConditions(e);
     317            return matchesBase(e) && matchesConditions(e);
    314318        }
    315319
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r6524 r6538  
    3838import java.util.Iterator;
    3939import java.util.List;
     40import java.util.regex.Matcher;
    4041import java.util.zip.GZIPInputStream;
    4142import java.util.zip.ZipFile;
     
    835836        return String.format("%d %s %d %s", days, trn("day", "days", days), elapsedTime/3600000, tr("h"));
    836837    }
     838
     839    /**
     840     * Returns a list of capture groups if {@link Matcher#matches()}, or {@code null}.
     841     * The first element (index 0) is the complete match.
     842     * Further elements correspond to the parts in parentheses of the regular expression.
     843     * @param m the matcher
     844     * @return a list of capture groups if {@link Matcher#matches()}, or {@code null}.
     845     */
     846    public static List<String> getMatches(final Matcher m) {
     847        if (m.matches()) {
     848            List<String> result = new ArrayList<String>(m.groupCount() + 1);
     849            for (int i = 0; i <= m.groupCount(); i++) {
     850                result.add(m.group(i));
     851            }
     852            return result;
     853        } else {
     854            return null;
     855        }
     856    }
    837857}
  • trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java

    r6534 r6538  
    44import org.junit.Test;
    55import org.openstreetmap.josm.Main;
     6import org.openstreetmap.josm.command.ChangePropertyCommand;
    67import org.openstreetmap.josm.data.Preferences;
    78import org.openstreetmap.josm.data.osm.Node;
     
    1011import org.openstreetmap.josm.data.osm.Tag;
    1112import org.openstreetmap.josm.data.osm.Way;
     13import org.openstreetmap.josm.data.validation.Severity;
    1214import org.openstreetmap.josm.tools.TextTagParser;
    1315
    1416import java.io.StringReader;
     17import java.text.MessageFormat;
    1518import java.util.LinkedHashSet;
    1619import java.util.List;
     
    3538        final List<MapCSSTagChecker.TagCheck> checks = MapCSSTagChecker.TagCheck.readMapCSS(new StringReader("" +
    3639                "*[natural=marsh] {\n" +
    37                 "   throwWarning: tr(\"{0} is deprecated\", \"natural=marsh\");\n" +
    38                 "   fixRemove: \"natural\";\n" +
     40                "   throwWarning: tr(\"{0} is deprecated\", \"{0.tag}\");\n" +
     41                "   fixRemove: \"{0.key}\";\n" +
    3942                "   fixAdd: \"natural=wetland\";\n" +
    4043                "   fixAdd: \"wetland=marsh\";\n" +
     
    4346        final MapCSSTagChecker.TagCheck check = checks.get(0);
    4447        assertThat(check, notNullValue());
    45         assertThat(check.change.get(0).apply(null), is(new Tag("natural")));
     48        assertThat(check.getDescription(), is("{0.tag} is deprecated"));
     49        assertThat(check.change.get(0).apply(null), is(new Tag("{0.key}")));
    4650        assertThat(check.change.get(1).apply(null), is(new Tag("natural", "wetland")));
    4751        assertThat(check.change.get(2).apply(null), is(new Tag("wetland", "marsh")));
    48         assertThat(check.errors.keySet().iterator().next(), is("natural=marsh is deprecated"));
    4952        final Node n1 = new Node();
    5053        n1.put("natural", "marsh");
    5154        assertTrue(check.matchesPrimitive(n1));
     55        assertThat(check.getErrorForPrimitive(n1).getMessage(), is("natural=marsh is deprecated"));
     56        assertThat(check.getErrorForPrimitive(n1).getSeverity(), is(Severity.WARNING));
     57        assertThat(((ChangePropertyCommand) check.fixPrimitive(n1).getChildren().iterator().next()).getTags().toString(),
     58                is("{natural=}"));
    5259        final Node n2 = new Node();
    5360        n2.put("natural", "wood");
    5461        assertFalse(check.matchesPrimitive(n2));
     62        assertThat(MapCSSTagChecker.TagCheck.insertArguments(check.selector.get(0), "The key is {0.key} and the value is {0.value}"),
     63                is("The key is natural and the value is marsh"));
    5564    }
    5665
     
    97106                final OsmPrimitive p = createPrimitiveForAssertion(i.getKey());
    98107                if (check.matchesPrimitive(p) != i.getValue()) {
    99                     final String error = "Expecting test '" + check.getMessage() + "' to " + (i.getValue() ? "" : "not ") + "match " + i.getKey() + ", i.e., " + p.getKeys();
     108                    final String error = MessageFormat.format("Expecting test ''{0}'' (i.e., {1}) to {2} {3} (i.e., {4})",
     109                            check.getMessage(), check.selector, i.getValue() ? "match" : "not match", i.getKey(), p.getKeys());
    100110                    System.err.println(error);
    101111                    assertionErrors.add(error);
Note: See TracChangeset for help on using the changeset viewer.