/// <summary>
        /// Finds and replaces constants (bool, number, string constants)
        /// </summary>
        /// <param name="tokens">tokens input</param>
        /// <returns>tokens</returns>
        private static List <FilterExpressionToken> ReplaceConstantExpressionTokens(List <FilterExpressionToken> tokens)
        {
            List <FilterExpressionToken> ret = new List <FilterExpressionToken>(tokens.Count);

            for (int i = 0; i < tokens.Count; i++)
            {
                FilterExpressionToken t = tokens[i];

                if (t is PrimitiveExpressionToken pet)
                {
                    if (pet.Token.IsStringToken())
                    {
                        ret.Add(new ConstantStringExpressionToken(pet.Token.CastToStringToken()));
                    }
                    else if (pet.Token.IsBoolToken())
                    {
                        ret.Add(new ConstantBoolExpressionToken(pet.Token.CastToBoolToken()));
                    }
                    else if (pet.Token.IsNumberToken())
                    {
                        ret.Add(new ConstantNumberExpressionToken(pet.Token.CastToNumberToken()));
                    }
                    else
                    {
                        ret.Add(pet);
                    }
                }
                else
                {
                    ret.Add(t);
                }
            }

            return(ret);
        }
        private static void EnsureMethodArgumentsAreValid(MethodCallExpressionToken mct, ref int callCount)
        {
            callCount++;

            if (callCount > 10 * 1000)
            {
                throw new InternalJsonPathwayException(
                          "Number of calls to EnsureTokensAreValidInner exceeded max expected number of 10000, possible stack overflow or infinite loop.");
            }

            if (mct.CalledOnExpression is MethodCallExpressionToken inner1)
            {
                EnsureMethodArgumentsAreValid(inner1, ref callCount);
            }

            foreach (FilterExpressionToken arg in mct.Arguments)
            {
                if (arg is MethodCallExpressionToken inner2)
                {
                    EnsureMethodArgumentsAreValid(inner2, ref callCount);
                }
            }

            FilterExpressionToken prim = mct.Arguments.FirstOrDefault(x => x is PrimitiveExpressionToken);

            if (prim != null)
            {
                throw new UnexpectedTokenException((prim as PrimitiveExpressionToken).Token);
            }
        }
        private static List <FilterExpressionToken> ReplaceMethodCallsOnArrayAccess(List <FilterExpressionToken> tokens)
        {
            List <ArrayAccessExpressionToken> arrayTokens = tokens.Where(x => x is ArrayAccessExpressionToken).Cast <ArrayAccessExpressionToken>().ToList();
            List <FilterExpressionToken>      ret         = tokens.ToList();

            foreach (ArrayAccessExpressionToken at in arrayTokens)
            {
                int arrayIndex = ret.IndexOf(at);
                int dotIndex   = arrayIndex + 1;

                if (dotIndex < ret.Count - 4 && ret[dotIndex] is PrimitiveExpressionToken petDot && petDot.Token.IsSymbolToken('.'))
                {
                    int methodNameIndex = dotIndex + 1;

                    if (ret[methodNameIndex] is PrimitiveExpressionToken petMethodName && petMethodName.Token.IsPropertyToken())
                    {
                        PropertyToken propToken = petMethodName.Token.CastToPropertyToken();
                        if (propToken.Escaped)
                        {
                            throw new UnexpectedTokenException(propToken, "Expected method call on array element");
                        }
                        string methodName = propToken.StringValue;

                        int openGroupIndex = methodNameIndex + 1;

                        if (openGroupIndex < ret.Count && ret[openGroupIndex] is OpenGroupToken ogt)
                        {
                            FilterExpressionToken closed = ret.FirstOrDefault(x => x is CloseGroupToken cgt && cgt.GroupId == ogt.GroupId);

                            if (closed == null)
                            {
                                throw new ParsingException("Failed to find ) for ( at " + ogt.StartIndex);
                            }

                            List <FilterExpressionToken> args = new List <FilterExpressionToken>();

                            int startIndex = ret.IndexOf(ogt);
                            int endIndex   = ret.IndexOf(closed);

                            for (int i = startIndex + 1; i < endIndex; i++)
                            {
                                args.Add(ret[i]);
                            }

                            args = FormatAndValidateMethodArgumentTokens(args);

                            ret[arrayIndex] = new MethodCallExpressionToken(at, methodName, args.ToArray());

                            for (int i = arrayIndex + 1; i <= endIndex; i++)
                            {
                                ret[i] = null;
                            }
                        }
                    }
                }
            }

            return(ret.Where(x => x != null).ToList());
        }
