Exemple #1
0
 /// <summary>
 /// Adds a document to the list of documents that are not indexed to mark it for indexing.
 /// </summary>
 /// <param name="doc">The document.</param>
 internal void MarkDocumentForIndexing(CdmDocumentDefinition doc)
 {
     lock (this.allDocuments)
     {
         this.docsNotIndexed[doc] = 1;
     }
 }
Exemple #2
0
 /// <summary>
 /// Removes a document from the list of documents that are not indexed to mark it as indexed.
 /// </summary>
 /// <param name="doc">The document.</param>
 internal void MarkDocumentAsIndexed(CdmDocumentDefinition doc)
 {
     lock (this.allDocuments)
     {
         this.docsNotIndexed.Remove(doc);
     }
 }
Exemple #3
0
        /// <summary>
        /// Returns a list of all the documents that are not indexed.
        /// </summary>
        internal List <CdmDocumentDefinition> ListDocsNotIndexed(CdmDocumentDefinition rootDoc, ISet <string> docsLoaded)
        {
            var docsNotIndexed = new List <CdmDocumentDefinition>();

            lock (this.allDocuments)
            {
                // gets all the documents that needs indexing and set the currentlyIndexing flag to true.
                foreach (var docPath in docsLoaded)
                {
                    var doc = this.FetchDocument(docPath);
                    if (doc == null)
                    {
                        continue;
                    }

                    // The root document that started this indexing process is already masked for indexing, don't mark it again.
                    if (doc != rootDoc)
                    {
                        if (this.MarkDocumentForIndexing(doc))
                        {
                            docsNotIndexed.Add(doc);
                        }
                    }
                    else
                    {
                        docsNotIndexed.Add(rootDoc);
                    }
                }
            }
            return(docsNotIndexed);
        }
        // Helper that fixes a path from local to absolute.
        // Gets the object from that path then looks at the document where the object is found.
        // If dirty, the document is saved with the original name.
        private async Task <bool> SaveDirtyLink(string relative, CopyOptions options)
        {
            // get the document object from the import
            string docPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(relative, this);

            if (docPath == null)
            {
                Logger.Error(nameof(CdmManifestDefinition), this.Ctx as ResolveContext, $"Invalid corpus path {relative}`, `saveDirtyLink", "saveDirtyLink");
                return(false);
            }
            CdmObject objAt = await Ctx.Corpus.FetchObjectAsync <CdmObject>(docPath);

            if (objAt == null)
            {
                Logger.Error(nameof(CdmManifestDefinition), this.Ctx as ResolveContext, $"Couldn't get object from path {docPath}", "saveDirtyLink");
                return(false);
            }

            CdmDocumentDefinition docImp = objAt.InDocument as CdmDocumentDefinition;

            if (docImp != null)
            {
                if (docImp.IsDirty)
                {
                    // save it with the same name
                    if (await docImp.SaveAsAsync(docImp.Name, true, options) == false)
                    {
                        Logger.Error(nameof(CdmManifestDefinition), this.Ctx as ResolveContext, $"failed saving document {docImp.Name}", "saveDirtyLink");
                        return(false);
                    }
                }
            }

            return(true);
        }
        /// <summary>
        /// Sets a document's status to loading if the document needs to be loaded.
        /// </summary>
        /// <param name="docName">The document name.</param>
        /// <returns>Whether a document needs to be loaded.</returns>
        internal bool NeedToLoadDocument(string docName, ConcurrentDictionary <CdmDocumentDefinition, byte> docsNowLoaded)
        {
            bool needToLoad           = false;
            CdmDocumentDefinition doc = null;

            lock (this.allDocuments)
            {
                if (this.docsNotLoaded.ContainsKey(docName) && !this.docsNotFound.ContainsKey(docName) && !this.docsCurrentlyLoading.ContainsKey(docName))
                {
                    // Set status to loading.
                    this.docsNotLoaded.Remove(docName);

                    // The document was loaded already, skip it.
                    if (this.pathLookup.TryGetValue(docName.ToLower(), out var lookup))
                    {
                        doc = lookup.Item2;
                    }
                    else
                    {
                        this.docsCurrentlyLoading.Add(docName, 1);
                        needToLoad = true;
                    }
                }
            }

            if (doc != null)
            {
                MarkDocumentAsLoadedOrFailed(doc, docName, docsNowLoaded);
            }

            return(needToLoad);
        }
