001// License: GPL. See LICENSE file for details. 002package org.openstreetmap.josm.data.validation.tests; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.Collection; 009import java.util.HashSet; 010import java.util.Set; 011import java.util.regex.Pattern; 012 013import org.openstreetmap.josm.Main; 014import org.openstreetmap.josm.data.osm.OsmPrimitive; 015import org.openstreetmap.josm.data.validation.Severity; 016import org.openstreetmap.josm.data.validation.Test; 017import org.openstreetmap.josm.data.validation.TestError; 018import org.openstreetmap.josm.tools.Predicates; 019import org.openstreetmap.josm.tools.Utils; 020 021/** 022 * Test that validates {@code lane:} tags. 023 * @since 6592 024 */ 025public class Lanes extends Test.TagTest { 026 027 private static final String[] BLACKLIST = { 028 "source:lanes", 029 "note:lanes", 030 "proposed:lanes", 031 "piste:lanes", 032 }; 033 034 /** 035 * Constructs a new {@code Lanes} test. 036 */ 037 public Lanes() { 038 super(tr("Lane tags"), tr("Test that validates ''lane:'' tags.")); 039 } 040 041 static int getLanesCount(String value) { 042 return value.isEmpty() ? 0 : value.replaceAll("[^|]", "").length() + 1; 043 } 044 045 protected void checkNumberOfLanesByKey(final OsmPrimitive p, String lanesKey, String message) { 046 final Collection<String> keysForPattern = new ArrayList<>(Utils.filter(p.keySet(), 047 Predicates.stringContainsPattern(Pattern.compile(":" + lanesKey + "$")))); 048 keysForPattern.removeAll(Arrays.asList(BLACKLIST)); 049 if (keysForPattern.size() < 1) { 050 // nothing to check 051 return; 052 } 053 final Set<Integer> lanesCount = new HashSet<>(Utils.transform(keysForPattern, new Utils.Function<String, Integer>() { 054 @Override 055 public Integer apply(String key) { 056 return getLanesCount(p.get(key)); 057 } 058 })); 059 if (lanesCount.size() > 1) { 060 // if not all numbers are the same 061 errors.add(new TestError(this, Severity.WARNING, message, 3100, p)); 062 } else if (lanesCount.size() == 1 && p.hasKey(lanesKey)) { 063 // ensure that lanes <= *:lanes 064 try { 065 if (Integer.parseInt(p.get(lanesKey)) > lanesCount.iterator().next()) { 066 errors.add(new TestError(this, Severity.WARNING, tr("Number of {0} greater than {1}", lanesKey, "*:" + lanesKey), 3100, p)); 067 } 068 } catch (NumberFormatException ignore) { 069 Main.debug(ignore.getMessage()); 070 } 071 } 072 } 073 074 protected void checkNumberOfLanes(final OsmPrimitive p) { 075 final String lanes = p.get("lanes"); 076 if (lanes == null) return; 077 final String forward = Utils.firstNonNull(p.get("lanes:forward"), "0"); 078 final String backward = Utils.firstNonNull(p.get("lanes:backward"), "0"); 079 try { 080 if (Integer.parseInt(lanes) < Integer.parseInt(forward) + Integer.parseInt(backward)) { 081 errors.add(new TestError(this, Severity.WARNING, 082 tr("Number of {0} greater than {1}", tr("{0}+{1}", "lanes:forward", "lanes:backward"), "lanes"), 3101, p)); 083 } 084 } catch (NumberFormatException ignore) { 085 Main.debug(ignore.getMessage()); 086 } 087 } 088 089 @Override 090 public void check(OsmPrimitive p) { 091 checkNumberOfLanesByKey(p, "lanes", tr("Number of lane dependent values inconsistent")); 092 checkNumberOfLanesByKey(p, "lanes:forward", tr("Number of lane dependent values inconsistent in forward direction")); 093 checkNumberOfLanesByKey(p, "lanes:backward", tr("Number of lane dependent values inconsistent in backward direction")); 094 checkNumberOfLanes(p); 095 } 096}