예제 #4
0
 public ArrayAccessExpressionToken(FilterExpressionToken executedOn, AllArrayElementsToken token)
     : this(executedOn, token as MultiCharToken)
 {
     if (token is null)
     {
         throw new ArgumentNullException(nameof(token));
     }
     IsAllArrayElements = true;
     StartIndex         = token.StartIndex;
 }
예제 #5
0
        public MethodCallExpressionToken(FilterExpressionToken calledOnExpression, string methodName, FilterExpressionToken[] arguments)
        {
            if (string.IsNullOrWhiteSpace(methodName))
            {
                throw new ArgumentException("Value not provided", nameof(methodName));
            }

            CalledOnExpression = calledOnExpression ?? throw new ArgumentNullException(nameof(calledOnExpression));
            MethodName         = methodName;
            Arguments          = arguments ?? throw new ArgumentNullException(nameof(arguments));
        }
예제 #6
0
        public ArrayAccessExpressionToken(FilterExpressionToken executedOn, ArrayElementsToken token)
            : this(executedOn, token as MultiCharToken)
        {
            if (token == null)
            {
                throw new ArgumentNullException(nameof(token));
            }

            SliceStart          = token.SliceStart;
            SliceEnd            = token.SliceEnd;
            SliceStep           = token.SliceStep;
            ExactElementsAccess = token.ExactElementsAccess;
            StartIndex          = token.StartIndex;
        }
