public void TestEvaluateExpression() { InputValues input = new InputValues() { maxCardinality = 1, minCardinality = 0, maxDepth = 32, nextDepth = 1, noMaxDepth = true, isArray = true, normalized = false, referenceOnly = true, structured = true }; List <Tuple <string, string> > exprAndExpectedResultList = new List <Tuple <string, string> >(); { exprAndExpectedResultList.Add(new Tuple <string, string>("(cardinality.maximum > 1) && (!referenceOnly)", "False")); exprAndExpectedResultList.Add(new Tuple <string, string>("", "False")); exprAndExpectedResultList.Add(new Tuple <string, string>(" ", "False")); exprAndExpectedResultList.Add(new Tuple <string, string>("always", "True")); exprAndExpectedResultList.Add(new Tuple <string, string>("!structured", "False")); exprAndExpectedResultList.Add(new Tuple <string, string>("referenceOnly || (depth > 5)", "True")); exprAndExpectedResultList.Add(new Tuple <string, string>("!(referenceOnly)", "False")); exprAndExpectedResultList.Add(new Tuple <string, string>("!(normalized && cardinality.maximum > 1)", "True")); exprAndExpectedResultList.Add(new Tuple <string, string>("true", "True")); exprAndExpectedResultList.Add(new Tuple <string, string>("(((true==true)))", "True")); exprAndExpectedResultList.Add(new Tuple <string, string>("!(normalized && isArray) || noMaxDepth", "False")); } foreach (var item in exprAndExpectedResultList) { ExpressionTree tree = new ExpressionTree(); Node treeTop = tree.ConstructExpressionTree(item.Item1); string expected = item.Item2; string actual = ExpressionTree.EvaluateExpressionTree(treeTop, input).ToString(); Assert.AreEqual(expected, actual); } }
/// <summary> /// A function to construct projection context and populate the resolved attribute set that ExtractResolvedAttributes method can then extract /// This function is the entry point for projection resolution. /// This function is expected to do the following 3 things: /// - Create an condition expression tree & default if appropriate /// - Create and initialize Projection Context /// - Process operations /// </summary> /// <param name="projDirective"></param> /// <param name="attrCtx"></param> /// <returns></returns> internal ProjectionContext ConstructProjectionContext(ProjectionDirective projDirective, CdmAttributeContext attrCtx) { ProjectionContext projContext = null; if (string.IsNullOrWhiteSpace(this.Condition)) { // if no condition is provided, get default condition and persist this.Condition = ConditionExpression.GetDefaultConditionExpression(this.Operations, this.Owner); } // create an expression tree based on the condition ExpressionTree tree = new ExpressionTree(); this.ConditionExpressionTreeRoot = tree.ConstructExpressionTree(this.Condition); if (this.ConditionExpressionTreeRoot == null) { Logger.Info(nameof(CdmProjection), this.Ctx, $"Optional expression missing. Implicit expression will automatically apply.", nameof(ConstructProjectionContext)); } if (attrCtx != null) { // Add projection to context tree AttributeContextParameters acpProj = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.Projection, Name = this.FetchObjectDefinitionName(), Regarding = projDirective.OwnerRef, IncludeTraits = false }; CdmAttributeContext acProj = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpProj); AttributeContextParameters acpSource = new AttributeContextParameters { under = acProj, type = CdmAttributeContextType.Source, Name = "source", Regarding = null, IncludeTraits = false }; CdmAttributeContext acSource = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpSource); if (this.Source.FetchObjectDefinition <CdmObjectDefinition>(projDirective.ResOpt).ObjectType == CdmObjectType.ProjectionDef) { // A Projection projContext = ((CdmProjection)this.Source.ExplicitReference).ConstructProjectionContext(projDirective, acSource); } else { // An Entity Reference AttributeContextParameters acpSourceProjection = new AttributeContextParameters { under = acSource, type = CdmAttributeContextType.Entity, Name = this.Source.NamedReference ?? this.Source.ExplicitReference.GetName(), Regarding = this.Source, IncludeTraits = false }; ResolvedAttributeSet ras = this.Source.FetchResolvedAttributes(projDirective.ResOpt, acpSourceProjection); // Initialize the projection context CdmCorpusContext ctx = (projDirective.Owner?.Ctx); ProjectionAttributeStateSet pasSet = null; // if polymorphic keep original source as previous state Dictionary <string, List <ProjectionAttributeState> > polySourceSet = null; if (projDirective.IsSourcePolymorphic) { polySourceSet = ProjectionResolutionCommonUtil.GetPolymorphicSourceSet(projDirective, ctx, this.Source, acpSourceProjection); } // now initialize projection attribute state pasSet = ProjectionResolutionCommonUtil.InitializeProjectionAttributeStateSet( projDirective, ctx, ras, isSourcePolymorphic: projDirective.IsSourcePolymorphic, polymorphicSet: polySourceSet); projContext = new ProjectionContext(projDirective, ras.AttributeContext) { CurrentAttributeStateSet = pasSet }; } bool isConditionValid = false; if (this.ConditionExpressionTreeRoot != null) { InputValues input = new InputValues() { noMaxDepth = projDirective.HasNoMaximumDepth, isArray = projDirective.IsArray, referenceOnly = projDirective.IsReferenceOnly, normalized = projDirective.IsNormalized, structured = projDirective.IsStructured, nextDepth = ++projDirective.CurrentDepth, maxDepth = projDirective.MaximumDepth, minCardinality = projDirective.Cardinality?._MinimumNumber, maxCardinality = projDirective.Cardinality?._MaximumNumber }; isConditionValid = ExpressionTree.EvaluateExpressionTree(this.ConditionExpressionTreeRoot, input); } if (isConditionValid && this.Operations != null && this.Operations.Count > 0) { // Just in case new operations were added programmatically, reindex operations for (int i = 0; i < this.Operations.Count; i++) { this.Operations[i].Index = i + 1; } // Operation AttributeContextParameters acpGenAttrSet = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.GeneratedSet, Name = "_generatedAttributeSet" }; CdmAttributeContext acGenAttrSet = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpGenAttrSet); AttributeContextParameters acpGenAttrRound0 = new AttributeContextParameters { under = acGenAttrSet, type = CdmAttributeContextType.GeneratedRound, Name = "_generatedAttributeRound0" }; CdmAttributeContext acGenAttrRound0 = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpGenAttrRound0); // Start with an empty list for each projection ProjectionAttributeStateSet pasOperations = new ProjectionAttributeStateSet(projContext.CurrentAttributeStateSet.Ctx); foreach (CdmOperationBase operation in this.Operations) { // Evaluate projections and apply to empty state ProjectionAttributeStateSet newPasOperations = operation.AppendProjectionAttributeState(projContext, pasOperations, acGenAttrRound0); // If the operations fails or it is not implemented the projection cannot be evaluated so keep previous valid state. if (newPasOperations != null) { pasOperations = newPasOperations; } } // Finally update the current state to the projection context projContext.CurrentAttributeStateSet = pasOperations; } else { // Pass Through - no operations to process } } return(projContext); }