Exemple #1
0
        internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null)
        {
            // find and cache the complete set of attributes
            ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder();

            rasb.ResolvedAttributeSet.AttributeContext = under;
            var def = this.FetchObjectDefinition <CdmObjectDefinition>(resOpt);

            if (def != null)
            {
                AttributeContextParameters acpRef = null;
                if (under != null)
                {
                    // ask for a 'pass through' context, that is, no new context at this level
                    acpRef = new AttributeContextParameters()
                    {
                        under = under,
                        type  = CdmAttributeContextType.PassThrough
                    };
                }
                ResolvedAttributeSet resAtts = (def as CdmObjectDefinitionBase).FetchResolvedAttributes(resOpt, acpRef);
                if (resAtts?.Set?.Count > 0)
                {
                    resAtts = resAtts.Copy();
                    rasb.MergeAttributes(resAtts);
                    rasb.RemoveRequestedAtts();
                }
            }
            else
            {
                string defName = this.FetchObjectDefinitionName();
                Logger.Warning(defName, this.Ctx, $"unable to resolve an object from the reference '{defName}'");
            }
            return(rasb);
        }
Exemple #2
0
        public CdmObject FetchResolvedReference(ResolveOptions resOpt = null)
        {
            if (resOpt == null)
            {
                resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives);
            }

            if (this.ExplicitReference != null)
            {
                return(this.ExplicitReference);
            }

            if (this.Ctx == null)
            {
                return(null);
            }

            ResolveContext ctx = this.Ctx as ResolveContext;
            CdmObjectBase  res = null;

            // if this is a special request for a resolved attribute, look that up now
            int seekResAtt = offsetAttributePromise(this.NamedReference);

            if (seekResAtt >= 0)
            {
                string entName = this.NamedReference.Substring(0, seekResAtt);
                string attName = this.NamedReference.Slice(seekResAtt + resAttToken.Length);
                // get the entity
                CdmObject ent = this.Ctx.Corpus.ResolveSymbolReference(resOpt, this.InDocument, entName, CdmObjectType.EntityDef, retry: true);
                if (ent == null)
                {
                    Logger.Warning(nameof(CdmObjectReferenceBase), ctx, $"unable to resolve an entity named '{entName}' from the reference '{this.NamedReference}");
                    return(null);
                }

                // get the resolved attribute
                ResolvedAttributeSet ras = (ent as CdmObjectDefinitionBase).FetchResolvedAttributes(resOpt);
                ResolvedAttribute    ra  = null;
                if (ras != null)
                {
                    ra = ras.Get(attName);
                }
                if (ra != null)
                {
                    res = ra.Target as dynamic;
                }
                else
                {
                    Logger.Warning(nameof(CdmObjectReferenceBase), ctx, $"couldn't resolve the attribute promise for '{this.NamedReference}'", $"{resOpt.WrtDoc.AtCorpusPath}");
                }
            }
            else
            {
                // normal symbolic reference, look up from the Corpus, it knows where everything is
                res = this.Ctx.Corpus.ResolveSymbolReference(resOpt, this.InDocument, this.NamedReference, this.ObjectType, retry: true);
            }

            return(res);
        }
Exemple #3
0
        internal ResolvedAttributeSet FetchAttributesWithTraits(ResolveOptions resOpt, dynamic queryFor)
        {
            ResolvedAttributeSet resolvedAttributeSet = this.FetchResolvedAttributes(resOpt);

            if (resolvedAttributeSet != null)
            {
                return(resolvedAttributeSet.FetchAttributesWithTraits(resOpt, queryFor));
            }
            return(null);
        }
