source: josm/trunk/src/org/openstreetmap/josm/data/osm/DatasetConsistencyTest.java@ 16119

Last change on this file since 16119 was 16119, checked in by Don-vip, 4 years ago

fix #18928 - fix various crashes with empty ways

  • Property svn:eol-style set to native
File size: 8.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import java.io.PrintWriter;
5import java.io.StringWriter;
6import java.io.Writer;
7
8import org.openstreetmap.josm.tools.JosmRuntimeException;
9import org.openstreetmap.josm.tools.Logging;
10import org.openstreetmap.josm.tools.Stopwatch;
11
12/**
13 * This class can be used to run consistency tests on dataset. Any errors found will be written to provided PrintWriter.
14 * <br>
15 * Texts here should not be translated because they're not intended for users but for josm developers.
16 * @since 2500
17 */
18public class DatasetConsistencyTest {
19
20 private static final int MAX_ERRORS = 100;
21 private final DataSet dataSet;
22 private final PrintWriter writer;
23 private int errorCount;
24
25 /**
26 * Constructs a new {@code DatasetConsistencyTest}.
27 * @param dataSet The dataset to test
28 * @param writer The writer used to write results
29 */
30 public DatasetConsistencyTest(DataSet dataSet, Writer writer) {
31 this.dataSet = dataSet;
32 this.writer = new PrintWriter(writer);
33 }
34
35 private void printError(String type, String message, Object... args) {
36 errorCount++;
37 if (errorCount <= MAX_ERRORS) {
38 writer.println('[' + type + "] " + String.format(message, args));
39 }
40 }
41
42 /**
43 * Checks that parent primitive is referred from its child members
44 */
45 public void checkReferrers() {
46 final Stopwatch stopwatch = Stopwatch.createStarted();
47 // It's also error when referred primitive's dataset is null but it's already covered by referredPrimitiveNotInDataset check
48 for (Way way : dataSet.getWays()) {
49 if (!way.isDeleted()) {
50 for (Node n : way.getNodes()) {
51 if (n.getDataSet() != null && !n.getReferrers().contains(way)) {
52 printError("WAY NOT IN REFERRERS", "%s is part of %s but is not in referrers", n, way);
53 }
54 }
55 }
56 }
57
58 for (Relation relation : dataSet.getRelations()) {
59 if (!relation.isDeleted()) {
60 for (RelationMember m : relation.getMembers()) {
61 if (m.getMember().getDataSet() != null && !m.getMember().getReferrers().contains(relation)) {
62 printError("RELATION NOT IN REFERRERS", "%s is part of %s but is not in referrers", m.getMember(), relation);
63 }
64 }
65 }
66 }
67 printElapsedTime(stopwatch);
68 }
69
70 /**
71 * Checks for womplete ways with incomplete nodes.
72 */
73 public void checkCompleteWaysWithIncompleteNodes() {
74 final Stopwatch stopwatch = Stopwatch.createStarted();
75 for (Way way : dataSet.getWays()) {
76 if (way.isUsable()) {
77 for (Node node : way.getNodes()) {
78 if (node.isIncomplete()) {
79 printError("USABLE HAS INCOMPLETE", "%s is usable but contains incomplete node '%s'", way, node);
80 }
81 }
82 }
83 }
84 printElapsedTime(stopwatch);
85 }
86
87 /**
88 * Checks for complete nodes without coordinates.
89 */
90 public void checkCompleteNodesWithoutCoordinates() {
91 final Stopwatch stopwatch = Stopwatch.createStarted();
92 for (Node node : dataSet.getNodes()) {
93 if (!node.isIncomplete() && node.isVisible() && !node.isLatLonKnown()) {
94 printError("COMPLETE WITHOUT COORDINATES", "%s is not incomplete but has null coordinates", node);
95 }
96 }
97 printElapsedTime(stopwatch);
98 }
99
100 /**
101 * Checks that nodes can be retrieved through their coordinates.
102 */
103 public void searchNodes() {
104 final Stopwatch stopwatch = Stopwatch.createStarted();
105 dataSet.getReadLock().lock();
106 try {
107 for (Node n : dataSet.getNodes()) {
108 // Call isDrawable() as an efficient replacement to previous checks (!deleted, !incomplete, getCoor() != null)
109 if (n.isDrawable() && !dataSet.containsNode(n)) {
110 printError("SEARCH NODES", "%s not found using Dataset.containsNode()", n);
111 }
112 }
113 } finally {
114 dataSet.getReadLock().unlock();
115 }
116 printElapsedTime(stopwatch);
117 }
118
119 /**
120 * Checks that ways can be retrieved through their bounding box.
121 */
122 public void searchWays() {
123 final Stopwatch stopwatch = Stopwatch.createStarted();
124 dataSet.getReadLock().lock();
125 try {
126 for (Way w : dataSet.getWays()) {
127 if (!w.isIncomplete() && !w.isDeleted() && w.getNodesCount() >= 2 && !dataSet.containsWay(w)) {
128 printError("SEARCH WAYS", "%s not found using Dataset.containsWay()", w);
129 }
130 }
131 } finally {
132 dataSet.getReadLock().unlock();
133 }
134 printElapsedTime(stopwatch);
135 }
136
137 private void checkReferredPrimitive(OsmPrimitive primitive, OsmPrimitive parent) {
138 if (primitive.getDataSet() == null) {
139 printError("NO DATASET", "%s is referenced by %s but not found in dataset", primitive, parent);
140 } else if (dataSet.getPrimitiveById(primitive) == null) {
141 printError("REFERENCED BUT NOT IN DATA", "%s is referenced by %s but not found in dataset", primitive, parent);
142 } else if (dataSet.getPrimitiveById(primitive) != primitive) {
143 printError("DIFFERENT INSTANCE", "%s is different instance that referred by %s", primitive, parent);
144 }
145
146 if (primitive.isDeleted()) {
147 printError("DELETED REFERENCED", "%s refers to deleted primitive %s", parent, primitive);
148 }
149 }
150
151 /**
152 * Checks that referred primitives are present in dataset.
153 */
154 public void referredPrimitiveNotInDataset() {
155 final Stopwatch stopwatch = Stopwatch.createStarted();
156 for (Way way : dataSet.getWays()) {
157 for (Node node : way.getNodes()) {
158 checkReferredPrimitive(node, way);
159 }
160 }
161
162 for (Relation relation : dataSet.getRelations()) {
163 for (RelationMember member : relation.getMembers()) {
164 checkReferredPrimitive(member.getMember(), relation);
165 }
166 }
167 printElapsedTime(stopwatch);
168 }
169
170 /**
171 * Checks for zero and one-node ways.
172 */
173 public void checkZeroNodesWays() {
174 final Stopwatch stopwatch = Stopwatch.createStarted();
175 for (Way way : dataSet.getWays()) {
176 if (way.isUsable() && way.isEmpty()) {
177 printError("WARN - ZERO NODES", "Way %s has zero nodes", way);
178 } else if (way.isUsable() && way.getNodesCount() == 1) {
179 printError("WARN - NO NODES", "Way %s has only one node", way);
180 }
181 }
182 printElapsedTime(stopwatch);
183 }
184
185 private void printElapsedTime(Stopwatch stopwatch) {
186 if (Logging.isDebugEnabled()) {
187 StackTraceElement item = Thread.currentThread().getStackTrace()[2];
188 String operation = getClass().getSimpleName() + '.' + item.getMethodName();
189 Logging.debug(stopwatch.toString(operation));
190 }
191 }
192
193 /**
194 * Runs test.
195 */
196 public void runTest() {
197 try {
198 final Stopwatch stopwatch = Stopwatch.createStarted();
199 referredPrimitiveNotInDataset();
200 checkReferrers();
201 checkCompleteWaysWithIncompleteNodes();
202 checkCompleteNodesWithoutCoordinates();
203 searchNodes();
204 searchWays();
205 checkZeroNodesWays();
206 printElapsedTime(stopwatch);
207 if (errorCount > MAX_ERRORS) {
208 writer.println((errorCount - MAX_ERRORS) + " more...");
209 }
210
211 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
212 writer.println("Exception during dataset integrity test:");
213 e.printStackTrace(writer);
214 Logging.warn(e);
215 }
216 }
217
218 /**
219 * Runs test on the given dataset.
220 * @param dataSet the dataset to test
221 * @return the errors as string
222 */
223 public static String runTests(DataSet dataSet) {
224 StringWriter writer = new StringWriter();
225 new DatasetConsistencyTest(dataSet, writer).runTest();
226 return writer.toString();
227 }
228}
Note: See TracBrowser for help on using the repository browser.