示例#1
0
        public override CdmObject Copy(ResolveOptions resOpt = null)
        {
            if (resOpt == null)
            {
                resOpt = new ResolveOptions(this);
            }

            CdmEntityDefinition copy = new CdmEntityDefinition(this.Ctx, this.EntityName, null);

            copy.ExtendsEntity = copy.ExtendsEntity != null ? (CdmEntityReference)this.ExtendsEntity.Copy(resOpt) : null;
            copy.ExtendsEntityResolutionGuidance = this.ExtendsEntityResolutionGuidance != null ? (CdmAttributeResolutionGuidance)this.ExtendsEntityResolutionGuidance.Copy(resOpt) : null;
            copy.AttributeContext = copy.AttributeContext != null ? (CdmAttributeContext)this.AttributeContext.Copy(resOpt) : null;
            foreach (var att in this.Attributes)
            {
                copy.Attributes.Add(att);
            }
            this.CopyDef(resOpt, copy);
            return(copy);
        }
示例#2
0
        /// <summary>
        /// Creates an Entity Declaration based on an Entity Definition and adds it to the list.
        /// </summary>
        /// <param name="entity">The entity definition used to create the entity declaration.</param>
        /// <param name="simpleRef">Parameter is unused for this collection. Kept for consistency with other CdmCollections.</param>
        /// <returns>The entity declaration that was added to the collection.</returns>
        public CdmEntityDeclarationDefinition Add(CdmEntityDefinition entity, bool simpleRef = false)
        {
            var cdmCorpus = this.Ctx.Corpus;

            if (entity.Owner == null)
            {
                entity.Owner = this.Owner;
                if (entity.Owner == null)
                {
                    throw new System.ArgumentException("Expected entity to have an \"Owner\" document set. Cannot create entity declaration to add to manifest.");
                }
            }

            var entityDeclaration = (this.Ctx.Corpus as CdmCorpusDefinition).MakeObject <CdmEntityDeclarationDefinition>(this.DefaultType, entity.EntityName, simpleRef);

            entityDeclaration.Owner       = this.Owner;
            entityDeclaration.EntityPath  = this.Ctx.Corpus.Storage.CreateRelativeCorpusPath($"{entity.Owner.AtCorpusPath}/{entity.EntityName}", this.Owner.InDocument);
            entityDeclaration.Explanation = entity.Explanation;
            return(this.Add(entityDeclaration));
        }
