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