Example #1
0
        /// <summary>
        /// Loads a folder or document given its obejct path.
        /// </summary>
        /// <param name="objectPath"></param>
        /// <param name="forceReload"></param>
        /// <param name="resOpt"></param>
        /// <returns></returns>
        private async Task <CdmContainerDefinition> _LoadFolderOrDocument(string objectPath, bool forceReload = false, ResolveOptions resOpt = null)
        {
            if (string.IsNullOrWhiteSpace(objectPath))
            {
                return(null);
            }

            // first check for namespace
            Tuple <string, string> pathTuple = StorageUtils.SplitNamespacePath(objectPath);

            if (pathTuple == null)
            {
                Logger.Error(this.corpus.Ctx, TAG, nameof(LoadFolderOrDocument), objectPath, CdmLogCode.ErrPathNullObjectPath);
                return(null);
            }
            string nameSpace = !string.IsNullOrWhiteSpace(pathTuple.Item1) ? pathTuple.Item1 : this.corpus.Storage.DefaultNamespace;

            objectPath = pathTuple.Item2;

            if (!objectPath.StartsWith("/"))
            {
                return(null);
            }

            var namespaceFolder = this.corpus.Storage.FetchRootFolder(nameSpace);
            StorageAdapterBase namespaceAdapter = this.corpus.Storage.FetchAdapter(nameSpace);

            if (namespaceFolder == null || namespaceAdapter == null)
            {
                Logger.Error(this.corpus.Ctx, TAG, nameof(LoadFolderOrDocument), objectPath, CdmLogCode.ErrStorageNamespaceNotRegistered, nameSpace);
                return(null);
            }
            CdmFolderDefinition lastFolder = namespaceFolder.FetchChildFolderFromPath(objectPath, false);

            // don't create new folders, just go as far as possible
            if (lastFolder == null)
            {
                return(null);
            }

            // maybe the search is for a folder?
            string lastPath = lastFolder.FolderPath;

            if (lastPath == objectPath)
            {
                return(lastFolder);
            }

            // remove path to folder and then look in the folder
            objectPath = StringUtils.Slice(objectPath, lastPath.Length);

            this.concurrentReadLock.Acquire();

            // During this step the document will be added to the pathLookup when it is added to a folder.
            var doc = await lastFolder.FetchDocumentFromFolderPathAsync(objectPath, forceReload, resOpt);

            this.concurrentReadLock.Release();

            return(doc);
        }
Example #2
0
        /// <summary>
        /// Fetches the child folder from the corpus path.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="adapter">The storage adapter where the folder can be found.</param>
        /// <param name="makeFolder">Create the folder if it doesn't exist.</param>
        /// <returns>The <see cref="CdmFolderDefinition"/>.</returns>
        internal CdmFolderDefinition FetchChildFolderFromPath(string path, bool makeFolder = false)
        {
            string name;
            string remainingPath            = path;
            CdmFolderDefinition childFolder = this;

            while (childFolder != null && remainingPath.IndexOf('/') != -1)
            {
                int first = remainingPath.IndexOf('/');
                if (first < 0)
                {
                    name          = path;
                    remainingPath = "";
                }
                else
                {
                    name          = StringUtils.Slice(remainingPath, 0, first);
                    remainingPath = StringUtils.Slice(remainingPath, first + 1);
                }

                if (name.ToLowerInvariant() != childFolder.Name.ToLowerInvariant())
                {
                    Logger.Error((ResolveContext)this.Ctx, Tag, nameof(FetchChildFolderFromPath), this.AtCorpusPath, CdmLogCode.ErrInvalidPath, path);
                    return(null);
                }

                // the end?
                if (remainingPath.Length == 0)
                {
                    return(childFolder);
                }

                first = remainingPath.IndexOf('/');
                string childFolderName = remainingPath;
                if (first != -1)
                {
                    childFolderName = StringUtils.Slice(remainingPath, 0, first);
                }
                else
                {
                    // the last part of the path will be considered part of the part depending on the makeFolder flag.
                    break;
                }

                // get next child folder.
                childFolder = childFolder.ChildFolders.GetOrCreate(childFolderName);
            }

            if (makeFolder)
            {
                childFolder = childFolder.ChildFolders.Add(remainingPath);
            }

            return(childFolder);
        }
Example #3
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));
         }
     }
 }
Example #4
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);
         }
     }
 }
