|
53 | 53 | import java.util.Collections;
|
54 | 54 | import java.util.List;
|
55 | 55 | import java.util.Locale;
|
| 56 | +import java.util.Stack; |
56 | 57 |
|
57 | 58 | /**
|
58 | 59 | * Utility class for converting to and from WKT
|
@@ -301,16 +302,95 @@ private Geometry parseGeometry(StreamTokenizer stream) throws IOException, Parse
|
301 | 302 | throw new IllegalArgumentException("Unknown geometry type: " + type);
|
302 | 303 | }
|
303 | 304 |
|
| 305 | + /** |
| 306 | + * Iterative version of |
| 307 | + * <!-- |
| 308 | + * ```java |
| 309 | + * private GeometryCollection<Geometry> parseGeometryCollectionA(StreamTokenizer stream) throws IOException, ParseException { |
| 310 | + * if (nextEmptyOrOpen(stream).equals(EMPTY)) { |
| 311 | + * return GeometryCollection.EMPTY; |
| 312 | + * } |
| 313 | + * List<Geometry> shapes = new ArrayList<>(); |
| 314 | + * shapes.add(parseGeometry(stream)); |
| 315 | + * while (nextCloserOrComma(stream).equals(COMMA)) { |
| 316 | + * shapes.add(parseGeometry(stream)); |
| 317 | + * } |
| 318 | + * return new GeometryCollection<>(shapes); |
| 319 | + * } |
| 320 | + * --> |
| 321 | + * to avoid StackOverflowError when there is a deeply nested structure of GeometryCollection. |
| 322 | + */ |
304 | 323 | private GeometryCollection<Geometry> parseGeometryCollection(StreamTokenizer stream) throws IOException, ParseException {
|
305 | 324 | if (nextEmptyOrOpen(stream).equals(EMPTY)) {
|
306 | 325 | return GeometryCollection.EMPTY;
|
307 | 326 | }
|
308 |
| - List<Geometry> shapes = new ArrayList<>(); |
309 |
| - shapes.add(parseGeometry(stream)); |
310 |
| - while (nextCloserOrComma(stream).equals(COMMA)) { |
311 |
| - shapes.add(parseGeometry(stream)); |
| 327 | + |
| 328 | + List<Geometry> topLevelShapes = new ArrayList<>(); |
| 329 | + Stack<List<Geometry>> stack = new Stack<>(); |
| 330 | + stack.push(topLevelShapes); |
| 331 | + boolean isFirstIteration = true; |
| 332 | + List<Geometry> currentLevelShapes = null; |
| 333 | + while (!stack.isEmpty()) { |
| 334 | + List<Geometry> previousShapes = stack.pop(); |
| 335 | + if (currentLevelShapes != null) { |
| 336 | + previousShapes.add(new GeometryCollection<>(currentLevelShapes)); |
| 337 | + } |
| 338 | + currentLevelShapes = previousShapes; |
| 339 | + |
| 340 | + if (isFirstIteration == true) { |
| 341 | + isFirstIteration = false; |
| 342 | + } else { |
| 343 | + if (!nextCloserOrComma(stream).equals(COMMA)) { |
| 344 | + continue; |
| 345 | + } |
| 346 | + } |
| 347 | + while (true) { |
| 348 | + final String type = nextWord(stream).toLowerCase(Locale.ROOT); |
| 349 | + switch (type) { |
| 350 | + case "point": |
| 351 | + currentLevelShapes.add(parsePoint(stream)); |
| 352 | + break; |
| 353 | + case "multipoint": |
| 354 | + currentLevelShapes.add(parseMultiPoint(stream)); |
| 355 | + break; |
| 356 | + case "linestring": |
| 357 | + currentLevelShapes.add(parseLine(stream)); |
| 358 | + break; |
| 359 | + case "multilinestring": |
| 360 | + currentLevelShapes.add(parseMultiLine(stream)); |
| 361 | + break; |
| 362 | + case "polygon": |
| 363 | + currentLevelShapes.add(parsePolygon(stream)); |
| 364 | + break; |
| 365 | + case "multipolygon": |
| 366 | + currentLevelShapes.add(parseMultiPolygon(stream)); |
| 367 | + break; |
| 368 | + case "bbox": |
| 369 | + currentLevelShapes.add(parseBBox(stream)); |
| 370 | + break; |
| 371 | + case "geometrycollection": |
| 372 | + if (nextEmptyOrOpen(stream).equals(EMPTY)) { |
| 373 | + currentLevelShapes.add(GeometryCollection.EMPTY); |
| 374 | + break; |
| 375 | + } else { |
| 376 | + stack.push(currentLevelShapes); |
| 377 | + currentLevelShapes = new ArrayList<>(); |
| 378 | + continue; |
| 379 | + } |
| 380 | + case "circle": // Not part of the standard, but we need it for internal serialization |
| 381 | + currentLevelShapes.add(parseCircle(stream)); |
| 382 | + break; |
| 383 | + default: |
| 384 | + throw new IllegalArgumentException("Unknown geometry type: " + type); |
| 385 | + } |
| 386 | + |
| 387 | + if (!nextCloserOrComma(stream).equals(COMMA)) { |
| 388 | + break; |
| 389 | + } |
| 390 | + } |
312 | 391 | }
|
313 |
| - return new GeometryCollection<>(shapes); |
| 392 | + |
| 393 | + return new GeometryCollection<>(topLevelShapes); |
314 | 394 | }
|
315 | 395 |
|
316 | 396 | private Point parsePoint(StreamTokenizer stream) throws IOException, ParseException {
|
|
0 commit comments