/// <summary> /// Projections require a new resolved attribute to be created multiple times /// This function allows us to create new resolved attributes based on a input attribute /// </summary> /// <param name="projCtx"></param> /// <param name="attrCtxUnder"></param> /// <param name="targetAttr"></param> /// <param name="overrideDefaultName"></param> /// <param name="addedSimpleRefTraits"></param> /// <returns></returns> internal static ResolvedAttribute CreateNewResolvedAttribute( ProjectionContext projCtx, CdmAttributeContext attrCtxUnder, CdmAttribute targetAttr, string overrideDefaultName = null, List <string> addedSimpleRefTraits = null) { targetAttr = targetAttr.Copy() as CdmAttribute; ResolvedAttribute newResAttr = new ResolvedAttribute( projCtx.ProjectionDirective.ResOpt, targetAttr, !string.IsNullOrWhiteSpace(overrideDefaultName) ? overrideDefaultName : targetAttr.GetName(), attrCtxUnder); targetAttr.InDocument = projCtx.ProjectionDirective.Owner.InDocument; if (addedSimpleRefTraits != null) { foreach (string trait in addedSimpleRefTraits) { if (targetAttr.AppliedTraits.Item(trait) == null) { targetAttr.AppliedTraits.Add(trait, true); } } } ResolvedTraitSet resTraitSet = targetAttr.FetchResolvedTraits(projCtx.ProjectionDirective.ResOpt); // Create deep a copy of traits to avoid conflicts in case of parameters if (resTraitSet != null) { newResAttr.ResolvedTraits = resTraitSet.DeepCopy(); } return(newResAttr); }
/// <inheritdoc /> internal override ProjectionAttributeStateSet AppendProjectionAttributeState( ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create new attribute context for the operation AttributeContextParameters attrCtxOpFKParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationReplaceAsForeignKey, Name = $"operation/index{Index}/operationReplaceAsForeignKey" }; CdmAttributeContext attrCtxOpFK = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpFKParam); // Create new attribute context for the AddedAttributeIdentity AttributeContextParameters attrCtxFKParam = new AttributeContextParameters { under = attrCtxOpFK, type = CdmAttributeContextType.AddedAttributeIdentity, Name = $"_foreignKey" }; CdmAttributeContext attrCtxFK = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxFKParam); // get the added attribute and applied trait CdmTypeAttributeDefinition subFK = this.ReplaceWith; List <string> addTrait = new List <string>() { "is.linkedEntity.identifier" }; // Create new resolved attribute, set the new attribute as target, and apply "is.linkedEntity.identifier" trait ResolvedAttribute resAttrNewFK = CreateNewResolvedAttribute(projCtx, attrCtxFK, subFK, addedSimpleRefTraits: addTrait); ProjectionAttributeStateSet outputFromOpPasSet = CreateNewProjectionAttributeStateSet(projCtx, projOutputSet, resAttrNewFK, this.Reference); return(outputFromOpPasSet); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { // find and cache the complete set of attributes // attributes definitions originate from and then get modified by subsequent re-defintions from (in this order): // the datatype used as an attribute, traits applied to that datatype, // the purpose of the attribute, dynamic traits applied to the attribute. ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); rasb.ResolvedAttributeSet.AttributeContext = under; // add this attribute to the set // make a new one and apply dynamic traits ResolvedAttribute newAtt = new ResolvedAttribute(resOpt, this, this.Name, under as CdmAttributeContext); rasb.OwnOne(newAtt); ResolvedTraitSet rts = this.FetchResolvedTraits(resOpt); // this context object holds all of the info about what needs to happen to resolve these attribute // make a copy and add defaults if missing CdmAttributeResolutionGuidance resGuideWithDefault; if (this.ResolutionGuidance != null) resGuideWithDefault = (CdmAttributeResolutionGuidance)this.ResolutionGuidance.Copy(resOpt); else resGuideWithDefault = new CdmAttributeResolutionGuidance(this.Ctx); // renameFormat is not currently supported for type attributes resGuideWithDefault.renameFormat = null; resGuideWithDefault.UpdateAttributeDefaults(null); AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuideWithDefault, rts); // from the traits of the datatype, purpose and applied here, see if new attributes get generated rasb.ApplyTraits(arc); rasb.GenerateApplierAttributes(arc, false); // false = don't apply these traits to added things // this may have added symbols to the dependencies, so merge them resOpt.SymbolRefSet.Merge(arc.ResOpt.SymbolRefSet); return rasb; }
/// <inheritdoc /> internal override ProjectionAttributeStateSet AppendProjectionAttributeState( ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create a new attribute context for the operation AttributeContextParameters attrCtxOpCombineAttrsParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationCombineAttributes, Name = $"operation/index{Index}/operationCombineAttributes" }; CdmAttributeContext attrCtxOpCombineAttrs = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpCombineAttrsParam); // Initialize a projection attribute context tree builder with the created attribute context for the operation ProjectionAttributeContextTreeBuilder attrCtxTreeBuilder = new ProjectionAttributeContextTreeBuilder(attrCtxOpCombineAttrs); // Get all the leaf level PAS nodes from the tree for each selected attribute and cache to a dictionary Dictionary <string, List <ProjectionAttributeState> > leafLevelCombineAttributeNames = new Dictionary <string, List <ProjectionAttributeState> >(); // Also, create a single list of leaf level PAS List <ProjectionAttributeState> leafLevelMergePASList = new List <ProjectionAttributeState>(); foreach (string select in this.Select) { List <ProjectionAttributeState> leafLevelListForCurrentSelect = ProjectionResolutionCommonUtil.GetLeafList(projCtx, select); if (leafLevelListForCurrentSelect != null && leafLevelListForCurrentSelect.Count > 0 && !leafLevelCombineAttributeNames.ContainsKey(select)) { leafLevelCombineAttributeNames.Add(select, leafLevelListForCurrentSelect); leafLevelMergePASList.AddRange(leafLevelListForCurrentSelect); } } // Create a List of top-level PAS objects that will be get merged based on the selected attributes List <ProjectionAttributeState> pasMergeList = new List <ProjectionAttributeState>(); // Run through the top-level PAS objects foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States) { if (leafLevelCombineAttributeNames.ContainsKey(currentPAS.CurrentResolvedAttribute.ResolvedName)) { // Attribute to Merge if (!pasMergeList.Contains(currentPAS)) { pasMergeList.Add(currentPAS); } } else { // Attribute to Pass Through // Create a projection attribute state for the non-selected / pass-through attribute by creating a copy of the current state // Copy() sets the current state as the previous state for the new one ProjectionAttributeState newPAS = currentPAS.Copy(); projOutputSet.Add(newPAS); } } if (pasMergeList.Count > 0) { CdmTypeAttributeDefinition mergeIntoAttribute = this.MergeInto as CdmTypeAttributeDefinition; // the merged attribute needs one new place to live, so here it is AttributeContextParameters mergedAttrCtxParam = new AttributeContextParameters { under = attrCtxOpCombineAttrs, type = CdmAttributeContextType.AttributeDefinition, Name = mergeIntoAttribute.GetName() }; CdmAttributeContext mergedAttrCtx = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, mergedAttrCtxParam); // Create new resolved attribute, set the new attribute as target ResolvedAttribute raNewMergeInto = CreateNewResolvedAttribute(projCtx, mergedAttrCtx, mergeIntoAttribute, null); // Create new output projection attribute state set ProjectionAttributeState newMergeIntoPAS = new ProjectionAttributeState(projOutputSet.Ctx) { CurrentResolvedAttribute = raNewMergeInto, PreviousStateList = pasMergeList }; // Create the attribute context parameters and just store it in the builder for now // We will create the attribute contexts at the end foreach (string select in leafLevelCombineAttributeNames.Keys) { if (leafLevelCombineAttributeNames.ContainsKey(select) && leafLevelCombineAttributeNames[select] != null && leafLevelCombineAttributeNames[select].Count > 0) { foreach (ProjectionAttributeState leafLevelForSelect in leafLevelCombineAttributeNames[select]) { attrCtxTreeBuilder.CreateAndStoreAttributeContextParameters(select, leafLevelForSelect, newMergeIntoPAS.CurrentResolvedAttribute, CdmAttributeContextType.AttributeDefinition, leafLevelForSelect.CurrentResolvedAttribute.AttCtx, // lineage is the source att newMergeIntoPAS.CurrentResolvedAttribute.AttCtx); // merge into points back here } } } projOutputSet.Add(newMergeIntoPAS); } // Create all the attribute contexts and construct the tree attrCtxTreeBuilder.ConstructAttributeContextTree(projCtx); return(projOutputSet); }
/// <summary> /// Creates the attribute context parameters for the searchFor, found, and action nodes and then stores them in different maps. /// The maps are used when constructing the actual attribute context tree. /// </summary> /// <param name="searchFor">The "search for" string</param> /// <param name="found">The projection attribute state that contains the "found" attribute</param> /// <param name="resAttrFromAction">The resolved attribute that resulted from the action</param> /// <param name="attrCtxType">The attribute context type to give the "action" attribute context parameter</param> internal void CreateAndStoreAttributeContextParameters(string searchFor, ProjectionAttributeState found, ResolvedAttribute resAttrFromAction, CdmAttributeContextType attrCtxType) { // searchFor is null when we have to construct attribute contexts for the excluded attributes in Include or the included attributes in Exclude, // as these attributes weren't searched for with a searchFor name. // If searchFor is null, just set it to have the same name as found so that it'll collapse in the final tree. if (searchFor == null) { searchFor = found.CurrentResolvedAttribute.ResolvedName; } // Create the attribute context parameter for the searchFor node and store it in the map as [searchFor name]:[attribute context parameter] AttributeContextParameters searchForAttrCtxParam = null; if (!searchForToSearchForAttrCtxParam.ContainsKey(searchFor)) { searchForAttrCtxParam = new AttributeContextParameters { under = root, type = CdmAttributeContextType.AttributeDefinition, Name = searchFor }; searchForToSearchForAttrCtxParam.Add(searchFor, searchForAttrCtxParam); } else { searchForToSearchForAttrCtxParam.TryGetValue(searchFor, out searchForAttrCtxParam); } // Create the attribute context parameter for the found node AttributeContextParameters foundAttrCtxParam = new AttributeContextParameters { under = root, // Set this to be under the root for now, as we may end up collapsing this node type = CdmAttributeContextType.AttributeDefinition, Name = $"{found.CurrentResolvedAttribute.ResolvedName}{(found.Ordinal != null ? "@" + found.Ordinal : "")}" }; // Store this in the map as [searchFor attribute context parameter]:[found attribute context parameters] // We store it this way so that we can create the found nodes under their corresponding searchFor nodes. if (!searchForAttrCtxParamToFoundAttrCtxParam.ContainsKey(searchForAttrCtxParam)) { searchForAttrCtxParamToFoundAttrCtxParam.Add(searchForAttrCtxParam, new List <AttributeContextParameters> { foundAttrCtxParam }); } else { List <AttributeContextParameters> foundAttrCtxParams = null; searchForAttrCtxParamToFoundAttrCtxParam.TryGetValue(searchForAttrCtxParam, out foundAttrCtxParams); foundAttrCtxParams.Add(foundAttrCtxParam); searchForAttrCtxParamToFoundAttrCtxParam[searchForAttrCtxParam] = foundAttrCtxParams; } // Create the attribute context parameter for the action node AttributeContextParameters actionAttrCtxParam = new AttributeContextParameters { under = root, // Set this to be under the root for now, as we may end up collapsing this node type = attrCtxType, // This type will be updated once we implement the new attribute context types Name = resAttrFromAction.ResolvedName }; // Store this in the map as [found attribute context parameter]:[action attribute context parameter] // We store it this way so that we can create the action nodes under their corresponding found nodes. foundAttrCtxParamToActionAttrCtxParam[foundAttrCtxParam] = actionAttrCtxParam; // Store the action attribute context parameter with the resolved attribute resulting out of the action. // This is so that we can point the action attribute context to the correct resolved attribute once the attribute context is created. actionAttrCtxParamToResAttr[actionAttrCtxParam] = resAttrFromAction; }
/// <inheritdoc /> internal override ProjectionAttributeStateSet AppendProjectionAttributeState( ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create a new attribute context for the operation AttributeContextParameters attrCtxOpArrayExpansionParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationArrayExpansion, Name = $"operation/index{Index}/operationArrayExpansion" }; CdmAttributeContext attrCtxOpArrayExpansion = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpArrayExpansionParam); // Expansion steps start at round 0 int round = 0; List <ProjectionAttributeState> projAttrStatesFromRounds = new List <ProjectionAttributeState>(); // Ordinal validation if (this.StartOrdinal > this.EndOrdinal) { Logger.Warning(TAG, this.Ctx, $"startOrdinal {this.StartOrdinal} should not be greater than endOrdinal {this.EndOrdinal}", nameof(AppendProjectionAttributeState)); } else { // Ordinals should start at startOrdinal or 0, whichever is larger. int startingOrdinal = Math.Max(0, (int)this.StartOrdinal); // Ordinals should end at endOrdinal or the maximum ordinal allowed (set in resolve options), whichever is smaller. if (this.EndOrdinal > projCtx.ProjectionDirective.ResOpt.MaxOrdinalForArrayExpansion) { Logger.Warning(TAG, this.Ctx, $"endOrdinal {this.EndOrdinal} is greater than the maximum allowed ordinal of {projCtx.ProjectionDirective.ResOpt.MaxOrdinalForArrayExpansion}. Using the maximum allowed ordinal instead.", nameof(AppendProjectionAttributeState)); } int endingOrdinal = Math.Min(projCtx.ProjectionDirective.ResOpt.MaxOrdinalForArrayExpansion, (int)this.EndOrdinal); // For each ordinal, create a copy of the input resolved attribute for (int i = startingOrdinal; i <= endingOrdinal; i++) { // Create a new attribute context for the round AttributeContextParameters attrCtxRoundParam = new AttributeContextParameters { under = attrCtxOpArrayExpansion, type = CdmAttributeContextType.GeneratedRound, Name = $"_generatedAttributeRound{round}" }; CdmAttributeContext attrCtxRound = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxRoundParam); // Iterate through all the projection attribute states generated from the source's resolved attributes // Each projection attribute state contains a resolved attribute that it is corresponding to foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States) { // Create a new attribute context for the expanded attribute with the current ordinal AttributeContextParameters attrCtxExpandedAttrParam = new AttributeContextParameters { under = attrCtxRound, type = CdmAttributeContextType.AttributeDefinition, Name = $"{currentPAS.CurrentResolvedAttribute.ResolvedName}@{i}" }; CdmAttributeContext attrCtxExpandedAttr = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxExpandedAttrParam); if (currentPAS.CurrentResolvedAttribute.Target is ResolvedAttributeSet) { Logger.Error(TAG, this.Ctx, "Array expansion operation does not support attribute groups."); projAttrStatesFromRounds.Clear(); break; } // Create a new resolved attribute for the expanded attribute ResolvedAttribute newResAttr = CreateNewResolvedAttribute(projCtx, attrCtxExpandedAttr, currentPAS.CurrentResolvedAttribute.Target, currentPAS.CurrentResolvedAttribute.ResolvedName); newResAttr.AttCtx.AddLineage(currentPAS.CurrentResolvedAttribute.AttCtx); // Create a projection attribute state for the expanded attribute ProjectionAttributeState newPAS = new ProjectionAttributeState(projOutputSet.Ctx) { CurrentResolvedAttribute = newResAttr, PreviousStateList = new List <ProjectionAttributeState> { currentPAS }, Ordinal = i }; projAttrStatesFromRounds.Add(newPAS); } if (i == endingOrdinal) { break; } // Increment the round round++; } } if (projAttrStatesFromRounds.Count == 0) { // No rounds were produced from the array expansion - input passes through foreach (ProjectionAttributeState pas in projCtx.CurrentAttributeStateSet.States) { projOutputSet.Add(pas); } } else { // Add all the projection attribute states containing the expanded attributes to the output foreach (ProjectionAttributeState pas in projAttrStatesFromRounds) { projOutputSet.Add(pas); } } return(projOutputSet); }
/// <inheritdoc /> internal override ProjectionAttributeStateSet AppendProjectionAttributeState( ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create a new attribute context for the operation AttributeContextParameters attrCtxOpAlterTraitsParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationAlterTraits, Name = $"operation/index{Index}/{this.GetName()}" }; CdmAttributeContext attrCtxOpAlterTraits = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpAlterTraitsParam); // Get the top-level attribute names of the selected attributes to apply // We use the top-level names because the applyTo list may contain a previous name our current resolved attributes had Dictionary <string, string> topLevelSelectedAttributeNames = this.ApplyTo != null?ProjectionResolutionCommonUtil.GetTopList(projCtx, this.ApplyTo) : null; foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States) { // Check if the current projection attribute state's resolved attribute is in the list of selected attributes // If this attribute is not in the list, then we are including it in the output without changes if (topLevelSelectedAttributeNames == null || topLevelSelectedAttributeNames.ContainsKey(currentPAS.CurrentResolvedAttribute.ResolvedName)) { // Create a new attribute context for the new attribute we will create AttributeContextParameters attrCtxNewAttrParam = new AttributeContextParameters { under = attrCtxOpAlterTraits, type = CdmAttributeContextType.AttributeDefinition, Name = currentPAS.CurrentResolvedAttribute.ResolvedName }; CdmAttributeContext attrCtxNewAttr = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxNewAttrParam); ResolvedAttribute newResAttr = null; if (currentPAS.CurrentResolvedAttribute.Target is ResolvedAttributeSet) { // Attribute group // Create a copy of resolved attribute set ResolvedAttributeSet resAttrNewCopy = ((ResolvedAttributeSet)currentPAS.CurrentResolvedAttribute.Target).Copy(); newResAttr = new ResolvedAttribute(projCtx.ProjectionDirective.ResOpt, resAttrNewCopy, currentPAS.CurrentResolvedAttribute.ResolvedName, attrCtxNewAttr); // the resolved attribute group obtained from previous projection operation may have a different set of traits comparing to the resolved attribute target. // We would want to take the set of traits from the resolved attribute. newResAttr.ResolvedTraits = currentPAS.CurrentResolvedAttribute.ResolvedTraits.DeepCopy(); } else if (currentPAS.CurrentResolvedAttribute.Target is CdmAttribute) { // Entity Attribute or Type Attribute newResAttr = CreateNewResolvedAttribute(projCtx, attrCtxNewAttr, currentPAS.CurrentResolvedAttribute, currentPAS.CurrentResolvedAttribute.ResolvedName); } else { Logger.Error(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.ErrProjUnsupportedSource, currentPAS.CurrentResolvedAttribute.Target.ObjectType.ToString(), this.GetName()); // Add the attribute without changes projOutputSet.Add(currentPAS); break; } newResAttr.ResolvedTraits = newResAttr.ResolvedTraits.MergeSet(this.ResolvedNewTraits(projCtx, currentPAS)); this.RemoveTraitsInNewAttribute(projCtx.ProjectionDirective.ResOpt, newResAttr); // Create a projection attribute state for the new attribute with new applied traits by creating a copy of the current state // Copy() sets the current state as the previous state for the new one ProjectionAttributeState newPAS = currentPAS.Copy(); // Update the resolved attribute to be the new attribute we created newPAS.CurrentResolvedAttribute = newResAttr; projOutputSet.Add(newPAS); } else { // Pass through projOutputSet.Add(currentPAS); } } return(projOutputSet); }
/// <inheritdoc /> internal override ProjectionAttributeStateSet AppendProjectionAttributeState( ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create a new attribute context for the operation AttributeContextParameters attrCtxOpRenameAttrsParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationRenameAttributes, Name = $"operation/index{Index}/operationRenameAttributes" }; CdmAttributeContext attrCtxOpRenameAttrs = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpRenameAttrsParam); // Get the list of attributes that will be renamed List <string> renameAttributes; if (this.ApplyTo != null) { renameAttributes = this.ApplyTo; } else { renameAttributes = new List <string>(); foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States) { renameAttributes.Add(currentPAS.CurrentResolvedAttribute.ResolvedName); } } // Get the top-level attribute names of the attributes to rename // We use the top-level names because the rename list may contain a previous name our current resolved attributes had Dictionary <string, string> topLevelRenameAttributeNames = ProjectionResolutionCommonUtil.GetTopList(projCtx, renameAttributes); string sourceAttributeName = projCtx.ProjectionDirective.OriginalSourceEntityAttributeName; // Initialize a projection attribute context tree builder with the created attribute context for the operation ProjectionAttributeContextTreeBuilder attrCtxTreeBuilder = new ProjectionAttributeContextTreeBuilder(attrCtxOpRenameAttrs); // Iterate through all the projection attribute states generated from the source's resolved attributes // Each projection attribute state contains a resolved attribute that it is corresponding to foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States) { // Check if the current projection attribute state's resolved attribute is in the list of attributes to rename // If this attribute is not in the rename list, then we are including it in the output without changes if (topLevelRenameAttributeNames.ContainsKey(currentPAS.CurrentResolvedAttribute.ResolvedName)) { if (currentPAS.CurrentResolvedAttribute.Target is CdmAttribute) { // The current attribute should be renamed string newAttributeName = GetNewAttributeName(currentPAS, sourceAttributeName); // Create new resolved attribute with the new name, set the new attribute as target ResolvedAttribute resAttrNew = CreateNewResolvedAttribute(projCtx, null, currentPAS.CurrentResolvedAttribute.Target, newAttributeName); // Get the attribute name the way it appears in the applyTo list string applyToName = topLevelRenameAttributeNames[currentPAS.CurrentResolvedAttribute.ResolvedName]; // Create the attribute context parameters and just store it in the builder for now // We will create the attribute contexts at the end attrCtxTreeBuilder.CreateAndStoreAttributeContextParameters(applyToName, currentPAS, resAttrNew, CdmAttributeContextType.AttributeDefinition, currentPAS.CurrentResolvedAttribute.AttCtx, // lineage is the original attribute null); // don't know who will point here yet // Create a projection attribute state for the renamed attribute by creating a copy of the current state // Copy() sets the current state as the previous state for the new one // We only create projection attribute states for attributes that are in the rename list ProjectionAttributeState newPAS = currentPAS.Copy(); // Update the resolved attribute to be the new renamed attribute we created newPAS.CurrentResolvedAttribute = resAttrNew; projOutputSet.Add(newPAS); } else { Logger.Warning(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.WarnProjRenameAttrNotSupported); // Add the attribute without changes projOutputSet.Add(currentPAS); } } else { // Pass through projOutputSet.Add(currentPAS); } } // Create all the attribute contexts and construct the tree attrCtxTreeBuilder.ConstructAttributeContextTree(projCtx); return(projOutputSet); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { // find and cache the complete set of attributes // attributes definitions originate from and then get modified by subsequent re-defintions from (in this order): // the entity used as an attribute, traits applied to that entity, // the purpose of the attribute, any traits applied to the attribute. ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); CdmEntityReference ctxEnt = this.Entity as CdmEntityReference; CdmAttributeContext underAtt = (CdmAttributeContext)under; AttributeContextParameters acpEnt = null; if (underAtt != null) { // make a context for this attreibute that holds the attributes that come up from the entity acpEnt = new AttributeContextParameters { under = underAtt, type = CdmAttributeContextType.Entity, Name = ctxEnt.FetchObjectDefinitionName(), Regarding = ctxEnt, IncludeTraits = true }; } ResolvedTraitSet rtsThisAtt = this.FetchResolvedTraits(resOpt); // this context object holds all of the info about what needs to happen to resolve these attributes. // make a copy and add defaults if missing CdmAttributeResolutionGuidance resGuideWithDefault; if (this.ResolutionGuidance != null) resGuideWithDefault = (CdmAttributeResolutionGuidance)this.ResolutionGuidance.Copy(resOpt); else resGuideWithDefault = new CdmAttributeResolutionGuidance(this.Ctx); resGuideWithDefault.UpdateAttributeDefaults(this.Name); AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuideWithDefault, rtsThisAtt); // complete cheating but is faster. // this purpose will remove all of the attributes that get collected here, so dumb and slow to go get them RelationshipInfo relInfo = this.GetRelationshipInfo(arc.ResOpt, arc); if (relInfo.IsByRef) { // make the entity context that a real recursion would have give us if (under != null) under = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEnt); // if selecting from one of many attributes, then make a context for each one if (under != null && relInfo.SelectsOne) { // the right way to do this is to get a resolved entity from the embedded entity and then // look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes // that seems like a disaster waiting to happen given endless looping, etc. // for now, just insist that only the top level entity attributes declared in the ref entity will work CdmEntityDefinition entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt) as CdmEntityDefinition; CdmCollection<CdmAttributeItem> attsPick = entPickFrom?.GetAttributeDefinitions(); if (entPickFrom != null && attsPick != null) { for (int i = 0; i < attsPick.Count; i++) { if (attsPick.AllItems[i].ObjectType == CdmObjectType.EntityAttributeDef) { // a table within a table. as expected with a selectsOne attribute // since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key // these are the same contexts that would get created if we recursed // first this attribute AttributeContextParameters acpEntAtt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeDefinition, Name = attsPick.AllItems[i].FetchObjectDefinitionName(), Regarding = attsPick.AllItems[i], IncludeTraits = true }; CdmAttributeContext pickUnder = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAtt); CdmEntityReference pickEnt = (attsPick.AllItems[i] as CdmEntityAttributeDefinition).Entity as CdmEntityReference; AttributeContextParameters acpEntAttEnt = new AttributeContextParameters { under = pickUnder, type = CdmAttributeContextType.Entity, Name = pickEnt.FetchObjectDefinitionName(), Regarding = pickEnt, IncludeTraits = true }; rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAttEnt); } } } } // if we got here because of the max depth, need to impose the directives to make the trait work as expected if (relInfo.MaxDepthExceeded) { if (arc.ResOpt.Directives == null) arc.ResOpt.Directives = new AttributeResolutionDirectiveSet(); arc.ResOpt.Directives.Add("referenceOnly"); } } else { ResolveOptions resLink = CopyResolveOptions(resOpt); resLink.SymbolRefSet = resOpt.SymbolRefSet; resLink.RelationshipDepth = relInfo.NextDepth; rasb.MergeAttributes((this.Entity as CdmEntityReference).FetchResolvedAttributes(resLink, acpEnt)); } // from the traits of purpose and applied here, see if new attributes get generated rasb.ResolvedAttributeSet.AttributeContext = underAtt; rasb.ApplyTraits(arc); rasb.GenerateApplierAttributes(arc, true); // true = apply the prepared traits to new atts // this may have added symbols to the dependencies, so merge them resOpt.SymbolRefSet.Merge(arc.ResOpt.SymbolRefSet); // use the traits for linked entity identifiers to record the actual foreign key links if (rasb.ResolvedAttributeSet?.Set != null && relInfo.IsByRef) { foreach (var att in rasb.ResolvedAttributeSet.Set) { var reqdTrait = att.ResolvedTraits.Find(resOpt, "is.linkedEntity.identifier"); if (reqdTrait == null) { continue; } if (reqdTrait.ParameterValues == null || reqdTrait.ParameterValues.Length == 0) { Logger.Warning(nameof(CdmEntityAttributeDefinition), this.Ctx as ResolveContext, "is.linkedEntity.identifier does not support arguments"); continue; } var entReferences = new List<string>(); var attReferences = new List<string>(); Action<CdmEntityReference, string> addEntityReference = (CdmEntityReference entRef, string nameSpace) => { var entDef = entRef.FetchObjectDefinition<CdmEntityDefinition>(resOpt); var identifyingTrait = entRef.FetchResolvedTraits(resOpt).Find(resOpt, "is.identifiedBy"); if (identifyingTrait != null && entDef != null) { var attRef = identifyingTrait.ParameterValues.FetchParameterValueByName("attribute").Value; string attNamePath = ((CdmObjectReferenceBase)attRef).NamedReference; string attName = attNamePath.Split('/').Last(); // path should be absolute and without a namespace string relativeEntPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(entDef.AtCorpusPath, entDef.InDocument); if (relativeEntPath.StartsWith($"{nameSpace}:")) { relativeEntPath = relativeEntPath.Substring(nameSpace.Length + 1); } entReferences.Add(relativeEntPath); attReferences.Add(attName); } }; if (relInfo.SelectsOne) { var entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt) as CdmEntityDefinition; var attsPick = entPickFrom?.GetAttributeDefinitions()?.Cast<CdmObject>().ToList(); if (entPickFrom != null && attsPick != null) { for (int i = 0; i < attsPick.Count; i++) { if (attsPick[i].ObjectType == CdmObjectType.EntityAttributeDef) { var entAtt = attsPick[i] as CdmEntityAttributeDefinition; addEntityReference(entAtt.Entity, this.InDocument.Namespace); } } } } else { addEntityReference(this.Entity, this.InDocument.Namespace); } var constantEntity = this.Ctx.Corpus.MakeObject<CdmConstantEntityDefinition>(CdmObjectType.ConstantEntityDef); constantEntity.EntityShape = this.Ctx.Corpus.MakeRef<CdmEntityReference>(CdmObjectType.EntityRef, "entityGroupSet", true); constantEntity.ConstantValues = entReferences.Select((entRef, idx) => new List<string> { entRef, attReferences[idx] }).ToList(); var traitParam = this.Ctx.Corpus.MakeRef<CdmEntityReference>(CdmObjectType.EntityRef, constantEntity, false); reqdTrait.ParameterValues.SetParameterValue(resOpt, "entityReferences", traitParam); } } // a 'structured' directive wants to keep all entity attributes together in a group if (arc.ResOpt.Directives?.Has("structured") == true) { ResolvedAttribute raSub = new ResolvedAttribute(rtsThisAtt.ResOpt, rasb.ResolvedAttributeSet, this.Name, (CdmAttributeContext)rasb.ResolvedAttributeSet.AttributeContext); if (relInfo.IsArray) { // put a resolved trait on this att group, yuck, hope I never need to do this again and then need to make a function for this CdmTraitReference tr = this.Ctx.Corpus.MakeObject<CdmTraitReference>(CdmObjectType.TraitRef, "is.linkedEntity.array", true); var t = tr.FetchObjectDefinition<CdmTraitDefinition>(resOpt); ResolvedTrait rt = new ResolvedTrait(t, null, new List<dynamic>(), new List<bool>()); raSub.ResolvedTraits = raSub.ResolvedTraits.Merge(rt, true); } rasb = new ResolvedAttributeSetBuilder(); rasb.OwnOne(raSub); } return rasb; }
/// <summary> /// Takes all the stored attribute context parameters, creates attribute contexts from them, and then constructs the tree. /// </summary> /// <param name="projCtx">The projection context</param> internal void ConstructAttributeContextTree(ProjectionContext projCtx) { // Iterate over all the searchFor attribute context parameters foreach (AttributeContextParameters searchForAttrCtxParam in this.searchForToSearchForAttrCtxParam.Values) { CdmAttributeContext searchForAttrCtx = null; // Fetch all the found attribute context parameters associated with this searchFor List <AttributeContextParameters> foundAttrCtxParams = null; searchForAttrCtxParamToFoundAttrCtxParam.TryGetValue(searchForAttrCtxParam, out foundAttrCtxParams); // Iterate over all the found attribute context parameters foreach (AttributeContextParameters foundAttrCtxParam in foundAttrCtxParams) { // We should only create the searchFor node when searchFor and found have different names. Else collapse the nodes together. if (!StringUtils.EqualsWithCase(searchForAttrCtxParam.Name, foundAttrCtxParam.Name)) { // Create the attribute context for searchFor if it hasn't been created already and set it as the parent of found if (searchForAttrCtx == null) { searchForAttrCtx = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, searchForAttrCtxParam); } foundAttrCtxParam.under = searchForAttrCtx; } // Fetch the action attribute context parameter associated with this found AttributeContextParameters actionAttrCtxParam = null; foundAttrCtxParamToActionAttrCtxParam.TryGetValue(foundAttrCtxParam, out actionAttrCtxParam); // We should only create the found node when found and action have different names. Else collapse the nodes together. if (!StringUtils.EqualsWithCase(foundAttrCtxParam.Name, actionAttrCtxParam.Name)) { // Create the attribute context for found and set it as the parent of action CdmAttributeContext foundAttrCtx = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, foundAttrCtxParam); actionAttrCtxParam.under = foundAttrCtx; } // Create the attribute context for action CdmAttributeContext actionAttrCtx = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, actionAttrCtxParam); // Fetch the resolved attribute that should now point at this action attribute context ResolvedAttribute resAttrFromAction = null; actionAttrCtxParamToResAttr.TryGetValue(actionAttrCtxParam, out resAttrFromAction); // make sure the lineage of the attribute stays linked up // there can be either (or both) a lineageOut and a lineageIn. // out lineage is where this attribute came from // in lineage should be pointing back at this context as a source CdmAttributeContext lineageOut = null; if (actionAttrCtxParamToLineageOut.TryGetValue(actionAttrCtxParam, out lineageOut) == true) { if (actionAttrCtx != null && lineageOut != null) { actionAttrCtx.AddLineage(lineageOut); } resAttrFromAction.AttCtx = actionAttrCtx; // probably the right context for this resAtt, unless ... } CdmAttributeContext lineageIn = null; if (actionAttrCtxParamToLineageIn.TryGetValue(actionAttrCtxParam, out lineageIn) == true) { if (actionAttrCtx != null && lineageIn != null) { lineageIn.AddLineage(actionAttrCtx); } resAttrFromAction.AttCtx = lineageIn; // if there is a lineageIn. it points to us as lineage, so it is best } } } }
/// <summary> /// Creates the attribute context parameters for the searchFor, found, and action nodes and then stores them in different maps. /// The maps are used when constructing the actual attribute context tree. /// </summary> /// <param name="searchFor">The "search for" string</param> /// <param name="found">The projection attribute state that contains the "found" attribute</param> /// <param name="resAttrFromAction">The resolved attribute that resulted from the action</param> /// <param name="attrCtxType">The attribute context type to give the "action" attribute context parameter</param> /// <param name="lineageOut">normally lineage goes from new context to the found. false means don't and maybe even flip it</param> /// <param name="lineageIn"></param> internal void CreateAndStoreAttributeContextParameters(string searchFor, ProjectionAttributeState found, ResolvedAttribute resAttrFromAction, CdmAttributeContextType attrCtxType, CdmAttributeContext lineageOut, CdmAttributeContext lineageIn) { // searchFor is null when we have to construct attribute contexts for the excluded attributes in Include or the included attributes in Exclude, // as these attributes weren't searched for with a searchFor name. // If searchFor is null, just set it to have the same name as found so that it'll collapse in the final tree. if (searchFor == null) { searchFor = found.CurrentResolvedAttribute.ResolvedName; } // Create the attribute context parameter for the searchFor node and store it in the map as [searchFor name]:[attribute context parameter] AttributeContextParameters searchForAttrCtxParam = null; if (!searchForToSearchForAttrCtxParam.ContainsKey(searchFor)) { searchForAttrCtxParam = new AttributeContextParameters { under = root, type = CdmAttributeContextType.AttributeDefinition, Name = searchFor }; searchForToSearchForAttrCtxParam.Add(searchFor, searchForAttrCtxParam); } else { searchForToSearchForAttrCtxParam.TryGetValue(searchFor, out searchForAttrCtxParam); } // Create the attribute context parameter for the found node AttributeContextParameters foundAttrCtxParam = new AttributeContextParameters { under = root, // Set this to be under the root for now, as we may end up collapsing this node type = CdmAttributeContextType.AttributeDefinition, Name = $"{found.CurrentResolvedAttribute.ResolvedName}{(found.Ordinal != null ? "@" + found.Ordinal : "")}" }; // Store this in the map as [searchFor attribute context parameter]:[found attribute context parameters] // We store it this way so that we can create the found nodes under their corresponding searchFor nodes. if (!searchForAttrCtxParamToFoundAttrCtxParam.ContainsKey(searchForAttrCtxParam)) { searchForAttrCtxParamToFoundAttrCtxParam.Add(searchForAttrCtxParam, new List <AttributeContextParameters>()); } List <AttributeContextParameters> foundAttrCtxParams = searchForAttrCtxParamToFoundAttrCtxParam[searchForAttrCtxParam]; foundAttrCtxParams.Add(foundAttrCtxParam); // Create the attribute context parameter for the action node AttributeContextParameters actionAttrCtxParam = new AttributeContextParameters { under = root, // Set this to be under the root for now, as we may end up collapsing this node type = attrCtxType, // This type will be updated once we implement the new attribute context types Name = resAttrFromAction.ResolvedName }; // Store this in the map as [found attribute context parameter]:[action attribute context parameter] // We store it this way so that we can create the action nodes under their corresponding found nodes. foundAttrCtxParamToActionAttrCtxParam[foundAttrCtxParam] = actionAttrCtxParam; // Store the action attribute context parameter with the resolved attribute resulting out of the action. // This is so that we can point the action attribute context to the correct resolved attribute once the attribute context is created. actionAttrCtxParamToResAttr[actionAttrCtxParam] = resAttrFromAction; // store the current resAtt as the lineage of the new one // of note, if no lineage is stored AND the resolved Att associated above holds an existing context? we will // flip the lineage when we make a new context and point 'back' to this new node. this means this new node should // point 'back' to the context of the source attribute if (lineageOut != null) { actionAttrCtxParamToLineageOut[actionAttrCtxParam] = lineageOut; } if (lineageIn != null) { actionAttrCtxParamToLineageIn[actionAttrCtxParam] = lineageIn; } }