Records elements used in a model, and allowing a simple JSON model to be produced for testing.
Exemple #1
0
        /// <summary>
        /// Parse the template, and capture paths used in the template to determine a suitable structure for the required model.
        /// </summary>
        /// <param name="templateSource">The template content to parse.</param>
        /// <param name="disableContentEscaping">In some cases, content should not be escaped (such as when rendering text bodies and subjects in emails). 
        /// By default, we use content escaping, but this parameter allows it to be disabled.</param>
        /// <returns></returns>
        public static ExtendedParseInformation ParseWithModelInference(string templateSource, bool disableContentEscaping = false)
        {
            var tokens = new Queue<TokenPair>(Tokenizer.Tokenize(templateSource));
            var options = new ParsingOptions { DisableContentSafety = disableContentEscaping };
            var inferredModel = new InferredTemplateModel();

            var internalTemplate = Parse(tokens, options, inferredModel);
            Func<IDictionary<String, object>, String> template = (model) =>
            {
                var retval = new StringBuilder();
                var context = new ContextObject()
                {
                    Value = model,
                    Key = ""
                };
                internalTemplate(retval, context);
                return retval.ToString();
            };

            var result = new ExtendedParseInformation()
            {
                InferredModel = inferredModel,
                ParsedTemplate = template
            };

            return result;
        }
Exemple #2
0
        /// <summary>
        /// Parse the template, and capture paths used in the template to determine a suitable structure for the required model.
        /// </summary>
        /// <param name="templateSource">The template content to parse.</param>
        /// <param name="disableContentEscaping">In some cases, content should not be escaped (such as when rendering text bodies and subjects in emails).
        /// By default, we use content escaping, but this parameter allows it to be disabled.</param>
        /// <returns></returns>
        public static ExtendedParseInformation ParseWithModelInference(string templateSource, bool disableContentEscaping = false)
        {
            var tokens  = new Queue <TokenPair> (Tokenizer.Tokenize(templateSource));
            var options = new ParsingOptions {
                DisableContentSafety = disableContentEscaping
            };
            var inferredModel = new InferredTemplateModel();

            var internalTemplate = Parse(tokens, options, inferredModel);
            Func <IDictionary <String, object>, String> template = (model) =>
            {
                var retval  = new StringBuilder();
                var context = new ContextObject()
                {
                    Value = model,
                    Key   = ""
                };
                internalTemplate(retval, context);
                return(retval.ToString());
            };

            var result = new ExtendedParseInformation()
            {
                InferredModel  = inferredModel,
                ParsedTemplate = template
            };

            return(result);
        }
Exemple #3
0
        /// <summary>
        /// Parse the template, and capture paths used in the template to determine a suitable structure for the required model.
        /// </summary>
        /// <param name="templateSource">The template content to parse.</param>
        /// <param name="options">Options for configuring/extending the parser.</param>
        /// <returns></returns>
        public static ExtendedParseInformation ParseWithModelInference(string templateSource, ParsingOptions options)
        {
            var tokensQueue   = GetTokensQueue(templateSource, options);
            var inferredModel = new InferredTemplateModel();

            var internalTemplate = Parse(tokensQueue, options, inferredModel);
            Func <IDictionary <String, object>, String> template = (model) =>
            {
                var retval  = new StringBuilder();
                var context = new ContextObject()
                {
                    Value = model,
                    Key   = ""
                };
                internalTemplate(retval, context);
                return(retval.ToString());
            };

            var result = new ExtendedParseInformation()
            {
                InferredModel  = inferredModel,
                ParsedTemplate = template
            };

            return(result);
        }
Exemple #4
0
        private static Action<StringBuilder, ContextObject> HandleCollectionOpen(TokenPair token, Queue<TokenPair> remainder, ParsingOptions options, InferredTemplateModel scope)
        {
            if (scope != null)
            {
                scope = scope.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.Collection);
            }

            var innerTemplate = Parse(remainder, options, scope);

            return (builder, context) =>
            {
                //if we're in the same scope, just negating, then we want to use the same object
                var c = context.GetContextForPath(token.Value);

                //"falsey" values by Javascript standards...
                if (!c.Exists()) return;

                if (c.Value is IEnumerable && !(c.Value is String) && !(c.Value is IDictionary<string, object>))
                {
                    var index = 0;
                    foreach (object i in c.Value as IEnumerable)
                    {
                        var innerContext = new ContextObject()
                        {
                            Value = i,
                            Key = String.Format("[{0}]", index),
                            Parent = c
                        };
                        innerTemplate(builder, innerContext);
                        index++;
                    }
                }
                else
                {
                    throw new IndexedParseException("'{0}' is used like an array by the template, but is a scalar value or object in your model.", token.Value);
                }
            };
        }
        private InferredTemplateModel GetContextForPath(Queue <String> elements)
        {
            var retval = this;

            if (elements.Any())
            {
                var element = elements.Dequeue();
                if (element.StartsWith(".."))
                {
                    if (Parent != null)
                    {
                        retval = Parent.GetContextForPath(elements);
                    }
                    else
                    {
                        //Calling "../" too much may be "ok" in that if we're at root,
                        //we may just stop recursion and traverse down the path.
                        retval = GetContextForPath(elements);
                    }
                }
                //TODO: handle array accessors and maybe "special" keys.
                else
                {
                    //ALWAYS return the context, even if the value is null.

                    InferredTemplateModel innerContext = null;
                    if (!this.Children.TryGetValue(element, out innerContext))
                    {
                        innerContext           = new InferredTemplateModel();
                        innerContext.Key       = element;
                        innerContext.Parent    = this;
                        this.Children[element] = innerContext;
                    }
                    retval = innerContext.GetContextForPath(elements);
                }
            }
            return(retval);
        }