Exemple #4
0
        internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null)
        {
            // find and cache the complete set of attributes
            // attributes definitions originate from and then get modified by subsequent re-defintions from (in this order):
            // the datatype used as an attribute, traits applied to that datatype,
            // the purpose of the attribute, dynamic traits applied to the attribute.
            ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder();

            rasb.ResolvedAttributeSet.AttributeContext = under;

            // add this attribute to the set
            // make a new one and apply dynamic traits
            ResolvedAttribute newAtt = new ResolvedAttribute(resOpt, this, this.Name, under as CdmAttributeContext);

            rasb.OwnOne(newAtt);

            ResolvedTraitSet rts = this.FetchResolvedTraits(resOpt);
            // this context object holds all of the info about what needs to happen to resolve these attribute
            // make a copy and add defaults if missing
            CdmAttributeResolutionGuidance resGuideWithDefault;

            if (this.ResolutionGuidance != null)
            {
                resGuideWithDefault = (CdmAttributeResolutionGuidance)this.ResolutionGuidance.Copy(resOpt);
            }
            else
            {
                resGuideWithDefault = new CdmAttributeResolutionGuidance(this.Ctx);
            }

            // renameFormat is not currently supported for type attributes
            resGuideWithDefault.renameFormat = null;

            resGuideWithDefault.UpdateAttributeDefaults(null, this);
            AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuideWithDefault, rts);

            // TODO: remove the resolution guidance if projection is being used
            // from the traits of the datatype, purpose and applied here, see if new attributes get generated
            rasb.ApplyTraits(arc);
            rasb.GenerateApplierAttributes(arc, false); // false = don't apply these traits to added things
            // this may have added symbols to the dependencies, so merge them
            resOpt.SymbolRefSet.Merge(arc.ResOpt.SymbolRefSet);

            if (this.Projection != null)
            {
                ProjectionDirective projDirective = new ProjectionDirective(resOpt, this);
                ProjectionContext   projCtx       = this.Projection.ConstructProjectionContext(projDirective, under, rasb.ResolvedAttributeSet);

                ResolvedAttributeSet ras = this.Projection.ExtractResolvedAttributes(projCtx, under);
                rasb.ResolvedAttributeSet = ras;
            }

            return(rasb);
        }
Exemple #5
0
        internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null)
        {
            ResolvedAttributeSetBuilder rasb     = new ResolvedAttributeSetBuilder();
            CdmAttributeContext         allUnder = under;

            if (under != null)
            {
                AttributeContextParameters acpAttGrp = new AttributeContextParameters
                {
                    under         = under,
                    type          = CdmAttributeContextType.AttributeGroup,
                    Name          = this.GetName(),
                    Regarding     = this,
                    IncludeTraits = false
                };
                under = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpAttGrp);
            }

            if (this.Members != null)
            {
                for (int i = 0; i < this.Members.Count; i++)
                {
                    CdmObjectBase att = this.Members[i] as CdmObjectBase;
                    AttributeContextParameters acpAtt = null;
                    if (under != null)
                    {
                        acpAtt = new AttributeContextParameters
                        {
                            under         = under,
                            type          = CdmAttributeContextType.AttributeDefinition,
                            Name          = att.FetchObjectDefinitionName(),
                            Regarding     = att,
                            IncludeTraits = false
                        };
                    }

                    ResolvedAttributeSet rasFromAtt = att.FetchResolvedAttributes(resOpt, acpAtt);
                    // before we just merge, need to handle the case of 'attribute restatement' AKA an entity with an attribute having the same name as an attribute
                    // from a base entity. thing might come out with different names, if they do, then any attributes owned by a similar named attribute before
                    // that didn't just pop out of that same named attribute now need to go away.
                    // mark any attributes formerly from this named attribute that don't show again as orphans
                    rasb.ResolvedAttributeSet.MarkOrphansForRemoval((att as CdmAttributeItem).FetchObjectDefinitionName(), rasFromAtt);
                    // now merge
                    rasb.MergeAttributes(rasFromAtt);
                }
            }
            rasb.ResolvedAttributeSet.AttributeContext = allUnder; // context must be the one expected from the caller's pov.

            // things that need to go away
            rasb.RemoveRequestedAtts();
            return(rasb);
        }
Exemple #6
0
        /// <summary>
        /// Create resolved attribute set based on the CurrentResolvedAttribute array
        /// </summary>
        /// <param name="projCtx"></param>
        /// <returns></returns>
        internal ResolvedAttributeSet ExtractResolvedAttributes(ProjectionContext projCtx)
        {
            ResolvedAttributeSet resolvedAttributeSet = new ResolvedAttributeSet();

            resolvedAttributeSet.AttributeContext = projCtx.CurrentAttributeContext;

            foreach (var pas in projCtx.CurrentAttributeStateSet.Values)
            {
                resolvedAttributeSet.Merge(pas.CurrentResolvedAttribute, pas.CurrentResolvedAttribute.AttCtx);
            }

            return(resolvedAttributeSet);
        }