Example #5
0
        /// <summary>
        /// changes a relative corpus path to be relative to the new folder
        /// </summary>
        private bool LocalizeCorpusPath(ref string path, CdmFolderDefinition newFolder)
        {
            // if this isn't a local path, then don't do anything to it
            if (string.IsNullOrWhiteSpace(path))
            {
                return(true);
            }

            // but first, if there was no previous folder (odd) then just localize as best we can
            CdmFolderDefinition oldFolder = this.Owner as CdmFolderDefinition;
            string newPath;

            if (oldFolder == null)
            {
                newPath = this.Ctx.Corpus.Storage.CreateRelativeCorpusPath(path, newFolder);
            }
            else
            {
                // if the current value != the absolute path, then assume it is a relative path
                string absPath = this.Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(path, oldFolder);
                if (absPath == path)
                {
                    newPath = absPath; // leave it alone
                }
                else
                {
                    // make it relative to the new folder then
                    newPath = this.Ctx.Corpus.Storage.CreateRelativeCorpusPath(absPath, newFolder);
                }
            }

            if (newPath == null)
            {
                return(false);
            }

            path = newPath;
            return(true);
        }
Example #6
0
        /// <summary>
        /// finds any relative corpus paths that are held within this document and makes them relative to the new folder instead
        /// </summary>
        internal bool LocalizeCorpusPaths(CdmFolderDefinition newFolder)
        {
            bool allWentWell = true;
            bool wasBlocking = this.Ctx.Corpus.blockDeclaredPathChanges;

            this.Ctx.Corpus.blockDeclaredPathChanges = true;

            // shout into the void
            Logger.Info(nameof(CdmDocumentDefinition), (ResolveContext)this.Ctx, $"Localizing corpus paths in document '{this.Name}'", nameof(LocalizeCorpusPaths));

            // find anything in the document that is a corpus path
            this.Visit("", new VisitCallback
            {
                Invoke = (iObject, path) =>
                {
                    // i don't like that document needs to know a little about these objects
                    // in theory, we could create a virtual function on cdmObject that localizes properties
                    // but then every object would need to know about the documents and paths and such ...
                    // also, i already wrote this code.
                    switch (iObject.ObjectType)
                    {
                    case CdmObjectType.Import:
                        {
                            CdmImport typeObj  = iObject as CdmImport;
                            typeObj.CorpusPath = LocalizeCorpusPath(typeObj.CorpusPath, newFolder, ref allWentWell) ?? typeObj.CorpusPath;
                            break;
                        }

                    case CdmObjectType.LocalEntityDeclarationDef:
                    case CdmObjectType.ReferencedEntityDeclarationDef:
                        {
                            CdmEntityDeclarationDefinition typeObj = iObject as CdmEntityDeclarationDefinition;
                            typeObj.EntityPath = LocalizeCorpusPath(typeObj.EntityPath, newFolder, ref allWentWell) ?? typeObj.EntityPath;
                            break;
                        }

                    case CdmObjectType.DataPartitionDef:
                        {
                            CdmDataPartitionDefinition typeObj = iObject as CdmDataPartitionDefinition;
                            typeObj.Location          = LocalizeCorpusPath(typeObj.Location, newFolder, ref allWentWell) ?? typeObj.Location;
                            typeObj.SpecializedSchema = LocalizeCorpusPath(typeObj.SpecializedSchema, newFolder, ref allWentWell) ?? typeObj.SpecializedSchema;
                            break;
                        }

                    case CdmObjectType.DataPartitionPatternDef:
                        {
                            CdmDataPartitionPatternDefinition typeObj = iObject as CdmDataPartitionPatternDefinition;
                            typeObj.RootLocation      = LocalizeCorpusPath(typeObj.RootLocation, newFolder, ref allWentWell) ?? typeObj.RootLocation;
                            typeObj.SpecializedSchema = LocalizeCorpusPath(typeObj.SpecializedSchema, newFolder, ref allWentWell) ?? typeObj.SpecializedSchema;
                            break;
                        }

                    case CdmObjectType.E2ERelationshipDef:
                        {
                            CdmE2ERelationship typeObj = iObject as CdmE2ERelationship;
                            typeObj.ToEntity           = LocalizeCorpusPath(typeObj.ToEntity, newFolder, ref allWentWell) ?? typeObj.ToEntity;
                            typeObj.FromEntity         = LocalizeCorpusPath(typeObj.FromEntity, newFolder, ref allWentWell) ?? typeObj.FromEntity;
                            break;
                        }

                    case CdmObjectType.ManifestDeclarationDef:
                        {
                            CdmManifestDeclarationDefinition typeObj = iObject as CdmManifestDeclarationDefinition;
                            typeObj.Definition = LocalizeCorpusPath(typeObj.Definition, newFolder, ref allWentWell) ?? typeObj.Definition;
                            break;
                        }
                    }
                    return(false);
                }
            }, null);

            this.Ctx.Corpus.blockDeclaredPathChanges = wasBlocking;

            return(allWentWell);
        }
