Index: trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 6591)
+++ trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 6592)
@@ -34,4 +34,5 @@
 import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes;
 import org.openstreetmap.josm.data.validation.tests.Highways;
+import org.openstreetmap.josm.data.validation.tests.Lanes;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
 import org.openstreetmap.josm.data.validation.tests.MultipolygonTest;
@@ -120,4 +121,5 @@
         OpeningHourTest.class, // 2901 .. 2999
         MapCSSTagChecker.class, // 3000 .. 3099
+        Lanes.class, // 3100 .. 3199
     };
     
Index: trunk/src/org/openstreetmap/josm/data/validation/tests/Lanes.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/Lanes.java	(revision 6592)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/Lanes.java	(revision 6592)
@@ -0,0 +1,50 @@
+package org.openstreetmap.josm.data.validation.tests;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.validation.Severity;
+import org.openstreetmap.josm.data.validation.Test;
+import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.tools.Predicates;
+import org.openstreetmap.josm.tools.Utils;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.regex.Pattern;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+public class Lanes extends Test.TagTest {
+
+    public Lanes() {
+        super(tr("Lane tags"));
+    }
+
+    static int getLanesCount(String value) {
+        return value.isEmpty() ? 0 : value.split("\\|").length;
+    }
+
+    protected void checkEqualNumberOfLanes(final OsmPrimitive p, Pattern keyPattern, String message) {
+        final Collection<String> keysForPattern = Utils.filter(p.keySet(), Predicates.stringContainsPattern(keyPattern));
+        if (keysForPattern.size() < 2) {
+            // nothing to check
+            return;
+        }
+        final Collection<Integer> lanesCount = Utils.transform(keysForPattern, new Utils.Function<String, Integer>() {
+            @Override
+            public Integer apply(String key) {
+                return getLanesCount(p.get(key));
+            }
+        });
+        // if not all numbers are the same
+        if (new HashSet<Integer>(lanesCount).size() > 1) {
+            errors.add(new TestError(this, Severity.WARNING, message, 3100, p));
+        }
+    }
+
+    @Override
+    public void check(OsmPrimitive p) {
+        checkEqualNumberOfLanes(p, Pattern.compile(":lanes$"), tr("Number of lane dependent values inconsistent"));
+        checkEqualNumberOfLanes(p, Pattern.compile(":lanes:forward"), tr("Number of lane dependent values inconsistent in forward direction"));
+        checkEqualNumberOfLanes(p, Pattern.compile(":lanes:backward"), tr("Number of lane dependent values inconsistent in backward direction"));
+    }
+}
Index: trunk/src/org/openstreetmap/josm/tools/Predicates.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Predicates.java	(revision 6591)
+++ trunk/src/org/openstreetmap/josm/tools/Predicates.java	(revision 6592)
@@ -11,4 +11,28 @@
 
     private Predicates() {
+    }
+
+    /**
+     * Returns the negation of {@code predicate}.
+     */
+    public static <T> Predicate<T> not(final Predicate<T> predicate) {
+        return new Predicate<T>() {
+            @Override
+            public boolean evaluate(T obj) {
+                return !predicate.evaluate(obj);
+            }
+        };
+    }
+
+    /**
+     * Returns a {@link Predicate} executing {@link Utils#equal}.
+     */
+    public static <T> Predicate<T> equalTo(final T ref) {
+        return new Predicate<T>() {
+            @Override
+            public boolean evaluate(T obj) {
+                return Utils.equal(obj, ref);
+            }
+        };
     }
 