Exemple #7
0
        /// <summary>
        /// Create resolved attribute set based on the CurrentResolvedAttribute array
        /// </summary>
        /// <param name="projCtx"></param>
        /// <returns></returns>
        internal ResolvedAttributeSet ExtractResolvedAttributes(ProjectionContext projCtx, CdmAttributeContext attCtxUnder)
        {
            ResolvedAttributeSet resolvedAttributeSet = new ResolvedAttributeSet
            {
                AttributeContext = attCtxUnder
            };

            foreach (var pas in projCtx.CurrentAttributeStateSet.States)
            {
                resolvedAttributeSet.Merge(pas.CurrentResolvedAttribute);
            }

            return(resolvedAttributeSet);
        }
Exemple #8
0
        internal void CollectContextFromAtts(ResolvedAttributeSet rasSub, HashSet <CdmAttributeContext> collected)
        {
            rasSub.Set.ForEach(ra =>
            {
                var raCtx = ra.AttCtx;
                collected.Add(raCtx);

                // the target for a resolved att can be a TypeAttribute OR it can be another ResolvedAttributeSet (meaning a group)
                if (ra.Target is ResolvedAttributeSet)
                {
                    // a group
                    CollectContextFromAtts(ra.Target as ResolvedAttributeSet, collected);
                }
            });
        }
        internal CdmAttributeContext CopyAttributeContextTree(ResolveOptions resOpt, CdmAttributeContext newNode, ResolvedAttributeSet ras, HashSet <CdmAttributeContext> attCtxSet = null, string moniker = null)
        {
            ResolvedAttribute ra = null;

            ras.AttCtx2ra.TryGetValue(this, out ra);
            if (ra != null)
            {
                ras.CacheAttributeContext(newNode, ra);
            }

            // add context to set
            if (attCtxSet != null)
            {
                attCtxSet.Add(newNode);
            }

            // add moniker if this is a reference
            //if (!string.IsNullOrWhiteSpace(moniker) && newNode.Definition?.NamedReference?.StartsWith(moniker) == false)
            if (!string.IsNullOrWhiteSpace(moniker) && newNode.Definition?.NamedReference != null)
            {
                newNode.Definition.NamedReference = $"{moniker}/{newNode.Definition.NamedReference}";
            }

            // now copy the children
            if (this.Contents?.Count > 0)
            {
                foreach (CdmObject child in this.Contents)
                {
                    CdmAttributeContext newChild = null;
                    if (child is CdmAttributeContext childAsAttributeContext)
                    {
                        newChild = childAsAttributeContext.CopyNode(resOpt) as CdmAttributeContext;

                        if (newNode != null)
                        {
                            newChild.SetParent(resOpt, newNode);
                        }
                        ResolvedAttributeSet currentRas = ras;
                        if (ra?.Target is ResolvedAttributeSet)
                        {
                            currentRas = ra.Target;
                        }
                        childAsAttributeContext.CopyAttributeContextTree(resOpt, newChild, currentRas, attCtxSet, moniker);
                    }
                }
            }
            return(newNode);
        }