Exemple #6
0
        private static Action <StringBuilder, ContextObject> Parse(Queue <TokenPair> tokens, ParsingOptions options, InferredTemplateModel currentScope = null)
        {
            var buildArray = new List <Action <StringBuilder, ContextObject> > ();

            while (tokens.Any())
            {
                var currentToken = tokens.Dequeue();
                switch (currentToken.Type)
                {
                case TokenType.Comment:
                    break;

                case TokenType.Content:
                    buildArray.Add(HandleContent(currentToken.Value));
                    break;

                case TokenType.CollectionOpen:
                    buildArray.Add(HandleCollectionOpen(currentToken, tokens, options, currentScope));
                    break;

                case TokenType.ElementOpen:
                    buildArray.Add(HandleElementOpen(currentToken, tokens, options, currentScope));
                    break;

                case TokenType.InvertedElementOpen:
                    buildArray.Add(HandleInvertedElementOpen(currentToken, tokens, options, currentScope));
                    break;

                case TokenType.CollectionClose:
                case TokenType.ElementClose:
                    // This should immediately return if we're in the element scope,
                    // and if we're not, this should have been detected by the tokenizer!
                    return((builder, context) =>
                    {
                        foreach (var a in buildArray)
                        {
                            a(builder, context);
                        }
                    });

                case TokenType.EscapedSingleValue:
                case TokenType.UnescapedSingleValue:
                    buildArray.Add(HandleSingleValue(currentToken, options, currentScope));
                    break;
                }
            }

            return((builder, context) =>
            {
                foreach (var a in buildArray)
                {
                    a(builder, context);
                }
            });
        }
Exemple #7
0
        private static Action <StringBuilder, ContextObject> HandleElementOpen(TokenPair token, Queue <TokenPair> remainder, ParsingOptions options, InferredTemplateModel scope)
        {
            if (scope != null)
            {
                scope = scope.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.ConditionalValue);
            }

            var innerTemplate = Parse(remainder, options, scope);

            return((builder, context) =>
            {
                var c = context.GetContextForPath(token.Value);
                //"falsey" values by Javascript standards...
                if (c.Exists())
                {
                    innerTemplate(builder, c);
                }
            });
        }
Exemple #8
0
        private static Action <StringBuilder, ContextObject> HandleCollectionOpen(TokenPair token, Queue <TokenPair> remainder, ParsingOptions options, InferredTemplateModel scope)
        {
            if (scope != null)
            {
                scope = scope.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.Collection);
            }

            var innerTemplate = Parse(remainder, options, scope);

            return((builder, context) =>
            {
                //if we're in the same scope, just negating, then we want to use the same object
                var c = context.GetContextForPath(token.Value);

                //"falsey" values by Javascript standards...
                if (!c.Exists())
                {
                    return;
                }

                if (c.Value is IEnumerable && !(c.Value is String) && !(c.Value is IDictionary <string, object>))
                {
                    var index = 0;
                    foreach (object i in c.Value as IEnumerable)
                    {
                        var innerContext = new ContextObject()
                        {
                            Value = i,
                            Key = String.Format("[{0}]", index),
                            Parent = c
                        };
                        innerTemplate(builder, innerContext);
                        index++;
                    }
                }
                else
                {
                    throw new IndexedParseException("'{0}' is used like an array by the template, but is a scalar value or object in your model.", token.Value);
                }
            });
        }
Exemple #9
0
        // private static string HtmlEncodeString(string context)
        // {
        //     return HttpUtility.HtmlEncode(context);
        // }

        private static Action <StringBuilder, ContextObject> HandleSingleValue(TokenPair token, ParsingOptions options, InferredTemplateModel scope)
        {
            if (scope != null)
            {
                scope = scope.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.Scalar);
            }

            return((builder, context) =>
            {
                if (context != null)
                {
                    //try to locate the value in the context, if it exists, append it.
                    var c = context.GetContextForPath(token.Value);
                    if (c.Value != null)
                    {
                        // if (token.Type == TokenType.EscapedSingleValue && !options.DisableContentSafety)
                        // {
                        //     builder.Append (HtmlEncodeString (c.ToString ()));
                        // }
                        // else
                        // {
                        builder.Append(c);
                        // }
                    }
                }
            });
        }
        private InferredTemplateModel GetContextForPath(Queue<String> elements)
        {
            var retval = this;
            if (elements.Any())
            {
                var element = elements.Dequeue();
                if (element.StartsWith(".."))
                {
                    if (Parent != null)
                    {
                        retval = Parent.GetContextForPath(elements);
                    }
                    else
                    {
                        //Calling "../" too much may be "ok" in that if we're at root,
                        //we may just stop recursion and traverse down the path.
                        retval = GetContextForPath(elements);
                    }
                }
                //TODO: handle array accessors and maybe "special" keys.
                else
                {
                    //ALWAYS return the context, even if the value is null.

                    InferredTemplateModel innerContext = null;
                    if (!this.Children.TryGetValue(element, out innerContext))
                    {
                        innerContext = new InferredTemplateModel();
                        innerContext.Key = element;
                        innerContext.Parent = this;
                        this.Children[element] = innerContext;
                    }
                    retval = innerContext.GetContextForPath(elements);
                }
            }
            return retval;
        }
