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 MINUS = '-'; private static final char PERIOD = '.'; private static final char REGEX = '/'; private final CharSequence charSequence; private int position; public CharacterIndex(CharSequence charSequence) { this.charSequence = charSequence; this.position = 0; } public int length() { return charSequence.length(); } 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 nextCharIs(char c) { return inBounds(position + 1) && (charSequence.charAt(position + 1) == c); } public int incrementPosition(int charCount) { return setPosition(position + charCount); } public int setPosition(int newPosition) { //position = min(newPosition, charSequence.length() - 1); position = newPosition; return position; } 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) { if (charAt(readPosition) == SINGLE_QUOTE) { boolean escaped = false; while (inBounds(readPosition)) { readPosition++; if (escaped) { escaped = false; continue; } if (charAt(readPosition) == ESCAPE) { escaped = true; continue; } if (charAt(readPosition) == SINGLE_QUOTE) { readPosition++; break; } } } } if (skipRegex) { if (charAt(readPosition) == REGEX) { while (inBounds(readPosition)) { readPosition++; if (charAt(readPosition) == REGEX) { readPosition++; break; } } } } 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 + 1, c); } public int nextIndexOfUnescaped(int startPosition, char c) { int readPosition = startPosition; char prev1; char prev2; while (!isOutOfBounds(readPosition)) { prev1 = charAtOr(readPosition - 1, ' '); prev2 = charAtOr(readPosition - 2, ' '); boolean ignore = (prev1 == '\\' && prev2 == '\\'); boolean escaped = (prev1 == '\\' && !ignore); if (charAt(readPosition) == c && !escaped) { 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 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 isOutOfBounds(position + 1); } public boolean hasMoreCharacters() { return inBounds(position + 1); } public boolean inBounds(int idx) { return (idx >= 0) && (idx < charSequence.length()); } 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); return Character.isDigit(c) || c == MINUS || c == PERIOD; } public CharacterIndex skipBlanks() { while (inBounds() && currentChar() == SPACE){ incrementPosition(1); } return this; } }