Exemple #10
0
        // the world's smallest complete query processor...
        internal void FindValue(ResolveOptions resOpt, dynamic attReturn, dynamic attSearch, string valueSearch, int order, Func <string, string> action)
        {
            int resultAtt = -1;
            int searchAtt = -1;

            if (attReturn is int)
            {
                resultAtt = attReturn;
            }
            if (attSearch is int)
            {
                searchAtt = attSearch;
            }

            if (resultAtt == -1 || searchAtt == -1)
            {
                // metadata library
                ResolvedAttributeSet ras = this.FetchResolvedAttributes(resOpt);
                // query validation and binding
                if (ras != null)
                {
                    int l = ras.Set.Count;
                    for (int i = 0; i < l; i++)
                    {
                        string name = ras.Set[i].ResolvedName;
                        if (resultAtt == -1 && name == attReturn)
                        {
                            resultAtt = i;
                        }
                        if (searchAtt == -1 && name == attSearch)
                        {
                            searchAtt = i;
                        }
                        if (resultAtt >= 0 && searchAtt >= 0)
                        {
                            break;
                        }
                    }
                }
            }

            // rowset processing
            if (resultAtt >= 0 && searchAtt >= 0)
            {
                if (this.ConstantValues != null && this.ConstantValues.Count > 0)
                {
                    int startAt   = 0;
                    int endBefore = this.ConstantValues.Count;
                    int increment = 1;
                    if (order == -1)
                    {
                        increment = -1;
                        startAt   = this.ConstantValues.Count - 1;
                        endBefore = -1;
                    }
                    for (int i = startAt; i != endBefore; i += increment)
                    {
                        if (this.ConstantValues[i][searchAtt] == valueSearch)
                        {
                            this.ConstantValues[i][resultAtt] = action(this.ConstantValues[i][resultAtt]);
                            return;
                        }
                    }
                }
            }
            return;
        }