示例#3
0
        /// <summary>
        /// Populates the relationships that the entities in the current manifest are involved in.
        /// </summary>
        public async Task PopulateManifestRelationshipsAsync(CdmRelationshipDiscoveryStyle option = CdmRelationshipDiscoveryStyle.All)
        {
            using (Logger.EnterScope(nameof(CdmManifestDefinition), Ctx, nameof(PopulateManifestRelationshipsAsync)))
            {
                this.Relationships.Clear();
                HashSet <string> relCache = new HashSet <string>();

                if (this.Entities != null)
                {
                    foreach (CdmEntityDeclarationDefinition entDec in this.Entities)
                    {
                        string entPath = await this.GetEntityPathFromDeclaration(entDec, this);

                        CdmEntityDefinition currEntity = await this.Ctx.Corpus.FetchObjectAsync <CdmEntityDefinition>(entPath);

                        if (currEntity == null)
                        {
                            continue;
                        }

                        // handle the outgoing relationships
                        List <CdmE2ERelationship> outgoingRels = this.Ctx.Corpus.FetchOutgoingRelationships(currEntity);
                        if (outgoingRels != null)
                        {
                            foreach (CdmE2ERelationship rel in outgoingRels)
                            {
                                string cacheKey = rel2CacheKey(rel);
                                if (!relCache.Contains(cacheKey) && this.IsRelAllowed(rel, option))
                                {
                                    this.Relationships.Add(this.LocalizeRelToManifest(rel));
                                    relCache.Add(cacheKey);
                                }
                            }
                        }

                        List <CdmE2ERelationship> incomingRels = this.Ctx.Corpus.FetchIncomingRelationships(currEntity);

                        if (incomingRels != null)
                        {
                            foreach (CdmE2ERelationship inRel in incomingRels)
                            {
                                // get entity object for current toEntity
                                CdmEntityDefinition currentInBase = await this.Ctx.Corpus.FetchObjectAsync <CdmEntityDefinition>(inRel.ToEntity, this);

                                if (currentInBase == null)
                                {
                                    continue;
                                }

                                // create graph of inheritance for to currentInBase
                                // graph represented by an array where entity at i extends entity at i+1
                                List <CdmEntityDefinition> toInheritanceGraph = new List <CdmEntityDefinition>();
                                while (currentInBase != null)
                                {
                                    var resOpt = new ResolveOptions
                                    {
                                        WrtDoc = currentInBase.InDocument
                                    };
                                    currentInBase = currentInBase.ExtendsEntity?.FetchObjectDefinition <CdmEntityDefinition>(resOpt);
                                    if (currentInBase != null)
                                    {
                                        toInheritanceGraph.Add(currentInBase);
                                    }
                                }

                                // add current incoming relationship
                                string cacheKey = rel2CacheKey(inRel);
                                if (!relCache.Contains(cacheKey) && this.IsRelAllowed(inRel, option))
                                {
                                    this.Relationships.Add(this.LocalizeRelToManifest(inRel));
                                    relCache.Add(cacheKey);
                                }

                                // if A points at B, A's base classes must point at B as well
                                foreach (CdmEntityDefinition baseEntity in toInheritanceGraph)
                                {
                                    List <CdmE2ERelationship> incomingRelsForBase = this.Ctx.Corpus.FetchIncomingRelationships(baseEntity);

                                    if (incomingRelsForBase != null)
                                    {
                                        foreach (CdmE2ERelationship inRelBase in incomingRelsForBase)
                                        {
                                            CdmE2ERelationship newRel = new CdmE2ERelationship(this.Ctx, "")
                                            {
                                                FromEntity          = inRelBase.FromEntity,
                                                FromEntityAttribute = inRelBase.FromEntityAttribute,
                                                ToEntity            = inRel.ToEntity,
                                                ToEntityAttribute   = inRel.ToEntityAttribute
                                            };

                                            string baseRelCacheKey = rel2CacheKey(newRel);
                                            if (!relCache.Contains(baseRelCacheKey) && this.IsRelAllowed(newRel, option))
                                            {
                                                this.Relationships.Add(this.LocalizeRelToManifest(newRel));
                                                relCache.Add(baseRelCacheKey);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (this.SubManifests != null)
                    {
                        foreach (CdmManifestDeclarationDefinition subManifestDef in this.SubManifests)
                        {
                            var corpusPath  = this.Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(subManifestDef.Definition, this);
                            var subManifest = await this.Ctx.Corpus.FetchObjectAsync <CdmManifestDefinition>(corpusPath);

                            await subManifest.PopulateManifestRelationshipsAsync(option);
                        }
                    }
                }
            }
        }
示例#4
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 the wrtDoc needs to be indexed (like it was just modified) then do that first
            if (!await resOpt.WrtDoc.IndexIfNeeded(resOpt, true))
            {
                Logger.Error(nameof(CdmEntityDefinition), this.Ctx as ResolveContext, $"Couldn't index source document.", 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);
            }

            // 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);
        }
        // the only thing we need this code for is testing!!!
        public override ResolvedEntityReferenceSet FetchResolvedEntityReferences(ResolveOptions resOpt = null)
        {
            if (resOpt == null)
            {
                resOpt = new ResolveOptions(this);
            }

            ResolvedTraitSet rtsThisAtt = this.FetchResolvedTraits(resOpt);
            CdmAttributeResolutionGuidance resGuide = (CdmAttributeResolutionGuidance)this.ResolutionGuidance;

            // this context object holds all of the info about what needs to happen to resolve these attributes
            AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuide, rtsThisAtt);

            RelationshipInfo relInfo = this.GetRelationshipInfo(resOpt, arc);
            if (relInfo.IsByRef && !relInfo.IsArray)
            {
                {
                    // only place this is used, so logic here instead of encapsulated. 
                    // make a set and the one ref it will hold
                    ResolvedEntityReferenceSet rers = new ResolvedEntityReferenceSet(resOpt);
                    ResolvedEntityReference rer = new ResolvedEntityReference();
                    // referencing attribute(s) come from this attribute
                    rer.Referencing.ResolvedAttributeSetBuilder.MergeAttributes(this.FetchResolvedAttributes(resOpt, null));
                    Func<CdmEntityReference, ResolvedEntityReferenceSide> resolveSide = entRef =>
                    {
                        ResolvedEntityReferenceSide sideOther = new ResolvedEntityReferenceSide(null, null);
                        if (entRef != null)
                        {
                            // reference to the other entity, hard part is the attribue name.
                            // by convention, this is held in a trait that identifies the key
                            sideOther.Entity = entRef.FetchObjectDefinition<CdmEntityDefinition>(resOpt);
                            if (sideOther.Entity != null)
                            {
                                CdmAttribute otherAttribute;
                                ResolveOptions otherOpts = new ResolveOptions { WrtDoc = resOpt.WrtDoc, Directives = resOpt.Directives };
                                ResolvedTrait t = entRef.FetchResolvedTraits(otherOpts).Find(otherOpts, "is.identifiedBy");
                                if (t?.ParameterValues?.Length > 0)
                                {
                                    dynamic otherRef = (t.ParameterValues.FetchParameterValueByName("attribute").Value);
                                    if (typeof(CdmObject).IsAssignableFrom(otherRef?.GetType()))
                                    {
                                        otherAttribute = (otherRef as CdmObject).FetchObjectDefinition<CdmObjectDefinition>(otherOpts) as CdmAttribute;
                                        if (otherAttribute != null)
                                        {
                                            sideOther.ResolvedAttributeSetBuilder.OwnOne(sideOther.Entity.FetchResolvedAttributes(otherOpts).Get(otherAttribute.GetName()).Copy());
                                        }
                                    }
                                }
                            }
                        }

                        return sideOther;
                    };

                    // either several or one entity
                    // for now, a sub for the 'select one' idea
                    if ((this.Entity as CdmEntityReference).ExplicitReference != null)
                    {
                        CdmEntityDefinition entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt);
                        CdmCollection<CdmAttributeItem> attsPick = entPickFrom.GetAttributeDefinitions();
                        if (attsPick != null && attsPick != null)
                        {
                            for (int i = 0; i < attsPick.Count; i++)
                            {
                                if (attsPick.AllItems[i].ObjectType == CdmObjectType.EntityAttributeDef)
                                {
                                    CdmEntityReference er = (attsPick.AllItems[i] as CdmEntityAttributeDefinition).Entity;
                                    rer.Referenced.Add(resolveSide(er));
                                }
                            }
                        }
                    }
                    else
                    {
                        rer.Referenced.Add(resolveSide(this.Entity as CdmEntityReference));
                    }

                    rers.Set.Add(rer);
                    return rers;
                }
            }
            return null;
        }
        internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null)
        {
            // find and cache the complete set of attributes
            // attributes definitions originate from and then get modified by subsequent re-defintions from (in this order):
            // the entity used as an attribute, traits applied to that entity,
            // the purpose of the attribute, any traits applied to the attribute.
            ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder();
            CdmEntityReference ctxEnt = this.Entity as CdmEntityReference;
            CdmAttributeContext underAtt = (CdmAttributeContext)under;
            AttributeContextParameters acpEnt = null;
            if (underAtt != null)
            {
                // make a context for this attreibute that holds the attributes that come up from the entity
                acpEnt = new AttributeContextParameters
                {
                    under = underAtt,
                    type = CdmAttributeContextType.Entity,
                    Name = ctxEnt.FetchObjectDefinitionName(),
                    Regarding = ctxEnt,
                    IncludeTraits = true
                };
            }

            ResolvedTraitSet rtsThisAtt = this.FetchResolvedTraits(resOpt);

            // this context object holds all of the info about what needs to happen to resolve these attributes.
            // make a copy and add defaults if missing
            CdmAttributeResolutionGuidance resGuideWithDefault;
            if (this.ResolutionGuidance != null)
                resGuideWithDefault = (CdmAttributeResolutionGuidance)this.ResolutionGuidance.Copy(resOpt);
            else
                resGuideWithDefault = new CdmAttributeResolutionGuidance(this.Ctx);
            resGuideWithDefault.UpdateAttributeDefaults(this.Name);

            AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuideWithDefault, rtsThisAtt);

            // complete cheating but is faster.
            // this purpose will remove all of the attributes that get collected here, so dumb and slow to go get them
            RelationshipInfo relInfo = this.GetRelationshipInfo(arc.ResOpt, arc);
            if (relInfo.IsByRef)
            {
                // make the entity context that a real recursion would have give us
                if (under != null)
                    under = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEnt);
                // if selecting from one of many attributes, then make a context for each one
                if (under != null && relInfo.SelectsOne)
                {
                    // the right way to do this is to get a resolved entity from the embedded entity and then 
                    // look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes
                    // that seems like a disaster waiting to happen given endless looping, etc.
                    // for now, just insist that only the top level entity attributes declared in the ref entity will work
                    CdmEntityDefinition entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt) as CdmEntityDefinition;
                    CdmCollection<CdmAttributeItem> attsPick = entPickFrom?.GetAttributeDefinitions();
                    if (entPickFrom != null && attsPick != null)
                    {
                        for (int i = 0; i < attsPick.Count; i++)
                        {
                            if (attsPick.AllItems[i].ObjectType == CdmObjectType.EntityAttributeDef)
                            {
                                // a table within a table. as expected with a selectsOne attribute
                                // since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key
                                // these are the same contexts that would get created if we recursed
                                // first this attribute
                                AttributeContextParameters acpEntAtt = new AttributeContextParameters
                                {
                                    under = under,
                                    type = CdmAttributeContextType.AttributeDefinition,
                                    Name = attsPick.AllItems[i].FetchObjectDefinitionName(),
                                    Regarding = attsPick.AllItems[i],
                                    IncludeTraits = true
                                };
                                CdmAttributeContext pickUnder = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAtt);
                                CdmEntityReference pickEnt = (attsPick.AllItems[i] as CdmEntityAttributeDefinition).Entity as CdmEntityReference;
                                AttributeContextParameters acpEntAttEnt = new AttributeContextParameters
                                {
                                    under = pickUnder,
                                    type = CdmAttributeContextType.Entity,
                                    Name = pickEnt.FetchObjectDefinitionName(),
                                    Regarding = pickEnt,
                                    IncludeTraits = true
                                };
                                rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAttEnt);
                            }
                        }
                    }
                }

                // if we got here because of the max depth, need to impose the directives to make the trait work as expected
                if (relInfo.MaxDepthExceeded)
                {
                    if (arc.ResOpt.Directives == null)
                        arc.ResOpt.Directives = new AttributeResolutionDirectiveSet();
                    arc.ResOpt.Directives.Add("referenceOnly");
                }
            }
            else
            {
                ResolveOptions resLink = CopyResolveOptions(resOpt);
                resLink.SymbolRefSet = resOpt.SymbolRefSet;
                resLink.RelationshipDepth = relInfo.NextDepth;
                rasb.MergeAttributes((this.Entity as CdmEntityReference).FetchResolvedAttributes(resLink, acpEnt));
            }

            // from the traits of purpose and applied here, see if new attributes get generated
            rasb.ResolvedAttributeSet.AttributeContext = underAtt;
            rasb.ApplyTraits(arc);
            rasb.GenerateApplierAttributes(arc, true); // true = apply the prepared traits to new atts
            // this may have added symbols to the dependencies, so merge them
            resOpt.SymbolRefSet.Merge(arc.ResOpt.SymbolRefSet);

            // use the traits for linked entity identifiers to record the actual foreign key links
            if (rasb.ResolvedAttributeSet?.Set != null && relInfo.IsByRef)
            {
                foreach (var att in rasb.ResolvedAttributeSet.Set)
                {
                    var reqdTrait = att.ResolvedTraits.Find(resOpt, "is.linkedEntity.identifier");
                    if (reqdTrait == null)
                    {
                        continue;
                    }

                    if (reqdTrait.ParameterValues == null || reqdTrait.ParameterValues.Length == 0)
                    {
                        Logger.Warning(nameof(CdmEntityAttributeDefinition), this.Ctx as ResolveContext, "is.linkedEntity.identifier does not support arguments");
                        continue;
                    }

                    var entReferences = new List<string>();
                    var attReferences = new List<string>();
                    Action<CdmEntityReference, string> addEntityReference = (CdmEntityReference entRef, string nameSpace) =>
                        {
                            var entDef = entRef.FetchObjectDefinition<CdmEntityDefinition>(resOpt);
                            var identifyingTrait = entRef.FetchResolvedTraits(resOpt).Find(resOpt, "is.identifiedBy");
                            if (identifyingTrait != null && entDef != null)
                            {
                                var attRef = identifyingTrait.ParameterValues.FetchParameterValueByName("attribute").Value;
                                string attNamePath = ((CdmObjectReferenceBase)attRef).NamedReference;
                                string attName = attNamePath.Split('/').Last();                                // path should be absolute and without a namespace
                                string relativeEntPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(entDef.AtCorpusPath, entDef.InDocument);
                                if (relativeEntPath.StartsWith($"{nameSpace}:"))
                                {
                                    relativeEntPath = relativeEntPath.Substring(nameSpace.Length + 1);
                                }
                                entReferences.Add(relativeEntPath);
                                attReferences.Add(attName);
                            }
                        };
                    if (relInfo.SelectsOne)
                    {
                        var entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt) as CdmEntityDefinition;
                        var attsPick = entPickFrom?.GetAttributeDefinitions()?.Cast<CdmObject>().ToList();
                        if (entPickFrom != null && attsPick != null)
                        {
                            for (int i = 0; i < attsPick.Count; i++)
                            {
                                if (attsPick[i].ObjectType == CdmObjectType.EntityAttributeDef)
                                {
                                    var entAtt = attsPick[i] as CdmEntityAttributeDefinition;
                                    addEntityReference(entAtt.Entity, this.InDocument.Namespace);
                                }
                            }
                        }
                    }
                    else
                    {
                        addEntityReference(this.Entity, this.InDocument.Namespace);
                    }

                    var constantEntity = this.Ctx.Corpus.MakeObject<CdmConstantEntityDefinition>(CdmObjectType.ConstantEntityDef);
                    constantEntity.EntityShape = this.Ctx.Corpus.MakeRef<CdmEntityReference>(CdmObjectType.EntityRef, "entityGroupSet", true);
                    constantEntity.ConstantValues = entReferences.Select((entRef, idx) => new List<string> { entRef, attReferences[idx] }).ToList();
                    var traitParam = this.Ctx.Corpus.MakeRef<CdmEntityReference>(CdmObjectType.EntityRef, constantEntity, false);
                    reqdTrait.ParameterValues.SetParameterValue(resOpt, "entityReferences", traitParam);
                }
            }

            // a 'structured' directive wants to keep all entity attributes together in a group
            if (arc.ResOpt.Directives?.Has("structured") == true)
            {
                ResolvedAttribute raSub = new ResolvedAttribute(rtsThisAtt.ResOpt, rasb.ResolvedAttributeSet, this.Name, (CdmAttributeContext)rasb.ResolvedAttributeSet.AttributeContext);
                if (relInfo.IsArray)
                {
                    // put a resolved trait on this att group, yuck, hope I never need to do this again and then need to make a function for this
                    CdmTraitReference tr = this.Ctx.Corpus.MakeObject<CdmTraitReference>(CdmObjectType.TraitRef, "is.linkedEntity.array", true);
                    var t = tr.FetchObjectDefinition<CdmTraitDefinition>(resOpt);
                    ResolvedTrait rt = new ResolvedTrait(t, null, new List<dynamic>(), new List<bool>());
                    raSub.ResolvedTraits = raSub.ResolvedTraits.Merge(rt, true);
                }
                rasb = new ResolvedAttributeSetBuilder();
                rasb.OwnOne(raSub);
            }

            return rasb;
        }