Exemple #6
0
        /// <summary>
        /// Helper that fixes a path from local to absolute.
        /// Gets the object from that path then looks at the document where the object is found.
        /// If dirty, the document is saved with the original name.
        /// </summary>
        /// <param name="relative"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        private async Task <bool> SaveDirtyLink(string relative, CopyOptions options)
        {
            // get the document object from the import
            string docPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(relative, this);

            if (docPath == null)
            {
                Logger.Error(this.Ctx as ResolveContext, Tag, nameof(SaveDirtyLink), this.AtCorpusPath, CdmLogCode.ErrValdnInvalidCorpusPath, relative);
                return(false);
            }
            CdmObject objAt = await Ctx.Corpus.FetchObjectAsync <CdmObject>(docPath);

            if (objAt == null)
            {
                Logger.Error(this.Ctx as ResolveContext, Tag, nameof(SaveDirtyLink), this.AtCorpusPath, CdmLogCode.ErrPersistObjectNotFound, docPath);
                return(false);
            }

            CdmDocumentDefinition docImp = objAt.InDocument;

            if (docImp != null && docImp.IsDirty)
            {
                // save it with the same name
                if (await docImp.SaveAsAsync(docImp.Name, true, options) == false)
                {
                    Logger.Error(this.Ctx as ResolveContext, Tag, nameof(SaveDirtyLink), this.AtCorpusPath, CdmLogCode.ErrDocEntityDocSavingFailure, docImp.Name);
                    return(false);
                }
            }
            return(true);
        }
Exemple #7
0
        /// <summary>
        /// Helper that fixes a path from local to absolute.Gets the object from that path.
        /// Created from SaveDirtyLink in order to be able to save docs in parallel.
        /// Represents the part of SaveDirtyLink that could not be parallelized.
        /// </summary>
        /// <param name="relativePath"></param>
        /// <returns></returns>
        private async Task <Tuple <CdmDocumentDefinition, bool> > FetchDocumentDefinition(string relativePath)
        {
            // get the document object from the import
            string docPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(relativePath, this);

            if (docPath == null)
            {
                Logger.Error(this.Ctx as ResolveContext, Tag, nameof(FetchDocumentDefinition), this.AtCorpusPath, CdmLogCode.ErrValdnInvalidCorpusPath, relativePath);
                return(Tuple.Create((CdmDocumentDefinition)null, false));
            }

            ResolveOptions resOpt = new ResolveOptions
            {
                ImportsLoadStrategy = ImportsLoadStrategy.Load
            };
            CdmObject objAt = await Ctx.Corpus.FetchObjectAsync <CdmObject>(docPath, null, resOpt);

            if (objAt == null)
            {
                Logger.Error(this.Ctx as ResolveContext, Tag, nameof(FetchDocumentDefinition), this.AtCorpusPath, CdmLogCode.ErrPersistObjectNotFound, docPath);
                return(Tuple.Create((CdmDocumentDefinition)null, false));
            }

            CdmDocumentDefinition docImp = objAt.InDocument;

            return(Tuple.Create(docImp, true));
        }
Exemple #8
0
        /// <summary>
        /// Marks a document for indexing if it has loaded successfully, or adds it to the list of documents not found if it failed to load.
        /// </summary>
        /// <param name="doc">The document that was loaded.</param>
        /// <param name="docName">The document name.</param>
        /// <param name="docsNowLoaded">The dictionary of documents that are now loaded.</param>
        /// <returns>Returns true if the document has loaded, false if it failed to load.</returns>
        internal bool MarkDocumentAsLoadedOrFailed(CdmDocumentDefinition doc, string docName, ConcurrentDictionary <CdmDocumentDefinition, byte> docsNowLoaded)
        {
            lock (this.allDocuments)
            {
                // Doc is no longer loading.
                this.docsCurrentlyLoading.Remove(docName);

                if (doc != null)
                {
                    // Doc is now loaded.
                    docsNowLoaded.TryAdd(doc, 1);
                    // Doc needs to be indexed.
                    this.docsNotIndexed.Add(doc, 1);
                    doc.CurrentlyIndexing = true;

                    return(true);
                }
                else
                {
                    // The doc failed to load, so set doc as not found.
                    this.docsNotFound.Add(docName, 1);

                    return(false);
                }
            }
        }
Exemple #9
0
        public override CdmObject Copy(ResolveOptions resOpt = null)
        {
            if (resOpt == null)
            {
                resOpt = new ResolveOptions(this);
            }

            CdmDocumentDefinition c = new CdmDocumentDefinition(this.Ctx, this.Name)
            {
                Ctx        = this.Ctx,
                IsDirty    = true,
                FolderPath = this.FolderPath,
                Schema     = this.Schema,
                JsonSchemaSemanticVersion = this.JsonSchemaSemanticVersion,
            };

            foreach (var def in this.Definitions)
            {
                c.Definitions.Add(def);
            }
            foreach (var imp in this.Imports)
            {
                c.Imports.Add(imp);
            }
            return(c);
        }
Exemple #10
0
 /// <summary>
 /// Removes a document from the list of documents that are not indexed to mark it as indexed.
 /// </summary>
 /// <param name="doc">The document.</param>
 internal void MarkDocumentAsIndexed(CdmDocumentDefinition doc)
 {
     lock (this.allDocuments)
     {
         doc.CurrentlyIndexing = false;
         this.docsCurrentlyIndexing.Remove(doc);
     }
 }
Exemple #11
0
 /// <summary>
 /// Adds a folder and document to the list of all documents in the corpus. Also adds the document path to the path lookup.
 /// </summary>
 /// <param name="path">The document path.</param>
 /// <param name="folder">The folder.</param>
 /// <param name="doc">The document.</param>
 internal void AddDocumentPath(string path, CdmFolderDefinition folder, CdmDocumentDefinition doc)
 {
     lock (this.allDocuments)
     {
         if (!this.pathLookup.ContainsKey(path))
         {
             this.allDocuments.Add(Tuple.Create(folder, doc));
             this.pathLookup.Add(path, Tuple.Create(folder, doc));
         }
     }
 }