Exemple #11
0
        /// <summary>
        /// A function to construct projection context and populate the resolved attribute set that ExtractResolvedAttributes method can then extract
        /// This function is the entry point for projection resolution.
        /// This function is expected to do the following 3 things:
        /// - Create an condition expression tree & default if appropriate
        /// - Create and initialize Projection Context
        /// - Process operations
        /// </summary>
        /// <param name="projDirective"></param>
        /// <param name="attrCtx"></param>
        /// <returns></returns>
        internal ProjectionContext ConstructProjectionContext(ProjectionDirective projDirective, CdmAttributeContext attrCtx)
        {
            ProjectionContext projContext = null;

            if (string.IsNullOrWhiteSpace(this.Condition))
            {
                // if no condition is provided, get default condition and persist
                this.Condition = ConditionExpression.GetDefaultConditionExpression(this.Operations, this.Owner);
            }
            // create an expression tree based on the condition
            ExpressionTree tree = new ExpressionTree();

            this.ConditionExpressionTreeRoot = tree.ConstructExpressionTree(this.Condition);
            if (this.ConditionExpressionTreeRoot == null)
            {
                Logger.Info(nameof(CdmProjection), this.Ctx, $"Optional expression missing. Implicit expression will automatically apply.", nameof(ConstructProjectionContext));
            }

            if (attrCtx != null)
            {
                // Add projection to context tree
                AttributeContextParameters acpProj = new AttributeContextParameters
                {
                    under         = attrCtx,
                    type          = CdmAttributeContextType.Projection,
                    Name          = this.FetchObjectDefinitionName(),
                    Regarding     = projDirective.OwnerRef,
                    IncludeTraits = false
                };
                CdmAttributeContext acProj = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpProj);

                AttributeContextParameters acpSource = new AttributeContextParameters
                {
                    under         = acProj,
                    type          = CdmAttributeContextType.Source,
                    Name          = "source",
                    Regarding     = null,
                    IncludeTraits = false
                };
                CdmAttributeContext acSource = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpSource);

                if (this.Source.FetchObjectDefinition <CdmObjectDefinition>(projDirective.ResOpt).ObjectType == CdmObjectType.ProjectionDef)
                {
                    // A Projection

                    projContext = ((CdmProjection)this.Source.ExplicitReference).ConstructProjectionContext(projDirective, acSource);
                }
                else
                {
                    // An Entity Reference

                    AttributeContextParameters acpSourceProjection = new AttributeContextParameters
                    {
                        under         = acSource,
                        type          = CdmAttributeContextType.Entity,
                        Name          = this.Source.NamedReference ?? this.Source.ExplicitReference.GetName(),
                        Regarding     = this.Source,
                        IncludeTraits = false
                    };
                    ResolvedAttributeSet ras = this.Source.FetchResolvedAttributes(projDirective.ResOpt, acpSourceProjection);

                    // Initialize the projection context

                    CdmCorpusContext ctx = (projDirective.Owner?.Ctx);

                    ProjectionAttributeStateSet pasSet = null;

                    // if polymorphic keep original source as previous state
                    Dictionary <string, List <ProjectionAttributeState> > polySourceSet = null;
                    if (projDirective.IsSourcePolymorphic)
                    {
                        polySourceSet = ProjectionResolutionCommonUtil.GetPolymorphicSourceSet(projDirective, ctx, this.Source, acpSourceProjection);
                    }

                    // now initialize projection attribute state
                    pasSet = ProjectionResolutionCommonUtil.InitializeProjectionAttributeStateSet(
                        projDirective,
                        ctx,
                        ras,
                        isSourcePolymorphic: projDirective.IsSourcePolymorphic,
                        polymorphicSet: polySourceSet);

                    projContext = new ProjectionContext(projDirective, ras.AttributeContext)
                    {
                        CurrentAttributeStateSet = pasSet
                    };
                }

                bool isConditionValid = false;
                if (this.ConditionExpressionTreeRoot != null)
                {
                    InputValues input = new InputValues()
                    {
                        noMaxDepth = projDirective.HasNoMaximumDepth,
                        isArray    = projDirective.IsArray,

                        referenceOnly = projDirective.IsReferenceOnly,
                        normalized    = projDirective.IsNormalized,
                        structured    = projDirective.IsStructured,

                        nextDepth = ++projDirective.CurrentDepth,
                        maxDepth  = projDirective.MaximumDepth,

                        minCardinality = projDirective.Cardinality?._MinimumNumber,
                        maxCardinality = projDirective.Cardinality?._MaximumNumber
                    };

                    isConditionValid = ExpressionTree.EvaluateExpressionTree(this.ConditionExpressionTreeRoot, input);
                }

                if (isConditionValid && this.Operations != null && this.Operations.Count > 0)
                {
                    // Just in case new operations were added programmatically, reindex operations
                    for (int i = 0; i < this.Operations.Count; i++)
                    {
                        this.Operations[i].Index = i + 1;
                    }

                    // Operation

                    AttributeContextParameters acpGenAttrSet = new AttributeContextParameters
                    {
                        under = attrCtx,
                        type  = CdmAttributeContextType.GeneratedSet,
                        Name  = "_generatedAttributeSet"
                    };
                    CdmAttributeContext acGenAttrSet = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpGenAttrSet);

                    AttributeContextParameters acpGenAttrRound0 = new AttributeContextParameters
                    {
                        under = acGenAttrSet,
                        type  = CdmAttributeContextType.GeneratedRound,
                        Name  = "_generatedAttributeRound0"
                    };
                    CdmAttributeContext acGenAttrRound0 = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpGenAttrRound0);

                    // Start with an empty list for each projection
                    ProjectionAttributeStateSet pasOperations = new ProjectionAttributeStateSet(projContext.CurrentAttributeStateSet.Ctx);
                    foreach (CdmOperationBase operation in this.Operations)
                    {
                        // Evaluate projections and apply to empty state
                        ProjectionAttributeStateSet newPasOperations = operation.AppendProjectionAttributeState(projContext, pasOperations, acGenAttrRound0);

                        // If the operations fails or it is not implemented the projection cannot be evaluated so keep previous valid state.
                        if (newPasOperations != null)
                        {
                            pasOperations = newPasOperations;
                        }
                    }

                    // Finally update the current state to the projection context
                    projContext.CurrentAttributeStateSet = pasOperations;
                }
                else
                {
                    // Pass Through - no operations to process
                }
            }

            return(projContext);
        }
