Ticket #21163: Lanes.java

File Lanes.java, 5.1 KB (added by DodalerAfenger, 3 years ago)

org.openstreetmap.josm.data.validation.tests

Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.Arrays;
7import java.util.Set;
8import java.util.stream.Collectors;
9
10import org.openstreetmap.josm.data.osm.OsmPrimitive;
11import org.openstreetmap.josm.data.osm.Way;
12import org.openstreetmap.josm.data.validation.Severity;
13import org.openstreetmap.josm.data.validation.Test;
14import org.openstreetmap.josm.data.validation.TestError;
15import org.openstreetmap.josm.tools.Logging;
16import org.openstreetmap.josm.tools.Utils;
17
18/**
19 * Test that validates {@code lane:} tags.
20 * @since 6592
21 */
22public class Lanes extends Test.TagTest {
23
24 private static final String[] BLACKLIST = {
25 "source:lanes",
26 "note:lanes",
27 "proposed:lanes",
28 "source:proposed:lanes",
29 "piste:lanes",
30 };
31
32 /**
33 * Constructs a new {@code Lanes} test.
34 */
35 public Lanes() {
36 super(tr("Lane tags"), tr("Test that validates ''lane:'' tags."));
37 }
38
39 static int getLanesCount(String value) {
40 return value.isEmpty() ? 0 : value.replaceAll("[^|]", "").length() + 1;
41 }
42
43 private int getLanesCountConditional(String value, Boolean isConditional, OsmPrimitive p) {
44 if (isConditional) {
45 // Also respect values like "yes|no|no @ (Mo-Fr 06:30-9:00); yes|no|no @ (Mo-Fr 17:30-19:00)"
46 final Set<Integer> allLanesCounts =
47 Arrays.stream(value.split(";"))
48 .map(removeConditional -> {return removeConditional.replaceAll("\\s*@\\s*\\(.*?\\)\\s*", "");})
49 .map(singleValue -> getLanesCount(singleValue))
50 .collect(Collectors.toSet());
51 if (allLanesCounts.size() > 1) {
52 // if not all numbers are the same
53 errors.add(TestError.builder(this, Severity.WARNING, 3100)
54 .message(tr("Different lanes counts {0} within conditional value '{1}'", allLanesCounts.toString(), value))
55 .primitives(p)
56 .build());
57 }
58 return allLanesCounts.iterator().next();
59 }
60 else {
61 return getLanesCount(value);
62 }
63 }
64
65 protected void checkNumberOfLanesByKey(final OsmPrimitive p, String lanesKey, String message) {
66 final Set<Integer> lanesCount =
67 p.keys()
68 .filter(x -> x.endsWith(":" + lanesKey) || x.endsWith(":" + lanesKey + ":conditional"))
69 .filter(x -> !Arrays.asList(BLACKLIST).contains(x))
70 .map(key -> getLanesCountConditional(p.get(key), key.endsWith(":conditional"), p))
71 .collect(Collectors.toSet());
72
73 if (lanesCount.size() > 1) {
74 // if not all numbers are the same
75 errors.add(TestError.builder(this, Severity.WARNING, 3100)
76 .message(message)
77 .primitives(p)
78 .build());
79 } else if (lanesCount.size() == 1 && p.hasKey(lanesKey)) {
80 // ensure that lanes <= *:lanes
81 try {
82 if (Integer.parseInt(p.get(lanesKey)) > lanesCount.iterator().next()) {
83 errors.add(TestError.builder(this, Severity.WARNING, 3100)
84 .message(tr("Number of {0} greater than {1}", lanesKey, "*:" + lanesKey))
85 .primitives(p)
86 .build());
87 }
88 } catch (NumberFormatException ignore) {
89 Logging.debug(ignore.getMessage());
90 }
91 }
92 }
93
94 protected void checkNumberOfLanes(final OsmPrimitive p) {
95 final String lanes = p.get("lanes");
96 if (lanes == null) return;
97 final String forward = Utils.firstNonNull(p.get("lanes:forward"), "0");
98 final String backward = Utils.firstNonNull(p.get("lanes:backward"), "0");
99 try {
100 if (Integer.parseInt(lanes) < Integer.parseInt(forward) + Integer.parseInt(backward)) {
101 errors.add(TestError.builder(this, Severity.WARNING, 3101)
102 .message(tr("Number of {0} greater than {1}", tr("{0}+{1}", "lanes:forward", "lanes:backward"), "lanes"))
103 .primitives(p)
104 .build());
105 }
106 } catch (NumberFormatException ignore) {
107 Logging.debug(ignore.getMessage());
108 }
109 }
110
111 @Override
112 public void check(OsmPrimitive p) {
113 checkNumberOfLanesByKey(p, "lanes", tr("Number of lane dependent values inconsistent"));
114 checkNumberOfLanesByKey(p, "lanes:forward", tr("Number of lane dependent values inconsistent in forward direction"));
115 checkNumberOfLanesByKey(p, "lanes:backward", tr("Number of lane dependent values inconsistent in backward direction"));
116 checkNumberOfLanes(p);
117 }
118
119 @Override
120 public boolean isPrimitiveUsable(OsmPrimitive p) {
121 return p.isTagged() && p instanceof Way && p.hasTag("highway") && super.isPrimitiveUsable(p);
122 }
123}