예제 #7
0
        private ArrayAccessExpressionToken(FilterExpressionToken executedOn, MultiCharToken arrayToken)
        {
            if (arrayToken == null)
            {
                throw new ArgumentNullException(nameof(arrayToken));
            }

            ExecutedOn = executedOn ?? throw new ArgumentNullException(nameof(executedOn));

            if (!(executedOn is PropertyExpressionToken) && !(executedOn is FilterExpressionToken))
            {
                throw new UnexpectedTokenException(arrayToken, "Array access can be applied to property or filter");
            }
        }
        private static List <FilterExpressionToken> ReplaceMethodCallsOnMethodsInner(List <FilterExpressionToken> tokens, ref int callCount, out int replacedCount)
        {
            callCount++;
            replacedCount = 0;

            if (callCount > 10 * 1000)
            {
                throw new InternalJsonPathwayException(
                          "Number of calls to ReplaceMethodCallsOnMethodsInner exceeded max expected number of 10000, possible stack overflow or infinite loop.");
            }

            List <FilterExpressionToken> ret = tokens.ToList();

            foreach (MethodCallExpressionToken mc in tokens.Where(x => x is MethodCallExpressionToken))
            {
                int index = ret.IndexOf(mc);

                if (index < ret.Count - 4 && ret[index + 1] is PrimitiveExpressionToken pet1 && pet1.Token.IsSymbolToken('.') &&
                    ret[index + 2] is PrimitiveExpressionToken pet2 && pet2.Token.IsPropertyToken() &&
                    ret[index + 3] is OpenGroupToken)
                {
                    OpenGroupToken        open  = ret[index + 3] as OpenGroupToken;
                    FilterExpressionToken close = ret.Single(x => x is CloseGroupToken cgt && cgt.GroupId == open.GroupId);

                    string methodName = (ret[index + 2] as PrimitiveExpressionToken).Token.CastToPropertyToken().StringValue;

                    List <FilterExpressionToken> args = new List <FilterExpressionToken>();

                    int closeIndex = ret.IndexOf(close);
                    for (int i = index + 4; i < closeIndex; i++)
                    {
                        args.Add(ret[i]);
                    }
                    args = FormatAndValidateMethodArgumentTokens(args);

                    ret[index] = new MethodCallExpressionToken(mc, methodName, args.ToArray());

                    for (int i = index + 1; i <= closeIndex; i++)
                    {
                        ret[i] = null;
                    }

                    replacedCount++;
                }
            }

            return(ret.Where(x => x != null).ToList());
        }
        private static List <FilterExpressionToken> ReplaceMethodCallsOnConstants(List <FilterExpressionToken> tokens)
        {
            List <FilterExpressionToken> ret = tokens.ToList();

            foreach (ConstantBaseExpressionToken ct in tokens.Where(x => x is ConstantBaseExpressionToken))
            {
                int index = ret.IndexOf(ct);

                if (index < ret.Count - 4 && ret[index + 1] is PrimitiveExpressionToken pet1 && pet1.Token.IsSymbolToken('.') &&
                    ret[index + 2] is PrimitiveExpressionToken pet2 && pet2.Token.IsPropertyToken() &&
                    ret[index + 3] is OpenGroupToken)
                {
                    OpenGroupToken        open  = ret[index + 3] as OpenGroupToken;
                    FilterExpressionToken close = ret.Single(x => x is CloseGroupToken cgt && cgt.GroupId == open.GroupId);

                    string methodName = (ret[index + 2] as PrimitiveExpressionToken).Token.CastToPropertyToken().StringValue;

                    List <FilterExpressionToken> args = new List <FilterExpressionToken>();

                    int closeIndex = ret.IndexOf(close);
                    for (int i = index + 4; i < closeIndex; i++)
                    {
                        args.Add(ret[i]);
                    }
                    args = FormatAndValidateMethodArgumentTokens(args);

                    ret[index] = new MethodCallExpressionToken(ct, methodName, args.ToArray());

                    for (int i = index + 1; i <= closeIndex; i++)
                    {
                        ret[i] = null;
                    }
                }
            }

            return(ret.Where(x => x != null).ToList());
        }
        /// <summary>
        /// Replaces chained property tokens with expression property tokens
        /// </summary>
        /// <param name="tokens">tokens input</param>
        /// <returns>tokens</returns>
        private static List <FilterExpressionToken> ReplacePropertyTokens(List <FilterExpressionToken> tokens)
        {
            List <PrimitiveExpressionToken> @symbols = tokens
                                                       .Where(x => x is PrimitiveExpressionToken pet && pet.Token.IsSymbolToken('@'))
                                                       .Cast <PrimitiveExpressionToken>()
                                                       .ToList();

            if (!symbols.Any())
            {
                return(tokens);
            }

            List <(PropertyExpressionToken prop, List <FilterExpressionToken> primitives)> replacements = new List <(PropertyExpressionToken, List <FilterExpressionToken>)>();

            foreach (PrimitiveExpressionToken st in @symbols)
            {
                List <PrimitiveExpressionToken> tokensForReplacement = new List <PrimitiveExpressionToken>();
                tokensForReplacement.Add(st);

                int index = tokens.IndexOf(st);
                index++;

                bool isPreviousDot = false;

                while (index < tokens.Count)
                {
                    FilterExpressionToken t = tokens[index];

                    isPreviousDot = index > 0 && (tokens[index - 1] is PrimitiveExpressionToken pet3 && pet3.Token.IsSymbolToken('.'));

                    bool isDot = t is PrimitiveExpressionToken pet && pet.Token.IsSymbolToken('.');
                    if (isDot)
                    {
                        if (isPreviousDot)
                        {
                            throw new UnexpectedTokenException((t as PrimitiveExpressionToken).Token);
                        }

                        index++;
                        continue;
                    }

                    if (t is PrimitiveExpressionToken pet2 && pet2.Token.IsPropertyToken())
                    {
                        PropertyToken prop = pet2.Token.CastToPropertyToken();

                        if (prop.Escaped && isPreviousDot)
                        {
                            throw new UnexpectedTokenException(prop, "Unexpected token after \".\" symbol");
                        }

                        tokensForReplacement.Add(t as PrimitiveExpressionToken);
                        index++;
                        continue;
                    }

                    if (t is PrimitiveExpressionToken pet4 && (pet4.Token.IsChildPropertiesToken() || pet4.Token.IsRecursivePropertiesToken()))
                    {
                        tokensForReplacement.Add(t as PrimitiveExpressionToken);
                        index++;
                        continue;
                    }

                    break;
                }

                List <PrimitiveExpressionToken> tokensToRemove = tokensForReplacement.ToList();
                if (tokensForReplacement.First().Token.IsSymbolToken('@'))
                {
                    tokensForReplacement = tokensForReplacement.Skip(1).ToList();
                }

                for (int i = 0; i < tokensForReplacement.Count - 1; i++)
                {
                    if (tokensForReplacement[i].Token.IsChildPropertiesToken() || tokensForReplacement[i].Token.IsRecursivePropertiesToken())
                    {
                        // only last property can be wildcard * or recursive ..
                        string accessor = tokensForReplacement[i].Token.IsChildPropertiesToken() ? "\"*\"" : "\"..\"";
                        throw new ParsingException($"Unexpected token accessor {accessor} at {tokensForReplacement[i].Token.StartIndex}" +
                                                   ", this kind of token is expected to be last in property chain");
                    }
                }

                PropertyExpressionToken propEx;
                Token last = tokensForReplacement.Last().Token;

                if (last.IsRecursivePropertiesToken())
                {
                    List <PrimitiveExpressionToken> props = tokensForReplacement.Where(x => !x.Token.IsSymbolToken('@')).ToList();
                    props = tokensForReplacement.Take(props.Count - 1).ToList();

                    propEx = new PropertyExpressionToken(
                        props.Select(x => x.Token.CastToPropertyToken()).ToArray(),
                        tokensForReplacement.Last().Token.CastToRecursivePropertiesToken(),
                        tokensForReplacement.First().StartIndex
                        );
                }
                else if (last.IsChildPropertiesToken())
                {
                    List <PrimitiveExpressionToken> props = tokensForReplacement.Where(x => !x.Token.IsSymbolToken('@')).ToList();
                    props = tokensForReplacement.Take(props.Count - 1).ToList();

                    propEx = new PropertyExpressionToken(
                        props.Select(x => x.Token.CastToPropertyToken()).ToArray(),
                        tokensForReplacement.Last().Token.CastToChildPropertiesToken(),
                        tokensForReplacement.First().StartIndex
                        );
                }
                else
                {
                    propEx = new PropertyExpressionToken(
                        tokensForReplacement.Where(x => !x.Token.IsSymbolToken('@')).Select(x => x.Token.CastToPropertyToken()).ToArray(),
                        tokensForReplacement.First().StartIndex
                        );
                }

                replacements.Add((propEx, tokensToRemove.Cast <FilterExpressionToken>().ToList()));
            }

            List <FilterExpressionToken> ret = tokens.ToList();

            foreach ((PropertyExpressionToken prop, List <FilterExpressionToken> primitives)r in replacements)
            {
                List <int> indexes = r.primitives.Select(x => ret.IndexOf(x)).ToList();

                for (int i = indexes.First(); i <= indexes.Last(); i++)
                {
                    ret[i] = null;
                }

                ret[indexes[0]] = r.prop;
            }

            return(ret.Where(x => x != null).ToList());
        }
예제 #11
0
 public PrimitiveFilterSubExpression(FilterExpressionToken token)
 {
     Token = token ?? throw new ArgumentNullException(nameof(token));
 }