/// <summary> /// Evaluate processors on given value /// </summary> /// <param name="valueToProcess"></param> /// <param name="configurationWithEventualProcessors"></param> /// <param name="fusionPath"></param> /// <param name="contextObject"></param> /// <returns></returns> protected object EvaluateProcessors(object valueToProcess, FusionAst configurationWithEventualProcessors, string fusionPath, AbstractFusionObject contextObject) { if (configurationWithEventualProcessors["__meta"]["processors"] != null) { var processorConfiguration = (FusionAst)configurationWithEventualProcessors["__meta"]["processors"].Clone(); // TODO sorting var sortedKeys = processorConfiguration.Children.Keys.Select(x => x.ToString()); foreach (var key in sortedKeys) { var processorPath = fusionPath + "/__meta/process/" + key; if (!EvaluateIfCondition(processorConfiguration[key], processorPath, contextObject)) { continue; } if (processorConfiguration[key].Children.ContainsKey("expression")) { processorPath += "/expression"; } PushContext("value", valueToProcess); var result = EvaluateInternal(processorPath, FailureBehavior.Exception, contextObject); if (GetLastEvaluationStatus() != EvaluationStatus.Skipped) { valueToProcess = result; } PopContext(); } } return(valueToProcess); }
public void Merge(FusionAst other) { if (Value == null) { Value = other.Value; } if (EelExpression == null || EelExpression.Length == 0) { EelExpression = other.EelExpression; } if (ObjectType == null || ObjectType.Length == 0) { ObjectType = other.ObjectType; } foreach (var keyValue in other.Children) { if (Children.ContainsKey(keyValue.Key)) { Children[keyValue.Key].Merge(keyValue.Value); } else { Children.Add(keyValue.Key, keyValue.Value); } } }
private FusionAst MergePrototypesWithConfigurationForPathSegment(FusionAst configuration, Dictionary <string, FusionAst> currentPrototypeDefinitions) { var currentPathSegmentType = configuration.ObjectType; if (currentPrototypeDefinitions.ContainsKey(currentPathSegmentType)) { var prototypeMergingOrder = new List <string>() { currentPathSegmentType }; if (currentPrototypeDefinitions[currentPathSegmentType]["__prototypeChain"].Value != null) { prototypeMergingOrder.AddRange((string[])currentPrototypeDefinitions[currentPathSegmentType]["__prototypeChain"].Value); } var currentPrototypeWithInheritanceTakenIntoAccount = new FusionAst(); foreach (var prototypeName in prototypeMergingOrder) { if (!currentPrototypeDefinitions.ContainsKey(prototypeName)) { throw new FusionException($"The Fusion object `{prototypeName}` which you tried to inherit from does not exist. Maybe you have a typo on the right hand side of your inheritance statement for {currentPathSegmentType}"); } currentPrototypeWithInheritanceTakenIntoAccount.Merge(currentPrototypeDefinitions[prototypeName]); } configuration.Merge(currentPrototypeWithInheritanceTakenIntoAccount); } return(configuration); }
/// <summary> /// Possibly prepares a new "@apply" context for the current fusionPath and pushes it to the stack. /// Returns true to express that new properties were puwhed and have to be popped during finalizePathEvaluation. /// /// Since "@apply" are not inherited every call of this method leads to a completely new "@apply" context, /// which is null by default /// </summary> /// <param name="fusionPath"></param> /// <param name="fusionConfiguration"></param> /// <returns></returns> private bool PrepareApplyValuesForFusionPath(string fusionPath, FusionAst fusionConfiguration) { var spreadValues = EvaluateApplyValues(fusionConfiguration, fusionPath); PushApplyValues(spreadValues); return(true); }
protected void Initialize() { currentLineNumber = 0; currentBlockCommentState = false; currentObjectPathStack = new Stack <string>(); objectTree = new FusionAst(); }
public void SetValue(string[] objectPathArray, object value) { if (objectPathArray.Length == 0) { this.Value = value; } else if (objectPathArray.Length == 1) { var currentKey = objectPathArray.Last(); objectPathArray = objectPathArray.Take(objectPathArray.Length - 1).ToArray(); if (Children.ContainsKey(currentKey) && value == null) { Children.Remove(currentKey); } else if (Children.ContainsKey(currentKey)) { if (value is FusionAst) { var subTree = value as FusionAst; Children[currentKey].Value = subTree.Value; Children[currentKey].EelExpression = subTree.EelExpression; Children[currentKey].ObjectType = subTree.ObjectType; foreach (var keyValue in subTree.Children) { Children[currentKey].Children.Add(keyValue.Key, keyValue.Value); } } } else { FusionAst newValue; if (value is FusionAst) { newValue = value as FusionAst; } else { newValue = new FusionAst(this) { Value = value }; } Children.Add(currentKey, newValue); } } else { if (!Children.ContainsKey(objectPathArray[0])) { Children.Add(objectPathArray[0], new FusionAst(this)); } Children[objectPathArray[0]].SetValue(objectPathArray.Skip(1).ToArray(), value); } }
private FusionAst MatchCurrentPathPart(string pathPart, FusionAst previousConfiguration, Dictionary <string, FusionAst> currentPrototypeDefinitions) { Match matches = Regex.Match(pathPart, @"^([^<]*)(<(.*?)>)?$"); if (!matches.Success) { throw new FusionException($"Path part `{pathPart}` not well-formed"); } var currentPathSegment = matches.Groups[1].Value; var configuration = new FusionAst(); if (previousConfiguration.Children.ContainsKey(currentPathSegment)) { configuration = (FusionAst)previousConfiguration[currentPathSegment].Clone(); } if (configuration["__prototypes"] != null) { currentPrototypeDefinitions = new Dictionary <string, FusionAst>(currentPrototypeDefinitions); foreach (var kv in configuration["__prototypes"].Children) { if (!currentPrototypeDefinitions.ContainsKey(kv.Key)) { currentPrototypeDefinitions.Add(kv.Key, kv.Value); } else { var clone = (FusionAst)currentPrototypeDefinitions[kv.Key].Clone(); clone.Merge(kv.Value); currentPrototypeDefinitions[kv.Key] = clone; } } } string currentPathSegmentType = null; if (configuration.ObjectType.Length > 0) { currentPathSegmentType = configuration.ObjectType; } if (matches.Groups[3].Value.Length > 0) { currentPathSegmentType = matches.Groups[3].Value; } if (currentPathSegmentType != null) { configuration.ObjectType = currentPathSegmentType; configuration = MergePrototypesWithConfigurationForPathSegment(configuration, currentPrototypeDefinitions); } if (!HasExpressionOrValue(configuration) && configuration.ObjectType.Length == 0 && configuration["__meta"]["class"] == null && configuration["__meta"]["process"] == null) { configuration.Value = ""; } return(configuration); }
/// <summary> /// Internal evaluation if given configuration is renderable /// </summary> /// <param name="fusionConfiguration"></param> /// <returns></returns> protected bool CanRenderWithConfiguration(FusionAst fusionConfiguration) { if (HasExpressionOrValue(fusionConfiguration)) { return(true); } if (fusionConfiguration["__meta"]["class"].Value != null && fusionConfiguration.ObjectType.Length > 0) { return(true); } return(false); }
private object EvaluateExpressionOrValueInternal(string fusionPath, FusionAst fusionConfiguration, Cache.EvaluationContext cacheContext, AbstractFusionObject contextObject) { if (!EvaluateIfCondition(fusionConfiguration, fusionPath, contextObject)) { FinalizePathEvaluation(cacheContext); return(null); } var evaluatedExpression = EvaluateEelExpressionOrSimpleValueWithProcessor(fusionPath, fusionConfiguration, contextObject); FinalizePathEvaluation(cacheContext); return(evaluatedExpression); }
public object Clone() { var clone = new FusionAst(); clone.Value = Value; clone.EelExpression = EelExpression; clone.ObjectType = ObjectType; foreach (var child in Children) { clone.Children.Add(child.Key, (FusionAst)child.Value.Clone()); } return(clone); }
private void ThrowExceptionForUnrenderablePathIfNeeded(string fusionPath, FusionAst fusionConfiguration, FailureBehavior behaviorIfPathNotFound) { // System.Console.WriteLine("Could not render at path " + fusionPath); if (!string.IsNullOrEmpty(fusionConfiguration.ObjectType)) { var objectType = fusionConfiguration.ObjectType; throw new FusionException($"The fusion object at path \"{fusionPath}\" could not be rendered:\n\t\tThe fusion object `{objectType}` is not completely defined (missing property `@class`). Most likely you didn't inherit from a basic object."); } if (behaviorIfPathNotFound == FailureBehavior.Exception) { throw new FusionException($"No fusion object found in path \"{fusionPath}\"\n\t\tPlease make sure to define one in your Fusion configuration."); } }
protected AbstractFusionObject InstantiateFusionObject(string fusionPath, FusionAst fusionConfiguration) { var fusionObjectType = fusionConfiguration.ObjectType; var fusionObjectClassName = (string)fusionConfiguration["__meta"]["class"].Value; if (fusionObjectClassName != null) { fusionObjectClassName = fusionObjectClassName.Replace("\\", "."); } if (!Regex.IsMatch(fusionPath, "<[^>]*>$")) { fusionPath += $"<{fusionObjectType}>"; } Type fusionObjectClassType; try { fusionObjectClassType = Type.GetType(fusionObjectClassName); } catch (Exception) { throw new FusionException($"The implementation class `{fusionObjectClassName}` defined for Fusion object of type `{fusionObjectType}` does not exist. Maybe a typo in the @class property"); } AbstractFusionObject fusionObject; try { fusionObject = (AbstractFusionObject)Activator.CreateInstance(fusionObjectClassType, new object[] { this, fusionPath, fusionObjectType }); } catch (Exception) { throw new FusionException($"Could not invoke fusion object implementation class `{fusionObjectClassName}` defined for Fusion object of type `{fusionObjectType}`"); } if (!(fusionObject is AbstractFusionObject)) { throw new FusionException($"Fusion object implementation class `{fusionObjectClassName}` defined for Fusion object of type `{fusionObjectType}` does not extends AbstractFusionObject"); } if (IsArrayFusionObject(fusionObject)) { if (fusionConfiguration["__meta"]["ignoreProperties"] != null) { var evaluatedIgnores = Evaluate(fusionPath + "/__meta/ignoreProperties", fusionObject); ((AbstractArrayFusionObject)fusionObject).SetIgnoreProperties(evaluatedIgnores is string[] ? (string[])evaluatedIgnores : new string[] { }); } SetPropertiesOnFusionObject((AbstractArrayFusionObject)fusionObject, fusionConfiguration); } return(fusionObject); }
/// <summary> /// Evaluate eventually existing meta "@if" conditionals inside the given configuration and path /// </summary> /// <param name="configurationWithEventualIf"></param> /// <param name="configurationPath"></param> /// <param name="contextObject"></param> /// <returns></returns> protected Boolean EvaluateIfCondition(FusionAst configurationWithEventualIf, string configurationPath, AbstractFusionObject contextObject = null) { if (configurationWithEventualIf["__meta"]["if"] != null) { foreach (var child in configurationWithEventualIf["__meta"]["if"].Children) { var conditionValue = EvaluateInternal(configurationPath + "/__meta/if/" + child.Key, FailureBehavior.Exception, contextObject); if (!(bool)conditionValue) { return(false); } } } return(true); }
public Runtime(FusionAst configuration, RuntimeConfiguration settings = null) { this.configuration = configuration; var emptyContext = new Dictionary <string, object>(); contextStack = new Stack <Dictionary <string, object> >() { }; contextStack.Push(emptyContext); var emptyApplyValues = new Dictionary <string, KeyValuePair <string, object> >(); applyValueStack = new Stack <Dictionary <string, KeyValuePair <string, object> > >(); applyValueStack.Push(emptyApplyValues); this.settings = settings ?? new RuntimeConfiguration(); exceptionHandlerFactory = new ExceptionHandlerFactory(); this.runtimeContentCache = new Cache.RuntimeContentCache(this); }
protected FusionAst GetConfigurationForPath(string fusionPath) { var pathParts = fusionPath.Split('/'); var configuration = this.configuration; var pathUntilNow = ""; Dictionary <string, FusionAst> currentPrototypeDefinitions = new Dictionary <string, FusionAst>(); if (configuration["__prototypes"] != null) { currentPrototypeDefinitions = ((FusionAst)configuration["__prototypes"]).Children; } foreach (var pathPart in pathParts) { pathUntilNow += "/" + pathPart; // cache configuration = MatchCurrentPathPart(pathPart, configuration, currentPrototypeDefinitions); } return(configuration); }
public FusionAst(FusionAst parent) { this.Children = new Dictionary <string, FusionAst>(); this.Parent = parent; }
/// <summary> /// Evaluate "@apply" for the given fusion key. /// /// If apply-definitions are found they are evaluated and the returned keys are combined. /// The result is returnd as dict with the following structure: /// /// <pre> /// { /// { "fuisonPath/key_1", { Key: "key_1", Value: "evaluated value 1"} }, /// { "fuisonPath/key_2", { Key: "key_2", Value: "evaluated value 2"} }, /// } /// </pre> /// </summary> /// <param name="configurationWithEventualProperties"></param> /// <param name="fusionPath"></param> /// <returns></returns> private Dictionary <string, KeyValuePair <string, object> > EvaluateApplyValues(FusionAst configurationWithEventualProperties, string fusionPath) { var result = new Dictionary <string, KeyValuePair <string, object> >(); if (!configurationWithEventualProperties["__meta"]["apply"].Equals(null)) { var fusionObjectType = configurationWithEventualProperties.ObjectType; if (!Regex.IsMatch(fusionPath, @"<[^>]*>$")) { fusionPath += $"<{fusionObjectType}>"; } var propertiesConfiguration = configurationWithEventualProperties["__meta"]["apply"]; // TODO sort var sortedKeys = propertiesConfiguration.Children.Keys.Select(x => x.ToString()); foreach (var key in sortedKeys) { if (key[0] == '_' && key[1] == '_' && Parser.ReservedKeys.Contains(key)) { continue; } var singleAppyPath = fusionPath + "/__meta/apply/" + key; if (!EvaluateIfCondition(propertiesConfiguration[key], singleAppyPath)) { continue; } if (propertiesConfiguration.Children.ContainsKey("expression")) { singleAppyPath += "/expression"; } var singleApplyValues = EvaluateInternal(singleAppyPath, FailureBehavior.Exception); if (GetLastEvaluationStatus() != EvaluationStatus.Skipped && singleApplyValues is System.Collections.IDictionary) { foreach (KeyValuePair <string, object> kvp in (System.Collections.IDictionary)singleApplyValues) { if (kvp.Key[0] == '_' && kvp.Key[1] == '_' && Parser.ReservedKeys.Contains(kvp.Key)) { continue; } result[fusionPath + '/' + kvp.Key] = kvp; } } } } return(result); }
protected void SetPropertiesOnFusionObject(AbstractArrayFusionObject fusionObject, FusionAst fusionConfiguration) { var propertiesProperty = fusionObject .GetType() .GetField("properties", BindingFlags.Instance | BindingFlags.NonPublic); if (propertiesProperty != null) { propertiesProperty.SetValue( fusionObject, fusionConfiguration.Children.Keys .Select(k => k.ToString()) .Where(k => !Parser.ReservedKeys.Contains(k)) .ToArray() ); } else { System.Console.WriteLine("no properties property"); System.Console.WriteLine(fusionObject.GetType()); System.Console.WriteLine(fusionObject.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic).Length); } // handle apply }
/// <summary> /// Checks if the current fusionAst hold an Eel expression or a simple value. /// </summary> /// <param name="fusionConfiguration"></param> /// <returns></returns> private bool HasExpressionOrValue(FusionAst fusionConfiguration) { return(!string.IsNullOrEmpty(fusionConfiguration.EelExpression) || fusionConfiguration.Value != null); }
private object EvaluateEelExpressionOrSimpleValueWithProcessor(string fusionPath, FusionAst valueConfiguration, AbstractFusionObject contextObject) { object evaluatedValue; if (valueConfiguration.EelExpression.Length > 0) { evaluatedValue = EvaluateEelExpression(valueConfiguration.EelExpression, contextObject); } else { evaluatedValue = valueConfiguration.Value; } evaluatedValue = EvaluateProcessors(evaluatedValue, valueConfiguration, fusionPath, contextObject); return(evaluatedValue); }
private object EvaluateObjectOrRetrieveFromCache(AbstractFusionObject fusionObject, string fusionPath, FusionAst fusionConfiguration, Cache.EvaluationContext cacheContext) { object output = null; var evaluateObject = true; var evaluationStatus = EvaluationStatus.Skipped; if (runtimeContentCache.preEvaluate(cacheContext, fusionObject, out object cachedResult)) { return(cachedResult); } if (!EvaluateIfCondition(fusionConfiguration, fusionPath, fusionObject)) { evaluateObject = false; } if (evaluateObject) { output = fusionObject.Evaluate(); evaluationStatus = EvaluationStatus.Executed; } lastEvaluationStatus = evaluationStatus; if (evaluateObject) { output = EvaluateProcessors(output, fusionConfiguration, fusionPath, fusionObject); } // TODO // runtimeContentCache.postProcess(cacheContext, fusionObject, output); return(output); }
/// <summary> /// Possibly prepares a new context for the current FusionObject and cache context and pushes it to the stack. /// Returns if a new context was pushed to the stack or not. /// </summary> /// <param name="fusionObject"></param> /// <param name="fusionPath"></param> /// <param name="fusionConfiguration"></param> /// <param name="cacheContext"></param> /// <returns></returns> private bool PrepareContextForFusionObject(AbstractFusionObject fusionObject, string fusionPath, FusionAst fusionConfiguration, object cacheContext) { var contextArray = GetCurrentContext(); var newContextArray = new Dictionary <string, object>(contextArray); if (fusionConfiguration["__meta"]["context"] != null) { foreach (var context in fusionConfiguration["__meta"]["context"].Children) { var contextValue = EvaluateInternal(fusionPath + "/__meta/context/" + context.Key, FailureBehavior.Exception, fusionObject); newContextArray.Add(context.Key, contextValue); } PushContextArray(newContextArray); return(true); } return(false); }