internal ContextObject LoopContextTraversable(Traversable elements, ScopeData scopeData, IMorestachioExpression morestachioExpression) { ContextObject context = this; while (elements != null && elements.HasValue) { context = context.GetContextForPathInternal(elements, scopeData, morestachioExpression); elements = elements.Next(); } return(context); }
private async ContextObjectPromise GetContextForPathInternal( Traversable elements, ScopeData scopeData, IMorestachioExpression morestachioExpression) { var retval = this; if (elements == null) { return(retval); } var preHandeld = HandlePathContext(elements, elements.Current, morestachioExpression, scopeData); if (preHandeld != null) { return(preHandeld); } var type = Value?.GetType(); if (elements.Current.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.GetContextForPathInternal(elements.Next(), scopeData, morestachioExpression); } } else if (elements.Current.Value == PathType.ParentSelector) //go one level up { if (Parent != null) { var parent = FindNextNaturalContextObject(); ContextObject parentsRetVal = null; if (parent != null && parent.Parent != null) { parentsRetVal = await parent.Parent .GetContextForPathInternal(elements.Next(), scopeData, morestachioExpression); } if (parentsRetVal != null) { retval = parentsRetVal; } else { retval = await GetContextForPathInternal(elements.Next(), scopeData, morestachioExpression); } } else { retval = await GetContextForPathInternal(elements.Next(), scopeData, morestachioExpression); } } else if (elements.Current.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(elements.Current.Key, CancellationToken, dictList.Select(e => e), this); break; default: { if (Value != null) { innerContext = Options.CreateContextObject(elements.Current.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.GetContextForPathInternal(elements.Next(), scopeData, morestachioExpression); } else if (elements.Current.Value == PathType.Boolean) { if (elements.Current.Key == "true" || elements.Current.Key == "false") { var booleanContext = Options.CreateContextObject(".", CancellationToken, elements.Current.Key == "true", this); booleanContext.IsNaturalContext = IsNaturalContext; return(await booleanContext.GetContextForPathInternal(elements.Next(), scopeData, morestachioExpression)); } } else if (elements.Current.Value == PathType.Null) { return(Options.CreateContextObject("x:null", CancellationToken, null, null)); } else if (elements.Current.Value == PathType.DataPath) { //await EnsureValue(); if (Value is null) { return(Options.CreateContextObject("x:null", CancellationToken, null)); } //TODO: handle array accessors and maybe "special" keys. //ALWAYS return the context, even if the value is null. var innerContext = Options.CreateContextObject(elements.Current.Key, CancellationToken, null, this); if (Options.ValueResolver?.CanResolve(type, Value, elements.Current.Key, innerContext) == true) { innerContext._value = Options.ValueResolver.Resolve(type, Value, elements.Current.Key, innerContext); } else if (!Options.HandleDictionaryAsObject && Value is IDictionary <string, object> ctx) { if (!ctx.TryGetValue(elements.Current.Key, out var o)) { Options.OnUnresolvedPath(new InvalidPathEventArgs(this, morestachioExpression, elements.Current.Key, Value?.GetType())); } innerContext._value = o; } else if (Value is IMorestachioPropertyResolver cResolver) { if (!cResolver.TryGetValue(elements.Current.Key, out var o)) { Options.OnUnresolvedPath(new InvalidPathEventArgs(this, morestachioExpression, elements.Current.Key, Value?.GetType())); } innerContext._value = o; } else if (Value != null) { if (Value is ICustomTypeDescriptor descriptor) { var propertyDescriptor = descriptor.GetProperties().Find(elements.Current.Key, false); if (propertyDescriptor != null) { innerContext._value = propertyDescriptor.GetValue(Value); } else { Options.OnUnresolvedPath(new InvalidPathEventArgs(this, morestachioExpression, elements.Current.Key, Value?.GetType())); } } else { var propertyInfo = type.GetTypeInfo().GetProperty(elements.Current.Key); if (propertyInfo != null) { innerContext._value = propertyInfo.GetValue(Value); } else { Options.OnUnresolvedPath(new InvalidPathEventArgs(this, morestachioExpression, elements.Current.Key, Value?.GetType())); } } } retval = await innerContext.GetContextForPathInternal(elements.Next(), scopeData, morestachioExpression); } return(retval); }