public string FetchValueString(ResolveOptions resOpt) { if (this.Value == null) { return(""); } if (this.Value is string || this.Value is JValue) { return((string)this.Value); } if (Value is CdmObject value) { CdmObjectDefinition def = value.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (value.ObjectType == CdmObjectType.EntityRef && def?.ObjectType == CdmObjectType.ConstantEntityDef) { CdmEntityReference entShape = (def as CdmConstantEntityDefinition).EntityShape; List <List <string> > entValues = (def as CdmConstantEntityDefinition).ConstantValues; if (entValues == null || entValues?.Count == 0) { return(""); } List <IDictionary <string, string> > rows = new List <IDictionary <string, string> >(); ResolvedAttributeSet shapeAtts = entShape.FetchResolvedAttributes(resOpt); for (int r = 0; r < entValues.Count; r++) { List <string> rowData = entValues[r]; IDictionary <string, string> row = new SortedDictionary <string, string>(); if (rowData?.Count > 0) { for (int c = 0; c < rowData.Count; c++) { string tvalue = rowData[c]; ResolvedAttribute colAtt = shapeAtts.Set[c]; if (colAtt != null) { row.Add(colAtt.ResolvedName, tvalue); } } rows.Add(row); } } return(JsonConvert.SerializeObject(rows, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new CamelCasePropertyNamesContractResolver() })); } dynamic data = value.CopyData(resOpt, new CopyOptions { StringRefs = false }); if (data is string) { return((string)data); } return(JsonConvert.SerializeObject(data, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new CamelCasePropertyNamesContractResolver() })); } return(""); }
internal override void ConstructResolvedTraits(ResolvedTraitSetBuilder rtsb, ResolveOptions resOpt) { CdmObjectDefinition objDef = this.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (objDef != null) { ResolvedTraitSet rtsInh = (objDef as CdmObjectDefinitionBase).FetchResolvedTraits(resOpt); if (rtsInh != null) { rtsInh = rtsInh.DeepCopy(); } rtsb.TakeReference(rtsInh); } else { string defName = this.FetchObjectDefinitionName(); Logger.Warning(defName, this.Ctx, $"unable to resolve an object from the reference '{defName}'"); } if (this.AppliedTraits != null) { foreach (CdmTraitReference at in this.AppliedTraits) { rtsb.MergeTraits(at.FetchResolvedTraits(resOpt)); } } }
public CdmObjectDefinition 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; CdmObjectDefinitionBase 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 CdmObjectDefinition ent = (this.Ctx.Corpus as CdmCorpusDefinition).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 as CdmCorpusDefinition).ResolveSymbolReference(resOpt, this.InDocument, this.NamedReference, this.ObjectType, retry: true); } return(res); }
/// <summary> /// Fetches the corresponding object definition for every object reference. /// </summary> /// <param name="objects"></param> /// <param name="resOpt"></param> internal void ResolveObjectDefinitions(ResolveOptions resOpt) { ResolveContext ctx = this.Ctx as ResolveContext; resOpt.IndexingDoc = this; foreach (var obj in this.InternalObjects) { switch (obj.ObjectType) { case CdmObjectType.AttributeRef: case CdmObjectType.AttributeGroupRef: case CdmObjectType.AttributeContextRef: case CdmObjectType.DataTypeRef: case CdmObjectType.EntityRef: case CdmObjectType.PurposeRef: case CdmObjectType.TraitRef: ctx.RelativePath = obj.DeclaredPath; CdmObjectReferenceBase reff = obj as CdmObjectReferenceBase; if (CdmObjectReferenceBase.offsetAttributePromise(reff.NamedReference) < 0) { CdmObjectDefinition resNew = reff.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (resNew == null) { // It's okay if references can't be resolved when shallow validation is enabled. if (resOpt.ShallowValidation) { Logger.Warning(ctx, Tag, nameof(ResolveObjectDefinitions), this.AtCorpusPath, CdmLogCode.WarnResolveReferenceFailure, reff.NamedReference); } else { Logger.Error(ctx, Tag, nameof(ResolveObjectDefinitions), this.AtCorpusPath, CdmLogCode.ErrResolveReferenceFailure, reff.NamedReference); // don't check in this file without both of these comments. handy for debug of failed lookups // CdmObjectDefinitionBase resTest = ref.FetchObjectDefinition(resOpt); } } else { Logger.Info(ctx, Tag, nameof(ResolveObjectDefinitions), this.AtCorpusPath, $"resolved '{reff.NamedReference}'"); } } break; case CdmObjectType.ParameterDef: // when a parameter has a datatype that is a cdm object, validate that any default value is the // right kind object CdmParameterDefinition parameter = obj as CdmParameterDefinition; parameter.ConstTypeCheck(resOpt, this, null); break; } } resOpt.IndexingDoc = null; }
internal bool IsDerivedFromDef(ResolveOptions resOpt, CdmObjectReference baseCdmObjectRef, string name, string seek) { if (seek == name) { return(true); } CdmObjectDefinition def = baseCdmObjectRef?.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (def != null) { return(def.IsDerivedFrom(seek, resOpt)); } return(false); }
/// <summary> /// Create a copy of the reference object /// </summary> public static dynamic CopyIdentifierRef(CdmObjectReference objRef, ResolveOptions resOpt, CopyOptions options) { string identifier = objRef.NamedReference; if (options == null || options.StringRefs == null || options.StringRefs == false) { return(identifier); } CdmObjectDefinition resolved = objRef.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (resolved == null) { return(identifier); } return(new { AtCorpusPath = resolved.AtCorpusPath, Identifier = identifier }); }
public ResolvedEntityReferenceSet FetchResolvedEntityReferences(ResolveOptions resOpt = null) { if (resOpt == null) { resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives); } CdmObjectDefinition cdmObjectDef = this.FetchResolvedReference(resOpt); if (cdmObjectDef != null) { return((cdmObjectDef as CdmAttributeGroupDefinition).FetchResolvedEntityReferences(resOpt)); } if (this.ExplicitReference != null) { return((this.ExplicitReference as CdmAttributeGroupDefinition).FetchResolvedEntityReferences(resOpt)); } return(null); }
internal CdmObjectDefinition SetObjectDef(CdmObjectDefinition def) { throw new InvalidOperationException("not a ref"); }
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; string cacheTag = ctx.Corpus.CreateDefinitionCacheTag(resOpt, this, kind, acpInContext != null ? "ctx" : ""); dynamic rasbCache = null; if (cacheTag != null) { ctx.Cache.TryGetValue(cacheTag, out rasbCache); } CdmAttributeContext underCtx = null; // 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(); // get the moniker that was found and needs to be appended to all // refs in the children attribute context nodes string fromMoniker = resOpt.FromMoniker; resOpt.FromMoniker = 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()); } this.resolvingAttributes = true; // if a new context node is needed for these attributes, make it now if (acpInContext != null) { underCtx = CdmAttributeContext.CreateChildUnder(resOpt, acpInContext); } rasbCache = this.ConstructResolvedAttributes(resOpt, underCtx); if (rasbCache != null) { this.resolvingAttributes = false; // 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 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; } if (!string.IsNullOrWhiteSpace(fromMoniker) && acpInContext != null && (this is CdmObjectReferenceBase) && (this as CdmObjectReferenceBase).NamedReference != null) { // create a fresh context CdmAttributeContext oldContext = acpInContext.under.Contents[acpInContext.under.Contents.Count - 1] as CdmAttributeContext; acpInContext.under.Contents.RemoveAt(acpInContext.under.Contents.Count - 1); underCtx = CdmAttributeContext.CreateChildUnder(resOpt, acpInContext); CdmAttributeContext newContext = oldContext.CopyAttributeContextTree(resOpt, underCtx, rasbCache.ResolvedAttributeSet, null, fromMoniker); // since THIS should be a refererence to a thing found in a moniker document, it already has a moniker in the reference // this function just added that same moniker to everything in the sub-tree but now this one symbol has too many // remove one string monikerPathAdded = $"{fromMoniker}/"; if (newContext.Definition != null && newContext.Definition.NamedReference != null && newContext.Definition.NamedReference.StartsWith(monikerPathAdded)) { // slice it off the front newContext.Definition.NamedReference = newContext.Definition.NamedReference.Substring(monikerPathAdded.Length); } } } } } else { // cache found. if we are building a context, then fix what we got instead of making a new one if (acpInContext != null) { // make the new context underCtx = CdmAttributeContext.CreateChildUnder(resOpt, acpInContext); rasbCache.ResolvedAttributeSet.AttributeContext.CopyAttributeContextTree(resOpt, underCtx, rasbCache.ResolvedAttributeSet, null, fromMoniker); } } // merge child document set with current currDocRefSet.Merge(resOpt.SymbolRefSet); resOpt.SymbolRefSet = currDocRefSet; this.Ctx.Corpus.isCurrentlyResolving = wasPreviouslyResolving; return(rasbCache?.ResolvedAttributeSet); }
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 ResolvedTraitSet FetchResolvedTraits(ResolveOptions resOpt = null) { if (resOpt == null) { resOpt = new ResolveOptions(this); } if (this.NamedReference != null && this.AppliedTraits == null) { const string kind = "rts"; ResolveContext ctx = this.Ctx as ResolveContext; string objDefName = this.FetchObjectDefinitionName(); string cacheTag = ((CdmCorpusDefinition)ctx.Corpus).CreateDefinitionCacheTag(resOpt, this, kind, "", true); dynamic rtsResultDynamic = null; if (cacheTag != null) { ctx.Cache.TryGetValue(cacheTag, out rtsResultDynamic); } ResolvedTraitSet rtsResult = rtsResultDynamic as ResolvedTraitSet; // store the previous document set, we will need to add it with // children found from the constructResolvedTraits call SymbolSet currSymRefSet = resOpt.SymbolRefSet; if (currSymRefSet == null) { currSymRefSet = new SymbolSet(); } resOpt.SymbolRefSet = new SymbolSet(); if (rtsResult == null) { CdmObjectDefinition objDef = this.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (objDef != null) { rtsResult = (objDef as CdmObjectDefinitionBase).FetchResolvedTraits(resOpt); if (rtsResult != null) { rtsResult = rtsResult.DeepCopy(); } // register set of possible docs ((CdmCorpusDefinition)ctx.Corpus).RegisterDefinitionReferenceSymbols(objDef, kind, resOpt.SymbolRefSet); // get the new cache tag now that we have the list of docs cacheTag = ((CdmCorpusDefinition)ctx.Corpus).CreateDefinitionCacheTag(resOpt, this, kind, "", true); if (!string.IsNullOrWhiteSpace(cacheTag)) { ctx.Cache[cacheTag] = rtsResult; } } } else { // cache was found // get the SymbolSet for this cached object string key = CdmCorpusDefinition.CreateCacheKeyFromObject(this, kind); ((CdmCorpusDefinition)ctx.Corpus).DefinitionReferenceSymbols.TryGetValue(key, out SymbolSet tempDocRefSet); resOpt.SymbolRefSet = tempDocRefSet; } // merge child document set with current currSymRefSet.Merge(resOpt.SymbolRefSet); resOpt.SymbolRefSet = currSymRefSet; return(rtsResult); } else { return(base.FetchResolvedTraits(resOpt)); } }
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); }