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