/// <summary> /// Clones the ContextObject into a new Detached object /// </summary> /// <returns></returns> public virtual ContextObject Clone() { var contextClone = new ContextObject(Options, Key, this) //note: Parent must be the original context so we can traverse up to an unmodified context { Value = Value, IsNaturalContext = false }; return(contextClone); }
/// <summary> /// ctor /// </summary> /// <param name="index">the current index of the item inside the collection</param> /// <param name="last">true if its the last item</param> /// <param name="options"></param> /// <param name="key"></param> public ContextCollection(long index, bool last, [NotNull] ParserOptions options, string key, [CanBeNull] ContextObject parent) : base(options, key, parent) { Index = index; Last = last; }
/// <summary> /// Clones the ContextObject into a new Detached object /// </summary> /// <returns></returns> public virtual ContextObject Copy() { var contextClone = new ContextObject(Options, Key, Parent) //note: Parent must be the original context so we can traverse up to an unmodified context { Value = Value, IsNaturalContext = true, AbortGeneration = AbortGeneration }; return(contextClone); }
/// <summary> /// Initializes a new instance of the <see cref="ContextObject"/> class. /// </summary> /// <param name="options">The options.</param> /// <param name="key">The key as seen in the Template</param> public ContextObject([NotNull] ParserOptions options, [NotNull] string key, [CanBeNull] ContextObject parent) { Options = options; Key = key; Parent = parent; if (Parent != null) { CancellationToken = Parent.CancellationToken; AbortGeneration = Parent.AbortGeneration; } IsNaturalContext = Parent?.IsNaturalContext ?? true; }
/// <summary> /// Clones the ContextObject into a new Detached object /// </summary> /// <returns></returns> public virtual ContextObject Clone() { var contextClone = new ContextObject(Options, Key) { CancellationToken = CancellationToken, Parent = Parent, AbortGeneration = AbortGeneration, Value = Value }; return(contextClone); }
/// <summary> /// Will walk the path by using the path seperator "." and evaluate the object at the end /// </summary> /// <param name="path"></param> /// <param name="scopeData"></param> /// <returns></returns> internal async Task <ContextObject> GetContextForPath(string path, ScopeData scopeData) { var elements = new Queue <string>(); foreach (var m in PathFinder.Matches(path).OfType <Match>()) { elements.Enqueue(m.Value); } if (elements.Any()) { //look at the first element if its an alias switch to that alias var peekPathPart = elements.Peek(); if (scopeData.Alias.TryGetValue(peekPathPart, out var alias)) { elements.Dequeue(); return(await alias.GetContextForPath(elements, scopeData)); } //check if this part of the path can be seen as an number if (Number.TryParse(peekPathPart, out var isNumber)) { elements.Dequeue(); ContextObject contextObject; if (elements.Count > 0) { var peekNextPathPart = elements.Peek(); if (Number.TryParse(peekPathPart + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + peekNextPathPart, out var floatingNumber)) { elements.Dequeue(); contextObject = new ContextObject(Options, ".", this); contextObject.Value = floatingNumber; contextObject.IsNaturalContext = IsNaturalContext; return(await contextObject.GetContextForPath(elements, scopeData)); } } contextObject = new ContextObject(Options, ".", this); contextObject.Value = isNumber; contextObject.IsNaturalContext = IsNaturalContext; return(await contextObject.GetContextForPath(elements, scopeData)); } } return(await GetContextForPath(elements, scopeData)); }
/// <inheritdoc /> protected override ContextObject HandlePathContext(Queue <KeyValuePair <string, PathType> > elements, KeyValuePair <string, PathType> path) { if (path.Value != PathType.DataPath) { return(null); } var innerContext = new ContextObject(Options, path.Key, this); object value = null; if (path.Key.Equals("$first")) { value = Index == 0; } else if (path.Key.Equals("$index")) { value = Index; } else if (path.Key.Equals("$middel")) { value = Index != 0 && !Last; } else if (path.Key.Equals("$last")) { value = Last; } else if (path.Key.Equals("$odd")) { value = Index % 2 != 0; } else if (path.Key.Equals("$even")) { value = Index % 2 == 0; } innerContext.Value = value; return(value == null ? null : innerContext); }
/// <inheritdoc /> protected override ContextObject HandlePathContext(Queue <string> elements, string path) { var innerContext = new ContextObject(Options, path); innerContext.Parent = this; object value = null; if (path.Equals("$first")) { value = Index == 0; } else if (path.Equals("$index")) { value = Index; } else if (path.Equals("$middel")) { value = Index != 0 && !Last; } else if (path.Equals("$last")) { value = Last; } else if (path.Equals("$odd")) { value = Index % 2 != 0; } else if (path.Equals("$even")) { value = Index % 2 == 0; } innerContext.Value = value; return(value == null ? null : innerContext); }
private async Task <ContextObject> GetContextForPath(Queue <string> elements, ScopeData scopeData) { var retval = this; if (elements.Any()) { var path = elements.Dequeue(); var preHandeld = HandlePathContext(elements, path); if (preHandeld != null) { return(preHandeld); } if (path.StartsWith("~")) //go the root object { var parent = Parent; var lastParent = parent; while (parent != null) { parent = parent.Parent; if (parent != null) { lastParent = parent; } } if (lastParent != null) { retval = await lastParent.GetContextForPath(elements, scopeData); } } else if (path.Equals("$recursion")) //go the root object { retval = new ContextObject(Options, path, this) { Value = scopeData.PartialDepth.Count }; } else if (path.StartsWith("..")) //go one level up { if (Parent != null) { var parentsRetVal = (await(FindNextNaturalContextObject()?.Parent?.GetContextForPath(elements, scopeData) ?? Task.FromResult((ContextObject)null))); if (parentsRetVal != null) { retval = parentsRetVal; } else { retval = await GetContextForPath(elements, scopeData); } } else { retval = await GetContextForPath(elements, scopeData); } } else { await EnsureValue(); var type = Value?.GetType(); if (path.StartsWith("?")) //enumerate ether an IDictionary, an cs object or an IEnumerable to a KeyValuePair array { //ALWAYS return the context, even if the value is null. var innerContext = new ContextObject(Options, path, this); switch (Value) { case IDictionary <string, object> dictList: innerContext.Value = dictList.Select(e => e); break; //This is a draft that i have discarded as its more likely to enumerate a single IEnumerable with #each alone //case IEnumerable ctx: // innerContext.Value = ctx.OfType<object>().Select((item, index) => new KeyValuePair<string, object>(index.ToString(), item)); // break; default: { if (Value != null) { innerContext.Value = type .GetTypeInfo() .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(e => !e.IsSpecialName && !e.GetIndexParameters().Any()) .Select(e => new KeyValuePair <string, object>(e.Name, e.GetValue(Value))); } break; } } retval = await innerContext.GetContextForPath(elements, scopeData); } //TODO: handle array accessors and maybe "special" keys. else { //ALWAYS return the context, even if the value is null. var innerContext = new ContextObject(Options, path, this); if (Options.ValueResolver?.CanResolve(type, Value, path, innerContext) == true) { innerContext.Value = Options.ValueResolver.Resolve(type, Value, path, innerContext); } else if (Value is IDictionary <string, object> ctx) { if (!ctx.TryGetValue(path, out var o)) { Options.OnUnresolvedPath(path, type); } innerContext.Value = o; } else if (Value != null) { var propertyInfo = type.GetTypeInfo().GetProperty(path); if (propertyInfo != null) { innerContext.Value = propertyInfo.GetValue(Value); } else { Options.OnUnresolvedPath(path, type); } } retval = await innerContext.GetContextForPath(elements, scopeData); } } } return(retval); }
private async Task <ContextObject> GetContextForPath( Queue <KeyValuePair <string, PathType> > elements, ScopeData scopeData) { var retval = this; if (elements.Any()) { var path = elements.Dequeue(); var preHandeld = HandlePathContext(elements, path); if (preHandeld != null) { return(preHandeld); } var type = Value?.GetType(); if (path.Value == PathType.RootSelector) //go the root object { var parent = Parent ?? this; var lastParent = parent; while (parent != null) { parent = parent.Parent; if (parent != null) { lastParent = parent; } } if (lastParent != null) { retval = await lastParent.GetContextForPath(elements, scopeData); } } else if (path.Value == PathType.ParentSelector) //go one level up { if (Parent != null) { var parentsRetVal = (await(FindNextNaturalContextObject()?.Parent?.GetContextForPath(elements, scopeData) ?? Task.FromResult((ContextObject)null))); if (parentsRetVal != null) { retval = parentsRetVal; } else { retval = await GetContextForPath(elements, scopeData); } } else { retval = await GetContextForPath(elements, scopeData); } } else if (path.Value == PathType.ObjectSelector) //enumerate ether an IDictionary, an cs object or an IEnumerable to a KeyValuePair array { await EnsureValue(); if (Value is null) { return(Options.CreateContextObject("x:null", CancellationToken, null)); } //ALWAYS return the context, even if the value is null. ContextObject innerContext = null; switch (Value) { case IDictionary <string, object> dictList: innerContext = Options.CreateContextObject(path.Key, CancellationToken, dictList.Select(e => e), this); break; default: { if (Value != null) { innerContext = Options.CreateContextObject(path.Key, CancellationToken, type .GetTypeInfo() .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(e => !e.IsSpecialName && !e.GetIndexParameters().Any()) .Select(e => new KeyValuePair <string, object>(e.Name, e.GetValue(Value))), this); } break; } } retval = await innerContext.GetContextForPath(elements, scopeData); } else if (path.Value == PathType.Number) { //check if this part of the path can be seen as an number if (Number.TryParse(path.Key, Options.CultureInfo, out var isNumber)) { var contextObject = Options.CreateContextObject(".", CancellationToken, isNumber, this); contextObject.IsNaturalContext = IsNaturalContext; return(await contextObject.GetContextForPath(elements, scopeData)); } } else if (path.Value == PathType.Boolean) { if (path.Key == "true" || path.Key == "false") { var booleanContext = Options.CreateContextObject(".", CancellationToken, path.Key == "true", this); booleanContext.IsNaturalContext = IsNaturalContext; return(await booleanContext.GetContextForPath(elements, scopeData)); } } else if (path.Value == PathType.DataPath) { if (path.Key.Equals("$recursion")) //go the root object { retval = Options.CreateContextObject(path.Key, CancellationToken, scopeData.PartialDepth.Count, this); } else { await EnsureValue(); if (Value is null) { return(Options.CreateContextObject("x:null", CancellationToken, null)); } //TODO: handle array accessors and maybe "special" keys. else { //ALWAYS return the context, even if the value is null. var innerContext = Options.CreateContextObject(path.Key, CancellationToken, null, this); if (Options.ValueResolver?.CanResolve(type, Value, path.Key, innerContext) == true) { innerContext.Value = Options.ValueResolver.Resolve(type, Value, path.Key, innerContext); } else if (Value is IDictionary <string, object> ctx) { if (!ctx.TryGetValue(path.Key, out var o)) { Options.OnUnresolvedPath(path.Key, type); } innerContext.Value = o; } else if (Value != null) { var propertyInfo = type.GetTypeInfo().GetProperty(path.Key); if (propertyInfo != null) { innerContext.Value = propertyInfo.GetValue(Value); } else { Options.OnUnresolvedPath(path.Key, type); } } retval = await innerContext.GetContextForPath(elements, scopeData); } } } } return(retval); }