Ticket #17055: 17055-enh-v2.patch

File 17055-enh-v2.patch, 14.2 KB (added by GerdP, 5 years ago)

previous version contained a bug

  • src/org/openstreetmap/josm/data/validation/tests/TagChecker.java

     
    1313import java.util.Collection;
    1414import java.util.Collections;
    1515import java.util.HashMap;
     16import java.util.HashSet;
    1617import java.util.List;
    1718import java.util.Locale;
    1819import java.util.Map;
     
    7677    /** The TagChecker data */
    7778    private static final List<CheckerData> checkerData = new ArrayList<>();
    7879    private static final List<String> ignoreDataStartsWith = new ArrayList<>();
    79     private static final List<String> ignoreDataEquals = new ArrayList<>();
     80    private static final Set<String> ignoreDataEquals = new HashSet<>();
    8081    private static final List<String> ignoreDataEndsWith = new ArrayList<>();
    8182    private static final List<Tag> ignoreDataTag = new ArrayList<>();
    8283
     
    141142    protected JCheckBox prefCheckPaintBeforeUpload;
    142143
    143144    // CHECKSTYLE.OFF: SingleSpaceSeparator
    144     protected static final int EMPTY_VALUES      = 1200;
    145     protected static final int INVALID_KEY       = 1201;
    146     protected static final int INVALID_VALUE     = 1202;
    147     protected static final int FIXME             = 1203;
    148     protected static final int INVALID_SPACE     = 1204;
    149     protected static final int INVALID_KEY_SPACE = 1205;
    150     protected static final int INVALID_HTML      = 1206; /* 1207 was PAINT */
    151     protected static final int LONG_VALUE        = 1208;
    152     protected static final int LONG_KEY          = 1209;
    153     protected static final int LOW_CHAR_VALUE    = 1210;
    154     protected static final int LOW_CHAR_KEY      = 1211;
    155     protected static final int MISSPELLED_VALUE  = 1212;
    156     protected static final int MISSPELLED_KEY    = 1213;
    157     protected static final int MULTIPLE_SPACES   = 1214;
     145    protected static final int EMPTY_VALUES             = 1200;
     146    protected static final int INVALID_KEY              = 1201;
     147    protected static final int INVALID_VALUE            = 1202;
     148    protected static final int FIXME                    = 1203;
     149    protected static final int INVALID_SPACE            = 1204;
     150    protected static final int INVALID_KEY_SPACE        = 1205;
     151    protected static final int INVALID_HTML             = 1206; /* 1207 was PAINT */
     152    protected static final int LONG_VALUE               = 1208;
     153    protected static final int LONG_KEY                 = 1209;
     154    protected static final int LOW_CHAR_VALUE           = 1210;
     155    protected static final int LOW_CHAR_KEY             = 1211;
     156    protected static final int MISSPELLED_VALUE         = 1212;
     157    protected static final int MISSPELLED_KEY           = 1213;
     158    protected static final int MULTIPLE_SPACES          = 1214;
     159    protected static final int TRUNCATED_VALUE          = 1215;
     160    protected static final int MISSPELLED_VALUE_NO_FIX  = 1216;
     161    protected static final int TRUNCATED_VALUE_NO_FIX   = 1217;
    158162    // CHECKSTYLE.ON: SingleSpaceSeparator
    159163    // 1250 and up is used by tagcheck
    160164
     
    387391     * @since 9023
    388392     */
    389393    public static boolean isTagIgnored(String key, String value) {
    390         boolean tagInPresets = isTagInPresets(key, value);
    391         boolean ignore = false;
    392 
     394        if (ignoreDataEquals.contains(key)) {
     395            return true;
     396        }
    393397        for (String a : ignoreDataStartsWith) {
    394398            if (key.startsWith(a)) {
    395                 ignore = true;
     399                return true;
    396400            }
    397401        }
    398         for (String a : ignoreDataEquals) {
    399             if (key.equals(a)) {
    400                 ignore = true;
    401             }
    402         }
    403402        for (String a : ignoreDataEndsWith) {
    404403            if (key.endsWith(a)) {
    405                 ignore = true;
     404                return true;
    406405            }
    407406        }
    408407
    409         if (!tagInPresets) {
     408        if (!isTagInPresets(key, value)) {
    410409            for (Tag a : ignoreDataTag) {
    411410                if (key.equals(a.getKey()) && value.equals(a.getValue())) {
    412                     ignore = true;
     411                    return true;
    413412                }
    414413            }
    415414        }
    416         return ignore;
     415        return false;
    417416    }
    418417
    419418    /**
     
    534533                } else if (!isTagInPresets(key, value)) {
    535534                    // try to fix common typos and check again if value is still unknown
    536535                    String fixedValue = harmonizeValue(prop.getValue());
    537                     Map<String, String> possibleValues = getPossibleValues(getPresetValues(key));
     536                    Set<String> possibleValues = getPresetValues(key);
    538537                    List<String> fixVals = new ArrayList<>();
    539                     if (!possibleValues.containsKey(fixedValue)) {
    540                         int minDist = 2;
     538                    List<String> sameStartVals = new ArrayList<>();
     539                    if (!possibleValues.contains(fixedValue)) {
     540                        int minDist = 10;
    541541                        String closest = null;
    542                         for (String possibleVal : possibleValues.keySet()) {
     542                        for (String possibleVal : possibleValues) {
     543                            if (possibleVal.isEmpty())
     544                                continue;
     545                            if (possibleVal.startsWith(fixedValue))
     546                                sameStartVals.add(possibleVal);
    543547                            int dist = Utils.getLevenshteinDistance(possibleVal, fixedValue);
    544548                            if (dist < minDist) {
    545549                                closest = possibleVal;
     
    550554                                fixVals.add(possibleVal);
    551555                            }
    552556                        }
    553                         if (minDist <= 1) {
     557                        fixedValue = null;
     558                        if (minDist <= 2) {
    554559                            if (fixVals.size() < 2) {
    555560                                fixedValue = closest;
    556561                            } else {
    557562                                Collections.sort(fixVals);
    558563                                // misspelled preset value with multiple good alternatives
    559                                 errors.add(TestError.builder(this, Severity.WARNING, MISSPELLED_VALUE)
     564                                errors.add(TestError.builder(this, Severity.WARNING, MISSPELLED_VALUE_NO_FIX)
    560565                                        .message(tr("Misspelled property value"),
    561566                                                marktr("Value ''{0}'' for key ''{1}'' looks like one of {2}."), prop.getValue(), key, fixVals)
    562567                                        .primitives(p)
     
    565570                                continue;
    566571                            }
    567572                        }
     573                        if (!sameStartVals.isEmpty()) {
     574                            if (sameStartVals.size() == 1) {
     575                                // only one preset value starts with the same characters
     576                                final String newValue = sameStartVals.get(0);
     577                                errors.add(TestError.builder(this, Severity.WARNING, TRUNCATED_VALUE)
     578                                        .message(tr("Probably truncated property value"),
     579                                                marktr("Value ''{0}'' for key ''{1}'' looks like truncated ''{2}''."), prop.getValue(), key, newValue)
     580                                        .primitives(p)
     581                                        .fix(() -> new ChangePropertyCommand(p, key, newValue))
     582                                        .build());
     583                                withErrors.put(p, "WPV");
     584                            } else {
     585                                errors.add(TestError.builder(this, Severity.WARNING, TRUNCATED_VALUE_NO_FIX)
     586                                        .message(tr("Ambiguous truncated property value"),
     587                                                marktr("Value ''{0}'' for key ''{1}'' not in presets."),
     588                                                prop.getValue(), key)
     589                                        .primitives(p).build());
     590                                withErrors.put(p, "WPV");
     591                            }
     592                            continue;
     593                        }
    568594                    }
    569                     if (possibleValues.containsKey(fixedValue)) {
    570                         final String newValue = possibleValues.get(fixedValue);
     595                    if (fixedValue != null && possibleValues.contains(fixedValue)) {
     596                        final String newValue = fixedValue;
    571597                        // misspelled preset value
    572598                        errors.add(TestError.builder(this, Severity.WARNING, MISSPELLED_VALUE)
    573599                                .message(tr("Misspelled property value"),
     
    602628          || value.toLowerCase(Locale.ENGLISH).contains("fixme") || value.contains("check and delete");
    603629    }
    604630
    605     private static Map<String, String> getPossibleValues(Set<String> values) {
    606         // generate a map with common typos
    607         Map<String, String> map = new HashMap<>();
    608         if (values != null) {
    609             for (String value : values) {
    610                 map.put(value, value);
    611                 if (value.contains("_")) {
    612                     map.put(value.replace("_", ""), value);
    613                 }
    614             }
    615         }
    616         return map;
    617     }
    618 
    619631    private static String harmonizeKey(String key) {
    620632        return Utils.strip(key.toLowerCase(Locale.ENGLISH).replace('-', '_').replace(':', '_').replace(' ', '_'), "-_;:,");
    621633    }
     
    778790            int code = testError.getCode();
    779791            return code == INVALID_KEY || code == EMPTY_VALUES || code == INVALID_SPACE ||
    780792                   code == INVALID_KEY_SPACE || code == INVALID_HTML || code == MISSPELLED_VALUE ||
    781                    code == MULTIPLE_SPACES;
     793                   code == MULTIPLE_SPACES || code == TRUNCATED_VALUE;
    782794        }
    783795
    784796        return false;
  • test/unit/org/openstreetmap/josm/data/validation/tests/TagCheckerTest.java

     
    1515import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1616import org.openstreetmap.josm.data.osm.OsmUtils;
    1717import org.openstreetmap.josm.data.osm.Tag;
     18import org.openstreetmap.josm.data.validation.Severity;
    1819import org.openstreetmap.josm.data.validation.TestError;
    1920import org.openstreetmap.josm.testutils.JOSMTestRules;
    2021
     
    7778        assertEquals(1, errors.size());
    7879        assertEquals("Misspelled property key", errors.get(0).getMessage());
    7980        assertEquals("Key 'Brand' looks like 'brand'.", errors.get(0).getDescription());
     81        assertEquals(Severity.WARNING, errors.get(0).getSeverity());
    8082        assertFalse(errors.get(0).isFixable());
    8183    }
    8284
     
    9092        assertEquals(1, errors.size());
    9193        assertEquals("Presets do not contain property key", errors.get(0).getMessage());
    9294        assertEquals("Key 'namez' not in presets.", errors.get(0).getDescription());
     95        assertEquals(Severity.OTHER, errors.get(0).getSeverity());
     96        assertFalse(errors.get(0).isFixable());
    9397    }
    9498
    9599    /**
     
    102106        assertEquals(1, errors.size());
    103107        assertEquals("Misspelled property value", errors.get(0).getMessage());
    104108        assertEquals("Value 'forrest' for key 'landuse' looks like 'forest'.", errors.get(0).getDescription());
     109        assertEquals(Severity.WARNING, errors.get(0).getSeverity());
     110        assertTrue(errors.get(0).isFixable());
    105111    }
    106112
    107113    /**
     
    114120        assertEquals(1, errors.size());
    115121        assertEquals("Misspelled property value", errors.get(0).getMessage());
    116122        assertEquals("Value 'servics' for key 'highway' looks like one of [service, services].", errors.get(0).getDescription());
     123        assertEquals(Severity.WARNING, errors.get(0).getSeverity());
     124        assertFalse(errors.get(0).isFixable());
    117125    }
    118126
    119127    /**
     128     * Check for misspelled value.
     129     * @throws IOException if any I/O error occurs
     130     */
     131    @Test
     132    public void testMisspelledTag3() throws IOException {
     133        final List<TestError> errors = test(OsmUtils.createPrimitive("node highway=residentail"));
     134        assertEquals(1, errors.size());
     135        assertEquals("Misspelled property value", errors.get(0).getMessage());
     136        assertEquals("Value 'residentail' for key 'highway' looks like 'residential'.", errors.get(0).getDescription());
     137        assertEquals(Severity.WARNING, errors.get(0).getSeverity());
     138        assertTrue(errors.get(0).isFixable());
     139    }
     140
     141    /**
     142     * Check for misspelled value.
     143     * @throws IOException if any I/O error occurs
     144     */
     145    @Test
     146    public void testMisspelledTag4() throws IOException {
     147        final List<TestError> errors = test(OsmUtils.createPrimitive("node highway=res"));
     148        assertEquals(1, errors.size());
     149        assertEquals("Ambiguous truncated property value", errors.get(0).getMessage());
     150        assertEquals("Value 'res' for key 'highway' not in presets.", errors.get(0).getDescription());
     151        assertEquals(Severity.WARNING, errors.get(0).getSeverity());
     152        assertFalse(errors.get(0).isFixable());
     153    }
     154
     155    /**
     156     * Check for truncated value.
     157     * @throws IOException if any I/O error occurs
     158     */
     159    @Test
     160    public void testTruncatedTag1() throws IOException {
     161        final List<TestError> errors = test(OsmUtils.createPrimitive("node highway=u"));
     162        assertEquals(1, errors.size());
     163        assertEquals("Probably truncated property value", errors.get(0).getMessage());
     164        assertEquals("Value 'u' for key 'highway' looks like truncated 'unclassified'.", errors.get(0).getDescription());
     165        assertEquals(Severity.WARNING, errors.get(0).getSeverity());
     166        assertTrue(errors.get(0).isFixable());
     167    }
     168
     169    /**
    120170     * Checks that tags specifically ignored are effectively not in internal presets.
    121171     * @throws IOException if any I/O error occurs
    122172     */