1
1
package core ;
2
2
3
3
import java .util .ArrayList ;
4
+ import java .util .HashMap ;
4
5
import java .util .List ;
6
+ import java .util .Map ;
5
7
6
8
public class StringOperations {
7
9
8
10
/**
9
11
* Determines whether a given position in a line of text is currently inside a string literal, accounting for
10
12
* escaped quotes.
11
13
*
12
- * <p>This method scans the input line from the beginning up to (but not including) the specified position.
13
- * It toggles the {@code inString} flag each time it encounters a non-escaped double quote character.
14
- * This helps determine whether the character at the given position falls within a string.</p>
14
+ * <p>
15
+ * When called, this method checks if the "in-string" state for the provided line has already been computed and cached.
16
+ * If so, it returns the cached result for the given position in O(1) time. If not, it computes the in-string state for
17
+ * every character in the line, caches the result, and then returns the value for the requested position.
18
+ * </p>
15
19
*
16
20
* @param line
17
21
* The input string to analyze.
18
22
* @param position
19
23
* The index position in the string to check.
20
24
*
21
25
* @return {@code true} if the position is within a string literal, {@code false} otherwise.
26
+ *
27
+ * @see #clearInStringCache()
22
28
*/
23
29
public static boolean isInString (String line , int position ) {
30
+ if (inStringCache == null ) inStringCache = new HashMap <String , Boolean []>();
31
+
32
+ // If the String is already in the cache, return the inString value for the given position.
33
+ Boolean [] inStringArr = inStringCache .get (line );
34
+ if (inStringArr != null ) {
35
+ return inStringArr [position ];
36
+ }
37
+
38
+ // Otherwise, precompute the cache entry for this String and call this method again.
39
+ inStringArr = new Boolean [line .length ()];
24
40
boolean inString = false ;
25
- for (int i = 0 ; i <= position ; i ++) { // Changed <= to < to exclude current position
26
- if (i == position && line .charAt (i ) == '"' && (i == 0 || line .charAt (i - 1 ) != '\\' )) {
27
- return false ;
28
- }
41
+ for (int i = 0 ; i < line .length (); i ++) {
29
42
if (line .charAt (i ) == '"' && (i == 0 || line .charAt (i - 1 ) != '\\' )) {
30
43
inString = !inString ;
31
44
}
45
+ inStringArr [i ] = inString ;
32
46
}
33
- return inString ;
47
+ inStringCache .put (line , inStringArr );
48
+ return isInString (line , position );
34
49
}
50
+ private static Map <String , Boolean []> inStringCache ;
51
+ public static void clearInStringCache () { inStringCache = null ; }
35
52
36
53
/**
37
- * Determines whether the specified position in a line of text is currently inside a JSON array.
38
- *
39
- * <p>This method traverses the line up to (but not including) the given position, tracking array nesting depth.
40
- * It increments depth when encountering a non-escaped '[' character outside of a string, and decrements it for ']'.
41
- * If the resulting depth is greater than 0 at the given position, the position is considered to be inside an array.</p>
54
+ * Determines whether the specified position in a line of text is currently inside a JSON array (which is marked by square brackets {@code []}).
55
+ * <p>
56
+ * This method uses caching to improve performance for repeated queries on the same string. When called, it checks if the
57
+ * "in-array" state for the provided line has already been computed and cached. If so, it returns the cached result for
58
+ * the given position in O(1) time. If not, it computes the in-array state for every character in the line, caches the
59
+ * result, and then returns the value for the requested position.
60
+ * </p>
42
61
*
43
62
* @param line
44
63
* The input string to analyze.
@@ -48,28 +67,81 @@ public static boolean isInString(String line, int position) {
48
67
* @return {@code true} if the position is within a JSON array, {@code false} otherwise.
49
68
*
50
69
* @see #isInString(String, int)
70
+ * @see #clearInArrayCache()
51
71
*/
52
72
public static boolean isInArray (String line , int position ) {
73
+ if (inArrayCache == null ) inArrayCache = new HashMap <>();
74
+
75
+ // If the String is already in the cache, return the inArray value for the given position.
76
+ Boolean [] inArrayArr = inArrayCache .get (line );
77
+ if (inArrayArr != null ) {
78
+ return inArrayArr [position ];
79
+ }
80
+
81
+ // Otherwise, precompute the cache entry for this String and call this method again.
82
+ inArrayArr = new Boolean [line .length ()];
53
83
int depth = 0 ;
54
- for (int i = 0 ; i < position ; i ++) {
55
- if (line .charAt (i ) == '[' && !isInString (line , i - 1 ))
84
+ for (int i = 0 ; i < line . length () ; i ++) {
85
+ if (line .charAt (i ) == '[' && ( i == 0 || !isInString (line , i - 1 ) ))
56
86
depth ++;
57
- else if (line .charAt (i ) == ']' && !isInString (line , i - 1 ))
87
+ else if (line .charAt (i ) == ']' && ( i == 0 || !isInString (line , i - 1 ) ))
58
88
depth --;
89
+ inArrayArr [i ] = depth > 0 ;
59
90
}
60
- return depth == 0 ? false : true ;
91
+ inArrayCache .put (line , inArrayArr );
92
+ return isInArray (line , position );
61
93
}
94
+ private static Map <String , Boolean []> inArrayCache ;
95
+ public static void clearInArrayCache () { inStringCache = null ; }
62
96
97
+
98
+ /**
99
+ * Determines whether the specified position in a line of text is currently inside a JSON object (which is marked by curly braces {@code {}}).
100
+ * <p>
101
+ * This method uses caching to improve performance for repeated queries on the same string. When called, it checks if the
102
+ * "in-object" state for the provided line has already been computed and cached. If so, it returns the cached result for
103
+ * the given position in O(1) time. If not, it computes the in-object state for every character in the line, caches the
104
+ * result, and then returns the value for the requested position.
105
+ * </p>
106
+ * <p>
107
+ * This caching approach greatly improves performance when the same string is analyzed multiple times, especially for
108
+ * large strings or when called in a loop.
109
+ * </p>
110
+ *
111
+ * @param line
112
+ * The input string to analyze.
113
+ * @param position
114
+ * The index position in the string to check.
115
+ *
116
+ * @return {@code true} if the position is within a JSON object, {@code false} otherwise.
117
+ *
118
+ * @see #isInString(String, int)
119
+ * @see #clearInObjectCache()
120
+ */
63
121
public static boolean isInObject (String line , int position ) {
122
+ if (inObjectCache == null ) inObjectCache = new HashMap <>();
123
+
124
+ // If the String is already in the cache, return the inObject value for the given position.
125
+ Boolean [] inObjectArr = inObjectCache .get (line );
126
+ if (inObjectArr != null ) {
127
+ return inObjectArr [position ];
128
+ }
129
+
130
+ // Otherwise, precompute the cache entry for this String and call this method again.
131
+ inObjectArr = new Boolean [line .length ()];
64
132
int depth = 0 ;
65
- for (int i = 0 ; i < position ; i ++) {
66
- if (line .charAt (i ) == '{' && !isInString (line , i ))
133
+ for (int i = 0 ; i < line . length () ; i ++) {
134
+ if (line .charAt (i ) == '{' && ( i == 0 || !isInString (line , i ) ))
67
135
depth ++;
68
- else if (line .charAt (i ) == '}' && !isInString (line , i ))
136
+ else if (line .charAt (i ) == '}' && ( i == 0 || !isInString (line , i ) ))
69
137
depth --;
138
+ inObjectArr [i ] = depth > 0 ;
70
139
}
71
- return depth == 0 ? false : true ;
140
+ inObjectCache .put (line , inObjectArr );
141
+ return isInObject (line , position );
72
142
}
143
+ private static Map <String , Boolean []> inObjectCache ;
144
+ public static void clearInObjectCache () { inObjectCache = null ; }
73
145
74
146
/**
75
147
* Checks if the given line contains at least one unquoted occurrence of the target character.
@@ -90,7 +162,7 @@ else if (line.charAt(i) == '}' && !isInString(line, i))
90
162
* @see #isInString(String, int)
91
163
*/
92
164
public static boolean containsUnquotedChar (String line , char target ) {
93
- return countUnquotedChar (line , target ) > 0 ;
165
+ return findFirstUnquotedChar (line , target ) != - 1 ;
94
166
}
95
167
96
168
/**
0 commit comments