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); }