/
CharacterIndex.java
323 lines (272 loc) · 9.68 KB
/
CharacterIndex.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.InvalidPathException;
public class CharacterIndex {
private static final char OPEN_PARENTHESIS = '(';
private static final char CLOSE_PARENTHESIS = ')';
private static final char CLOSE_SQUARE_BRACKET = ']';
private static final char SPACE = ' ';
private static final char ESCAPE = '\\';
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private static final char MINUS = '-';
private static final char PERIOD = '.';
private static final char REGEX = '/';
//workaround for issue: https://github.com/json-path/JsonPath/issues/590
private static final char SCI_E = 'E';
private static final char SCI_e = 'e';
private final CharSequence charSequence;
private int position;
private int endPosition;
public CharacterIndex(CharSequence charSequence) {
this.charSequence = charSequence;
this.position = 0;
this.endPosition = charSequence.length() - 1;
}
public int length() {
return endPosition + 1;
}
public char charAt(int idx) {
return charSequence.charAt(idx);
}
public char currentChar() {
return charSequence.charAt(position);
}
public boolean currentCharIs(char c) {
return (charSequence.charAt(position) == c);
}
public boolean lastCharIs(char c) {
return charSequence.charAt(endPosition) == c;
}
public boolean nextCharIs(char c) {
return inBounds(position + 1) && (charSequence.charAt(position + 1) == c);
}
public int incrementPosition(int charCount) {
return setPosition(position + charCount);
}
public int decrementEndPosition(int charCount) {
return setEndPosition(endPosition - charCount);
}
public int setPosition(int newPosition) {
//position = min(newPosition, charSequence.length() - 1);
position = newPosition;
return position;
}
private int setEndPosition(int newPosition) {
endPosition = newPosition;
return endPosition;
}
public int position(){
return position;
}
public int indexOfClosingSquareBracket(int startPosition) {
int readPosition = startPosition;
while (inBounds(readPosition)) {
if(charAt(readPosition) == CLOSE_SQUARE_BRACKET){
return readPosition;
}
readPosition++;
}
return -1;
}
public int indexOfMatchingCloseChar(int startPosition, char openChar, char closeChar, boolean skipStrings, boolean skipRegex) {
if(charAt(startPosition) != openChar){
throw new InvalidPathException("Expected " + openChar + " but found " + charAt(startPosition));
}
int opened = 1;
int readPosition = startPosition + 1;
while (inBounds(readPosition)) {
if (skipStrings) {
char quoteChar = charAt(readPosition);
if (quoteChar == SINGLE_QUOTE || quoteChar == DOUBLE_QUOTE){
readPosition = nextIndexOfUnescaped(readPosition, quoteChar);
if(readPosition == -1){
throw new InvalidPathException("Could not find matching close quote for " + quoteChar + " when parsing : " + charSequence);
}
readPosition++;
}
}
if (skipRegex) {
if (charAt(readPosition) == REGEX) {
readPosition = nextIndexOfUnescaped(readPosition, REGEX);
if(readPosition == -1){
throw new InvalidPathException("Could not find matching close for " + REGEX + " when parsing regex in : " + charSequence);
}
readPosition++;
}
}
if (charAt(readPosition) == openChar) {
opened++;
}
if (charAt(readPosition) == closeChar) {
opened--;
if (opened == 0) {
return readPosition;
}
}
readPosition++;
}
return -1;
}
public int indexOfClosingBracket(int startPosition, boolean skipStrings, boolean skipRegex) {
return indexOfMatchingCloseChar(startPosition, OPEN_PARENTHESIS, CLOSE_PARENTHESIS, skipStrings, skipRegex);
}
public int indexOfNextSignificantChar(char c) {
return indexOfNextSignificantChar(position, c);
}
public int indexOfNextSignificantChar(int startPosition, char c) {
int readPosition = startPosition + 1;
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) {
readPosition++;
}
if (charAt(readPosition) == c) {
return readPosition;
} else {
return -1;
}
}
public int nextIndexOf(char c) {
return nextIndexOf(position + 1, c);
}
public int nextIndexOf(int startPosition, char c) {
int readPosition = startPosition;
while (!isOutOfBounds(readPosition)) {
if (charAt(readPosition) == c) {
return readPosition;
}
readPosition++;
}
return -1;
}
public int nextIndexOfUnescaped(char c) {
return nextIndexOfUnescaped(position, c);
}
public int nextIndexOfUnescaped(int startPosition, char c) {
int readPosition = startPosition + 1;
boolean inEscape = false;
while (!isOutOfBounds(readPosition)) {
if(inEscape){
inEscape = false;
} else if('\\' == charAt(readPosition)){
inEscape = true;
} else if (c == charAt(readPosition)){
return readPosition;
}
readPosition ++;
}
return -1;
}
public char charAtOr(int postition, char defaultChar){
if(!inBounds(postition)) return defaultChar;
else return charAt(postition);
}
public boolean nextSignificantCharIs(int startPosition, char c) {
int readPosition = startPosition + 1;
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) {
readPosition++;
}
return !isOutOfBounds(readPosition) && charAt(readPosition) == c;
}
public boolean nextSignificantCharIs(char c) {
return nextSignificantCharIs(position, c);
}
public char nextSignificantChar() {
return nextSignificantChar(position);
}
public char nextSignificantChar(int startPosition) {
int readPosition = startPosition + 1;
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) {
readPosition++;
}
if (!isOutOfBounds(readPosition)) {
return charAt(readPosition);
} else {
return ' ';
}
}
public void readSignificantChar(char c) {
if (skipBlanks().currentChar() != c) {
throw new InvalidPathException(String.format("Expected character: %c", c));
}
incrementPosition(1);
}
public boolean hasSignificantSubSequence(CharSequence s) {
skipBlanks();
if (! inBounds(position + s.length() - 1)) {
return false;
}
if (! subSequence(position, position + s.length()).equals(s)) {
return false;
}
incrementPosition(s.length());
return true;
}
public int indexOfPreviousSignificantChar(int startPosition){
int readPosition = startPosition - 1;
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) {
readPosition--;
}
if (!isOutOfBounds(readPosition)) {
return readPosition;
} else {
return -1;
}
}
public int indexOfPreviousSignificantChar(){
return indexOfPreviousSignificantChar(position);
}
public char previousSignificantChar(int startPosition) {
int previousSignificantCharIndex = indexOfPreviousSignificantChar(startPosition);
if(previousSignificantCharIndex == -1) return ' ';
else return charAt(previousSignificantCharIndex);
}
public char previousSignificantChar() {
return previousSignificantChar(position);
}
public boolean currentIsTail() {
return position >= endPosition;
}
public boolean hasMoreCharacters() {
return inBounds(position + 1);
}
public boolean inBounds(int idx) {
return (idx >= 0) && (idx <= endPosition);
}
public boolean inBounds() {
return inBounds(position);
}
public boolean isOutOfBounds(int idx) {
return !inBounds(idx);
}
public CharSequence subSequence(int start, int end) {
return charSequence.subSequence(start, end);
}
public CharSequence charSequence() {
return charSequence;
}
@Override
public String toString() {
return charSequence.toString();
}
public boolean isNumberCharacter(int readPosition) {
char c = charAt(readPosition);
//workaround for issue: https://github.com/json-path/JsonPath/issues/590
return Character.isDigit(c) || c == MINUS || c == PERIOD || c == SCI_E || c == SCI_e;
}
public CharacterIndex skipBlanks() {
while (inBounds() && position < endPosition && currentChar() == SPACE){
incrementPosition(1);
}
return this;
}
private CharacterIndex skipBlanksAtEnd() {
while (inBounds() && position < endPosition && lastCharIs(SPACE)){
decrementEndPosition(1);
}
return this;
}
public CharacterIndex trim() {
skipBlanks();
skipBlanksAtEnd();
return this;
}
}