Exemple #1
0
        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);
        }
Exemple #4
0
 internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null)
 {
     return(null);
 }
Exemple #5
0
        /// <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);
        }
Exemple #6
0
        /// <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);
        }
Exemple #9
0
        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);
        }
Exemple #14
0
        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);
        }
Exemple #15
0
        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);
        }
Exemple #16
0
        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);
        }
Exemple #17
0
        /// <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);
        }
Exemple #18
0
        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);
        }
Exemple #21
0
        /// <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);
        }
Exemple #22
0
        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);
        }
Exemple #23
0
        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);
        }
Exemple #24
0
        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);
        }