Example #7
0
 /// <summary>
 /// Constructs a CdmDocumentCollection by using the parent constructor and DocumentDef as the default type.
 /// </summary>
 /// <param name="ctx">The context.</param>
 /// <param name="owner">The folder that contains this collection.</param>
 public CdmDocumentCollection(CdmCorpusContext ctx, CdmFolderDefinition owner)
     : base(ctx, owner, CdmObjectType.DocumentDef)
 {
 }
Example #8
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);
        }
        /// <summary>
        /// Fetches the child folder from the corpus path.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="adapter">The storage adapter where the folder can be found.</param>
        /// <param name="makeFolder">Create the folder if it doesn't exist.</param>
        /// <returns>The <see cref="CdmFolderDefinition"/>.</returns>
        internal async Task <CdmFolderDefinition> FetchChildFolderFromPathAsync(string path, bool makeFolder = false)
        {
            string name;
            string remainingPath;
            int    first = path.IndexOf('/', 0);

            if (first < 0)
            {
                name          = path;
                remainingPath = "";
            }
            else
            {
                name          = StringUtils.Slice(path, 0, first);
                remainingPath = StringUtils.Slice(path, first + 1);
            }

            if (name.ToLowerInvariant() == this.Name.ToLowerInvariant())
            {
                // the end?
                if (remainingPath.Length == 0)
                {
                    return(this);
                }
                // check children folders
                CdmFolderDefinition result = null;
                if (this.ChildFolders != null)
                {
                    foreach (var f in this.ChildFolders)
                    {
                        result = await f.FetchChildFolderFromPathAsync(remainingPath, makeFolder);

                        if (result != null)
                        {
                            break;
                        }
                    }
                }
                if (result != null)
                {
                    return(result);
                }

                // get the next folder
                first = remainingPath.IndexOf("/");
                name  = first > 0 ? remainingPath.Slice(0, first) : remainingPath;

                if (first != -1)
                {
                    var childPath = await this.ChildFolders.Add(name).FetchChildFolderFromPathAsync(remainingPath, makeFolder);

                    if (childPath == null)
                    {
                        Logger.Error(nameof(CdmFolderDefinition), (ResolveContext)this.Ctx, $"Invalid path '{path}'", nameof(FetchChildFolderFromPathAsync));
                    }
                    return(childPath);
                }

                if (makeFolder)
                {
                    // huh, well need to make the fold here
                    return(await this.ChildFolders.Add(name).FetchChildFolderFromPathAsync(remainingPath, makeFolder));
                }
                else
                {
                    // good enough, return where we got to
                    return(this);
                }
            }

            return(null);
        }
Example #10
0
        /// <summary>
        /// Fetches the child folder from the corpus path.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="adapter">The storage adapter where the folder can be found.</param>
        /// <param name="makeFolder">Create the folder if it doesn't exist.</param>
        /// <returns>The <see cref="CdmFolderDefinition"/>.</returns>
        internal CdmFolderDefinition FetchChildFolderFromPath(string path, bool makeFolder = false)
        {
            string name;
            string remainingPath            = path;
            CdmFolderDefinition childFolder = this;

            while (childFolder != null && remainingPath.IndexOf('/') != -1)
            {
                int first = remainingPath.IndexOf('/');
                if (first < 0)
                {
                    name          = path;
                    remainingPath = "";
                }
                else
                {
                    name          = StringUtils.Slice(remainingPath, 0, first);
                    remainingPath = StringUtils.Slice(remainingPath, first + 1);
                }

                if (name.ToLowerInvariant() != childFolder.Name.ToLowerInvariant())
                {
                    Logger.Error(nameof(CdmFolderDefinition), (ResolveContext)this.Ctx, $"Invalid path '{path}'", nameof(FetchChildFolderFromPath));
                    return(null);
                }

                // the end?
                if (remainingPath.Length == 0)
                {
                    return(childFolder);
                }

                first = remainingPath.IndexOf('/');
                string childFolderName = remainingPath;
                if (first != -1)
                {
                    childFolderName = StringUtils.Slice(remainingPath, 0, first);
                }
                else
                {
                    // the last part of the path will be considered part of the part depending on the makeFolder flag.
                    break;
                }

                // check children folders
                CdmFolderDefinition result = null;
                if (this.ChildFolders != null)
                {
                    foreach (var folder in childFolder.ChildFolders)
                    {
                        if (childFolderName.ToLowerInvariant() == folder.Name.ToLowerInvariant())
                        {
                            // found our folder.
                            result = folder;
                            break;
                        }
                    }
                }

                if (result == null)
                {
                    result = childFolder.ChildFolders.Add(childFolderName);
                }

                childFolder = result;
            }

            if (makeFolder)
            {
                childFolder = childFolder.ChildFolders.Add(remainingPath);
            }

            return(childFolder);
        }