Skip to content

Commit 1ef63ad

Browse files
committed
Switch to iterative version of WKT format parser
Signed-off-by: Heemin Kim <[email protected]>
1 parent fbe048f commit 1ef63ad

File tree

2 files changed

+86
-5
lines changed

2 files changed

+86
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6565
- Fix double invocation of postCollection when MultiBucketCollector is present ([#14015](https://github.com/opensearch-project/OpenSearch/pull/14015))
6666

6767
### Security
68+
- Switch to iterative version of WKT format parser ([#14086](https://github.com/opensearch-project/OpenSearch/pull/14086))
6869

6970
[Unreleased 2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.13...2.x

libs/geo/src/main/java/org/opensearch/geometry/utils/WellKnownText.java

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import java.util.Collections;
5454
import java.util.List;
5555
import java.util.Locale;
56+
import java.util.Stack;
5657

5758
/**
5859
* Utility class for converting to and from WKT
@@ -301,16 +302,95 @@ private Geometry parseGeometry(StreamTokenizer stream) throws IOException, Parse
301302
throw new IllegalArgumentException("Unknown geometry type: " + type);
302303
}
303304

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+
*/
304323
private GeometryCollection<Geometry> parseGeometryCollection(StreamTokenizer stream) throws IOException, ParseException {
305324
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
306325
return GeometryCollection.EMPTY;
307326
}
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+
}
312391
}
313-
return new GeometryCollection<>(shapes);
392+
393+
return new GeometryCollection<>(topLevelShapes);
314394
}
315395

316396
private Point parsePoint(StreamTokenizer stream) throws IOException, ParseException {

0 commit comments

Comments
 (0)