Exemple #12
0
 /// <summary>
 /// Removes a folder and document from the list of all documents in the corpus. Also removes the document path from the path lookup.
 /// </summary>
 /// <param name="path">The document path.</param>
 /// <param name="folder">The folder.</param>
 /// <param name="doc">The document.</param>
 internal void RemoveDocumentPath(string path, CdmFolderDefinition folder, CdmDocumentDefinition doc)
 {
     lock (this.allDocuments)
     {
         if (this.pathLookup.ContainsKey(path))
         {
             this.pathLookup.Remove(path);
             int index = this.allDocuments.IndexOf(Tuple.Create(folder, doc));
             this.allDocuments.RemoveAt(index);
         }
     }
 }
Exemple #13
0
        virtual internal async Task <bool> SaveLinkedDocuments(CopyOptions options = null)
        {
            List <CdmDocumentDefinition> docs = new List <CdmDocumentDefinition>();

            if (options == null)
            {
                options = new CopyOptions();
            }

            if (this.Imports != null)
            {
                // the only linked documents would be the imports
                foreach (CdmImport imp in this.Imports)
                {
                    // get the document object from the import
                    string docPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(imp.CorpusPath, this);
                    if (docPath == null)
                    {
                        Logger.Error((ResolveContext)this.Ctx, Tag, nameof(SaveLinkedDocuments), this.AtCorpusPath, CdmLogCode.ErrValdnInvalidCorpusPath, imp.CorpusPath);
                        return(false);
                    }
                    try
                    {
                        CdmObject objAt = await Ctx.Corpus.FetchObjectAsync <CdmObject>(docPath);

                        if (objAt == null)
                        {
                            Logger.Error((ResolveContext)this.Ctx, Tag, nameof(SaveLinkedDocuments), this.AtCorpusPath, CdmLogCode.ErrPersistObjectNotFound, imp.CorpusPath);
                            return(false);
                        }
                        CdmDocumentDefinition docImp = objAt.InDocument;
                        if (docImp != null && docImp.IsDirty)
                        {
                            docs.Add(docImp);
                        }
                    }
                    catch (Exception e)
                    {
                        Logger.Error((ResolveContext)this.Ctx, Tag, nameof(SaveLinkedDocuments), this.AtCorpusPath, CdmLogCode.ErrPersistObjectNotFound, imp.CorpusPath + " " + e.Message);
                        return(false);
                    }
                }
                foreach (var docImp in docs)
                {
                    if (await docImp.SaveAsAsync(docImp.Name, true, options) == false)
                    {
                        Logger.Error((ResolveContext)this.Ctx, Tag, nameof(SaveLinkedDocuments), this.AtCorpusPath, CdmLogCode.ErrDocImportSavingFailure, docImp.Name);
                        return(false);
                    }
                }
            }
            return(true);
        }
Exemple #14
0
 /// <summary>
 /// Saves CdmDocumentDefinition if dirty.
 /// Was created from SaveDirtyLink in order to be able to save docs in parallel.
 /// Represents the part of SaveDirtyLink that could be parallelized.
 /// </summary>
 /// <param name="docImp"></param>
 /// <param name="options"></param>
 /// <returns></returns>
 private async Task <bool> SaveDocumentIfDirty(CdmDocumentDefinition docImp, CopyOptions options)
 {
     if (docImp != null && docImp.IsDirty)
     {
         // save it with the same name
         if (await docImp.SaveAsAsync(docImp.Name, true, options) == false)
         {
             Logger.Error(this.Ctx as ResolveContext, Tag, nameof(SaveDocumentIfDirty), this.AtCorpusPath, CdmLogCode.ErrDocEntityDocSavingFailure, docImp.Name);
             return(false);
         }
     }
     return(true);
 }
Exemple #15
0
        /// <summary>
        /// Adds a document to the list of documents that are not indexed to mark it for indexing.
        /// </summary>
        /// <param name="doc">The document.</param>
        /// <returns>If the document needs indexing or not.</returns>
        internal bool MarkDocumentForIndexing(CdmDocumentDefinition doc)
        {
            lock (this.allDocuments)
            {
                if (doc.NeedsIndexing && !doc.CurrentlyIndexing)
                {
                    // If the document was not indexed before and it's not currently being indexed.
                    this.docsCurrentlyIndexing.Add(doc);
                    doc.CurrentlyIndexing = true;
                }

                return(doc.NeedsIndexing);
            }
        }
