|
12 | 12 | import java.util.Arrays; |
13 | 13 | import java.util.HashSet; |
14 | 14 | import java.util.Set; |
15 | | -import java.util.regex.Pattern; |
16 | 15 |
|
17 | 16 | public class ThematicBreakParser extends AbstractBlockParser { |
18 | 17 |
|
19 | | - static Pattern PATTERN = Pattern.compile("^(?:(?:\\*[ \t]*){3,}|(?:_[ \t]*){3,}|(?:-[ \t]*){3,})[ \t]*$"); |
| 18 | + /** |
| 19 | + * Checks if the given input matches the thematic break pattern without using regex |
| 20 | + * to avoid StackOverflowError with very long sequences. |
| 21 | + * |
| 22 | + * Pattern: ^(?:(?:\*[ \t]*){3,}|(?:_[ \t]*){3,}|(?:-[ \t]*){3,})[ \t]*$ |
| 23 | + * - At least 3 occurrences of *, _, or - characters |
| 24 | + * - Characters can be separated by spaces or tabs |
| 25 | + * - Only one type of character allowed per line |
| 26 | + * - Line can end with spaces or tabs |
| 27 | + */ |
| 28 | + private static boolean matchesThematicBreak(BasedSequence input) { |
| 29 | + int length = input.length(); |
| 30 | + if (length == 0) return false; |
| 31 | + |
| 32 | + // Skip leading whitespace |
| 33 | + int pos = 0; |
| 34 | + while (pos < length && isWhitespace(input.charAt(pos))) { |
| 35 | + pos++; |
| 36 | + } |
| 37 | + |
| 38 | + if (pos >= length) return false; |
| 39 | + |
| 40 | + char patternChar = input.charAt(pos); |
| 41 | + if (patternChar != '*' && patternChar != '_' && patternChar != '-') { |
| 42 | + return false; |
| 43 | + } |
| 44 | + |
| 45 | + int charCount = 0; |
| 46 | + while (pos < length) { |
| 47 | + char c = input.charAt(pos); |
| 48 | + if (c == patternChar) { |
| 49 | + charCount++; |
| 50 | + } else if (isWhitespace(c)) { |
| 51 | + // Whitespace is allowed between pattern characters |
| 52 | + } else { |
| 53 | + // Invalid character found |
| 54 | + return false; |
| 55 | + } |
| 56 | + pos++; |
| 57 | + } |
| 58 | + |
| 59 | + return charCount >= 3; |
| 60 | + } |
| 61 | + |
| 62 | + private static boolean isWhitespace(char c) { |
| 63 | + return c == ' ' || c == '\t'; |
| 64 | + } |
20 | 65 |
|
21 | 66 | final private ThematicBreak block = new ThematicBreak(); |
22 | 67 |
|
@@ -96,7 +141,7 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar |
96 | 141 | } |
97 | 142 | BasedSequence line = state.getLine(); |
98 | 143 | final BasedSequence input = line.subSequence(state.getNextNonSpaceIndex(), line.length()); |
99 | | - if (PATTERN.matcher(input).matches()) { |
| 144 | + if (matchesThematicBreak(input)) { |
100 | 145 | return BlockStart.of(new ThematicBreakParser(line.subSequence(state.getIndex()))).atIndex(line.length()); |
101 | 146 | } else { |
102 | 147 | return BlockStart.none(); |
|
0 commit comments