Example #1
0
        internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null)
        {
            // find and cache the complete set of attributes
            // attributes definitions originate from and then get modified by subsequent re-definitions from (in this order):
            // an extended entity, traits applied to extended entity, exhibited traits of main entity, the (datatype or entity) used as an attribute, traits applied to that datatype or entity,
            // the relationsip of the attribute, the attribute definition itself and included attribute groups, dynamic traits applied to the attribute.
            this.Rasb = new ResolvedAttributeSetBuilder();
            this.Rasb.ResolvedAttributeSet.AttributeContext = under;

            if (this.ExtendsEntity != null)
            {
                CdmObjectReference         extRef          = this.ExtendsEntityRef;
                CdmAttributeContext        extendsRefUnder = null;
                AttributeContextParameters acpExtEnt       = null;

                if (under != null)
                {
                    AttributeContextParameters acpExt = new AttributeContextParameters
                    {
                        under         = under,
                        type          = CdmAttributeContextType.EntityReferenceExtends,
                        Name          = "extends",
                        Regarding     = null,
                        IncludeTraits = false
                    };
                    extendsRefUnder = this.Rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpExt);
                }

                if (extRef.ExplicitReference != null && extRef.FetchObjectDefinition <CdmObjectDefinition>(resOpt).ObjectType == CdmObjectType.ProjectionDef)
                {
                    // A Projection

                    CdmObjectDefinition extRefObjDef = extRef.FetchObjectDefinition <CdmObjectDefinition>(resOpt);
                    if (extendsRefUnder != null)
                    {
                        acpExtEnt = new AttributeContextParameters
                        {
                            under         = extendsRefUnder,
                            type          = CdmAttributeContextType.Projection,
                            Name          = extRefObjDef.GetName(),
                            Regarding     = extRef,
                            IncludeTraits = false
                        };
                    }

                    ProjectionDirective projDirective = new ProjectionDirective(resOpt, this, ownerRef: extRef);
                    CdmProjection       projDef       = (CdmProjection)extRefObjDef;
                    ProjectionContext   projCtx       = projDef.ConstructProjectionContext(projDirective, extendsRefUnder);

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

                    if (extendsRefUnder != null)
                    {
                        // usually the extended entity is a reference to a name.
                        // it is allowed however to just define the entity inline.
                        acpExtEnt = new AttributeContextParameters
                        {
                            under         = extendsRefUnder,
                            type          = CdmAttributeContextType.Entity,
                            Name          = extRef.NamedReference ?? extRef.ExplicitReference.GetName(),
                            Regarding     = extRef,
                            IncludeTraits = false
                        };
                    }

                    // save moniker, extended entity may attach a different moniker that we do not
                    // want to pass along to getting this entities attributes
                    string oldMoniker = resOpt.FromMoniker;

                    this.Rasb.MergeAttributes((this.ExtendsEntityRef as CdmObjectReferenceBase).FetchResolvedAttributes(resOpt, acpExtEnt));

                    if (!resOpt.CheckAttributeCount(this.Rasb.ResolvedAttributeSet.ResolvedAttributeCount))
                    {
                        Logger.Error(nameof(CdmEntityDefinition), this.Ctx, $"Maximum number of resolved attributes reached for the entity: {this.EntityName}.");
                        return(null);
                    }

                    if (this.ExtendsEntityResolutionGuidance != null)
                    {
                        // some guidance was given on how to integrate the base attributes into the set. apply that guidance
                        ResolvedTraitSet rtsBase = this.FetchResolvedTraits(resOpt);

                        // this context object holds all of the info about what needs to happen to resolve these attributes.
                        // make a copy and set defaults if needed
                        CdmAttributeResolutionGuidance resGuide = (CdmAttributeResolutionGuidance)this.ExtendsEntityResolutionGuidance.Copy(resOpt);
                        resGuide.UpdateAttributeDefaults(resGuide.FetchObjectDefinitionName());
                        // holds all the info needed by the resolver code
                        AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuide, rtsBase);

                        this.Rasb.GenerateApplierAttributes(arc, false); // true = apply the prepared traits to new atts
                    }

                    // reset to the old moniker
                    resOpt.FromMoniker = oldMoniker;
                }
            }

            this.Rasb.MarkInherited();
            this.Rasb.ResolvedAttributeSet.AttributeContext = under;

            if (this.Attributes != null)
            {
                int l = this.Attributes.Count;
                for (int i = 0; i < l; i++)
                {
                    dynamic                    att      = this.Attributes.AllItems[i];
                    CdmAttributeContext        attUnder = under;
                    AttributeContextParameters acpAtt   = null;
                    if (under != null)
                    {
                        acpAtt = new AttributeContextParameters
                        {
                            under         = under,
                            type          = CdmAttributeContextType.AttributeDefinition,
                            Name          = att.FetchObjectDefinitionName(),
                            Regarding     = att,
                            IncludeTraits = false
                        };
                    }
                    this.Rasb.MergeAttributes(att.FetchResolvedAttributes(resOpt, acpAtt));

                    if (!resOpt.CheckAttributeCount(this.Rasb.ResolvedAttributeSet.ResolvedAttributeCount))
                    {
                        Logger.Error(nameof(CdmEntityDefinition), this.Ctx, $"Maximum number of resolved attributes reached for the entity: {this.EntityName}.");
                        return(null);
                    }
                }
            }
            this.Rasb.MarkOrder();
            this.Rasb.ResolvedAttributeSet.AttributeContext = under;

            // things that need to go away
            this.Rasb.RemoveRequestedAtts();

            return(this.Rasb);
        }
        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);
        }