Exemple #16
0
        /// <inheritdoc />
        public override CdmObject Copy(ResolveOptions resOpt = null, CdmObject host = null)
        {
            if (resOpt == null)
            {
                resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives);
            }

            CdmDocumentDefinition copy;

            if (host == null)
            {
                copy = new CdmDocumentDefinition(this.Ctx, this.Name);
            }
            else
            {
                copy      = host as CdmDocumentDefinition;
                copy.Ctx  = this.Ctx;
                copy.Name = this.Name;
                copy.Definitions.Clear();
                copy.DeclarationsIndexed  = false;
                copy.InternalDeclarations = new ConcurrentDictionary <string, CdmObjectBase>();
                copy.NeedsIndexing        = true;
                copy.Imports.Clear();
                copy.ImportsIndexed   = false;
                copy.ImportPriorities = null;
            }

            copy.InDocument = copy;
            copy.IsDirty    = true;
            copy.FolderPath = this.FolderPath;
            copy.Schema     = this.Schema;
            copy.JsonSchemaSemanticVersion = this.JsonSchemaSemanticVersion;
            copy.DocumentVersion           = this.DocumentVersion;

            foreach (var def in this.Definitions)
            {
                copy.Definitions.Add(def);
            }
            foreach (var imp in this.Imports)
            {
                copy.Imports.Add(imp);
            }
            return(copy);
        }
        /// <summary>
        /// Fetches the document from the folder path.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="adapter">The storage adapter where the document can be found.</param>
        /// <param name="forceReload">If true, reload the object from file and replace the current object with it.</param>
        /// <returns>The <see cref="CdmDocumentDefinition"/>.</returns>
        internal async Task <CdmDocumentDefinition> FetchDocumentFromFolderPathAsync(string objectPath, StorageAdapter adapter, bool forceReload = false)
        {
            string docName;
            string remainingPath;
            int    first = objectPath.IndexOf('/', 0);

            if (first < 0)
            {
                remainingPath = "";
                docName       = objectPath;
            }
            else
            {
                remainingPath = objectPath.Slice(first + 1);
                docName       = objectPath.Substring(0, first);
            }

            // got that doc?
            CdmDocumentDefinition doc = null;

            if (this.DocumentLookup.ContainsKey(docName))
            {
                doc = this.DocumentLookup[docName];
                if (!forceReload)
                {
                    return(doc);
                }
                // remove them from the caches since they will be back in a moment
                if ((doc as CdmDocumentDefinition).IsDirty)
                {
                    Logger.Warning(nameof(CdmFolderDefinition), this.Ctx, $"discarding changes in document: {doc.Name}");
                }
                this.Documents.Remove(docName);
            }

            // go get the doc
            doc = await PersistenceLayer.LoadDocumentFromPathAsync(this, docName, doc);

            return(doc);
        }
Exemple #18
0
        /// <summary>
        /// Fetches the document from the folder path.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="adapter">The storage adapter where the document can be found.</param>
        /// <param name="forceReload">If true, reload the object from file and replace the current object with it.</param>
        /// <returns>The <see cref="CdmDocumentDefinition"/>.</returns>
        internal async Task <CdmDocumentDefinition> FetchDocumentFromFolderPathAsync(string objectPath, StorageAdapter adapter, bool forceReload = false, ResolveOptions resOpt = null)
        {
            string docName;
            string remainingPath;
            int    first = objectPath.IndexOf('/', 0);

            if (first < 0)
            {
                remainingPath = "";
                docName       = objectPath;
            }
            else
            {
                remainingPath = objectPath.Slice(first + 1);
                docName       = objectPath.Substring(0, first);
            }

            // got that doc?
            CdmDocumentDefinition doc = null;

            if (this.DocumentLookup.ContainsKey(docName))
            {
                doc = this.DocumentLookup[docName];
                if (!forceReload)
                {
                    return(doc);
                }
                // remove them from the caches since they will be back in a moment
                if ((doc as CdmDocumentDefinition).IsDirty)
                {
                    Logger.Warning(this.Ctx, Tag, nameof(FetchDocumentFromFolderPathAsync), this.AtCorpusPath, CdmLogCode.WarnDocChangesDiscarded, doc.Name);
                }
                this.Documents.Remove(docName);
            }

            // go get the doc
            doc = await this.Corpus.Persistence.LoadDocumentFromPathAsync(this, docName, doc, resOpt);

            return(doc);
        }
