/// <inheritdoc /> public void WriteXml(XmlWriter writer) { writer.WriteAttributeString(nameof(Location), Location.ToFormatString()); if (EndsWithDelimiter) { writer.WriteAttributeString(nameof(EndsWithDelimiter), bool.TrueString); } if (PathParts.Any()) { writer.WriteStartElement("Path"); foreach (var pathPart in PathParts.ToArray()) { writer.WriteElementString(pathPart.Value.ToString(), pathPart.Key); } writer.WriteEndElement(); //</Path> } if (FormatterName != null) { writer.WriteStartElement("Format"); writer.WriteAttributeString(nameof(FormatterName), FormatterName); foreach (var expressionArgument in Formats) { writer.WriteStartElement("Argument"); expressionArgument.WriteXml(writer); writer.WriteEndElement(); //</Argument> } writer.WriteEndElement(); //</Format> } }
/// <inheritdoc /> public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue(nameof(PathParts), PathParts.ToArray()); info.AddValue(nameof(Formats), Formats); info.AddValue(nameof(FormatterName), FormatterName); info.AddValue(nameof(Location), Location.ToFormatString()); info.AddValue(nameof(EndsWithDelimiter), EndsWithDelimiter); }
/// <inheritdoc /> protected bool Equals(MorestachioExpression other) { if (other.PathParts.Count != PathParts.Count) { return(false); } if (other.Formats.Count != Formats.Count) { return(false); } if (other.FormatterName != FormatterName) { return(false); } if (!other.Location.Equals(Location)) { return(false); } var parts = PathParts.ToArray(); var otherParts = other.PathParts.ToArray(); if (parts.Length != otherParts.Length || Formats.Count != other.Formats.Count) { return(false); } for (var index = 0; index < parts.Length; index++) { var thisPart = parts[index]; var thatPart = otherParts[index]; if (thatPart.Value != thisPart.Value || thatPart.Key != thisPart.Key) { return(false); } } for (var index = 0; index < Formats.Count; index++) { var thisArgument = Formats[index]; var thatArgument = other.Formats[index]; if (!thisArgument.Equals(thatArgument)) { return(false); } } return(true); }
/// <inheritdoc /> public CompiledExpression Compile() { if (!PathParts.HasValue && Formats.Count == 0 && FormatterName == null) { return((contextObject, data) => contextObject.ToPromise()); } if (!PathParts.HasValue && Formats.Count == 1 && FormatterName == "") { //this enables the usage of brackets. A formatter that has no name and one argument e.g ".(data + 1)" or "(data + 1)" should be considered a bracket return((contextObject, data) => Formats[0].GetValue(contextObject, data)); } if (PathParts.Count == 1 && PathParts.Current.Value == PathType.Null) { return((contextObject, scopeData) => scopeData.ParserOptions .CreateContextObject("x:null", null).ToPromise()); } var pathParts = PathParts.ToArray(); Func <ContextObject, ScopeData, IMorestachioExpression, ContextObject> getContext = null; if (pathParts.Length != 0) { var pathQueue = new Func <ContextObject, ScopeData, IMorestachioExpression, ContextObject> [pathParts.Length]; var idx = 0; if (pathParts.Length > 0 && pathParts.First().Value == PathType.DataPath) { var key = pathParts[0].Key; pathQueue[idx++] = ((context, scopeData, expression) => { var variable = scopeData.GetVariable(context, key); if (variable != null) { return(variable); } return(context.ExecuteDataPath(key, expression, context.Value?.GetType(), scopeData)); }); } for (; idx < pathQueue.Length;) { var pathPart = pathParts[idx]; var key = pathPart.Key; switch (pathPart.Value) { case PathType.DataPath: pathQueue[idx++] = ((contextObject, scopeData, expression) => { return(contextObject.ExecuteDataPath(key, expression, contextObject.Value?.GetType(), scopeData)); }); break; case PathType.RootSelector: pathQueue[idx++] = ((contextObject, scopeData, expression) => { return(contextObject.ExecuteRootSelector()); }); break; case PathType.ParentSelector: pathQueue[idx++] = ((contextObject, scopeData, expression) => { var natContext = contextObject.FindNextNaturalContextObject(); return(natContext?.Parent ?? contextObject); }); break; case PathType.ObjectSelector: pathQueue[idx++] = ((contextObject, scopeData, expression) => { return(contextObject.ExecuteObjectSelector(key, contextObject.Value?.GetType(), scopeData)); }); break; case PathType.Null: pathQueue[idx++] = ((contextObject, scopeData, expression) => { return(scopeData.ParserOptions.CreateContextObject("x:null", null)); }); break; case PathType.Boolean: var booleanValue = key == "true"; pathQueue[idx++] = ((contextObject, scopeData, expression) => { var booleanContext = scopeData.ParserOptions.CreateContextObject(".", booleanValue); booleanContext.IsNaturalContext = true; return(booleanContext); }); break; case PathType.SelfAssignment: case PathType.ThisPath: pathQueue[idx++] = ((contextObject, scopeDate, expression) => contextObject); break; default: throw new ArgumentOutOfRangeException(); } } if (pathQueue.Length == 1) { getContext = pathQueue[0]; } else { getContext = (contextObject, data, expression) => { for (var index = 0; index < pathQueue.Length; index++) { var func = pathQueue[index]; contextObject = func(contextObject, data, expression); } return(contextObject); }; } } if (!Formats.Any() && FormatterName == null) { if (getContext == null) { return((contextObject, data) => contextObject.ToPromise()); } return((contextObject, data) => getContext(contextObject, data, this).ToPromise()); } var formatsCompiled = Formats.ToDictionary(f => f, f => { if (f.IsCompileTimeEval()) { return(f.GetCompileTimeValue()); } return(f.Compile()); }).ToArray(); var arguments = new FormatterArgumentType[formatsCompiled.Length]; var allConstants = formatsCompiled.All(e => e.Key.IsCompileTimeEval()); if (allConstants) { for (var index = 0; index < formatsCompiled.Length; index++) { var keyValuePair = formatsCompiled[index]; arguments[index] = new FormatterArgumentType(index, keyValuePair.Key.Name, keyValuePair.Value); } } FormatterCache cache = null; async Promise CallFormatter( ContextObject naturalContext, ContextObject outputContext, ScopeData scopeData) { if (!allConstants) { for (var index = 0; index < formatsCompiled.Length; index++) { var formatterArgument = formatsCompiled[index]; object value; if (formatterArgument.Value is CompiledExpression cex) { value = (await cex(naturalContext, scopeData))?.Value; } else { value = formatterArgument.Value; } arguments[index] = new FormatterArgumentType(index, formatterArgument.Key.Name, value); } } if (cache == null) { cache = outputContext.PrepareFormatterCall( outputContext.Value?.GetType() ?? typeof(object), FormatterName, arguments, scopeData); } if (cache != null) { outputContext.Value = await scopeData.ParserOptions.Formatters.Execute(cache, outputContext.Value, scopeData.ParserOptions, arguments); outputContext.MakeSyntetic(); } } if (getContext == null) { return(async(contextObject, scopeData) => { var ctx = scopeData.ParserOptions.CreateContextObject("", contextObject.Value, contextObject); contextObject = contextObject.FindNextNaturalContextObject(); await CallFormatter(contextObject, ctx, scopeData); return ctx; }); } return(async(contextObject, scopeData) => { var ctx = getContext(contextObject, scopeData, this); await CallFormatter(contextObject, ctx, scopeData); return ctx; }); }