Exemple #12
0
        /// <inheritdoc />
        internal override ProjectionAttributeStateSet AppendProjectionAttributeState(
            ProjectionContext projCtx,
            ProjectionAttributeStateSet projOutputSet,
            CdmAttributeContext attrCtx)
        {
            // Create a new attribute context for the operation
            AttributeContextParameters attrCtxOpAlterTraitsParam = new AttributeContextParameters
            {
                under = attrCtx,
                type  = CdmAttributeContextType.OperationAlterTraits,
                Name  = $"operation/index{Index}/{this.GetName()}"
            };
            CdmAttributeContext attrCtxOpAlterTraits = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpAlterTraitsParam);

            // Get the top-level attribute names of the selected attributes to apply
            // We use the top-level names because the applyTo list may contain a previous name our current resolved attributes had
            Dictionary <string, string> topLevelSelectedAttributeNames = this.ApplyTo != null?ProjectionResolutionCommonUtil.GetTopList(projCtx, this.ApplyTo) : null;

            foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States)
            {
                // Check if the current projection attribute state's resolved attribute is in the list of selected attributes
                // If this attribute is not in the list, then we are including it in the output without changes
                if (topLevelSelectedAttributeNames == null || topLevelSelectedAttributeNames.ContainsKey(currentPAS.CurrentResolvedAttribute.ResolvedName))
                {
                    // Create a new attribute context for the new attribute we will create
                    AttributeContextParameters attrCtxNewAttrParam = new AttributeContextParameters
                    {
                        under = attrCtxOpAlterTraits,
                        type  = CdmAttributeContextType.AttributeDefinition,
                        Name  = currentPAS.CurrentResolvedAttribute.ResolvedName
                    };
                    CdmAttributeContext attrCtxNewAttr = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxNewAttrParam);

                    ResolvedAttribute newResAttr = null;

                    if (currentPAS.CurrentResolvedAttribute.Target is ResolvedAttributeSet)
                    {
                        // Attribute group
                        // Create a copy of resolved attribute set
                        ResolvedAttributeSet resAttrNewCopy = ((ResolvedAttributeSet)currentPAS.CurrentResolvedAttribute.Target).Copy();
                        newResAttr = new ResolvedAttribute(projCtx.ProjectionDirective.ResOpt, resAttrNewCopy, currentPAS.CurrentResolvedAttribute.ResolvedName, attrCtxNewAttr);

                        // the resolved attribute group obtained from previous projection operation may have a different set of traits comparing to the resolved attribute target.
                        // We would want to take the set of traits from the resolved attribute.
                        newResAttr.ResolvedTraits = currentPAS.CurrentResolvedAttribute.ResolvedTraits.DeepCopy();
                    }
                    else if (currentPAS.CurrentResolvedAttribute.Target is CdmAttribute)
                    {
                        // Entity Attribute or Type Attribute
                        newResAttr = CreateNewResolvedAttribute(projCtx, attrCtxNewAttr, currentPAS.CurrentResolvedAttribute, currentPAS.CurrentResolvedAttribute.ResolvedName);
                    }
                    else
                    {
                        Logger.Error(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.ErrProjUnsupportedSource, currentPAS.CurrentResolvedAttribute.Target.ObjectType.ToString(), this.GetName());
                        // Add the attribute without changes
                        projOutputSet.Add(currentPAS);
                        break;
                    }

                    newResAttr.ResolvedTraits = newResAttr.ResolvedTraits.MergeSet(this.ResolvedNewTraits(projCtx, currentPAS));
                    this.RemoveTraitsInNewAttribute(projCtx.ProjectionDirective.ResOpt, newResAttr);

                    // Create a projection attribute state for the new attribute with new applied traits by creating a copy of the current state
                    // Copy() sets the current state as the previous state for the new one
                    ProjectionAttributeState newPAS = currentPAS.Copy();

                    // Update the resolved attribute to be the new attribute we created
                    newPAS.CurrentResolvedAttribute = newResAttr;

                    projOutputSet.Add(newPAS);
                }
                else
                {
                    // Pass through
                    projOutputSet.Add(currentPAS);
                }
            }

            return(projOutputSet);
        }
