JPath
class JPath
using Newtonsoft.Json.Utilities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace Newtonsoft.Json.Linq.JsonPath
{
internal class JPath
{
private readonly string _expression;
private int _currentIndex;
public List<PathFilter> Filters { get; set; }
public JPath(string expression)
{
ValidationUtils.ArgumentNotNull(expression, "expression");
_expression = expression;
Filters = new List<PathFilter>();
ParseMain();
}
private void ParseMain()
{
int currentIndex = _currentIndex;
EatWhitespace();
if (_expression.Length != _currentIndex) {
if (_expression[_currentIndex] == '$') {
if (_expression.Length == 1)
return;
char c = _expression[_currentIndex + 1];
if (c == '.' || c == '[') {
_currentIndex++;
currentIndex = _currentIndex;
}
}
if (!ParsePath(Filters, currentIndex, false)) {
int currentIndex2 = _currentIndex;
EatWhitespace();
if (_currentIndex < _expression.Length)
throw new JsonException("Unexpected character while parsing path: " + _expression[currentIndex2]);
}
}
}
private bool ParsePath(List<PathFilter> filters, int currentPartStartIndex, bool query)
{
bool flag = false;
bool flag2 = false;
bool flag3 = false;
bool flag4 = false;
while (_currentIndex < _expression.Length && !flag4) {
char c = _expression[_currentIndex];
switch (c) {
case '(':
case '[':
if (_currentIndex > currentPartStartIndex) {
string name = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
object obj2;
if (!flag) {
FieldFilter fieldFilter2 = new FieldFilter();
fieldFilter2.Name = name;
obj2 = fieldFilter2;
} else {
ScanFilter scanFilter2 = new ScanFilter();
scanFilter2.Name = name;
obj2 = scanFilter2;
}
PathFilter item2 = (PathFilter)obj2;
filters.Add(item2);
flag = false;
}
filters.Add(ParseIndexer(c));
_currentIndex++;
currentPartStartIndex = _currentIndex;
flag2 = true;
flag3 = false;
break;
case ')':
case ']':
flag4 = true;
break;
case ' ':
if (_currentIndex < _expression.Length)
flag4 = true;
break;
case '.':
if (_currentIndex > currentPartStartIndex) {
string text = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
if (text == "*")
text = null;
object obj;
if (!flag) {
FieldFilter fieldFilter = new FieldFilter();
fieldFilter.Name = text;
obj = fieldFilter;
} else {
ScanFilter scanFilter = new ScanFilter();
scanFilter.Name = text;
obj = scanFilter;
}
PathFilter item = (PathFilter)obj;
filters.Add(item);
flag = false;
}
if (_currentIndex + 1 < _expression.Length && _expression[_currentIndex + 1] == '.') {
flag = true;
_currentIndex++;
}
_currentIndex++;
currentPartStartIndex = _currentIndex;
flag2 = false;
flag3 = true;
break;
default:
if (query && (c == '=' || c == '<' || c == '!' || c == '>' || c == '|' || c == '&'))
flag4 = true;
else {
if (flag2)
throw new JsonException("Unexpected character following indexer: " + c);
_currentIndex++;
}
break;
}
}
bool flag5 = _currentIndex == _expression.Length;
if (_currentIndex > currentPartStartIndex) {
string text2 = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex).TrimEnd(new char[0]);
if (text2 == "*")
text2 = null;
object obj3;
if (!flag) {
FieldFilter fieldFilter3 = new FieldFilter();
fieldFilter3.Name = text2;
obj3 = fieldFilter3;
} else {
ScanFilter scanFilter3 = new ScanFilter();
scanFilter3.Name = text2;
obj3 = scanFilter3;
}
PathFilter item3 = (PathFilter)obj3;
filters.Add(item3);
} else if (flag3 && (flag5 || query)) {
throw new JsonException("Unexpected end while parsing path.");
}
return flag5;
}
private PathFilter ParseIndexer(char indexerOpenChar)
{
_currentIndex++;
char indexerCloseChar = (indexerOpenChar == '[') ? ']' : ')';
EnsureLength("Path ended with open indexer.");
EatWhitespace();
if (_expression[_currentIndex] == '\'')
return ParseQuotedField(indexerCloseChar);
if (_expression[_currentIndex] == '?')
return ParseQuery(indexerCloseChar);
return ParseArrayIndexer(indexerCloseChar);
}
private PathFilter ParseArrayIndexer(char indexerCloseChar)
{
int currentIndex = _currentIndex;
int? nullable = null;
List<int> list = null;
int num = 0;
int? start = null;
int? end = null;
int? step = null;
while (_currentIndex < _expression.Length) {
char c = _expression[_currentIndex];
if (c == ' ') {
nullable = _currentIndex;
EatWhitespace();
} else {
if (c == indexerCloseChar) {
int num2 = (nullable ?? _currentIndex) - currentIndex;
if (list != null) {
if (num2 == 0)
throw new JsonException("Array index expected.");
string value = _expression.Substring(currentIndex, num2);
int item = Convert.ToInt32(value, CultureInfo.InvariantCulture);
list.Add(item);
ArrayMultipleIndexFilter arrayMultipleIndexFilter = new ArrayMultipleIndexFilter();
arrayMultipleIndexFilter.Indexes = list;
return arrayMultipleIndexFilter;
}
if (num > 0) {
if (num2 > 0) {
string value2 = _expression.Substring(currentIndex, num2);
int value3 = Convert.ToInt32(value2, CultureInfo.InvariantCulture);
if (num != 1)
step = value3;
else
end = value3;
}
ArraySliceFilter arraySliceFilter = new ArraySliceFilter();
arraySliceFilter.Start = start;
arraySliceFilter.End = end;
arraySliceFilter.Step = step;
return arraySliceFilter;
}
if (num2 == 0)
throw new JsonException("Array index expected.");
string value4 = _expression.Substring(currentIndex, num2);
int value5 = Convert.ToInt32(value4, CultureInfo.InvariantCulture);
ArrayIndexFilter arrayIndexFilter = new ArrayIndexFilter();
arrayIndexFilter.Index = value5;
return arrayIndexFilter;
}
switch (c) {
case ',': {
int num4 = (nullable ?? _currentIndex) - currentIndex;
if (num4 == 0)
throw new JsonException("Array index expected.");
if (list == null)
list = new List<int>();
string value8 = _expression.Substring(currentIndex, num4);
list.Add(Convert.ToInt32(value8, CultureInfo.InvariantCulture));
_currentIndex++;
EatWhitespace();
currentIndex = _currentIndex;
nullable = null;
break;
}
case '*':
_currentIndex++;
EnsureLength("Path ended with open indexer.");
EatWhitespace();
if (_expression[_currentIndex] != indexerCloseChar)
throw new JsonException("Unexpected character while parsing path indexer: " + c);
return new ArrayIndexFilter();
case ':': {
int num3 = (nullable ?? _currentIndex) - currentIndex;
if (num3 > 0) {
string value6 = _expression.Substring(currentIndex, num3);
int value7 = Convert.ToInt32(value6, CultureInfo.InvariantCulture);
switch (num) {
case 0:
start = value7;
break;
case 1:
end = value7;
break;
default:
step = value7;
break;
}
}
num++;
_currentIndex++;
EatWhitespace();
currentIndex = _currentIndex;
nullable = null;
break;
}
default:
if (!char.IsDigit(c) && c != '-')
throw new JsonException("Unexpected character while parsing path indexer: " + c);
if (nullable.HasValue)
throw new JsonException("Unexpected character while parsing path indexer: " + c);
_currentIndex++;
break;
}
}
}
throw new JsonException("Path ended with open indexer.");
}
private void EatWhitespace()
{
while (_currentIndex < _expression.Length && _expression[_currentIndex] == ' ') {
_currentIndex++;
}
}
private PathFilter ParseQuery(char indexerCloseChar)
{
_currentIndex++;
EnsureLength("Path ended with open indexer.");
if (_expression[_currentIndex] != '(')
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
_currentIndex++;
QueryExpression expression = ParseExpression();
_currentIndex++;
EnsureLength("Path ended with open indexer.");
EatWhitespace();
if (_expression[_currentIndex] != indexerCloseChar)
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
QueryFilter queryFilter = new QueryFilter();
queryFilter.Expression = expression;
return queryFilter;
}
private QueryExpression ParseExpression()
{
QueryExpression queryExpression = null;
CompositeExpression compositeExpression = null;
while (_currentIndex < _expression.Length) {
EatWhitespace();
if (_expression[_currentIndex] != '@')
throw new JsonException("Unexpected character while parsing path query: " + _expression[_currentIndex]);
_currentIndex++;
List<PathFilter> list = new List<PathFilter>();
if (ParsePath(list, _currentIndex, true))
throw new JsonException("Path ended with open query.");
EatWhitespace();
EnsureLength("Path ended with open query.");
object value = null;
QueryOperator queryOperator;
if (_expression[_currentIndex] == ')' || _expression[_currentIndex] == '|' || _expression[_currentIndex] == '&')
queryOperator = QueryOperator.Exists;
else {
queryOperator = ParseOperator();
EatWhitespace();
EnsureLength("Path ended with open query.");
value = ParseValue();
EatWhitespace();
EnsureLength("Path ended with open query.");
}
BooleanQueryExpression booleanQueryExpression = new BooleanQueryExpression();
booleanQueryExpression.Path = list;
booleanQueryExpression.Operator = queryOperator;
booleanQueryExpression.Value = ((queryOperator != QueryOperator.Exists) ? new JValue(value) : null);
BooleanQueryExpression booleanQueryExpression2 = booleanQueryExpression;
if (_expression[_currentIndex] == ')') {
if (compositeExpression != null) {
compositeExpression.Expressions.Add(booleanQueryExpression2);
return queryExpression;
}
return booleanQueryExpression2;
}
if (_expression[_currentIndex] == '&' && Match("&&")) {
if (compositeExpression == null || compositeExpression.Operator != QueryOperator.And) {
CompositeExpression compositeExpression2 = new CompositeExpression();
compositeExpression2.Operator = QueryOperator.And;
CompositeExpression compositeExpression3 = compositeExpression2;
compositeExpression?.Expressions.Add(compositeExpression3);
compositeExpression = compositeExpression3;
if (queryExpression == null)
queryExpression = compositeExpression;
}
compositeExpression.Expressions.Add(booleanQueryExpression2);
}
if (_expression[_currentIndex] == '|' && Match("||")) {
if (compositeExpression == null || compositeExpression.Operator != QueryOperator.Or) {
CompositeExpression compositeExpression4 = new CompositeExpression();
compositeExpression4.Operator = QueryOperator.Or;
CompositeExpression compositeExpression5 = compositeExpression4;
compositeExpression?.Expressions.Add(compositeExpression5);
compositeExpression = compositeExpression5;
if (queryExpression == null)
queryExpression = compositeExpression;
}
compositeExpression.Expressions.Add(booleanQueryExpression2);
}
}
throw new JsonException("Path ended with open query.");
}
private object ParseValue()
{
char c = _expression[_currentIndex];
if (c == '\'')
return ReadQuotedString();
if (!char.IsDigit(c)) {
switch (c) {
case '-':
break;
case 't':
if (Match("true"))
return true;
goto IL_0149;
case 'f':
if (Match("false"))
return false;
goto IL_0149;
case 'n':
if (Match("null"))
return null;
goto IL_0149;
default:
goto IL_0149;
}
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(c);
_currentIndex++;
while (_currentIndex < _expression.Length) {
c = _expression[_currentIndex];
if (c == ' ' || c == ')') {
string text = stringBuilder.ToString();
if (text.IndexOfAny(new char[3] {
'.',
'E',
'e'
}) != -1) {
if (double.TryParse(text, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out double result))
return result;
throw new JsonException("Could not read query value.");
}
if (long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out long result2))
return result2;
throw new JsonException("Could not read query value.");
}
stringBuilder.Append(c);
_currentIndex++;
}
goto IL_0149;
IL_0149:
throw new JsonException("Could not read query value.");
}
private string ReadQuotedString()
{
StringBuilder stringBuilder = new StringBuilder();
_currentIndex++;
while (_currentIndex < _expression.Length) {
char c = _expression[_currentIndex];
if (c == '\\' && _currentIndex + 1 < _expression.Length) {
_currentIndex++;
if (_expression[_currentIndex] == '\'')
stringBuilder.Append('\'');
else {
if (_expression[_currentIndex] != '\\')
throw new JsonException("Unknown escape chracter: \\" + _expression[_currentIndex]);
stringBuilder.Append('\\');
}
_currentIndex++;
} else {
if (c == '\'') {
_currentIndex++;
return stringBuilder.ToString();
}
_currentIndex++;
stringBuilder.Append(c);
}
}
throw new JsonException("Path ended with an open string.");
}
private bool Match(string s)
{
int num = _currentIndex;
foreach (char c in s) {
if (num >= _expression.Length || _expression[num] != c)
return false;
num++;
}
_currentIndex = num;
return true;
}
private QueryOperator ParseOperator()
{
if (_currentIndex + 1 >= _expression.Length)
throw new JsonException("Path ended with open query.");
if (Match("=="))
return QueryOperator.Equals;
if (Match("!=") || Match("<>"))
return QueryOperator.NotEquals;
if (Match("<="))
return QueryOperator.LessThanOrEquals;
if (Match("<"))
return QueryOperator.LessThan;
if (Match(">="))
return QueryOperator.GreaterThanOrEquals;
if (Match(">"))
return QueryOperator.GreaterThan;
throw new JsonException("Could not read query operator.");
}
private PathFilter ParseQuotedField(char indexerCloseChar)
{
List<string> list = null;
while (_currentIndex < _expression.Length) {
string text = ReadQuotedString();
EatWhitespace();
EnsureLength("Path ended with open indexer.");
if (_expression[_currentIndex] == indexerCloseChar) {
if (list != null) {
list.Add(text);
FieldMultipleFilter fieldMultipleFilter = new FieldMultipleFilter();
fieldMultipleFilter.Names = list;
return fieldMultipleFilter;
}
FieldFilter fieldFilter = new FieldFilter();
fieldFilter.Name = text;
return fieldFilter;
}
if (_expression[_currentIndex] != ',')
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
_currentIndex++;
EatWhitespace();
if (list == null)
list = new List<string>();
list.Add(text);
}
throw new JsonException("Path ended with open indexer.");
}
private void EnsureLength(string message)
{
if (_currentIndex >= _expression.Length)
throw new JsonException(message);
}
internal IEnumerable<JToken> Evaluate(JToken t, bool errorWhenNoMatch)
{
return Evaluate(Filters, t, errorWhenNoMatch);
}
internal static IEnumerable<JToken> Evaluate(List<PathFilter> filters, JToken t, bool errorWhenNoMatch)
{
IEnumerable<JToken> enumerable = new JToken[1] {
t
};
foreach (PathFilter filter in filters) {
enumerable = filter.ExecuteFilter(enumerable, errorWhenNoMatch);
}
return enumerable;
}
}
}