Exemple #19
0
        internal async Task <bool> IndexIfNeeded(ResolveOptions resOpt)
        {
            if (this.NeedsIndexing)
            {
                // make the corpus internal machinery pay attention to this document for this call
                CdmCorpusDefinition   corpus = (this.Folder as CdmFolderDefinition).Corpus;
                CdmDocumentDefinition oldDoc = this;

                ConcurrentDictionary <CdmDocumentDefinition, byte> docsJustAdded = new ConcurrentDictionary <CdmDocumentDefinition, byte>();
                ConcurrentDictionary <string, byte> docsNotFound = new ConcurrentDictionary <string, byte>();
                await corpus.ResolveImportsAsync(this, docsJustAdded, docsNotFound);

                // maintain actual current doc
                (corpus.Ctx as ResolveContext).CurrentDoc          = oldDoc;
                (this.Ctx.Corpus.Ctx as ResolveContext).CurrentDoc = oldDoc;
                docsJustAdded[this] = 1;

                return(corpus.IndexDocuments(resOpt, docsJustAdded));
            }

            return(true);
        }
        /// <inheritdoc />
        public override CdmObject Copy(ResolveOptions resOpt = null, CdmObject host = null)
        {
            if (resOpt == null)
            {
                resOpt = new ResolveOptions(this);
            }

            CdmDocumentDefinition copy;

            if (host == null)
            {
                copy = new CdmDocumentDefinition(this.Ctx, this.Name);
            }
            else
            {
                copy      = host as CdmDocumentDefinition;
                copy.Ctx  = this.Ctx;
                copy.Name = this.Name;
                copy.Definitions.Clear();
                copy.Imports.Clear();
            }

            copy.InDocument = copy;
            copy.IsDirty    = true;
            copy.FolderPath = this.FolderPath;
            copy.Schema     = this.Schema;
            copy.JsonSchemaSemanticVersion = this.JsonSchemaSemanticVersion;

            foreach (var def in this.Definitions)
            {
                copy.Definitions.Add(def);
            }
            foreach (var imp in this.Imports)
            {
                copy.Imports.Add(imp);
            }
            return(copy);
        }
Exemple #21
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 #22
0
 /// <summary>
 /// Constructs a CdmImportCollection.
 /// </summary>
 /// <param name="ctx">The context.</param>
 /// <param name="owner">The owner of the collection. This class is customized for <see cref="CdmDocumentDefinition"/>.</param>
 public CdmImportCollection(CdmCorpusContext ctx, CdmDocumentDefinition owner)
     : base(ctx, owner, Enums.CdmObjectType.Import)
 {
 }
        private int PrioritizeImports(HashSet <CdmDocumentDefinition> processedSet, IDictionary <CdmDocumentDefinition, int> priorityMap, int sequence, IDictionary <string, CdmDocumentDefinition> monikerMap, bool skipMonikered = false)
        {
            // goal is to make a map from the reverse order of imports (breadth first) to the first (aka last) sequence number in that list.
            // This gives the semantic that the 'last/shallowest' definition for a duplicate symbol wins,
            // the lower in this list a document shows up, the higher priority its definitions are for resolving conflicts.
            // for 'moniker' imports, keep track of the 'last/shallowest' use of each moniker tag.

            // if already in list, don't do this again
            if (processedSet.Contains(this))
            {
                return(sequence);
            }
            processedSet.Add(this);

            if (this.Imports != null)
            {
                // first add the imports done at this level only
                int l = this.Imports.Count;
                // reverse order
                for (int i = l - 1; i >= 0; i--)
                {
                    CdmImport             imp    = this.Imports.AllItems[i];
                    CdmDocumentDefinition impDoc = imp.ResolvedDocument as CdmDocumentDefinition;
                    // don't add the moniker imports to the priority list
                    bool isMoniker = !string.IsNullOrWhiteSpace(imp.Moniker);
                    if (imp.ResolvedDocument != null && !isMoniker)
                    {
                        if (priorityMap.ContainsKey(impDoc) == false)
                        {
                            // add doc
                            priorityMap.Add(impDoc, sequence);
                            sequence++;
                        }
                    }
                }

                // now add the imports of the imports
                for (int i = l - 1; i >= 0; i--)
                {
                    CdmImport             imp    = this.Imports.AllItems[i];
                    CdmDocumentDefinition impDoc = imp.ResolvedDocument as CdmDocumentDefinition;
                    // don't add the moniker imports to the priority list
                    bool isMoniker = !string.IsNullOrWhiteSpace(imp.Moniker);
                    if (impDoc?.ImportPriorities != null)
                    {
                        // lucky, already done so avoid recursion and copy
                        ImportPriorities impPriSub = impDoc.GetImportPriorities();
                        impPriSub.ImportPriority.Remove(impDoc); // because already added above
                        foreach (var ip in impPriSub.ImportPriority)
                        {
                            if (priorityMap.ContainsKey(ip.Key) == false)
                            {
                                // add doc
                                priorityMap.Add(ip.Key, sequence);
                                sequence++;
                            }
                        }
                        if (!isMoniker)
                        {
                            foreach (var mp in impPriSub.MonikerPriorityMap)
                            {
                                monikerMap[mp.Key] = mp.Value;
                            }
                        }
                    }
                    else if (impDoc != null)
                    {
                        // skip the monikered imports from here if this is a monikered import itself and we are only collecting the dependencies
                        sequence = impDoc.PrioritizeImports(processedSet, priorityMap, sequence, monikerMap, isMoniker);
                    }
                }
                // skip the monikered imports from here if this is a monikered import itself and we are only collecting the dependencies
                if (!skipMonikered)
                {
                    // moniker imports are prioritized by the 'closest' use of the moniker to the starting doc. so last one found in this recursion
                    for (int i = 0; i < l; i++)
                    {
                        CdmImport imp = this.Imports.AllItems[i];
                        if (imp.ResolvedDocument != null && imp.Moniker != null)
                        {
                            monikerMap[imp.Moniker] = imp.ResolvedDocument as CdmDocumentDefinition;
                        }
                    }
                }
            }
            return(sequence);
        }