Exemple #13
0
        internal bool AssociateTreeCopyWithAttributes(ResolveOptions resOpt, ResolvedAttributeSet ras)
        {
            // deep copy the tree. while doing this also collect a map from old attCtx to new equivalent
            // this is where the returned tree fits in
            var cachedCtx = ras.AttributeContext;

            if (cachedCtx.CopyAttributeContextTree(resOpt, this) == null)
            {
                return(false);
            }
            ras.AttributeContext = this;

            // run over the resolved attributes in the copy and use the map to swap the old ctx for the new version
            Action <ResolvedAttributeSet> fixResolveAttributeCtx = null;

            fixResolveAttributeCtx = (rasSub) =>
            {
                rasSub.Set.ForEach(ra =>
                {
                    ra.AttCtx = resOpt.MapOldCtxToNewCtx[ra.AttCtx];
                    // the target for a resolved att can be a typeAttribute OR it can be another resolvedAttributeSet (meaning a group)
                    if (ra.Target is ResolvedAttributeSet)
                    {
                        (ra.Target as ResolvedAttributeSet).AttributeContext = ra.AttCtx;
                        fixResolveAttributeCtx(ra.Target as ResolvedAttributeSet);
                    }
                });
            };
            fixResolveAttributeCtx(ras);

            // now fix any lineage references
            Action <CdmAttributeContext, CdmAttributeContext> FixAttCtxNodeLineage = null;

            FixAttCtxNodeLineage = (ac, acParent) =>
            {
                if (ac == null)
                {
                    return;
                }
                if (acParent != null && ac.Parent != null && ac.Parent.ExplicitReference != null)
                {
                    ac.Parent.ExplicitReference = acParent;
                }
                if (ac.Lineage != null && ac.Lineage.Count > 0)
                {
                    // fix lineage
                    foreach (var lin in ac.Lineage)
                    {
                        if (lin.ExplicitReference != null)
                        {
                            // swap the actual object for the one in the new tree
                            lin.ExplicitReference = resOpt.MapOldCtxToNewCtx[lin.ExplicitReference as CdmAttributeContext];
                        }
                    }
                }

                if (ac.Contents == null || ac.Contents.Count == 0)
                {
                    return;
                }
                // look at all children
                foreach (CdmAttributeContext subSub in ac.Contents)
                {
                    FixAttCtxNodeLineage(subSub, ac);
                }
            };
            FixAttCtxNodeLineage(this, null);

            return(true);
        }
        // the only thing we need this code for is testing!!!
        public override ResolvedEntityReferenceSet FetchResolvedEntityReferences(ResolveOptions resOpt = null)
        {
            if (resOpt == null)
            {
                resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives);
            }

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

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

            RelationshipInfo relInfo = this.GetRelationshipInfo(resOpt, arc);

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

                        return(sideOther);
                    };

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

                    rers.Set.Add(rer);
                    return(rers);
                }
            }
            return(null);
        }
        internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null)
        {
            // find and cache the complete set of attributes
            // attributes definitions originate from and then get modified by subsequent re-defintions from (in this order):
            // the entity used as an attribute, traits applied to that entity,
            // the purpose of the attribute, any traits applied to the attribute.
            ResolvedAttributeSetBuilder rasb     = new ResolvedAttributeSetBuilder();
            CdmEntityReference          ctxEnt   = this.Entity;
            CdmAttributeContext         underAtt = under;
            AttributeContextParameters  acpEnt   = null;

            var ctxEntObjDef = ctxEnt.FetchObjectDefinition <CdmObjectDefinition>(resOpt);

            if (ctxEntObjDef.ObjectType == CdmObjectType.ProjectionDef)
            {
                // A Projection

                ProjectionDirective projDirective = new ProjectionDirective(resOpt, this, ownerRef: ctxEnt);
                CdmProjection       projDef       = (CdmProjection)ctxEntObjDef;
                ProjectionContext   projCtx       = projDef.ConstructProjectionContext(projDirective, under);

                ResolvedAttributeSet ras = projDef.ExtractResolvedAttributes(projCtx);
                rasb.ResolvedAttributeSet = ras;
            }
            else
            {
                // An Entity Reference

                if (underAtt != null)
                {
                    // make a context for this attreibute that holds the attributes that come up from the entity
                    acpEnt = new AttributeContextParameters
                    {
                        under         = underAtt,
                        type          = CdmAttributeContextType.Entity,
                        Name          = ctxEnt.FetchObjectDefinitionName(),
                        Regarding     = ctxEnt,
                        IncludeTraits = true
                    };
                }

                ResolvedTraitSet rtsThisAtt = this.FetchResolvedTraits(resOpt);

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

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

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

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

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

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

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

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

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

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

            return(rasb);
        }