diff --git a/libs/grok/src/main/java/org/opensearch/grok/Grok.java b/libs/grok/src/main/java/org/opensearch/grok/Grok.java
index 7aa3347ba4f4b..394e9d67ff319 100644
--- a/libs/grok/src/main/java/org/opensearch/grok/Grok.java
+++ b/libs/grok/src/main/java/org/opensearch/grok/Grok.java
@@ -39,11 +39,14 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.Stack;
import java.util.function.Consumer;
@@ -138,51 +141,127 @@ private Grok(
this.captureConfig = unmodifiableList(captureConfig);
}
- /**
- * Entry point to recursively validate the pattern bank for circular dependencies and malformed URLs
- * via depth-first traversal. This implementation does not include memoization.
- */
- private void validatePatternBank() {
+// /**
+// * Entry point to recursively validate the pattern bank for circular dependencies and malformed URLs
+// * via depth-first traversal. This implementation does not include memoization.
+// */
+// private void validatePatternBank() {
+// for (String patternName : patternBank.keySet()) {
+// validatePatternBank(patternName, new Stack<>());
+// }
+// }
+//
+// /**
+// * Checks whether patterns reference each other in a circular manner and, if so, fail with an exception.
+// * Also checks for malformed pattern definitions and fails with an exception.
+// *
+// * In a pattern, anything between %{
and }
or :
is considered
+// * a reference to another named pattern. This method will navigate to all these named patterns and
+// * check for a circular reference.
+// */
+// private void validatePatternBank(String patternName, Stack path) {
+// String pattern = patternBank.get(patternName);
+// boolean isSelfReference = pattern.contains("%{" + patternName + "}") || pattern.contains("%{" + patternName + ":");
+// if (isSelfReference) {
+// throwExceptionForCircularReference(patternName, pattern);
+// } else if (path.contains(patternName)) {
+// // current pattern name is already in the path, fetch its predecessor
+// String prevPatternName = path.pop();
+// String prevPattern = patternBank.get(prevPatternName);
+// throwExceptionForCircularReference(prevPatternName, prevPattern, patternName, path);
+// }
+// path.push(patternName);
+// for (int i = pattern.indexOf("%{"); i != -1; i = pattern.indexOf("%{", i + 1)) {
+// int begin = i + 2;
+// int syntaxEndIndex = pattern.indexOf('}', begin);
+// if (syntaxEndIndex == -1) {
+// throw new IllegalArgumentException("Malformed pattern [" + patternName + "][" + pattern + "]");
+// }
+// int semanticNameIndex = pattern.indexOf(':', begin);
+// int end = syntaxEndIndex;
+// if (semanticNameIndex != -1) {
+// end = Math.min(syntaxEndIndex, semanticNameIndex);
+// }
+// String dependsOnPattern = pattern.substring(begin, end);
+// validatePatternBank(dependsOnPattern, path);
+// }
+// path.pop();
+// }
+
+// private Map patternBank = new HashMap<>();
+
+ public void validatePatternBank() {
for (String patternName : patternBank.keySet()) {
- validatePatternBank(patternName, new Stack<>());
+ validatePatternBankIterative(patternName);
}
}
- /**
- * Checks whether patterns reference each other in a circular manner and, if so, fail with an exception.
- * Also checks for malformed pattern definitions and fails with an exception.
- *
- * In a pattern, anything between %{
and }
or :
is considered
- * a reference to another named pattern. This method will navigate to all these named patterns and
- * check for a circular reference.
- */
- private void validatePatternBank(String patternName, Stack path) {
- String pattern = patternBank.get(patternName);
- boolean isSelfReference = pattern.contains("%{" + patternName + "}") || pattern.contains("%{" + patternName + ":");
- if (isSelfReference) {
- throwExceptionForCircularReference(patternName, pattern);
- } else if (path.contains(patternName)) {
- // current pattern name is already in the path, fetch its predecessor
- String prevPatternName = path.pop();
- String prevPattern = patternBank.get(prevPatternName);
- throwExceptionForCircularReference(prevPatternName, prevPattern, patternName, path);
- }
- path.push(patternName);
- for (int i = pattern.indexOf("%{"); i != -1; i = pattern.indexOf("%{", i + 1)) {
- int begin = i + 2;
- int syntaxEndIndex = pattern.indexOf('}', begin);
- if (syntaxEndIndex == -1) {
- throw new IllegalArgumentException("Malformed pattern [" + patternName + "][" + pattern + "]");
+ private void validatePatternBankIterative(String patternName) {
+ Stack path = new Stack<>();
+ Stack stack = new Stack<>();
+ Set visited = new HashSet<>();
+
+ stack.push(new PatternState(patternName, 0));
+
+ while (!stack.isEmpty()) {
+ PatternState currentState = stack.pop();
+ String currentPatternName = currentState.patternName;
+ int startIndex = currentState.startIndex;
+
+ if (path.contains(currentPatternName)) {
+ // Current pattern name is already in the path, indicating a circular reference.
+ String prevPatternName = path.pop();
+ String prevPattern = patternBank.get(prevPatternName);
+ throwExceptionForCircularReference(prevPatternName, prevPattern, currentPatternName, path);
}
- int semanticNameIndex = pattern.indexOf(':', begin);
- int end = syntaxEndIndex;
- if (semanticNameIndex != -1) {
- end = Math.min(syntaxEndIndex, semanticNameIndex);
+
+ path.push(currentPatternName);
+
+ String pattern = patternBank.get(currentPatternName);
+ if (pattern.contains("%{" + currentPatternName + "}") || pattern.contains("%{" + currentPatternName + ":")) {
+ throwExceptionForCircularReference(currentPatternName, pattern);
}
- String dependsOnPattern = pattern.substring(begin, end);
- validatePatternBank(dependsOnPattern, path);
+
+ boolean hasDependencies = false;
+ for (int i = startIndex; i < pattern.length(); i = pattern.indexOf("%{", i + 1)) {
+ if (i == -1) {
+ break;
+ }
+ int begin = i + 2;
+ int syntaxEndIndex = pattern.indexOf('}', begin);
+ if (syntaxEndIndex == -1) {
+ throw new IllegalArgumentException("Malformed pattern [" + currentPatternName + "][" + pattern + "]");
+ }
+ int semanticNameIndex = pattern.indexOf(':', begin);
+ int end = syntaxEndIndex;
+ if (semanticNameIndex != -1) {
+ end = Math.min(syntaxEndIndex, semanticNameIndex);
+ }
+ String dependsOnPattern = pattern.substring(begin, end);
+
+ if (!visited.contains(dependsOnPattern)) {
+ stack.push(new PatternState(currentPatternName, i + 1));
+ stack.push(new PatternState(dependsOnPattern, 0));
+ hasDependencies = true;
+ break;
+ }
+ }
+
+ if (!hasDependencies) {
+ visited.add(currentPatternName);
+ path.pop();
+ }
+ }
+ }
+
+ private class PatternState {
+ String patternName;
+ int startIndex;
+
+ PatternState(String patternName, int startIndex) {
+ this.patternName = patternName;
+ this.startIndex = startIndex;
}
- path.pop();
}
private static void throwExceptionForCircularReference(String patternName, String pattern) {