Exemple #11
0
        private static Action<StringBuilder, ContextObject> Parse(Queue<TokenPair> tokens, ParsingOptions options, InferredTemplateModel currentScope = null)
        {
            var buildArray = new List<Action<StringBuilder, ContextObject>>();

            while (tokens.Any())
            {
                var currentToken = tokens.Dequeue();
                switch (currentToken.Type)
                {
                    case TokenType.Comment:
                        break;
                    case TokenType.Content:
                        buildArray.Add(HandleContent(currentToken.Value));
                        break;
                    case TokenType.CollectionOpen:
                        buildArray.Add(HandleCollectionOpen(currentToken, tokens, options,  currentScope));
                        break;
                    case TokenType.ElementOpen:
                        buildArray.Add(HandleElementOpen(currentToken, tokens, options, currentScope));
                        break;
                    case TokenType.InvertedElementOpen:
                        buildArray.Add(HandleInvertedElementOpen(currentToken, tokens, options, currentScope));
                        break;
                    case TokenType.CollectionClose:
                    case TokenType.ElementClose:
                        // This should immediately return if we're in the element scope,
                        // and if we're not, this should have been detected by the tokenizer!
                        return (builder, context) =>
                        {
                            foreach (var a in buildArray)
                            {
                                a(builder, context);
                            }
                        };
                    case TokenType.EscapedSingleValue:
                    case TokenType.UnescapedSingleValue:
                        buildArray.Add(HandleSingleValue(currentToken, options, currentScope));
                        break;
                }
            }

            return (builder, context) =>
            {
                foreach (var a in buildArray)
                {
                    a(builder, context);
                }
            };
        }
Exemple #12
0
        private static Action<StringBuilder, ContextObject> HandleSingleValue(TokenPair token, ParsingOptions options, InferredTemplateModel scope )
        {
            if (scope != null)
            {
                scope = scope.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.Scalar);
            }

            return (builder, context) =>
            {
                if (context != null)
                {
                    //try to locate the value in the context, if it exists, append it.
                    var c = context.GetContextForPath(token.Value);
                    if (c.Value != null)
                    {
                        if (token.Type == TokenType.EscapedSingleValue && !options.DisableContentSafety)
                        {
                            builder.Append(HtmlEncodeString(c.ToString()));
                        }
                        else
                        {
                            builder.Append(c);
                        }
                    }
                }
            };
        }
Exemple #13
0
        private static Action<StringBuilder, ContextObject> HandleInvertedElementOpen(TokenPair token, Queue<TokenPair> remainder,
            ParsingOptions options, InferredTemplateModel scope)
        {
            if (scope != null)
            {
                scope = scope.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.ConditionalValue);
            }

            var innerTemplate = Parse(remainder, options, scope);

            return (builder, context) =>
            {
                var c = context.GetContextForPath(token.Value);
                //"falsey" values by Javascript standards...
                if (!c.Exists())
                {
                    innerTemplate(builder, c);
                }
            };
        }
Exemple #14
0
        private static Action <StringBuilder, ContextObject> HandleCollectionOpen(TokenPair token, Queue <TokenPair> remainder, ParsingOptions options, InferredTemplateModel scope)
        {
            if (scope != null)
            {
                scope = scope.GetInferredModelForPath(token.Value, InferredTemplateModel.UsedAs.Collection);
            }

            var innerTemplate = Parse(remainder, options, scope);

            return((builder, context) =>
            {
                //if we're in the same scope, just negating, then we want to use the same object
                var c = context.GetContextForPath(token.Value);

                //"falsey" values by Javascript standards...
                if (!c.Exists())
                {
                    return;
                }

                IEnumerable cVal = null;

                if (c.Value is IEnumerable && !(c.Value is String) && !(c.Value is IDictionary <string, object>))
                {
                    cVal = c.Value as IEnumerable;
                }
                else
                {
                    //Ok, this is a scalar value or an Object. So lets box it into an IEnumerable
                    cVal = new ArrayList()
                    {
                        c.Value
                    };
                }

                var index = 0;
                foreach (object i in cVal)
                {
                    var innerContext = new ContextObject()
                    {
                        Value = i,
                        Key = String.Format("[{0}]", index),
                        Parent = c
                    };
                    innerTemplate(builder, innerContext);
                    index++;
                }
            });
        }