Exemple #24
0
        internal string ImportPathToDoc(CdmDocumentDefinition docDest)
        {
            HashSet <CdmDocumentDefinition> avoidLoop = new HashSet <CdmDocumentDefinition>();
            Func <CdmDocumentDefinition, string, string> InternalImportPathToDoc = null;

            InternalImportPathToDoc = (docCheck, path) =>
            {
                if (docCheck == docDest)
                {
                    return("");
                }
                if (avoidLoop.Contains(docCheck))
                {
                    return(null);
                }
                avoidLoop.Add(docCheck);
                // if the docDest is one of the monikered imports of docCheck, then add the moniker and we are cool
                if (docCheck.ImportPriorities?.MonikerPriorityMap?.Count > 0)
                {
                    foreach (var monPair in docCheck.ImportPriorities?.MonikerPriorityMap)
                    {
                        if (monPair.Value == docDest)
                        {
                            return($"{path}{monPair.Key}/");
                        }
                    }
                }
                // ok, what if the document can be reached directly from the imports here
                ImportInfo impInfo = null;
                if (docCheck.ImportPriorities?.ImportPriority?.TryGetValue(docDest, out impInfo) == false)
                {
                    impInfo = null;
                }
                if (impInfo != null && impInfo.IsMoniker == false)
                {
                    // good enough
                    return(path);
                }

                // still nothing, now we need to check those docs deeper
                if (docCheck.ImportPriorities?.MonikerPriorityMap?.Count > 0)
                {
                    foreach (var monPair in docCheck.ImportPriorities?.MonikerPriorityMap)
                    {
                        string pathFound = InternalImportPathToDoc(monPair.Value, $"{path}{monPair.Key}/");
                        if (pathFound != null)
                        {
                            return(pathFound);
                        }
                    }
                }
                if (docCheck.ImportPriorities?.ImportPriority?.Count > 0)
                {
                    foreach (var impInfoPair in docCheck.ImportPriorities.ImportPriority)
                    {
                        if (!impInfoPair.Value.IsMoniker)
                        {
                            string pathFound = InternalImportPathToDoc(impInfoPair.Key, path);
                            if (pathFound != null)
                            {
                                return(pathFound);
                            }
                        }
                    }
                }
                return(null);
            };

            return(InternalImportPathToDoc(this, ""));
        }
