/// <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)); }
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); }