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); }
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); }
virtual internal ResolvedAttributeSetBuilder FetchObjectFromCache(ResolveOptions resOpt, AttributeContextParameters acpInContext) { const string kind = "rasb"; ResolveContext ctx = this.Ctx as ResolveContext; string cacheTag = ctx.Corpus.CreateDefinitionCacheTag(resOpt, this, kind, acpInContext != null ? "ctx" : ""); ResolvedAttributeSetBuilder rasbCache = null; if (cacheTag != null) { ctx.AttributeCache.TryGetValue(cacheTag, out rasbCache); } return(rasbCache); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); 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++) { dynamic att = this.Members.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 }; } rasb.MergeAttributes(att.FetchResolvedAttributes(resOpt, acpAtt)); } } rasb.ResolvedAttributeSet.AttributeContext = under; // things that need to go away rasb.RemoveRequestedAtts(); return(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-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 as CdmEntityReference; CdmAttributeContext underAtt = (CdmAttributeContext)under; AttributeContextParameters acpEnt = null; if (underAtt != null) { // make a context for this attreibute that holds the attributes that come up from the entity acpEnt = new AttributeContextParameters { under = underAtt, type = CdmAttributeContextType.Entity, Name = ctxEnt.FetchObjectDefinitionName(), Regarding = ctxEnt, IncludeTraits = true }; } ResolvedTraitSet rtsThisAtt = this.FetchResolvedTraits(resOpt); // this context object holds all of the info about what needs to happen to resolve these attributes. // make a copy and add defaults if missing CdmAttributeResolutionGuidance resGuideWithDefault; if (this.ResolutionGuidance != null) resGuideWithDefault = (CdmAttributeResolutionGuidance)this.ResolutionGuidance.Copy(resOpt); else resGuideWithDefault = new CdmAttributeResolutionGuidance(this.Ctx); resGuideWithDefault.UpdateAttributeDefaults(this.Name); AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuideWithDefault, rtsThisAtt); // complete cheating but is faster. // this purpose will remove all of the attributes that get collected here, so dumb and slow to go get them RelationshipInfo relInfo = this.GetRelationshipInfo(arc.ResOpt, arc); if (relInfo.IsByRef) { // make the entity context that a real recursion would have give us if (under != null) under = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEnt); // if selecting from one of many attributes, then make a context for each one if (under != null && relInfo.SelectsOne) { // the right way to do this is to get a resolved entity from the embedded entity and then // look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes // that seems like a disaster waiting to happen given endless looping, etc. // for now, just insist that only the top level entity attributes declared in the ref entity will work CdmEntityDefinition entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt) as CdmEntityDefinition; CdmCollection<CdmAttributeItem> attsPick = entPickFrom?.GetAttributeDefinitions(); if (entPickFrom != null && attsPick != null) { for (int i = 0; i < attsPick.Count; i++) { if (attsPick.AllItems[i].ObjectType == CdmObjectType.EntityAttributeDef) { // a table within a table. as expected with a selectsOne attribute // since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key // these are the same contexts that would get created if we recursed // first this attribute AttributeContextParameters acpEntAtt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeDefinition, Name = attsPick.AllItems[i].FetchObjectDefinitionName(), Regarding = attsPick.AllItems[i], IncludeTraits = true }; CdmAttributeContext pickUnder = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAtt); CdmEntityReference pickEnt = (attsPick.AllItems[i] as CdmEntityAttributeDefinition).Entity as CdmEntityReference; AttributeContextParameters acpEntAttEnt = new AttributeContextParameters { under = pickUnder, type = CdmAttributeContextType.Entity, Name = pickEnt.FetchObjectDefinitionName(), Regarding = pickEnt, IncludeTraits = true }; rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAttEnt); } } } } // if we got here because of the max depth, need to impose the directives to make the trait work as expected if (relInfo.MaxDepthExceeded) { if (arc.ResOpt.Directives == null) arc.ResOpt.Directives = new AttributeResolutionDirectiveSet(); arc.ResOpt.Directives.Add("referenceOnly"); } } else { ResolveOptions resLink = CopyResolveOptions(resOpt); resLink.SymbolRefSet = resOpt.SymbolRefSet; resLink.RelationshipDepth = relInfo.NextDepth; rasb.MergeAttributes((this.Entity as CdmEntityReference).FetchResolvedAttributes(resLink, acpEnt)); } // from the traits of purpose and applied here, see if new attributes get generated rasb.ResolvedAttributeSet.AttributeContext = underAtt; rasb.ApplyTraits(arc); rasb.GenerateApplierAttributes(arc, true); // true = apply the prepared traits to new atts // this may have added symbols to the dependencies, so merge them resOpt.SymbolRefSet.Merge(arc.ResOpt.SymbolRefSet); // use the traits for linked entity identifiers to record the actual foreign key links if (rasb.ResolvedAttributeSet?.Set != null && relInfo.IsByRef) { foreach (var att in rasb.ResolvedAttributeSet.Set) { var reqdTrait = att.ResolvedTraits.Find(resOpt, "is.linkedEntity.identifier"); if (reqdTrait == null) { continue; } if (reqdTrait.ParameterValues == null || reqdTrait.ParameterValues.Length == 0) { Logger.Warning(nameof(CdmEntityAttributeDefinition), this.Ctx as ResolveContext, "is.linkedEntity.identifier does not support arguments"); continue; } var entReferences = new List<string>(); var attReferences = new List<string>(); Action<CdmEntityReference, string> addEntityReference = (CdmEntityReference entRef, string nameSpace) => { var entDef = entRef.FetchObjectDefinition<CdmEntityDefinition>(resOpt); var identifyingTrait = entRef.FetchResolvedTraits(resOpt).Find(resOpt, "is.identifiedBy"); if (identifyingTrait != null && entDef != null) { var attRef = identifyingTrait.ParameterValues.FetchParameterValueByName("attribute").Value; string attNamePath = ((CdmObjectReferenceBase)attRef).NamedReference; string attName = attNamePath.Split('/').Last(); // path should be absolute and without a namespace string relativeEntPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(entDef.AtCorpusPath, entDef.InDocument); if (relativeEntPath.StartsWith($"{nameSpace}:")) { relativeEntPath = relativeEntPath.Substring(nameSpace.Length + 1); } entReferences.Add(relativeEntPath); attReferences.Add(attName); } }; if (relInfo.SelectsOne) { var entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt) as CdmEntityDefinition; var attsPick = entPickFrom?.GetAttributeDefinitions()?.Cast<CdmObject>().ToList(); if (entPickFrom != null && attsPick != null) { for (int i = 0; i < attsPick.Count; i++) { if (attsPick[i].ObjectType == CdmObjectType.EntityAttributeDef) { var entAtt = attsPick[i] as CdmEntityAttributeDefinition; addEntityReference(entAtt.Entity, this.InDocument.Namespace); } } } } else { addEntityReference(this.Entity, this.InDocument.Namespace); } var constantEntity = this.Ctx.Corpus.MakeObject<CdmConstantEntityDefinition>(CdmObjectType.ConstantEntityDef); constantEntity.EntityShape = this.Ctx.Corpus.MakeRef<CdmEntityReference>(CdmObjectType.EntityRef, "entityGroupSet", true); constantEntity.ConstantValues = entReferences.Select((entRef, idx) => new List<string> { entRef, attReferences[idx] }).ToList(); var traitParam = this.Ctx.Corpus.MakeRef<CdmEntityReference>(CdmObjectType.EntityRef, constantEntity, false); reqdTrait.ParameterValues.SetParameterValue(resOpt, "entityReferences", traitParam); } } // a 'structured' directive wants to keep all entity attributes together in a group if (arc.ResOpt.Directives?.Has("structured") == true) { ResolvedAttribute raSub = new ResolvedAttribute(rtsThisAtt.ResOpt, rasb.ResolvedAttributeSet, this.Name, (CdmAttributeContext)rasb.ResolvedAttributeSet.AttributeContext); if (relInfo.IsArray) { // put a resolved trait on this att group, yuck, hope I never need to do this again and then need to make a function for this CdmTraitReference tr = this.Ctx.Corpus.MakeObject<CdmTraitReference>(CdmObjectType.TraitRef, "is.linkedEntity.array", true); var t = tr.FetchObjectDefinition<CdmTraitDefinition>(resOpt); ResolvedTrait rt = new ResolvedTrait(t, null, new List<dynamic>(), new List<bool>()); raSub.ResolvedTraits = raSub.ResolvedTraits.Merge(rt, true); } rasb = new ResolvedAttributeSetBuilder(); rasb.OwnOne(raSub); } return rasb; }
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); acpExtEnt = new AttributeContextParameters { under = extendsRefUnder, type = CdmAttributeContextType.Entity, Name = extRef.NamedReference, 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 (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)); } } this.Rasb.MarkOrder(); this.Rasb.ResolvedAttributeSet.AttributeContext = under; // things that need to go away this.Rasb.RemoveRequestedAtts(); return(this.Rasb); }
internal ResolvedAttributeSet FetchResolvedAttributes(ResolveOptions resOpt = null, AttributeContextParameters acpInContext = null) { bool wasPreviouslyResolving = this.Ctx.Corpus.isCurrentlyResolving; this.Ctx.Corpus.isCurrentlyResolving = true; if (resOpt == null) { resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives); } bool inCircularReference = false; bool wasInCircularReference = resOpt.InCircularReference; if (this is CdmEntityDefinition entity) { inCircularReference = resOpt.CurrentlyResolvingEntities.Contains(entity); resOpt.CurrentlyResolvingEntities.Add(entity); resOpt.InCircularReference = inCircularReference; // uncomment this line as a test to turn off allowing cycles //if (inCircularReference) //{ // return new ResolvedAttributeSet(); //} } int currentDepth = resOpt.DepthInfo.CurrentDepth; const string kind = "rasb"; ResolveContext ctx = this.Ctx as ResolveContext; ResolvedAttributeSetBuilder rasbResult = null; ResolvedAttributeSetBuilder rasbCache = this.FetchObjectFromCache(resOpt, acpInContext); CdmAttributeContext underCtx; // store the previous document set, we will need to add it with // children found from the constructResolvedTraits call SymbolSet currDocRefSet = resOpt.SymbolRefSet; if (currDocRefSet == null) { currDocRefSet = new SymbolSet(); } resOpt.SymbolRefSet = new SymbolSet(); // if using the cache passes the maxDepth, we cannot use it if (rasbCache != null && resOpt.DepthInfo.CurrentDepth + rasbCache.ResolvedAttributeSet.DepthTraveled > resOpt.DepthInfo.MaxDepth) { rasbCache = null; } if (rasbCache == null) { // a new context node is needed for these attributes, // this tree will go into the cache, so we hang it off a placeholder parent // when it is used from the cache (or now), then this placeholder parent is ignored and the things under it are // put into the 'receiving' tree underCtx = CdmAttributeContext.GetUnderContextForCacheContext(resOpt, this.Ctx, acpInContext); rasbCache = this.ConstructResolvedAttributes(resOpt, underCtx); if (rasbCache != null) { // register set of possible docs CdmObjectDefinition oDef = this.FetchObjectDefinition <CdmObjectDefinitionBase>(resOpt); if (oDef != null) { ctx.Corpus.RegisterDefinitionReferenceSymbols(oDef, kind, resOpt.SymbolRefSet); if (this.ObjectType == CdmObjectType.EntityDef) { // if we just got attributes for an entity, take the time now to clean up this cached tree and prune out // things that don't help explain where the final set of attributes came from if (underCtx != null) { var scopesForAttributes = new HashSet <CdmAttributeContext>(); underCtx.CollectContextFromAtts(rasbCache.ResolvedAttributeSet, scopesForAttributes); // the context node for every final attribute if (!underCtx.PruneToScope(scopesForAttributes)) { return(null); } } } // get the new cache tag now that we have the list of docs string cacheTag = ctx.Corpus.CreateDefinitionCacheTag(resOpt, this, kind, acpInContext != null ? "ctx" : null); // save this as the cached version if (!string.IsNullOrWhiteSpace(cacheTag)) { ctx.AttributeCache[cacheTag] = rasbCache; } } // get the 'underCtx' of the attribute set from the acp that is wired into // the target tree underCtx = rasbCache.ResolvedAttributeSet.AttributeContext?.GetUnderContextFromCacheContext(resOpt, acpInContext); } } else { // get the 'underCtx' of the attribute set from the cache. The one stored there was build with a different // acp and is wired into the fake placeholder. so now build a new underCtx wired into the output tree but with // copies of all cached children underCtx = rasbCache.ResolvedAttributeSet.AttributeContext?.GetUnderContextFromCacheContext(resOpt, acpInContext); //underCtx.ValidateLineage(resOpt); // debugging } if (rasbCache != null) { // either just built something or got from cache // either way, same deal: copy resolved attributes and copy the context tree associated with it // 1. deep copy the resolved att set (may have groups) and leave the attCtx pointers set to the old tree // 2. deep copy the tree. // 1. deep copy the resolved att set (may have groups) and leave the attCtx pointers set to the old tree rasbResult = new ResolvedAttributeSetBuilder { ResolvedAttributeSet = rasbCache.ResolvedAttributeSet.Copy() }; // 2. deep copy the tree and map the context references. if (underCtx != null) // null context? means there is no tree, probably 0 attributes came out { if (!underCtx.AssociateTreeCopyWithAttributes(resOpt, rasbResult.ResolvedAttributeSet)) { return(null); } } } if (this is CdmEntityAttributeDefinition) { // current depth should now be set to this entity attribute level resOpt.DepthInfo.CurrentDepth = currentDepth; // now at the top of the chain where max depth does not influence the cache if (currentDepth == 0) { resOpt.DepthInfo.MaxDepthExceeded = false; } } if (!inCircularReference && this.ObjectType == CdmObjectType.EntityDef) { // should be removed from the root level only // if it is in a circular reference keep it there resOpt.CurrentlyResolvingEntities.Remove(this as CdmEntityDefinition); } resOpt.InCircularReference = wasInCircularReference; // merge child document set with current currDocRefSet.Merge(resOpt.SymbolRefSet); resOpt.SymbolRefSet = currDocRefSet; this.Ctx.Corpus.isCurrentlyResolving = wasPreviouslyResolving; return(rasbResult?.ResolvedAttributeSet); }
internal ResolvedAttributeSet FetchResolvedAttributes(ResolveOptions resOpt = null, AttributeContextParameters acpInContext = null) { bool wasPreviouslyResolving = this.Ctx.Corpus.isCurrentlyResolving; this.Ctx.Corpus.isCurrentlyResolving = true; if (resOpt == null) { resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives); } const string kind = "rasb"; ResolveContext ctx = this.Ctx as ResolveContext; ResolvedAttributeSetBuilder rasbResult = null; // keep track of the context node that the results of this call would like to use as the parent CdmAttributeContext parentCtxForResult = null; ResolvedAttributeSetBuilder rasbCache = this.FetchObjectFromCache(resOpt, acpInContext); CdmAttributeContext underCtx = null; if (acpInContext != null) { parentCtxForResult = acpInContext.under; } // store the previous document set, we will need to add it with // children found from the constructResolvedTraits call SymbolSet currDocRefSet = resOpt.SymbolRefSet; if (currDocRefSet == null) { currDocRefSet = new SymbolSet(); } resOpt.SymbolRefSet = new SymbolSet(); // if using the cache passes the maxDepth, we cannot use it if (rasbCache != null && resOpt.DepthInfo != null && resOpt.DepthInfo.CurrentDepth + rasbCache.ResolvedAttributeSet.DepthTraveled > resOpt.DepthInfo.MaxDepth) { rasbCache = null; } if (rasbCache == null) { if (this.resolvingAttributes) { // re-entered this attribute through some kind of self or looping reference. this.Ctx.Corpus.isCurrentlyResolving = wasPreviouslyResolving; //return new ResolvedAttributeSet(); // uncomment this line as a test to turn off allowing cycles resOpt.InCircularReference = true; this.circularReference = true; } this.resolvingAttributes = true; // a new context node is needed for these attributes, // this tree will go into the cache, so we hang it off a placeholder parent // when it is used from the cache (or now), then this placeholder parent is ignored and the things under it are // put into the 'receiving' tree underCtx = CdmAttributeContext.GetUnderContextForCacheContext(resOpt, this.Ctx, acpInContext); rasbCache = this.ConstructResolvedAttributes(resOpt, underCtx); this.resolvingAttributes = false; if (rasbCache != null) { // register set of possible docs CdmObjectDefinition oDef = this.FetchObjectDefinition <CdmObjectDefinitionBase>(resOpt); if (oDef != null) { ctx.Corpus.RegisterDefinitionReferenceSymbols(oDef, kind, resOpt.SymbolRefSet); // get the new cache tag now that we have the list of docs string cacheTag = ctx.Corpus.CreateDefinitionCacheTag(resOpt, this, kind, acpInContext != null ? "ctx" : null); // save this as the cached version if (!string.IsNullOrWhiteSpace(cacheTag)) { ctx.Cache[cacheTag] = rasbCache; } } // get the 'underCtx' of the attribute set from the acp that is wired into // the target tree underCtx = (rasbCache as ResolvedAttributeSetBuilder).ResolvedAttributeSet.AttributeContext?.GetUnderContextFromCacheContext(resOpt, acpInContext); } if (this.circularReference) { resOpt.InCircularReference = false; } } else { // get the 'underCtx' of the attribute set from the cache. The one stored there was build with a different // acp and is wired into the fake placeholder. so now build a new underCtx wired into the output tree but with // copies of all cached children underCtx = (rasbCache as ResolvedAttributeSetBuilder).ResolvedAttributeSet.AttributeContext?.GetUnderContextFromCacheContext(resOpt, acpInContext); //underCtx.ValidateLineage(resOpt); // debugging } if (rasbCache != null) { // either just built something or got from cache // either way, same deal: copy resolved attributes and copy the context tree associated with it // 1. deep copy the resolved att set (may have groups) and leave the attCtx pointers set to the old tree // 2. deep copy the tree. // 1. deep copy the resolved att set (may have groups) and leave the attCtx pointers set to the old tree rasbResult = new ResolvedAttributeSetBuilder(); rasbResult.ResolvedAttributeSet = (rasbCache as ResolvedAttributeSetBuilder).ResolvedAttributeSet.Copy(); // 2. deep copy the tree and map the context references. if (underCtx != null) // null context? means there is no tree, probably 0 attributes came out { if (underCtx.AssociateTreeCopyWithAttributes(resOpt, rasbResult.ResolvedAttributeSet) == false) { return(null); } } } DepthInfo currDepthInfo = resOpt.DepthInfo; if (this is CdmEntityAttributeDefinition && currDepthInfo != null) { // if we hit the maxDepth, we are now going back up currDepthInfo.CurrentDepth--; // now at the top of the chain where max depth does not influence the cache if (currDepthInfo.CurrentDepth <= 0) { resOpt.DepthInfo = null; } } // merge child document set with current currDocRefSet.Merge(resOpt.SymbolRefSet); resOpt.SymbolRefSet = currDocRefSet; this.Ctx.Corpus.isCurrentlyResolving = wasPreviouslyResolving; return(rasbResult?.ResolvedAttributeSet); }