示例#7
0
        /// <summary>
        /// Creates a resolved copy of the manifest.
        /// newEntityDocumentNameFormat specifies a pattern to use when creating documents for resolved entites.
        /// The default is "{f}resolved/{n}.cdm.json" to avoid a document name conflict with documents in the same folder as the manifest.
        /// Every instance of the string {n} is replaced with the entity name from the source manifest.
        /// Every instance of the string {f} is replaced with the folder path from the source manifest to the source entity
        /// (if there is one that is possible as a relative location, else nothing).
        /// </summary>
        /// <param name="newManifestName"></param>
        /// <param name="newEntityDocumentNameFormat"></param>
        /// <param name="Directives"></param>
        /// <returns></returns>
        public async Task <CdmManifestDefinition> CreateResolvedManifestAsync(string newManifestName, string newEntityDocumentNameFormat, AttributeResolutionDirectiveSet Directives = null)
        {
            using (Logger.EnterScope(nameof(CdmManifestDefinition), Ctx, nameof(CreateResolvedManifestAsync)))
            {
                if (this.Entities == null)
                {
                    return(null);
                }
                if (this.Owner == null)
                {
                    Logger.Error(this.Ctx as ResolveContext, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, CdmLogCode.ErrResolveManifestFailed, this.ManifestName);
                    return(null);
                }

                if (newEntityDocumentNameFormat == null)
                {
                    newEntityDocumentNameFormat = "{f}resolved/{n}.cdm.json";
                }
                else if (newEntityDocumentNameFormat == "") // for back compat
                {
                    newEntityDocumentNameFormat = "{n}.cdm.json";
                }
                else if (!newEntityDocumentNameFormat.Contains("{n}")) // for back compat
                {
                    newEntityDocumentNameFormat = newEntityDocumentNameFormat + "/{n}.cdm.json";
                }

                string sourceManifestPath       = this.Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(this.AtCorpusPath, this);
                string sourceManifestFolderPath = this.Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(this.Owner.AtCorpusPath, this);

                int resolvedManifestPathSplit = newManifestName.LastIndexOf("/") + 1;
                CdmFolderDefinition resolvedManifestFolder;
                if (resolvedManifestPathSplit > 0)
                {
                    var resolvedManifestPath = newManifestName.Substring(0, resolvedManifestPathSplit);
                    var newFolderPath        = this.Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(resolvedManifestPath, this);
                    resolvedManifestFolder = await this.Ctx.Corpus.FetchObjectAsync <CdmFolderDefinition>(newFolderPath);

                    if (resolvedManifestFolder == null)
                    {
                        Logger.Error(this.Ctx as ResolveContext, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, CdmLogCode.ErrResolveFolderNotFound, newFolderPath);
                        return(null);
                    }
                    newManifestName = newManifestName.Substring(resolvedManifestPathSplit);
                }
                else
                {
                    resolvedManifestFolder = this.Owner as CdmFolderDefinition;
                }

                if (resolvedManifestFolder.Documents.Item(newManifestName) != null)
                {
                    Logger.Error(this.Ctx as ResolveContext, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, CdmLogCode.ErrResolveManifestExists, newManifestName, resolvedManifestFolder.AtCorpusPath);
                    return(null);
                }

                Logger.Debug(this.Ctx as ResolveContext, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, $"resolving manifest {sourceManifestPath}");

                // Using the references present in the resolved entities, get an entity
                // create an imports doc with all the necessary resolved entity references and then resolve it
                // sometimes they might send the docname, that makes sense a bit, don't include the suffix in the name
                if (newManifestName.ToLowerInvariant().EndsWith(".manifest.cdm.json"))
                {
                    newManifestName = newManifestName.Substring(0, newManifestName.Length - ".manifest.cdm.json".Length);
                }
                var resolvedManifest = new CdmManifestDefinition(this.Ctx, newManifestName);

                // bring over any imports in this document or other bobbles
                resolvedManifest.Schema          = this.Schema;
                resolvedManifest.Explanation     = this.Explanation;
                resolvedManifest.DocumentVersion = this.DocumentVersion;
                foreach (CdmImport imp in this.Imports)
                {
                    resolvedManifest.Imports.Add((CdmImport)imp.Copy());
                }

                // add the new document to the folder
                if (resolvedManifestFolder.Documents.Add(resolvedManifest) == null)
                {
                    // when would this happen?
                    return(null);
                }

                foreach (var entity in this.Entities)
                {
                    string entityPath = await this.GetEntityPathFromDeclaration(entity, this);

                    CdmEntityDefinition entDef = await this.Ctx.Corpus.FetchObjectAsync <CdmEntityDefinition>(entityPath);

                    if (entDef == null)
                    {
                        Logger.Error(this.Ctx, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, CdmLogCode.ErrResolveEntityFailure, entityPath);
                    }

                    if (entDef.InDocument.Owner == null)
                    {
                        Logger.Error(this.Ctx as ResolveContext, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, CdmLogCode.ErrDocIsNotFolder, entDef.EntityName);
                        return(null);
                    }
                    // get the path from this manifest to the source entity. this will be the {f} replacement value
                    string sourceEntityFullPath = this.Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(entDef.InDocument.Owner.AtCorpusPath, this);
                    string f = "";
                    if (sourceEntityFullPath.StartsWith(sourceManifestFolderPath))
                    {
                        f = sourceEntityFullPath.Substring(sourceManifestFolderPath.Length);
                    }

                    string newDocumentFullPath = newEntityDocumentNameFormat.Replace("{n}", entDef.EntityName);

                    newDocumentFullPath = newDocumentFullPath.Replace("{f}", f);
                    newDocumentFullPath = this.Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(newDocumentFullPath, this);

                    int    newDocumentPathSplit = newDocumentFullPath.LastIndexOf("/") + 1;
                    string newDocumentPath      = newDocumentFullPath.Substring(0, newDocumentPathSplit);
                    string newDocumentName      = newDocumentFullPath.Substring(newDocumentPathSplit);

                    // make sure the new folder exists
                    var folder = await this.Ctx.Corpus.FetchObjectAsync <CdmFolderDefinition>(newDocumentPath);

                    if (folder == null)
                    {
                        Logger.Error(this.Ctx as ResolveContext, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, CdmLogCode.ErrResolveFolderNotFound, newDocumentPath);
                        return(null);
                    }

                    // Next create the resolved entity
                    AttributeResolutionDirectiveSet withDirectives = Directives != null ? Directives : this.Ctx.Corpus.DefaultResolutionDirectives;
                    var resOpt = new ResolveOptions
                    {
                        WrtDoc     = entDef.InDocument,
                        Directives = withDirectives?.Copy()
                    };

                    Logger.Debug(this.Ctx as ResolveContext, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, $"resolving entity {sourceEntityFullPath} to document {newDocumentFullPath}");

                    var resolvedEntity = await entDef.CreateResolvedEntityAsync(entDef.EntityName, resOpt, folder, newDocumentName);

                    if (resolvedEntity == null)
                    {
                        // Fail all resolution, if any one entity resolution fails
                        return(null);
                    }

                    var result = entity.Copy(resOpt) as CdmEntityDeclarationDefinition;
                    if (result.ObjectType == CdmObjectType.LocalEntityDeclarationDef)
                    {
                        result.EntityPath = this.Ctx.Corpus.Storage.CreateRelativeCorpusPath(resolvedEntity.AtCorpusPath, resolvedManifest) ?? result.AtCorpusPath;
                    }

                    resolvedManifest.Entities.Add(result);
                }

                Logger.Debug(this.Ctx as ResolveContext, Tag, nameof(CreateResolvedManifestAsync), this.AtCorpusPath, "calculating relationships");

                // calculate the entity graph for just this manifest and any submanifests
                await this.Ctx.Corpus.CalculateEntityGraphAsync(resolvedManifest);

                // stick results into the relationships list for the manifest
                // only put in relationships that are between the entities that are used in the manifest
                await resolvedManifest.PopulateManifestRelationshipsAsync(CdmRelationshipDiscoveryStyle.Exclusive);

                // needed until Matt's changes with collections where I can propigate
                resolvedManifest.IsDirty = true;
                return(resolvedManifest);
            }
        }