Exemple #25
0
        override internal async Task <bool> SaveLinkedDocuments(CopyOptions options = null)
        {
            HashSet <string> links = new HashSet <string>();

            if (options == null)
            {
                options = new CopyOptions();
            }

            if (this.Imports != null)
            {
                this.Imports.ToList().ForEach(x => links.Add(x.CorpusPath));
            }
            if (this.Entities != null)
            {
                // only the local entity declarations please
                foreach (CdmEntityDeclarationDefinition def in this.Entities)
                {
                    if (def.ObjectType == CdmObjectType.LocalEntityDeclarationDef)
                    {
                        CdmLocalEntityDeclarationDefinition defImp = def as CdmLocalEntityDeclarationDefinition;
                        links.Add(defImp.EntityPath);

                        // also, partitions can have their own schemas
                        if (defImp.DataPartitions != null)
                        {
                            foreach (CdmDataPartitionDefinition part in defImp.DataPartitions)
                            {
                                if (part.SpecializedSchema != null)
                                {
                                    links.Add(part.SpecializedSchema);
                                }
                            }
                        }
                        // so can patterns
                        if (defImp.DataPartitionPatterns != null)
                        {
                            foreach (CdmDataPartitionPatternDefinition part in defImp.DataPartitionPatterns)
                            {
                                if (part.SpecializedSchema != null)
                                {
                                    links.Add(part.SpecializedSchema);
                                }
                            }
                        }
                    }
                }
            }

            // Get all Cdm documents sequentially
            List <CdmDocumentDefinition> docs = new List <CdmDocumentDefinition>();

            foreach (var link in links)
            {
                CdmDocumentDefinition document = await FetchDocumentDefinition(link);

                if (document == null)
                {
                    return(false);
                }
                docs.Add(document);
            }

            // Save all dirty Cdm documents in parallel
            IEnumerable <Task <bool> > tasks = docs.Select(doc => SaveDocumentIfDirty(doc, options));
            var results = await Task.WhenAll(tasks);

            if (this.SubManifests != null)
            {
                foreach (var subDeclaration in this.SubManifests)
                {
                    CdmManifestDefinition subManifest = await FetchDocumentDefinition(subDeclaration.Definition) as CdmManifestDefinition;

                    if (subManifest == null || !await SaveDocumentIfDirty(subManifest, options))
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
        /// <summary>
        /// Checks if the trait argumnet value matchs the data type defined on the trait parameter
        /// </summary>
        /// <param name="resOpt"></param>
        /// <param name="wrtDoc"></param>
        /// <param name="argumentValue"></param>
        /// <returns></returns>
        internal dynamic ConstTypeCheck(ResolveOptions resOpt, CdmDocumentDefinition wrtDoc, dynamic argumentValue)
        {
            ResolveContext ctx         = this.Ctx as ResolveContext;
            dynamic        replacement = argumentValue;

            // if parameter type is entity, then the value should be an entity or ref to one
            // same is true of 'dataType' dataType
            if (this.DataTypeRef == null)
            {
                return(replacement);
            }

            CdmDataTypeDefinition dt = this.DataTypeRef.FetchObjectDefinition <CdmDataTypeDefinition>(resOpt);

            if (dt == null)
            {
                Logger.Error(ctx, Tag, nameof(ConstTypeCheck), this.AtCorpusPath, CdmLogCode.ErrUnrecognizedDataType, this.Name);
                return(null);
            }

            // compare with passed in value or default for parameter
            dynamic pValue = argumentValue;

            if (pValue == null)
            {
                pValue      = this.DefaultValue;
                replacement = pValue;
            }
            if (pValue != null)
            {
                if (dt.IsDerivedFrom("cdmObject", resOpt))
                {
                    List <CdmObjectType> expectedTypes = new List <CdmObjectType>();
                    string expected = null;
                    if (dt.IsDerivedFrom("entity", resOpt))
                    {
                        expectedTypes.Add(CdmObjectType.ConstantEntityDef);
                        expectedTypes.Add(CdmObjectType.EntityRef);
                        expectedTypes.Add(CdmObjectType.EntityDef);
                        expectedTypes.Add(CdmObjectType.ProjectionDef);
                        expected = "entity";
                    }
                    else if (dt.IsDerivedFrom("attribute", resOpt))
                    {
                        expectedTypes.Add(CdmObjectType.AttributeRef);
                        expectedTypes.Add(CdmObjectType.TypeAttributeDef);
                        expectedTypes.Add(CdmObjectType.EntityAttributeDef);
                        expected = "attribute";
                    }
                    else if (dt.IsDerivedFrom("dataType", resOpt))
                    {
                        expectedTypes.Add(CdmObjectType.DataTypeRef);
                        expectedTypes.Add(CdmObjectType.DataTypeDef);
                        expected = "dataType";
                    }
                    else if (dt.IsDerivedFrom("purpose", resOpt))
                    {
                        expectedTypes.Add(CdmObjectType.PurposeRef);
                        expectedTypes.Add(CdmObjectType.PurposeDef);
                        expected = "purpose";
                    }
                    else if (dt.IsDerivedFrom("traitGroup", resOpt))
                    {
                        expectedTypes.Add(CdmObjectType.TraitGroupRef);
                        expectedTypes.Add(CdmObjectType.TraitGroupDef);
                        expected = "traitGroup";
                    }
                    else if (dt.IsDerivedFrom("trait", resOpt))
                    {
                        expectedTypes.Add(CdmObjectType.TraitRef);
                        expectedTypes.Add(CdmObjectType.TraitDef);
                        expected = "trait";
                    }
                    else if (dt.IsDerivedFrom("attributeGroup", resOpt))
                    {
                        expectedTypes.Add(CdmObjectType.AttributeGroupRef);
                        expectedTypes.Add(CdmObjectType.AttributeGroupDef);
                        expected = "attributeGroup";
                    }

                    if (expectedTypes.Count == 0)
                    {
                        Logger.Error(ctx, Tag, nameof(ConstTypeCheck), wrtDoc.FolderPath + wrtDoc.Name, CdmLogCode.ErrUnexpectedDataType, this.Name);
                    }

                    // if a string constant, resolve to an object ref.
                    CdmObjectType foundType  = CdmObjectType.Error;
                    Type          pValueType = pValue.GetType();

                    if (typeof(CdmObject).IsAssignableFrom(pValueType))
                    {
                        foundType = (pValue as CdmObject).ObjectType;
                    }
                    string foundDesc = ctx.RelativePath;
                    if (!(pValue is CdmObject))
                    {
                        // pValue is a string or JValue
                        pValue = (string)pValue;
                        if (pValue == "this.attribute" && expected == "attribute")
                        {
                            // will get sorted out later when resolving traits
                            foundType = CdmObjectType.AttributeRef;
                        }
                        else
                        {
                            foundDesc = pValue;
                            int seekResAtt = CdmObjectReferenceBase.offsetAttributePromise(pValue);
                            if (seekResAtt >= 0)
                            {
                                // get an object there that will get resolved later after resolved attributes
                                replacement = new CdmAttributeReference(ctx, pValue, true);
                                (replacement as CdmAttributeReference).Ctx        = ctx;
                                (replacement as CdmAttributeReference).InDocument = wrtDoc;
                                foundType = CdmObjectType.AttributeRef;
                            }
                            else
                            {
                                CdmObjectBase lu = ctx.Corpus.ResolveSymbolReference(resOpt, wrtDoc, pValue, CdmObjectType.Error, retry: true);
                                if (lu != null)
                                {
                                    if (expected == "attribute")
                                    {
                                        replacement = new CdmAttributeReference(ctx, pValue, true);
                                        (replacement as CdmAttributeReference).Ctx        = ctx;
                                        (replacement as CdmAttributeReference).InDocument = wrtDoc;
                                        foundType = CdmObjectType.AttributeRef;
                                    }
                                    else
                                    {
                                        replacement = lu;
                                        foundType   = (replacement as CdmObject).ObjectType;
                                    }
                                }
                            }
                        }
                    }
                    if (expectedTypes.IndexOf(foundType) == -1)
                    {
                        Logger.Error(ctx, Tag, nameof(ConstTypeCheck), wrtDoc.AtCorpusPath, CdmLogCode.ErrResolutionFailure, this.Name, expected, foundDesc, expected);
                    }
                    else
                    {
                        Logger.Info(ctx, Tag, nameof(ConstTypeCheck), wrtDoc.AtCorpusPath, $"resolved '{foundDesc}'");
                    }
                }
            }

            return(replacement);
        }
Exemple #27
0
        private int PrioritizeImports(HashSet <CdmDocumentDefinition> processedSet, ImportPriorities importPriorities, int sequence, bool skipMonikered)
        {
            // goal is to make a map from the reverse order of imports (breadth first) to the first (aka last) sequence number in that list.
            // This gives the semantic that the 'last/shallowest' definition for a duplicate symbol wins,
            // the lower in this list a document shows up, the higher priority its definitions are for resolving conflicts.
            // for 'moniker' imports, keep track of the 'last/shallowest' use of each moniker tag.

            // maps document to priority.
            IDictionary <CdmDocumentDefinition, int> priorityMap = importPriorities.ImportPriority;

            // maps moniker to document.
            IDictionary <string, CdmDocumentDefinition> monikerMap = importPriorities.MonikerPriorityMap;

            // if already in list, don't do this again
            if (processedSet.Contains(this))
            {
                // if the first document in the priority map is this then the document was the starting point of the recursion.
                // and if this document is present in the processedSet we know that there is a cicular list of imports.
                if (priorityMap.ContainsKey(this) && priorityMap[this] == 0)
                {
                    importPriorities.hasCircularImport = true;
                }

                return(sequence);
            }
            processedSet.Add(this);

            if (this.Imports != null)
            {
                var reversedImports = this.Imports.Reverse();

                // first add the imports done at this level only in reverse order.
                foreach (var imp in reversedImports)
                {
                    var  impDoc    = imp.Document;
                    bool isMoniker = !string.IsNullOrWhiteSpace(imp.Moniker);

                    // don't add the moniker imports to the priority list.
                    if (impDoc != null && !isMoniker && !priorityMap.ContainsKey(impDoc))
                    {
                        // add doc.
                        priorityMap.Add(impDoc, sequence);
                        sequence++;
                    }
                }

                // now add the imports of the imports.
                foreach (var imp in reversedImports)
                {
                    CdmDocumentDefinition impDoc = imp.Document;
                    bool isMoniker = !string.IsNullOrWhiteSpace(imp.Moniker);

                    // if the document has circular imports its order on the impDoc.ImportPriorities list is not correct.
                    // since the document itself will always be the first one on the list.
                    if (impDoc?.ImportPriorities != null && impDoc?.ImportPriorities.hasCircularImport == false)
                    {
                        // lucky, already done so avoid recursion and copy.
                        ImportPriorities impPriSub = impDoc.GetImportPriorities();
                        impPriSub.ImportPriority.Remove(impDoc); // because already added above.

                        foreach (var ip in impPriSub.ImportPriority)
                        {
                            if (priorityMap.ContainsKey(ip.Key) == false)
                            {
                                // add doc.
                                priorityMap.Add(ip.Key, sequence);
                                sequence++;
                            }
                        }

                        // if the import is not monikered then merge its monikerMap to this one.
                        if (!isMoniker)
                        {
                            foreach (var mp in impPriSub.MonikerPriorityMap)
                            {
                                monikerMap[mp.Key] = mp.Value;
                            }
                        }
                    }
                    else if (impDoc != null)
                    {
                        // skip the monikered imports from here if this is a monikered import itself and we are only collecting the dependencies.
                        sequence = impDoc.PrioritizeImports(processedSet, importPriorities, sequence, isMoniker);
                    }
                }

                // skip the monikered imports from here if this is a monikered import itself and we are only collecting the dependencies.
                if (!skipMonikered)
                {
                    // moniker imports are prioritized by the 'closest' use of the moniker to the starting doc. so last one found in this recursion.
                    foreach (var imp in this.Imports)
                    {
                        bool isMoniker = !string.IsNullOrWhiteSpace(imp.Moniker);
                        if (imp.Document != null && isMoniker)
                        {
                            monikerMap[imp.Moniker] = imp.Document;
                        }
                    }
                }
            }

            return(sequence);
        }
 /// <summary>
 /// Constructs a CdmDefinitionCollection.
 /// </summary>
 /// <param name="ctx">The context.</param>
 /// <param name="owner">The owner of the collection. Has to be a <see cref="CdmDocumentDefinition"/>.</param>
 public CdmDefinitionCollection(CdmCorpusContext ctx, CdmDocumentDefinition owner)
     : base(ctx, owner, CdmObjectType.EntityDef)
 {
 }
Exemple #29
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);
        }