internal ResolvedAttributeSet FetchResolvedAttributes(ResolveOptions resOpt = null, AttributeContextParameters acpInContext = null) { if (resOpt == null) { resOpt = new ResolveOptions(this); } const string kind = "rasb"; ResolveContext ctx = this.Ctx as ResolveContext; string cacheTag = ((CdmCorpusDefinition)ctx.Corpus).CreateDefinitionCacheTag(resOpt, this, kind, acpInContext != null ? "ctx" : ""); dynamic rasbCache = null; if (cacheTag != null) { ctx.Cache.TryGetValue(cacheTag, out rasbCache); } CdmAttributeContext underCtx = null; // store the previous document set, we will need to add it with // children found from the constructResolvedTraits call SymbolSet currDocRefSet = resOpt.SymbolRefSet; if (currDocRefSet == null) { currDocRefSet = new SymbolSet(); } resOpt.SymbolRefSet = new SymbolSet(); // get the moniker that was found and needs to be appended to all // refs in the children attribute context nodes string fromMoniker = resOpt.FromMoniker; resOpt.FromMoniker = null; if (rasbCache == null) { if (this.resolvingAttributes) { // re-entered this attribute through some kind of self or looping reference. return(new ResolvedAttributeSet()); } this.resolvingAttributes = true; // if a new context node is needed for these attributes, make it now if (acpInContext != null) { underCtx = CdmAttributeContext.CreateChildUnder(resOpt, acpInContext); } rasbCache = this.ConstructResolvedAttributes(resOpt, underCtx); this.resolvingAttributes = false; // register set of possible docs CdmObjectDefinition oDef = this.FetchObjectDefinition <CdmObjectDefinitionBase>(resOpt); if (oDef != null) { ((CdmCorpusDefinition)ctx.Corpus).RegisterDefinitionReferenceSymbols(oDef, kind, resOpt.SymbolRefSet); // get the new cache tag now that we have the list of docs cacheTag = ((CdmCorpusDefinition)ctx.Corpus).CreateDefinitionCacheTag(resOpt, this, kind, acpInContext != null ? "ctx" : null); // save this as the cached version if (!string.IsNullOrWhiteSpace(cacheTag)) { ctx.Cache[cacheTag] = rasbCache; } if (!string.IsNullOrWhiteSpace(fromMoniker) && acpInContext != null && (this as CdmObjectReferenceBase).NamedReference != null) { // create a fresh context CdmAttributeContext oldContext = acpInContext.under.Contents[acpInContext.under.Contents.Count - 1] as CdmAttributeContext; acpInContext.under.Contents.RemoveAt(acpInContext.under.Contents.Count - 1); underCtx = CdmAttributeContext.CreateChildUnder(resOpt, acpInContext); CdmAttributeContext newContext = oldContext.CopyAttributeContextTree(resOpt, underCtx, rasbCache.ResolvedAttributeSet, null, fromMoniker); // since THIS should be a refererence to a thing found in a moniker document, it already has a moniker in the reference // this function just added that same moniker to everything in the sub-tree but now this one symbol has too many // remove one string monikerPathAdded = $"{fromMoniker}/"; if (newContext.Definition != null && newContext.Definition.NamedReference != null && newContext.Definition.NamedReference.StartsWith(monikerPathAdded)) { // slice it off the front newContext.Definition.NamedReference = newContext.Definition.NamedReference.Substring(monikerPathAdded.Length); } } } } else { // cache found. if we are building a context, then fix what we got instead of making a new one if (acpInContext != null) { // make the new context underCtx = CdmAttributeContext.CreateChildUnder(resOpt, acpInContext); rasbCache.ResolvedAttributeSet.AttributeContext.CopyAttributeContextTree(resOpt, underCtx, rasbCache.ResolvedAttributeSet, null, fromMoniker); } } // merge child document set with current currDocRefSet.Merge(resOpt.SymbolRefSet); resOpt.SymbolRefSet = currDocRefSet; return(rasbCache?.ResolvedAttributeSet); }
/// <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 }; HashSet <string> attributesAddedToContext = new HashSet <string>(); // 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]) { // When dealing with a polymorphic entity, it is possible that multiple entities have an attribute with the same name // Only one attribute with each name should be added otherwise the attribute context will end up with duplicated nodes if (!attributesAddedToContext.Contains(leafLevelForSelect.CurrentResolvedAttribute.ResolvedName)) { attributesAddedToContext.Add(leafLevelForSelect.CurrentResolvedAttribute.ResolvedName); 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); }
/// <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(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.WarnValdnOrdinalStartEndOrder, this.StartOrdinal.ToString(), this.EndOrdinal.ToString()); } 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(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.WarnValdnMaxOrdinalTooHigh, this.EndOrdinal.ToString(), projCtx.ProjectionDirective.ResOpt.MaxOrdinalForArrayExpansion.ToString()); } 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(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.ErrProjUnsupportedAttrGroups); projAttrStatesFromRounds.Clear(); break; } // Create a new resolved attribute for the expanded attribute ResolvedAttribute newResAttr = CreateNewResolvedAttribute(projCtx, attrCtxExpandedAttr, currentPAS.CurrentResolvedAttribute, currentPAS.CurrentResolvedAttribute.ResolvedName); // 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); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { return(null); }
/// <summary> /// Create resolved attribute set based on the CurrentResolvedAttribute array /// </summary> /// <param name="projCtx"></param> /// <returns></returns> internal ResolvedAttributeSet ExtractResolvedAttributes(ProjectionContext projCtx, CdmAttributeContext attCtxUnder) { ResolvedAttributeSet resolvedAttributeSet = new ResolvedAttributeSet { AttributeContext = attCtxUnder }; foreach (var pas in projCtx.CurrentAttributeStateSet.States) { resolvedAttributeSet.Merge(pas.CurrentResolvedAttribute); } return(resolvedAttributeSet); }
/// <inheritdoc /> internal override ProjectionAttributeStateSet AppendProjectionAttributeState( ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create a new attribute context for the operation AttributeContextParameters attrCtxOpIncludeAttrsParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationIncludeAttributes, Name = $"operation/index{Index}/operationIncludeAttributes" }; CdmAttributeContext attrCtxOpIncludeAttrs = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpIncludeAttrsParam); // Get the top-level attribute names for each of the included attributes // Since the include operation allows providing either current state resolved attribute names // or the previous state resolved attribute names, we search for the name in the PAS tree // and fetch the top level resolved attribute names. Dictionary <string, string> topLevelIncludeAttributeNames = ProjectionResolutionCommonUtil.GetTopList(projCtx, this.IncludeAttributes); // Initialize a projection attribute context tree builder with the created attribute context for the operation ProjectionAttributeContextTreeBuilder attrCtxTreeBuilder = new ProjectionAttributeContextTreeBuilder(attrCtxOpIncludeAttrs); // Iterate through all the PAS in the PASSet generated from the projection source's resolved attributes foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States) { // Check if the current PAS's RA is in the list of attributes to include. if (topLevelIncludeAttributeNames.ContainsKey(currentPAS.CurrentResolvedAttribute.ResolvedName)) { // Get the attribute name the way it appears in the include list string includeAttributeName = null; topLevelIncludeAttributeNames.TryGetValue(currentPAS.CurrentResolvedAttribute.ResolvedName, out includeAttributeName); // 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(includeAttributeName, currentPAS, currentPAS.CurrentResolvedAttribute, CdmAttributeContextType.AttributeDefinition, currentPAS.CurrentResolvedAttribute.AttCtx, // lineage is the included attribute null); // don't know who will point here yet // Create a projection attribute state for the included 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 in the include list ProjectionAttributeState newPAS = currentPAS.Copy(); projOutputSet.Add(newPAS); } else { // 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(null, currentPAS, currentPAS.CurrentResolvedAttribute, CdmAttributeContextType.AttributeDefinition, currentPAS.CurrentResolvedAttribute.AttCtx, // lineage is the excluded attribute null); // don't know who will point here, probably nobody, I mean, we got excluded } } // Create all the attribute contexts and construct the tree attrCtxTreeBuilder.ConstructAttributeContextTree(projCtx); return(projOutputSet); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); CdmAttributeContext allUnder = under; if (under != null) { AttributeContextParameters acpAttGrp = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeGroup, Name = this.GetName(), Regarding = this, IncludeTraits = false }; under = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpAttGrp); } if (this.Members != null) { for (int i = 0; i < this.Members.Count; i++) { dynamic att = this.Members.AllItems[i]; AttributeContextParameters acpAtt = null; if (under != null) { acpAtt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeDefinition, Name = att.FetchObjectDefinitionName(), Regarding = att, IncludeTraits = false }; } ResolvedAttributeSet rasFromAtt = att.FetchResolvedAttributes(resOpt, acpAtt); // before we just merge, need to handle the case of 'attribute restatement' AKA an entity with an attribute having the same name as an attribute // from a base entity. thing might come out with different names, if they do, then any attributes owned by a similar named attribute before // that didn't just pop out of that same named attribute now need to go away. // mark any attributes formerly from this named attribute that don't show again as orphans rasb.ResolvedAttributeSet.MarkOrphansForRemoval((att as CdmAttributeItem).FetchObjectDefinitionName(), rasFromAtt); // now merge rasb.MergeAttributes(rasFromAtt); } } rasb.ResolvedAttributeSet.AttributeContext = allUnder; // context must be the one expected from the caller's pov. // things that need to go away rasb.RemoveRequestedAtts(); return(rasb); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { // find and cache the complete set of attributes ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); rasb.ResolvedAttributeSet.AttributeContext = under; var def = this.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (def != null) { AttributeContextParameters acpRef = null; if (under != null) { // ask for a 'pass through' context, that is, no new context at this level acpRef = new AttributeContextParameters() { under = under, type = CdmAttributeContextType.PassThrough }; } ResolvedAttributeSet resAtts = (def as CdmObjectDefinitionBase).FetchResolvedAttributes(resOpt, acpRef); if (resAtts?.Set?.Count > 0) { resAtts = resAtts.Copy(); rasb.MergeAttributes(resAtts); rasb.RemoveRequestedAtts(); } } else { string defName = this.FetchObjectDefinitionName(); Logger.Warning(defName, this.Ctx, $"unable to resolve an object from the reference '{defName}'"); } return(rasb); }
internal static CdmAttributeContext CreateChildUnder(ResolveOptions resOpt, AttributeContextParameters acp) { if (acp == null) { return(null); } if (acp.type == CdmAttributeContextType.PassThrough) { return(acp.under as CdmAttributeContext); } // this flag makes sure we hold on to any resolved object refs when things get copied ResolveOptions resOptCopy = resOpt.Copy(); resOptCopy.SaveResolutionsOnCopy = true; CdmObjectReference definition = null; ResolvedTraitSet rtsApplied = null; // get a simple reference to definition object to avoid getting the traits that might be part of this ref // included in the link to the definition. if (acp.Regarding != null) { // make a portable reference. this MUST be fixed up when the context node lands in the final document definition = (acp.Regarding as CdmObjectBase).CreatePortableReference(resOptCopy); // now get the traits applied at this reference (applied only, not the ones that are part of the definition of the object) // and make them the traits for this context if (acp.IncludeTraits) { rtsApplied = (acp.Regarding as CdmObjectBase).FetchResolvedTraits(resOptCopy); } } CdmAttributeContext underChild = acp.under.Ctx.Corpus.MakeObject <CdmAttributeContext>(CdmObjectType.AttributeContextDef, acp.Name); // need context to make this a 'live' object underChild.Ctx = acp.under.Ctx; underChild.InDocument = (acp.under as CdmAttributeContext).InDocument; underChild.Type = acp.type; underChild.Definition = definition; // add traits if there are any if (rtsApplied?.Set != null) { rtsApplied.Set.ForEach(rt => { var traitRef = CdmObjectBase.ResolvedTraitToTraitRef(resOptCopy, rt); underChild.ExhibitsTraits.Add(traitRef); }); } // add to parent underChild.SetParent(resOptCopy, acp.under as CdmAttributeContext); if (resOptCopy.MapOldCtxToNewCtx != null) { resOptCopy.MapOldCtxToNewCtx[underChild] = underChild; // so we can find every node, not only the replaced ones } return(underChild); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); if (under != null) { AttributeContextParameters acpAttGrp = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeGroup, Name = this.GetName(), Regarding = this, IncludeTraits = false }; under = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpAttGrp); } if (this.Members != null) { for (int i = 0; i < this.Members.Count; i++) { dynamic att = this.Members.AllItems[i]; CdmAttributeContext attUnder = under; AttributeContextParameters acpAtt = null; if (under != null) { acpAtt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeDefinition, Name = att.FetchObjectDefinitionName(), Regarding = att, IncludeTraits = false }; } rasb.MergeAttributes(att.FetchResolvedAttributes(resOpt, acpAtt)); } } rasb.ResolvedAttributeSet.AttributeContext = under; // things that need to go away rasb.RemoveRequestedAtts(); return(rasb); }
/// <summary> /// Create a new artifact attribute and add it to the projOutputSet. /// </summary> /// <param name="projCtx">The projection context.</param> /// <param name="projOutputSet">The projection attribute state set.</param> /// <param name="attrCtx">The attribute context.</param> /// <returns></returns> private void AddNewArtifactAttributeState(ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create a new attribute context for the operation AttributeContextParameters attrCtxOpAddArtifactAttrParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationAddArtifactAttribute, Name = $"operation/index{Index}/{this.GetName()}" }; CdmAttributeContext attrCtxOpAddArtifactAttr = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpAddArtifactAttrParam); if (this.NewAttribute is CdmTypeAttributeDefinition) { // Create a new attribute context for the new artifact attribute we will create AttributeContextParameters attrCtxNewAttrParam = new AttributeContextParameters { under = attrCtxOpAddArtifactAttr, type = CdmAttributeContextType.AddedAttributeNewArtifact, Name = this.NewAttribute.FetchObjectDefinitionName() }; CdmAttributeContext attrCtxNewAttr = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxNewAttrParam); ResolvedAttribute newResAttr = CreateNewResolvedAttribute(projCtx, attrCtxNewAttr, (CdmAttribute)this.NewAttribute); // Create a new projection attribute state for the new artifact attribute and add it to the output set // There is no previous state for the newly created attribute ProjectionAttributeState newPAS = new ProjectionAttributeState(projOutputSet.Ctx) { CurrentResolvedAttribute = newResAttr }; projOutputSet.Add(newPAS); } else if (this.NewAttribute is CdmEntityAttributeDefinition || this.NewAttribute is CdmAttributeGroupReference) { var typeStr = this.NewAttribute is CdmEntityAttributeDefinition ? "an entity attribute" : "an attribute group"; Logger.Warning(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.WarnProjAddArtifactAttrNotSupported, typeStr); } else { Logger.Error(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.ErrProjUnsupportedSource, this.NewAttribute.ObjectType.ToString(), this.GetName()); } }
/// <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); // 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(projCtx.ProjectionDirective.OriginalSourceAttributeName, currentPAS); // Create new resolved attribute with the new name, set the new attribute as target ResolvedAttribute resAttrNew = CreateNewResolvedAttribute(projCtx, null, currentPAS.CurrentResolvedAttribute, 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) { ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); AttributeContextParameters acpEnt = null; if (under != null) { acpEnt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.Entity, Name = this.EntityShape.FetchObjectDefinitionName(), Regarding = this.EntityShape, IncludeTraits = true }; } if (this.EntityShape != null) { rasb.MergeAttributes(this.EntityShape.FetchResolvedAttributes(resOpt, acpEnt)); } // things that need to go away rasb.RemoveRequestedAtts(); return(rasb); }
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, this); 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); }
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-definitions from (in this order): // an extended entity, traits applied to extended entity, exhibited traits of main entity, the (datatype or entity) used as an attribute, traits applied to that datatype or entity, // the relationsip of the attribute, the attribute definition itself and included attribute groups, dynamic traits applied to the attribute. this.Rasb = new ResolvedAttributeSetBuilder(); this.Rasb.ResolvedAttributeSet.AttributeContext = under; if (this.ExtendsEntity != null) { CdmObjectReference extRef = this.ExtendsEntityRef; CdmAttributeContext extendsRefUnder = null; AttributeContextParameters acpExtEnt = null; if (under != null) { AttributeContextParameters acpExt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.EntityReferenceExtends, Name = "extends", Regarding = null, IncludeTraits = false }; extendsRefUnder = this.Rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpExt); } if (extRef.ExplicitReference != null && extRef.FetchObjectDefinition <CdmObjectDefinition>(resOpt).ObjectType == CdmObjectType.ProjectionDef) { // A Projection CdmObjectDefinition extRefObjDef = extRef.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (extendsRefUnder != null) { acpExtEnt = new AttributeContextParameters { under = extendsRefUnder, type = CdmAttributeContextType.Projection, Name = extRefObjDef.GetName(), Regarding = extRef, IncludeTraits = false }; } ProjectionDirective projDirective = new ProjectionDirective(resOpt, this, ownerRef: extRef); CdmProjection projDef = (CdmProjection)extRefObjDef; ProjectionContext projCtx = projDef.ConstructProjectionContext(projDirective, extendsRefUnder); this.Rasb.ResolvedAttributeSet = projDef.ExtractResolvedAttributes(projCtx); } else { // An Entity Reference if (extendsRefUnder != null) { // usually the extended entity is a reference to a name. // it is allowed however to just define the entity inline. acpExtEnt = new AttributeContextParameters { under = extendsRefUnder, type = CdmAttributeContextType.Entity, Name = extRef.NamedReference ?? extRef.ExplicitReference.GetName(), Regarding = extRef, IncludeTraits = false }; } // save moniker, extended entity may attach a different moniker that we do not // want to pass along to getting this entities attributes string oldMoniker = resOpt.FromMoniker; this.Rasb.MergeAttributes((this.ExtendsEntityRef as CdmObjectReferenceBase).FetchResolvedAttributes(resOpt, acpExtEnt)); if (!resOpt.CheckAttributeCount(this.Rasb.ResolvedAttributeSet.ResolvedAttributeCount)) { Logger.Error(nameof(CdmEntityDefinition), this.Ctx, $"Maximum number of resolved attributes reached for the entity: {this.EntityName}."); return(null); } if (this.ExtendsEntityResolutionGuidance != null) { // some guidance was given on how to integrate the base attributes into the set. apply that guidance ResolvedTraitSet rtsBase = this.FetchResolvedTraits(resOpt); // this context object holds all of the info about what needs to happen to resolve these attributes. // make a copy and set defaults if needed CdmAttributeResolutionGuidance resGuide = (CdmAttributeResolutionGuidance)this.ExtendsEntityResolutionGuidance.Copy(resOpt); resGuide.UpdateAttributeDefaults(resGuide.FetchObjectDefinitionName()); // holds all the info needed by the resolver code AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuide, rtsBase); this.Rasb.GenerateApplierAttributes(arc, false); // true = apply the prepared traits to new atts } // reset to the old moniker resOpt.FromMoniker = oldMoniker; } } this.Rasb.MarkInherited(); this.Rasb.ResolvedAttributeSet.AttributeContext = under; if (this.Attributes != null) { int l = this.Attributes.Count; for (int i = 0; i < l; i++) { dynamic att = this.Attributes.AllItems[i]; CdmAttributeContext attUnder = under; AttributeContextParameters acpAtt = null; if (under != null) { acpAtt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeDefinition, Name = att.FetchObjectDefinitionName(), Regarding = att, IncludeTraits = false }; } this.Rasb.MergeAttributes(att.FetchResolvedAttributes(resOpt, acpAtt)); if (!resOpt.CheckAttributeCount(this.Rasb.ResolvedAttributeSet.ResolvedAttributeCount)) { Logger.Error(nameof(CdmEntityDefinition), this.Ctx, $"Maximum number of resolved attributes reached for the entity: {this.EntityName}."); return(null); } } } this.Rasb.MarkOrder(); this.Rasb.ResolvedAttributeSet.AttributeContext = under; // things that need to go away this.Rasb.RemoveRequestedAtts(); return(this.Rasb); }
internal bool FinalizeAttributeContext(ResolveOptions resOpt, string pathStart, CdmDocumentDefinition docHome, CdmDocumentDefinition docFrom, string monikerForDocFrom, bool finished = false) { // run over the attCtx tree again and 'fix it' fix means replace the parent and lineage reference path strings with // final values from new home and set the inDocument and fix any references to definitions // keep track of the paths to documents for fixing symbol refs. expensive to compute Dictionary <CdmDocumentDefinition, string> foundDocPaths = new Dictionary <CdmDocumentDefinition, string>(); if (!string.IsNullOrWhiteSpace(monikerForDocFrom)) { monikerForDocFrom = $"{monikerForDocFrom}/"; } // first step makes sure every node in the tree has a good path for itself and a good document // second pass uses the paths from nodes to fix references to other nodes Action <CdmObject, string> FixAttCtxNodePaths = null; FixAttCtxNodePaths = (subItem, pathFrom) => { CdmAttributeContext ac = subItem as CdmAttributeContext; if (ac == null) { return; } ac.InDocument = docHome; // fix up the reference to defintion. need to get path from this document to the // add moniker if this is a reference if (ac.Definition != null) { ac.Definition.InDocument = docHome; if (ac.Definition?.NamedReference != null) { // need the real path to this thing from the explicitRef held in the portable reference // the real path is {monikerFrom/}{path from 'from' document to document holding the explicit ref/{declaredPath of explicitRef}} // if we have never looked up the path between docs, do that now CdmDocumentDefinition docFromDef = ac.Definition.ExplicitReference.InDocument; // if all parts not set, this is a broken portal ref! string pathBetweenDocs; if (foundDocPaths.TryGetValue(docFromDef, out pathBetweenDocs) == false) { pathBetweenDocs = docFrom.ImportPathToDoc(docFromDef); if (pathBetweenDocs == null) { // hmm. hmm. pathBetweenDocs = ""; } foundDocPaths[docFrom] = pathBetweenDocs; } (ac.Definition as CdmObjectReferenceBase).LocalizePortableReference(resOpt, $"{monikerForDocFrom}{pathBetweenDocs}"); } } // doc of parent ref if (ac.Parent != null) { ac.Parent.InDocument = docHome; } // doc of lineage refs if (ac.Lineage != null && ac.Lineage.Count > 0) { foreach (var lin in ac.Lineage) { lin.InDocument = docHome; } } string divider = (string.IsNullOrEmpty(ac.AtCorpusPath) || !pathFrom.EndsWith("/")) ? "/" : ""; ac.AtCorpusPath = $"{pathFrom}{divider}{ac.Name}"; if (ac.Contents == null || ac.Contents.Count == 0) { return; } // look at all children foreach (var subSub in ac.Contents) { if (subSub.ObjectType == CdmObjectType.AttributeContextDef) { FixAttCtxNodePaths(subSub, ac.AtCorpusPath); } } }; FixAttCtxNodePaths(this, pathStart); // now fix any lineage and parent references Action <CdmObject> FixAttCtxNodeLineage = null; FixAttCtxNodeLineage = (subItem) => { CdmAttributeContext ac = subItem as CdmAttributeContext; if (ac == null) { return; } // for debugLineage, write id //ac.Name = $"{ac.Name}({ac.Id})"; // parent ref if (ac.Parent != null && ac.Parent.ExplicitReference != null) { ac.Parent.NamedReference = (ac.Parent.ExplicitReference as CdmAttributeContext).AtCorpusPath; // for debugLineage, write id //ac.Parent.NamedReference = $"{ (ac.Parent.ExplicitReference as CdmAttributeContext).AtCorpusPath}({ac.Parent.ExplicitReference.Id})"; } // fix lineage if (ac.Lineage != null && ac.Lineage.Count > 0) { foreach (var lin in ac.Lineage) { if (lin.ExplicitReference != null) { // use the new path as the ref lin.NamedReference = (lin.ExplicitReference as CdmAttributeContext).AtCorpusPath; // for debugLineage, write id //lin.NamedReference = $"{ (lin.ExplicitReference as CdmAttributeContext).AtCorpusPath}({lin.ExplicitReference.Id})"; } } } if (ac.Contents == null || ac.Contents.Count == 0) { return; } // look at all children foreach (var subSub in ac.Contents) { FixAttCtxNodeLineage(subSub); } }; FixAttCtxNodeLineage(this); if (finished) { resOpt.SaveResolutionsOnCopy = false; resOpt.MapOldCtxToNewCtx = null; } return(true); }
/// <summary> /// Creates a resolved copy of the entity. /// </summary> public async Task <CdmEntityDefinition> CreateResolvedEntityAsync(string newEntName, ResolveOptions resOpt = null, CdmFolderDefinition folder = null, string newDocName = null) { if (resOpt == null) { resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives); } if (resOpt.WrtDoc == null) { Logger.Error(nameof(CdmEntityDefinition), this.Ctx as ResolveContext, $"No WRT document was supplied.", nameof(CreateResolvedEntityAsync)); return(null); } if (string.IsNullOrEmpty(newEntName)) { Logger.Error(nameof(CdmEntityDefinition), this.Ctx as ResolveContext, $"No Entity Name provided.", nameof(CreateResolvedEntityAsync)); return(null); } if (folder == null) { folder = this.InDocument.Folder; } string fileName = (string.IsNullOrEmpty(newDocName)) ? $"{newEntName}.cdm.json" : newDocName; string origDoc = this.InDocument.AtCorpusPath; // Don't overwite the source document string targetAtCorpusPath = $"{this.Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(folder.AtCorpusPath, folder)}{fileName}"; if (StringUtils.EqualsWithIgnoreCase(targetAtCorpusPath, origDoc)) { Logger.Error(nameof(CdmEntityDefinition), this.Ctx as ResolveContext, $"Attempting to replace source entity's document '{targetAtCorpusPath}'", nameof(CreateResolvedEntityAsync)); return(null); } // if the wrtDoc needs to be indexed (like it was just modified) then do that first if (!await(resOpt.WrtDoc as CdmDocumentDefinition).IndexIfNeeded(resOpt)) { Logger.Error(nameof(CdmEntityDefinition), this.Ctx as ResolveContext, $"Couldn't index source document.", nameof(CreateResolvedEntityAsync)); return(null); } // make the top level attribute context for this entity // for this whole section where we generate the attribute context tree and get resolved attributes // set the flag that keeps all of the parent changes and document dirty from from happening bool wasResolving = this.Ctx.Corpus.isCurrentlyResolving; this.Ctx.Corpus.isCurrentlyResolving = true; string entName = newEntName; ResolveContext ctx = this.Ctx as ResolveContext; CdmAttributeContext attCtxEnt = ctx.Corpus.MakeObject <CdmAttributeContext>(CdmObjectType.AttributeContextDef, entName, true); attCtxEnt.Ctx = ctx; attCtxEnt.InDocument = this.InDocument; // cheating a bit to put the paths in the right place AttributeContextParameters acp = new AttributeContextParameters { under = attCtxEnt, type = CdmAttributeContextType.AttributeGroup, Name = "attributeContext" }; CdmAttributeContext attCtxAC = CdmAttributeContext.CreateChildUnder(resOpt, acp); AttributeContextParameters acpEnt = new AttributeContextParameters { under = attCtxAC, type = CdmAttributeContextType.Entity, Name = entName, Regarding = ctx.Corpus.MakeObject <CdmObject>(CdmObjectType.EntityRef, this.GetName(), true) }; // use this whenever we need to keep references pointing at things that were already found. used when 'fixing' references by localizing to a new document ResolveOptions resOptCopy = CdmObjectBase.CopyResolveOptions(resOpt); resOptCopy.SaveResolutionsOnCopy = true; // resolve attributes with this context. the end result is that each resolved attribute // points to the level of the context where it was created var ras = this.FetchResolvedAttributes(resOptCopy, acpEnt); if (ras == null) { this.resolvingAttributes = false; return(null); } // create a new copy of the attribute context for this entity HashSet <CdmAttributeContext> allAttCtx = new HashSet <CdmAttributeContext>(); CdmAttributeContext newNode = attCtxEnt.CopyNode(resOpt) as CdmAttributeContext; attCtxEnt = attCtxEnt.CopyAttributeContextTree(resOpt, newNode, ras, allAttCtx, "resolvedFrom"); CdmAttributeContext attCtx = (attCtxEnt.Contents[0] as CdmAttributeContext).Contents[0] as CdmAttributeContext; this.Ctx.Corpus.isCurrentlyResolving = wasResolving; // make a new document in given folder if provided or the same folder as the source entity folder.Documents.Remove(fileName); CdmDocumentDefinition docRes = folder.Documents.Add(fileName); // add a import of the source document origDoc = this.Ctx.Corpus.Storage.CreateRelativeCorpusPath(origDoc, docRes); // just in case we missed the prefix docRes.Imports.Add(origDoc, "resolvedFrom"); // make the empty entity CdmEntityDefinition entResolved = docRes.Definitions.Add(entName); // set the context to the copy of the tree. fix the docs on the context nodes entResolved.AttributeContext = attCtx; attCtx.Visit($"{entName}/attributeContext/", new VisitCallback { Invoke = (obj, path) => { obj.InDocument = docRes; return(false); } }, null); // add the traits of the entity ResolvedTraitSet rtsEnt = this.FetchResolvedTraits(resOpt); rtsEnt.Set.ForEach(rt => { var traitRef = CdmObjectBase.ResolvedTraitToTraitRef(resOptCopy, rt); (entResolved as CdmObjectDefinition).ExhibitsTraits.Add(traitRef); }); // the attributes have been named, shaped, etc for this entity so now it is safe to go and // make each attribute context level point at these final versions of attributes IDictionary <string, int> attPath2Order = new Dictionary <string, int>(); Action <ResolvedAttributeSet, string> pointContextAtResolvedAtts = null; pointContextAtResolvedAtts = (rasSub, path) => { rasSub.Set.ForEach(ra => { List <CdmAttributeContext> raCtxInEnt = new List <CdmAttributeContext>(); HashSet <CdmAttributeContext> raCtxSet = null; rasSub.Ra2attCtxSet.TryGetValue(ra, out raCtxSet); // find the correct attCtx for this copy, intersect these two sets // (iterate over the shortest list) if (allAttCtx.Count < raCtxSet.Count) { foreach (CdmAttributeContext currAttCtx in allAttCtx) { if (raCtxSet.Contains(currAttCtx)) { raCtxInEnt.Add(currAttCtx); } } } else { foreach (CdmAttributeContext currAttCtx in raCtxSet) { if (allAttCtx.Contains(currAttCtx)) { raCtxInEnt.Add(currAttCtx); } } } foreach (CdmAttributeContext raCtx in raCtxInEnt) { var refs = raCtx.Contents; // there might be more than one explanation for where and attribute came from when things get merges as they do // this won't work when I add the structured attributes to avoid name collisions string attRefPath = path + ra.ResolvedName; if ((ra.Target as CdmAttribute)?.GetType().GetMethod("GetObjectType") != null) { if (!attPath2Order.ContainsKey(attRefPath)) { var attRef = this.Ctx.Corpus.MakeObject <CdmObjectReferenceBase>(CdmObjectType.AttributeRef, attRefPath, true); // only need one explanation for this path to the insert order attPath2Order.Add(attRef.NamedReference, ra.InsertOrder); refs.Add(attRef); } } else { attRefPath += "/members/"; pointContextAtResolvedAtts(ra.Target as ResolvedAttributeSet, attRefPath); } } }); }; pointContextAtResolvedAtts(ras, entName + "/hasAttributes/"); // generated attribute structures may end up with 0 attributes after that. prune them Func <CdmObject, bool, bool> CleanSubGroup = null; CleanSubGroup = (subItem, underGenerated) => { if (subItem.ObjectType == CdmObjectType.AttributeRef) { return(true); // not empty } CdmAttributeContext ac = subItem as CdmAttributeContext; if (ac.Type == CdmAttributeContextType.GeneratedSet) { underGenerated = true; } if (ac.Contents == null || ac.Contents.Count == 0) { return(false); // empty } // look at all children, make a set to remove List <CdmAttributeContext> toRemove = new List <CdmAttributeContext>(); foreach (var subSub in ac.Contents) { if (CleanSubGroup(subSub, underGenerated) == false) { bool potentialTarget = underGenerated; if (potentialTarget == false) { // cast is safe because we returned false meaning empty and not a attribute ref // so is this the set holder itself? potentialTarget = (subSub as CdmAttributeContext).Type == CdmAttributeContextType.GeneratedSet; } if (potentialTarget) { toRemove.Add(subSub as CdmAttributeContext); } } } foreach (var toDie in toRemove) { ac.Contents.Remove(toDie); } return(ac.Contents.Count != 0); }; CleanSubGroup(attCtx, false); Func <CdmAttributeContext, int?> orderContents = null; // create an all-up ordering of attributes at the leaves of this tree based on insert order // sort the attributes in each context by their creation order and mix that with the other sub-contexts that have been sorted Func <CdmObject, int?> getOrderNum = (item) => { if (item.ObjectType == CdmObjectType.AttributeContextDef && orderContents != null) { return(orderContents(item as CdmAttributeContext)); } else if (item.ObjectType == CdmObjectType.AttributeRef) { string attName = (item as CdmAttributeReference).NamedReference; int o = attPath2Order[attName]; return(o); } else { return(-1); // put the mystery item on top. } }; orderContents = (CdmAttributeContext under) => { if (under.LowestOrder == null) { under.LowestOrder = -1; // used for group with nothing but traits if (under.Contents.Count == 1) { under.LowestOrder = getOrderNum(under.Contents[0]); } else { under.Contents.AllItems.Sort((l, r) => { int lNum = -1; int rNum = -1; int?aux; aux = getOrderNum(l); if (aux != null) { lNum = (int)aux; } aux = getOrderNum(r); if (aux != null) { rNum = (int)aux; } if (lNum != -1 && (under.LowestOrder == -1 || lNum < under.LowestOrder)) { under.LowestOrder = lNum; } if (rNum != -1 && (under.LowestOrder == -1 || rNum < under.LowestOrder)) { under.LowestOrder = rNum; } return(lNum - rNum); }); } } return(under.LowestOrder); }; orderContents((CdmAttributeContext)attCtx); // resolved attributes can gain traits that are applied to an entity when referenced // since these traits are described in the context, it is redundant and messy to list them in the attribute // so, remove them. create and cache a set of names to look for per context // there is actually a hierarchy to all attributes from the base entity should have all traits applied independently of the // sub-context they come from. Same is true of attribute entities. so do this recursively top down var ctx2traitNames = new Dictionary <CdmAttributeContext, HashSet <string> >(); Action <CdmAttributeContext, HashSet <string> > collectContextTraits = null; collectContextTraits = (subAttCtx, inheritedTraitNames) => { var traitNamesHere = new HashSet <string>(inheritedTraitNames); var traitsHere = subAttCtx.ExhibitsTraits; if (traitsHere != null) { foreach (var tat in traitsHere) { traitNamesHere.Add(tat.NamedReference); } } ctx2traitNames.Add(subAttCtx, traitNamesHere); subAttCtx.Contents.AllItems.ForEach((cr) => { if (cr.ObjectType == CdmObjectType.AttributeContextDef) { // do this for all types? collectContextTraits(cr as CdmAttributeContext, traitNamesHere); } }); }; collectContextTraits(attCtx, new HashSet <string>()); // add the attributes, put them in attribute groups if structure needed IDictionary <ResolvedAttribute, string> resAtt2RefPath = new Dictionary <ResolvedAttribute, string>(); Action <ResolvedAttributeSet, dynamic, string> addAttributes = null; addAttributes = (rasSub, container, path) => { rasSub.Set.ForEach(ra => { string attPath = path + ra.ResolvedName; // use the path of the context associated with this attribute to find the new context that matches on path HashSet <CdmAttributeContext> raCtxSet = null; rasSub.Ra2attCtxSet.TryGetValue(ra, out raCtxSet); CdmAttributeContext raCtx = null; // find the correct attCtx for this copy // (interate over the shortest list) if (allAttCtx.Count < raCtxSet.Count) { foreach (CdmAttributeContext currAttCtx in allAttCtx) { if (raCtxSet.Contains(currAttCtx)) { raCtx = currAttCtx; break; } } } else { foreach (CdmAttributeContext currAttCtx in raCtxSet) { if (allAttCtx.Contains(currAttCtx)) { raCtx = currAttCtx; break; } } } if (ra.Target is ResolvedAttributeSet) { // this is a set of attributes. // make an attribute group to hold them CdmAttributeGroupDefinition attGrp = this.Ctx.Corpus.MakeObject <CdmAttributeGroupDefinition>(CdmObjectType.AttributeGroupDef, ra.ResolvedName, false); attGrp.AttributeContext = this.Ctx.Corpus.MakeObject <CdmAttributeContextReference>(CdmObjectType.AttributeContextRef, raCtx.AtCorpusPath, true); // take any traits from the set and make them look like traits exhibited by the group HashSet <string> avoidSet = ctx2traitNames[raCtx]; ResolvedTraitSet rtsAtt = ra.ResolvedTraits; rtsAtt.Set.ForEach(rt => { if (rt.Trait.Ugly != true) { // don't mention your ugly traits if (avoidSet?.Contains(rt.TraitName) != true) { // avoid the ones from the context var traitRef = CdmObjectBase.ResolvedTraitToTraitRef(resOptCopy, rt); (attGrp as CdmObjectDefinitionBase).ExhibitsTraits.Add(traitRef); } } }); // wrap it in a reference and then recurse with this as the new container CdmAttributeGroupReference attGrpRef = this.Ctx.Corpus.MakeObject <CdmAttributeGroupReference>(CdmObjectType.AttributeGroupRef, null, false); attGrpRef.ExplicitReference = attGrp; container.AddAttributeDef(attGrpRef); // isn't this where ... addAttributes(ra.Target as ResolvedAttributeSet, attGrp, attPath + "/members/"); } else { CdmTypeAttributeDefinition att = this.Ctx.Corpus.MakeObject <CdmTypeAttributeDefinition>(CdmObjectType.TypeAttributeDef, ra.ResolvedName, false); att.AttributeContext = this.Ctx.Corpus.MakeObject <CdmAttributeContextReference>(CdmObjectType.AttributeContextRef, raCtx.AtCorpusPath, true); HashSet <string> avoidSet = ctx2traitNames[raCtx]; ResolvedTraitSet rtsAtt = ra.ResolvedTraits; rtsAtt.Set.ForEach(rt => { if (rt.Trait.Ugly != true) { // don't mention your ugly traits if (avoidSet?.Contains(rt.TraitName) != true) { // avoid the ones from the context var traitRef = CdmObjectBase.ResolvedTraitToTraitRef(resOptCopy, rt); ((CdmTypeAttributeDefinition)att).AppliedTraits.Add(traitRef); } } }); // none of the dataformat traits have the bit set that will make them turn into a property // this is intentional so that the format traits make it into the resolved object // but, we still want a guess as the data format, so get it and set it. var impliedDataFormat = att.DataFormat; if (impliedDataFormat != CdmDataFormat.Unknown) { att.DataFormat = impliedDataFormat; } container.AddAttributeDef(att); resAtt2RefPath[ra] = attPath; } }); }; addAttributes(ras, entResolved, entName + "/hasAttributes/"); // any resolved traits that hold arguments with attribute refs should get 'fixed' here Action <CdmTraitReference, string, bool> replaceTraitAttRef = (tr, entityHint, isAttributeContext) => { if (tr.Arguments != null) { foreach (CdmArgumentDefinition arg in tr.Arguments.AllItems) { dynamic v = arg.UnResolvedValue != null ? arg.UnResolvedValue : arg.Value; // is this an attribute reference? if (v != null && (v as CdmObject)?.ObjectType == CdmObjectType.AttributeRef) { // only try this if the reference has no path to it (only happens with intra-entity att refs) var attRef = v as CdmAttributeReference; if (!string.IsNullOrEmpty(attRef.NamedReference) && attRef.NamedReference.IndexOf('/') == -1) { if (arg.UnResolvedValue == null) { arg.UnResolvedValue = arg.Value; } // give a promise that can be worked out later. assumption is that the attribute must come from this entity. var newAttRef = this.Ctx.Corpus.MakeRef <CdmAttributeReference>(CdmObjectType.AttributeRef, entityHint + "/(resolvedAttributes)/" + attRef.NamedReference, true); // inDocument is not propagated during resolution, so set it here newAttRef.InDocument = arg.InDocument; arg.Value = newAttRef; } } } } }; // fix entity traits if (entResolved.ExhibitsTraits != null) { foreach (var et in entResolved.ExhibitsTraits) { replaceTraitAttRef(et, newEntName, false); } } // fix context traits Action <CdmAttributeContext, string> fixContextTraits = null; fixContextTraits = (subAttCtx, entityHint) => { var traitsHere = subAttCtx.ExhibitsTraits; if (traitsHere != null) { foreach (var tr in traitsHere) { replaceTraitAttRef(tr, entityHint, true); } } subAttCtx.Contents.AllItems.ForEach((cr) => { if (cr.ObjectType == CdmObjectType.AttributeContextDef) { // if this is a new entity context, get the name to pass along CdmAttributeContext subSubAttCtx = (CdmAttributeContext)cr; string subEntityHint = entityHint; if (subSubAttCtx.Type == CdmAttributeContextType.Entity) { subEntityHint = subSubAttCtx.Definition.NamedReference; } // do this for all types fixContextTraits(subSubAttCtx, subEntityHint); } }); }; fixContextTraits(attCtx, newEntName); // and the attribute traits var entAttributes = entResolved.Attributes; if (entAttributes != null) { foreach (var entAtt in entAttributes) { CdmTraitCollection attTraits = entAtt.AppliedTraits; if (attTraits != null) { foreach (var tr in attTraits) { replaceTraitAttRef(tr, newEntName, false); } } } } // we are about to put this content created in the context of various documents (like references to attributes from base entities, etc.) // into one specific document. all of the borrowed refs need to work. so, re-write all string references to work from this new document // the catch-22 is that the new document needs these fixes done before it can be used to make these fixes. // the fix needs to happen in the middle of the refresh // trigger the document to refresh current content into the resolved OM attCtx.Parent = null; // remove the fake parent that made the paths work ResolveOptions resOptNew = CdmObjectBase.CopyResolveOptions(resOpt); resOptNew.LocalizeReferencesFor = docRes; resOptNew.WrtDoc = docRes; if (!await docRes.RefreshAsync(resOptNew)) { Logger.Error(nameof(CdmEntityDefinition), this.Ctx as ResolveContext, $"Failed to index the resolved document.", nameof(CreateResolvedEntityAsync)); return(null); } // get a fresh ref entResolved = docRes.FetchObjectFromDocumentPath(entName, resOptNew) as CdmEntityDefinition; this.Ctx.Corpus.resEntMap[this.AtCorpusPath] = entResolved.AtCorpusPath; return(entResolved); }
internal bool ValidateLineage(ResolveOptions resOpt) { // run over the attCtx tree and validate that it is self consistent on lineage // collect all nodes in the tree HashSet <CdmAttributeContext> attCtxInTree = new HashSet <CdmAttributeContext>(); Action <CdmObject> collectAllNodes = null; collectAllNodes = (subItem) => { CdmAttributeContext ac = subItem as CdmAttributeContext; if (ac == null) { return; } attCtxInTree.Add(ac); if (ac.Contents == null || ac.Contents.Count == 0) { return; } // look at all children foreach (var subSub in ac.Contents) { if (subSub.ObjectType == CdmObjectType.AttributeContextDef) { collectAllNodes(subSub); } } }; collectAllNodes(this); // now make sure every lineage ref is in that set Func <CdmObject, bool> CheckLineage = null; CheckLineage = (subItem) => { CdmAttributeContext ac = subItem as CdmAttributeContext; if (ac == null) { return(true); } if (ac.Lineage != null && ac.Lineage.Count > 0) { foreach (var lin in ac.Lineage) { if (!attCtxInTree.Contains(lin.ExplicitReference as CdmAttributeContext)) { return(false); } //if (!resOpt.MapOldCtxToNewCtx.ContainsKey(lin.ExplicitReference as CdmAttributeContext)) //{ //return false; //} } } if (ac.Contents == null || ac.Contents.Count == 0) { return(true); } // look at all children foreach (var subSub in ac.Contents) { if (subSub.ObjectType == CdmObjectType.AttributeContextDef) { if (CheckLineage(subSub) == false) { return(false); } } } return(true); }; CheckLineage(this); return(true); }
internal ResolvedAttributeSet FetchResolvedAttributes(ResolveOptions resOpt = null, AttributeContextParameters acpInContext = null) { bool wasPreviouslyResolving = this.Ctx.Corpus.isCurrentlyResolving; this.Ctx.Corpus.isCurrentlyResolving = true; if (resOpt == null) { resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives); } const string kind = "rasb"; ResolveContext ctx = this.Ctx as ResolveContext; ResolvedAttributeSetBuilder rasbResult = null; // keep track of the context node that the results of this call would like to use as the parent CdmAttributeContext parentCtxForResult = null; ResolvedAttributeSetBuilder rasbCache = this.FetchObjectFromCache(resOpt, acpInContext); CdmAttributeContext underCtx = null; if (acpInContext != null) { parentCtxForResult = acpInContext.under; } // store the previous document set, we will need to add it with // children found from the constructResolvedTraits call SymbolSet currDocRefSet = resOpt.SymbolRefSet; if (currDocRefSet == null) { currDocRefSet = new SymbolSet(); } resOpt.SymbolRefSet = new SymbolSet(); // if using the cache passes the maxDepth, we cannot use it if (rasbCache != null && resOpt.DepthInfo != null && resOpt.DepthInfo.CurrentDepth + rasbCache.ResolvedAttributeSet.DepthTraveled > resOpt.DepthInfo.MaxDepth) { rasbCache = null; } if (rasbCache == null) { if (this.resolvingAttributes) { // re-entered this attribute through some kind of self or looping reference. this.Ctx.Corpus.isCurrentlyResolving = wasPreviouslyResolving; //return new ResolvedAttributeSet(); // uncomment this line as a test to turn off allowing cycles resOpt.InCircularReference = true; this.circularReference = true; } this.resolvingAttributes = true; // a new context node is needed for these attributes, // this tree will go into the cache, so we hang it off a placeholder parent // when it is used from the cache (or now), then this placeholder parent is ignored and the things under it are // put into the 'receiving' tree underCtx = CdmAttributeContext.GetUnderContextForCacheContext(resOpt, this.Ctx, acpInContext); rasbCache = this.ConstructResolvedAttributes(resOpt, underCtx); this.resolvingAttributes = false; if (rasbCache != null) { // register set of possible docs CdmObjectDefinition oDef = this.FetchObjectDefinition <CdmObjectDefinitionBase>(resOpt); if (oDef != null) { ctx.Corpus.RegisterDefinitionReferenceSymbols(oDef, kind, resOpt.SymbolRefSet); // get the new cache tag now that we have the list of docs string cacheTag = ctx.Corpus.CreateDefinitionCacheTag(resOpt, this, kind, acpInContext != null ? "ctx" : null); // save this as the cached version if (!string.IsNullOrWhiteSpace(cacheTag)) { ctx.Cache[cacheTag] = rasbCache; } } // get the 'underCtx' of the attribute set from the acp that is wired into // the target tree underCtx = (rasbCache as ResolvedAttributeSetBuilder).ResolvedAttributeSet.AttributeContext?.GetUnderContextFromCacheContext(resOpt, acpInContext); } if (this.circularReference) { resOpt.InCircularReference = false; } } else { // get the 'underCtx' of the attribute set from the cache. The one stored there was build with a different // acp and is wired into the fake placeholder. so now build a new underCtx wired into the output tree but with // copies of all cached children underCtx = (rasbCache as ResolvedAttributeSetBuilder).ResolvedAttributeSet.AttributeContext?.GetUnderContextFromCacheContext(resOpt, acpInContext); //underCtx.ValidateLineage(resOpt); // debugging } if (rasbCache != null) { // either just built something or got from cache // either way, same deal: copy resolved attributes and copy the context tree associated with it // 1. deep copy the resolved att set (may have groups) and leave the attCtx pointers set to the old tree // 2. deep copy the tree. // 1. deep copy the resolved att set (may have groups) and leave the attCtx pointers set to the old tree rasbResult = new ResolvedAttributeSetBuilder(); rasbResult.ResolvedAttributeSet = (rasbCache as ResolvedAttributeSetBuilder).ResolvedAttributeSet.Copy(); // 2. deep copy the tree and map the context references. if (underCtx != null) // null context? means there is no tree, probably 0 attributes came out { if (underCtx.AssociateTreeCopyWithAttributes(resOpt, rasbResult.ResolvedAttributeSet) == false) { return(null); } } } DepthInfo currDepthInfo = resOpt.DepthInfo; if (this is CdmEntityAttributeDefinition && currDepthInfo != null) { // if we hit the maxDepth, we are now going back up currDepthInfo.CurrentDepth--; // now at the top of the chain where max depth does not influence the cache if (currDepthInfo.CurrentDepth <= 0) { resOpt.DepthInfo = null; } } // merge child document set with current currDocRefSet.Merge(resOpt.SymbolRefSet); resOpt.SymbolRefSet = currDocRefSet; this.Ctx.Corpus.isCurrentlyResolving = wasPreviouslyResolving; return(rasbResult?.ResolvedAttributeSet); }
/// <inheritdoc /> internal override ProjectionAttributeStateSet AppendProjectionAttributeState( ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create a new attribute context for the operation AttributeContextParameters attrCtxOpExcludeAttrsParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationExcludeAttributes, Name = $"operation/index{Index}/operationExcludeAttributes" }; CdmAttributeContext attrCtxOpExcludeAttrs = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpExcludeAttrsParam); // Get the top-level attribute names of the attributes to exclude // We use the top-level names because the exclude list may contain a previous name our current resolved attributes had Dictionary <string, string> topLevelExcludeAttributeNames = ProjectionResolutionCommonUtil.GetTopList(projCtx, this.ExcludeAttributes); // Initialize a projection attribute context tree builder with the created attribute context for the operation ProjectionAttributeContextTreeBuilder attrCtxTreeBuilder = new ProjectionAttributeContextTreeBuilder(attrCtxOpExcludeAttrs); // 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 exclude // If this attribute is not in the exclude list, then we are including it in the output if (!topLevelExcludeAttributeNames.ContainsKey(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(null, currentPAS, currentPAS.CurrentResolvedAttribute, CdmAttributeContextType.AttributeDefinition, currentPAS.CurrentResolvedAttribute.AttCtx, // lineage is the included attribute null); // don't know who will point here yet // Create a projection attribute state for the included 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 not in the exclude list ProjectionAttributeState newPAS = currentPAS.Copy(); projOutputSet.Add(newPAS); } else { // The current projection attribute state's resolved attribute is in the exclude list // Get the attribute name the way it appears in the exclude list string excludeAttributeName = null; topLevelExcludeAttributeNames.TryGetValue(currentPAS.CurrentResolvedAttribute.ResolvedName, out excludeAttributeName); // 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(excludeAttributeName, currentPAS, currentPAS.CurrentResolvedAttribute, CdmAttributeContextType.AttributeDefinition, currentPAS.CurrentResolvedAttribute.AttCtx, // lineage is the included attribute null); // don't know who will point here yet, excluded, so... this could be the end for you. } } // Create all the attribute contexts and construct the tree attrCtxTreeBuilder.ConstructAttributeContextTree(projCtx); return(projOutputSet); }
/// <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); // 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, acGenAttrSet); // 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); }
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-definitions from (in this order): // an extended entity, traits applied to extended entity, exhibited traits of main entity, the (datatype or entity) used as an attribute, traits applied to that datatype or entity, // the relationsip of the attribute, the attribute definition itself and included attribute groups, dynamic traits applied to the attribute. this.Rasb = new ResolvedAttributeSetBuilder(); this.Rasb.ResolvedAttributeSet.AttributeContext = under; if (this.ExtendsEntity != null) { CdmObjectReference extRef = this.ExtendsEntityRef; CdmAttributeContext extendsRefUnder = null; AttributeContextParameters acpExtEnt = null; if (under != null) { AttributeContextParameters acpExt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.EntityReferenceExtends, Name = "extends", Regarding = null, IncludeTraits = false }; extendsRefUnder = this.Rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpExt); acpExtEnt = new AttributeContextParameters { under = extendsRefUnder, type = CdmAttributeContextType.Entity, Name = extRef.NamedReference, Regarding = extRef, IncludeTraits = false }; } // save moniker, extended entity may attach a different moniker that we do not // want to pass along to getting this entities attributes string oldMoniker = resOpt.FromMoniker; this.Rasb.MergeAttributes((this.ExtendsEntityRef as CdmObjectReferenceBase).FetchResolvedAttributes(resOpt, acpExtEnt)); if (this.ExtendsEntityResolutionGuidance != null) { // some guidance was given on how to integrate the base attributes into the set. apply that guidance ResolvedTraitSet rtsBase = this.FetchResolvedTraits(resOpt); // this context object holds all of the info about what needs to happen to resolve these attributes. // make a copy and set defaults if needed CdmAttributeResolutionGuidance resGuide = (CdmAttributeResolutionGuidance)this.ExtendsEntityResolutionGuidance.Copy(resOpt); resGuide.UpdateAttributeDefaults(resGuide.FetchObjectDefinitionName()); // holds all the info needed by the resolver code AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuide, rtsBase); this.Rasb.GenerateApplierAttributes(arc, false); // true = apply the prepared traits to new atts } // reset to the old moniker resOpt.FromMoniker = oldMoniker; } this.Rasb.MarkInherited(); this.Rasb.ResolvedAttributeSet.AttributeContext = under; if (this.Attributes != null) { int l = this.Attributes.Count; for (int i = 0; i < l; i++) { dynamic att = this.Attributes.AllItems[i]; CdmAttributeContext attUnder = under; AttributeContextParameters acpAtt = null; if (under != null) { acpAtt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeDefinition, Name = att.FetchObjectDefinitionName(), Regarding = att, IncludeTraits = false }; } this.Rasb.MergeAttributes(att.FetchResolvedAttributes(resOpt, acpAtt)); } } this.Rasb.MarkOrder(); this.Rasb.ResolvedAttributeSet.AttributeContext = under; // things that need to go away this.Rasb.RemoveRequestedAtts(); return(this.Rasb); }
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; CdmAttributeContext underAtt = under; AttributeContextParameters acpEnt = null; var ctxEntObjDef = ctxEnt.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (ctxEntObjDef?.ObjectType == CdmObjectType.ProjectionDef) { // A Projection ProjectionDirective projDirective = new ProjectionDirective(resOpt, this, ownerRef: ctxEnt); CdmProjection projDef = (CdmProjection)ctxEntObjDef; ProjectionContext projCtx = projDef.ConstructProjectionContext(projDirective, under); ResolvedAttributeSet ras = projDef.ExtractResolvedAttributes(projCtx); rasb.ResolvedAttributeSet = ras; } else { // An Entity Reference 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?.Attributes; 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; CdmAttributeContextType pickEntType = (pickEnt.FetchObjectDefinition <CdmObjectDefinition>(resOpt).ObjectType == CdmObjectType.ProjectionDef) ? CdmAttributeContextType.Projection : CdmAttributeContextType.Entity; AttributeContextParameters acpEntAttEnt = new AttributeContextParameters { under = pickUnder, type = pickEntType, 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.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) { if (att.ResolvedTraits != null) { 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); if (entDef != null) { var otherResTraits = entRef.FetchResolvedTraits(resOpt); ResolvedTrait identifyingTrait; if (otherResTraits != null && (identifyingTrait = otherResTraits.Find(resOpt, "is.identifiedBy")) != 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?.Attributes.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, 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.ResolvedAttributeSet.AttributeContext = raSub.AttCtx; // this got set to null with the new builder rasb.OwnOne(raSub); } } return(rasb); }
internal bool PruneToScope(HashSet <CdmAttributeContext> scopeSet) { // run over the whole tree and make a set of the nodes that should be saved for sure. This is anything NOT under a generated set // (so base entity chains, entity attributes entity definitions) // for testing, don't delete this //Func<CdmObject, long> CountNodes = null; //CountNodes = (subItem) => //{ // if (!(subItem is CdmAttributeContext)) // { // return 1; // } // CdmAttributeContext ac = subItem as CdmAttributeContext; // if (ac.Contents == null || ac.Contents.Count == 0) // { // return 1; // } // // look at all children // long total = 0; // foreach (var subSub in ac.Contents) // { // total += CountNodes(subSub); // } // return 1 + total; //}; //System.Diagnostics.Debug.WriteLine($"Pre Prune {CountNodes(this)}"); // so ... the change from the old behavior is to depend on the lineage pointers to save the attribute defs // in the 'structure' part of the tree that might matter. keep all of the other structure info and keep some // special nodes (like the ones that have removed attributes) that won't get found from lineage trace but that are // needed to understand what took place in resolution HashSet <CdmAttributeContext> nodesToSave = new HashSet <CdmAttributeContext>(); // helper that save the passed node and anything up the parent chain Func <CdmAttributeContext, bool> SaveParentNodes = null; SaveParentNodes = (currNode) => { if (nodesToSave.Contains(currNode)) { return(true); } nodesToSave.Add(currNode); // get the parent if (currNode.Parent?.ExplicitReference != null) { return(SaveParentNodes(currNode.Parent.ExplicitReference as CdmAttributeContext)); } return(true); }; // helper that saves the current node (and parents) plus anything in the lineage (with their parents) Func <CdmAttributeContext, bool> SaveLineageNodes = null; SaveLineageNodes = (currNode) => { if (!SaveParentNodes(currNode)) { return(false); } if (currNode.Lineage != null && currNode.Lineage.Count > 0) { foreach (var lin in currNode.Lineage) { if (lin.ExplicitReference != null) { if (!SaveLineageNodes(lin.ExplicitReference as CdmAttributeContext)) { return(false); } } } } return(true); }; Func <CdmObject, bool, bool, bool, bool> SaveStructureNodes = null; SaveStructureNodes = (subItem, inGenerated, inProjection, inRemove) => { if (!(subItem is CdmAttributeContext)) { return(true); } CdmAttributeContext ac = subItem as CdmAttributeContext; if (ac.Type == CdmAttributeContextType.GeneratedSet) { inGenerated = true; // special mode where we hate everything except the removed att notes } if (inGenerated && ac.Type == CdmAttributeContextType.OperationExcludeAttributes) { inRemove = true; // triggers us to know what to do in the next code block. } bool removedAttribute = false; if (ac.Type == CdmAttributeContextType.AttributeDefinition) { // empty attribute nodes are descriptions of source attributes that may or may not be needed. lineage will sort it out. // the exception is for attribute descriptions under a remove attributes operation. they are gone from the resolved att set, so // no history would remain if (inRemove) { removedAttribute = true; } else if (ac.Contents == null || ac.Contents.Count == 0) { return(true); } } // this attribute was removed by a projection operation, but we want to keep the node to indicate what the operation did if (ac.Type == CdmAttributeContextType.AttributeExcluded) { removedAttribute = true; } if (!inGenerated || removedAttribute) { // mark this as something worth saving, sometimes // these get discovered at the leaf of a tree that we want to mostly ignore, so can cause a // discontinuity in the 'save' chains, so fix that SaveLineageNodes(ac); } if (ac.Type == CdmAttributeContextType.Projection) { inProjection = true; // track this so we can do the next thing ... } if (ac.Type == CdmAttributeContextType.Entity && inProjection) { // this is far enough, the entity that is somewhere under a projection chain // things under this might get saved through lineage, but down to this point will get in for sure return(true); } if (ac.Contents == null || ac.Contents.Count == 0) { return(true); } // look at all children foreach (var subSub in ac.Contents) { if (!SaveStructureNodes(subSub, inGenerated, inProjection, inRemove)) { return(false); } } return(true); }; if (!SaveStructureNodes(this, false, false, false)) { return(false); } // next, look at the attCtx for every resolved attribute. follow the lineage chain and mark all of those nodes as ones to save // also mark any parents of those as savers // so, do that ^^^ for every primary context found earlier foreach (var primCtx in scopeSet) { if (!SaveLineageNodes(primCtx)) { return(false); } } // now the cleanup, we have a set of the nodes that should be saved // run over the tree and re-build the contents collection with only the things to save Func <CdmObject, bool> CleanSubGroup = null; CleanSubGroup = (subItem) => { if (subItem.ObjectType == CdmObjectType.AttributeRef) { return(true); // not empty } CdmAttributeContext ac = subItem as CdmAttributeContext; if (!nodesToSave.Contains(ac)) { return(false); // don't even look at content, this all goes away } if (ac.Contents != null && ac.Contents.Count > 0) { // need to clean up the content array without triggering the code that fixes in document or paths var newContent = new List <CdmObject>(); foreach (var sub in ac.Contents) { // true means keep this as a child if (CleanSubGroup(sub)) { newContent.Add(sub); } } // clear the old content and replace ac.Contents.Clear(); ac.Contents.AddRange(newContent); } return(true); }; CleanSubGroup(this); //System.Diagnostics.Debug.WriteLine($"Post Prune {CountNodes(this)}"); return(true); }