/// <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); }
public void TestGetDefaultConditionExpression() { CdmCorpusDefinition corpus = TestHelper.GetLocalCorpus(testsSubpath, "TestGetDefaultConditionExpression"); corpus.Storage.Mount("local", new LocalAdapter(TestHelper.GetActualOutputFolderPath(testsSubpath, "TestGetDefaultConditionExpression"))); CdmFolderDefinition localRoot = corpus.Storage.FetchRootFolder("local"); CdmManifestDefinition manifestDefault = CreateDefaultManifest(corpus, localRoot); CdmEntityDefinition entityTestSource = CreateEntityTestSource(corpus, manifestDefault, localRoot); // projection for a non entity attribute CdmOperationCollection opColl = new CdmOperationCollection(corpus.Ctx, entityTestSource); { // add 1st FK opColl.Add(new CdmOperationReplaceAsForeignKey(corpus.Ctx)); Assert.AreEqual($" (referenceOnly || noMaxDepth || (depth > maxDepth)) ", ConditionExpression.GetDefaultConditionExpression(opColl, owner: entityTestSource)); // add 2nd FK opColl.Add(new CdmOperationReplaceAsForeignKey(corpus.Ctx)); Assert.AreEqual($" (referenceOnly || noMaxDepth || (depth > maxDepth)) ", ConditionExpression.GetDefaultConditionExpression(opColl, owner: entityTestSource)); opColl.Clear(); // add AddCount opColl.Add(new CdmOperationAddCountAttribute(corpus.Ctx)); Assert.AreEqual($" (!structured) ", ConditionExpression.GetDefaultConditionExpression(opColl, owner: entityTestSource)); // add ArrayExpansion opColl.Add(new CdmOperationArrayExpansion(corpus.Ctx)); Assert.AreEqual($" (!structured) ", ConditionExpression.GetDefaultConditionExpression(opColl, owner: entityTestSource)); opColl.Clear(); // add AddSupporting opColl.Add(new CdmOperationAddSupportingAttribute(corpus.Ctx)); Assert.AreEqual($" (true) ", ConditionExpression.GetDefaultConditionExpression(opColl, owner: entityTestSource)); } CdmEntityAttributeDefinition entityTestEntityAttribute = corpus.MakeObject <CdmEntityAttributeDefinition>(CdmObjectType.EntityAttributeDef, nameOrRef: "TestEntityAttribute", simpleNameRef: false); // projection for a non entity attribute CdmOperationCollection opCollEA = new CdmOperationCollection(corpus.Ctx, entityTestEntityAttribute); { // add 1st FK opCollEA.Add(new CdmOperationReplaceAsForeignKey(corpus.Ctx)); Assert.AreEqual($" ( (!normalized) || (cardinality.maximum <= 1) ) && (referenceOnly || noMaxDepth || (depth > maxDepth)) ", ConditionExpression.GetDefaultConditionExpression(opCollEA, owner: entityTestEntityAttribute)); // add 2nd FK opCollEA.Add(new CdmOperationReplaceAsForeignKey(corpus.Ctx)); Assert.AreEqual($" ( (!normalized) || (cardinality.maximum <= 1) ) && (referenceOnly || noMaxDepth || (depth > maxDepth)) ", ConditionExpression.GetDefaultConditionExpression(opCollEA, owner: entityTestEntityAttribute)); opCollEA.Clear(); // add AddCount opCollEA.Add(new CdmOperationAddCountAttribute(corpus.Ctx)); Assert.AreEqual($" ( (!normalized) || (cardinality.maximum <= 1) ) && (!structured) ", ConditionExpression.GetDefaultConditionExpression(opCollEA, owner: entityTestEntityAttribute)); // add ArrayExpansion opCollEA.Add(new CdmOperationArrayExpansion(corpus.Ctx)); Assert.AreEqual($" ( (!normalized) || (cardinality.maximum <= 1) ) && (!structured) ", ConditionExpression.GetDefaultConditionExpression(opCollEA, owner: entityTestEntityAttribute)); opCollEA.Clear(); // add AddSupporting opCollEA.Add(new CdmOperationAddSupportingAttribute(corpus.Ctx)); Assert.AreEqual($" ( (!normalized) || (cardinality.maximum <= 1) ) && (true) ", ConditionExpression.GetDefaultConditionExpression(opCollEA, owner: entityTestEntityAttribute)); } }