internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { // find and cache the complete set of attributes ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); rasb.ResolvedAttributeSet.AttributeContext = under; var def = this.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (def != null) { AttributeContextParameters acpRef = null; if (under != null) { // ask for a 'pass through' context, that is, no new context at this level acpRef = new AttributeContextParameters() { under = under, type = CdmAttributeContextType.PassThrough }; } ResolvedAttributeSet resAtts = (def as CdmObjectDefinitionBase).FetchResolvedAttributes(resOpt, acpRef); if (resAtts?.Set?.Count > 0) { resAtts = resAtts.Copy(); rasb.MergeAttributes(resAtts); rasb.RemoveRequestedAtts(); } } else { string defName = this.FetchObjectDefinitionName(); Logger.Warning(defName, this.Ctx, $"unable to resolve an object from the reference '{defName}'"); } return(rasb); }
public CdmObject FetchResolvedReference(ResolveOptions resOpt = null) { if (resOpt == null) { resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives); } if (this.ExplicitReference != null) { return(this.ExplicitReference); } if (this.Ctx == null) { return(null); } ResolveContext ctx = this.Ctx as ResolveContext; CdmObjectBase res = null; // if this is a special request for a resolved attribute, look that up now int seekResAtt = offsetAttributePromise(this.NamedReference); if (seekResAtt >= 0) { string entName = this.NamedReference.Substring(0, seekResAtt); string attName = this.NamedReference.Slice(seekResAtt + resAttToken.Length); // get the entity CdmObject ent = this.Ctx.Corpus.ResolveSymbolReference(resOpt, this.InDocument, entName, CdmObjectType.EntityDef, retry: true); if (ent == null) { Logger.Warning(nameof(CdmObjectReferenceBase), ctx, $"unable to resolve an entity named '{entName}' from the reference '{this.NamedReference}"); return(null); } // get the resolved attribute ResolvedAttributeSet ras = (ent as CdmObjectDefinitionBase).FetchResolvedAttributes(resOpt); ResolvedAttribute ra = null; if (ras != null) { ra = ras.Get(attName); } if (ra != null) { res = ra.Target as dynamic; } else { Logger.Warning(nameof(CdmObjectReferenceBase), ctx, $"couldn't resolve the attribute promise for '{this.NamedReference}'", $"{resOpt.WrtDoc.AtCorpusPath}"); } } else { // normal symbolic reference, look up from the Corpus, it knows where everything is res = this.Ctx.Corpus.ResolveSymbolReference(resOpt, this.InDocument, this.NamedReference, this.ObjectType, retry: true); } return(res); }
internal ResolvedAttributeSet FetchAttributesWithTraits(ResolveOptions resOpt, dynamic queryFor) { ResolvedAttributeSet resolvedAttributeSet = this.FetchResolvedAttributes(resOpt); if (resolvedAttributeSet != null) { return(resolvedAttributeSet.FetchAttributesWithTraits(resOpt, queryFor)); } return(null); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { // find and cache the complete set of attributes // attributes definitions originate from and then get modified by subsequent re-defintions from (in this order): // the 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); }
/// <summary> /// Create resolved attribute set based on the CurrentResolvedAttribute array /// </summary> /// <param name="projCtx"></param> /// <returns></returns> internal ResolvedAttributeSet ExtractResolvedAttributes(ProjectionContext projCtx) { ResolvedAttributeSet resolvedAttributeSet = new ResolvedAttributeSet(); resolvedAttributeSet.AttributeContext = projCtx.CurrentAttributeContext; foreach (var pas in projCtx.CurrentAttributeStateSet.Values) { resolvedAttributeSet.Merge(pas.CurrentResolvedAttribute, pas.CurrentResolvedAttribute.AttCtx); } return(resolvedAttributeSet); }
/// <summary> /// Create resolved attribute set based on the CurrentResolvedAttribute array /// </summary> /// <param name="projCtx"></param> /// <returns></returns> internal ResolvedAttributeSet ExtractResolvedAttributes(ProjectionContext projCtx, CdmAttributeContext attCtxUnder) { ResolvedAttributeSet resolvedAttributeSet = new ResolvedAttributeSet { AttributeContext = attCtxUnder }; foreach (var pas in projCtx.CurrentAttributeStateSet.States) { resolvedAttributeSet.Merge(pas.CurrentResolvedAttribute); } return(resolvedAttributeSet); }
internal void CollectContextFromAtts(ResolvedAttributeSet rasSub, HashSet <CdmAttributeContext> collected) { rasSub.Set.ForEach(ra => { var raCtx = ra.AttCtx; collected.Add(raCtx); // the target for a resolved att can be a TypeAttribute OR it can be another ResolvedAttributeSet (meaning a group) if (ra.Target is ResolvedAttributeSet) { // a group CollectContextFromAtts(ra.Target as ResolvedAttributeSet, collected); } }); }
internal CdmAttributeContext CopyAttributeContextTree(ResolveOptions resOpt, CdmAttributeContext newNode, ResolvedAttributeSet ras, HashSet <CdmAttributeContext> attCtxSet = null, string moniker = null) { ResolvedAttribute ra = null; ras.AttCtx2ra.TryGetValue(this, out ra); if (ra != null) { ras.CacheAttributeContext(newNode, ra); } // add context to set if (attCtxSet != null) { attCtxSet.Add(newNode); } // add moniker if this is a reference //if (!string.IsNullOrWhiteSpace(moniker) && newNode.Definition?.NamedReference?.StartsWith(moniker) == false) if (!string.IsNullOrWhiteSpace(moniker) && newNode.Definition?.NamedReference != null) { newNode.Definition.NamedReference = $"{moniker}/{newNode.Definition.NamedReference}"; } // now copy the children if (this.Contents?.Count > 0) { foreach (CdmObject child in this.Contents) { CdmAttributeContext newChild = null; if (child is CdmAttributeContext childAsAttributeContext) { newChild = childAsAttributeContext.CopyNode(resOpt) as CdmAttributeContext; if (newNode != null) { newChild.SetParent(resOpt, newNode); } ResolvedAttributeSet currentRas = ras; if (ra?.Target is ResolvedAttributeSet) { currentRas = ra.Target; } childAsAttributeContext.CopyAttributeContextTree(resOpt, newChild, currentRas, attCtxSet, moniker); } } } return(newNode); }
// the world's smallest complete query processor... internal void FindValue(ResolveOptions resOpt, dynamic attReturn, dynamic attSearch, string valueSearch, int order, Func <string, string> action) { int resultAtt = -1; int searchAtt = -1; if (attReturn is int) { resultAtt = attReturn; } if (attSearch is int) { searchAtt = attSearch; } if (resultAtt == -1 || searchAtt == -1) { // metadata library ResolvedAttributeSet ras = this.FetchResolvedAttributes(resOpt); // query validation and binding if (ras != null) { int l = ras.Set.Count; for (int i = 0; i < l; i++) { string name = ras.Set[i].ResolvedName; if (resultAtt == -1 && name == attReturn) { resultAtt = i; } if (searchAtt == -1 && name == attSearch) { searchAtt = i; } if (resultAtt >= 0 && searchAtt >= 0) { break; } } } } // rowset processing if (resultAtt >= 0 && searchAtt >= 0) { if (this.ConstantValues != null && this.ConstantValues.Count > 0) { int startAt = 0; int endBefore = this.ConstantValues.Count; int increment = 1; if (order == -1) { increment = -1; startAt = this.ConstantValues.Count - 1; endBefore = -1; } for (int i = startAt; i != endBefore; i += increment) { if (this.ConstantValues[i][searchAtt] == valueSearch) { this.ConstantValues[i][resultAtt] = action(this.ConstantValues[i][resultAtt]); return; } } } } return; }
/// <summary> /// A function to construct projection context and populate the resolved attribute set that ExtractResolvedAttributes method can then extract /// This function is the entry point for projection resolution. /// This function is expected to do the following 3 things: /// - Create an condition expression tree & default if appropriate /// - Create and initialize Projection Context /// - Process operations /// </summary> /// <param name="projDirective"></param> /// <param name="attrCtx"></param> /// <returns></returns> internal ProjectionContext ConstructProjectionContext(ProjectionDirective projDirective, CdmAttributeContext attrCtx) { ProjectionContext projContext = null; if (string.IsNullOrWhiteSpace(this.Condition)) { // if no condition is provided, get default condition and persist this.Condition = ConditionExpression.GetDefaultConditionExpression(this.Operations, this.Owner); } // create an expression tree based on the condition ExpressionTree tree = new ExpressionTree(); this.ConditionExpressionTreeRoot = tree.ConstructExpressionTree(this.Condition); if (this.ConditionExpressionTreeRoot == null) { Logger.Info(nameof(CdmProjection), this.Ctx, $"Optional expression missing. Implicit expression will automatically apply.", nameof(ConstructProjectionContext)); } if (attrCtx != null) { // Add projection to context tree AttributeContextParameters acpProj = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.Projection, Name = this.FetchObjectDefinitionName(), Regarding = projDirective.OwnerRef, IncludeTraits = false }; CdmAttributeContext acProj = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpProj); AttributeContextParameters acpSource = new AttributeContextParameters { under = acProj, type = CdmAttributeContextType.Source, Name = "source", Regarding = null, IncludeTraits = false }; CdmAttributeContext acSource = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpSource); if (this.Source.FetchObjectDefinition <CdmObjectDefinition>(projDirective.ResOpt).ObjectType == CdmObjectType.ProjectionDef) { // A Projection projContext = ((CdmProjection)this.Source.ExplicitReference).ConstructProjectionContext(projDirective, acSource); } else { // An Entity Reference AttributeContextParameters acpSourceProjection = new AttributeContextParameters { under = acSource, type = CdmAttributeContextType.Entity, Name = this.Source.NamedReference ?? this.Source.ExplicitReference.GetName(), Regarding = this.Source, IncludeTraits = false }; ResolvedAttributeSet ras = this.Source.FetchResolvedAttributes(projDirective.ResOpt, acpSourceProjection); // Initialize the projection context CdmCorpusContext ctx = (projDirective.Owner?.Ctx); ProjectionAttributeStateSet pasSet = null; // if polymorphic keep original source as previous state Dictionary <string, List <ProjectionAttributeState> > polySourceSet = null; if (projDirective.IsSourcePolymorphic) { polySourceSet = ProjectionResolutionCommonUtil.GetPolymorphicSourceSet(projDirective, ctx, this.Source, acpSourceProjection); } // now initialize projection attribute state pasSet = ProjectionResolutionCommonUtil.InitializeProjectionAttributeStateSet( projDirective, ctx, ras, isSourcePolymorphic: projDirective.IsSourcePolymorphic, polymorphicSet: polySourceSet); projContext = new ProjectionContext(projDirective, ras.AttributeContext) { CurrentAttributeStateSet = pasSet }; } bool isConditionValid = false; if (this.ConditionExpressionTreeRoot != null) { InputValues input = new InputValues() { noMaxDepth = projDirective.HasNoMaximumDepth, isArray = projDirective.IsArray, referenceOnly = projDirective.IsReferenceOnly, normalized = projDirective.IsNormalized, structured = projDirective.IsStructured, nextDepth = ++projDirective.CurrentDepth, maxDepth = projDirective.MaximumDepth, minCardinality = projDirective.Cardinality?._MinimumNumber, maxCardinality = projDirective.Cardinality?._MaximumNumber }; isConditionValid = ExpressionTree.EvaluateExpressionTree(this.ConditionExpressionTreeRoot, input); } if (isConditionValid && this.Operations != null && this.Operations.Count > 0) { // Just in case new operations were added programmatically, reindex operations for (int i = 0; i < this.Operations.Count; i++) { this.Operations[i].Index = i + 1; } // Operation AttributeContextParameters acpGenAttrSet = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.GeneratedSet, Name = "_generatedAttributeSet" }; CdmAttributeContext acGenAttrSet = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpGenAttrSet); AttributeContextParameters acpGenAttrRound0 = new AttributeContextParameters { under = acGenAttrSet, type = CdmAttributeContextType.GeneratedRound, Name = "_generatedAttributeRound0" }; CdmAttributeContext acGenAttrRound0 = CdmAttributeContext.CreateChildUnder(projDirective.ResOpt, acpGenAttrRound0); // Start with an empty list for each projection ProjectionAttributeStateSet pasOperations = new ProjectionAttributeStateSet(projContext.CurrentAttributeStateSet.Ctx); foreach (CdmOperationBase operation in this.Operations) { // Evaluate projections and apply to empty state ProjectionAttributeStateSet newPasOperations = operation.AppendProjectionAttributeState(projContext, pasOperations, acGenAttrRound0); // If the operations fails or it is not implemented the projection cannot be evaluated so keep previous valid state. if (newPasOperations != null) { pasOperations = newPasOperations; } } // Finally update the current state to the projection context projContext.CurrentAttributeStateSet = pasOperations; } else { // Pass Through - no operations to process } } return(projContext); }
/// <inheritdoc /> internal override ProjectionAttributeStateSet AppendProjectionAttributeState( ProjectionContext projCtx, ProjectionAttributeStateSet projOutputSet, CdmAttributeContext attrCtx) { // Create a new attribute context for the operation AttributeContextParameters attrCtxOpAlterTraitsParam = new AttributeContextParameters { under = attrCtx, type = CdmAttributeContextType.OperationAlterTraits, Name = $"operation/index{Index}/{this.GetName()}" }; CdmAttributeContext attrCtxOpAlterTraits = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpAlterTraitsParam); // Get the top-level attribute names of the selected attributes to apply // We use the top-level names because the applyTo list may contain a previous name our current resolved attributes had Dictionary <string, string> topLevelSelectedAttributeNames = this.ApplyTo != null?ProjectionResolutionCommonUtil.GetTopList(projCtx, this.ApplyTo) : null; foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States) { // Check if the current projection attribute state's resolved attribute is in the list of selected attributes // If this attribute is not in the list, then we are including it in the output without changes if (topLevelSelectedAttributeNames == null || topLevelSelectedAttributeNames.ContainsKey(currentPAS.CurrentResolvedAttribute.ResolvedName)) { // Create a new attribute context for the new attribute we will create AttributeContextParameters attrCtxNewAttrParam = new AttributeContextParameters { under = attrCtxOpAlterTraits, type = CdmAttributeContextType.AttributeDefinition, Name = currentPAS.CurrentResolvedAttribute.ResolvedName }; CdmAttributeContext attrCtxNewAttr = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxNewAttrParam); ResolvedAttribute newResAttr = null; if (currentPAS.CurrentResolvedAttribute.Target is ResolvedAttributeSet) { // Attribute group // Create a copy of resolved attribute set ResolvedAttributeSet resAttrNewCopy = ((ResolvedAttributeSet)currentPAS.CurrentResolvedAttribute.Target).Copy(); newResAttr = new ResolvedAttribute(projCtx.ProjectionDirective.ResOpt, resAttrNewCopy, currentPAS.CurrentResolvedAttribute.ResolvedName, attrCtxNewAttr); // the resolved attribute group obtained from previous projection operation may have a different set of traits comparing to the resolved attribute target. // We would want to take the set of traits from the resolved attribute. newResAttr.ResolvedTraits = currentPAS.CurrentResolvedAttribute.ResolvedTraits.DeepCopy(); } else if (currentPAS.CurrentResolvedAttribute.Target is CdmAttribute) { // Entity Attribute or Type Attribute newResAttr = CreateNewResolvedAttribute(projCtx, attrCtxNewAttr, currentPAS.CurrentResolvedAttribute, currentPAS.CurrentResolvedAttribute.ResolvedName); } else { Logger.Error(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.ErrProjUnsupportedSource, currentPAS.CurrentResolvedAttribute.Target.ObjectType.ToString(), this.GetName()); // Add the attribute without changes projOutputSet.Add(currentPAS); break; } newResAttr.ResolvedTraits = newResAttr.ResolvedTraits.MergeSet(this.ResolvedNewTraits(projCtx, currentPAS)); this.RemoveTraitsInNewAttribute(projCtx.ProjectionDirective.ResOpt, newResAttr); // Create a projection attribute state for the new attribute with new applied traits by creating a copy of the current state // Copy() sets the current state as the previous state for the new one ProjectionAttributeState newPAS = currentPAS.Copy(); // Update the resolved attribute to be the new attribute we created newPAS.CurrentResolvedAttribute = newResAttr; projOutputSet.Add(newPAS); } else { // Pass through projOutputSet.Add(currentPAS); } } return(projOutputSet); }
internal bool AssociateTreeCopyWithAttributes(ResolveOptions resOpt, ResolvedAttributeSet ras) { // deep copy the tree. while doing this also collect a map from old attCtx to new equivalent // this is where the returned tree fits in var cachedCtx = ras.AttributeContext; if (cachedCtx.CopyAttributeContextTree(resOpt, this) == null) { return(false); } ras.AttributeContext = this; // run over the resolved attributes in the copy and use the map to swap the old ctx for the new version Action <ResolvedAttributeSet> fixResolveAttributeCtx = null; fixResolveAttributeCtx = (rasSub) => { rasSub.Set.ForEach(ra => { ra.AttCtx = resOpt.MapOldCtxToNewCtx[ra.AttCtx]; // the target for a resolved att can be a typeAttribute OR it can be another resolvedAttributeSet (meaning a group) if (ra.Target is ResolvedAttributeSet) { (ra.Target as ResolvedAttributeSet).AttributeContext = ra.AttCtx; fixResolveAttributeCtx(ra.Target as ResolvedAttributeSet); } }); }; fixResolveAttributeCtx(ras); // now fix any lineage references Action <CdmAttributeContext, CdmAttributeContext> FixAttCtxNodeLineage = null; FixAttCtxNodeLineage = (ac, acParent) => { if (ac == null) { return; } if (acParent != null && ac.Parent != null && ac.Parent.ExplicitReference != null) { ac.Parent.ExplicitReference = acParent; } if (ac.Lineage != null && ac.Lineage.Count > 0) { // fix lineage foreach (var lin in ac.Lineage) { if (lin.ExplicitReference != null) { // swap the actual object for the one in the new tree lin.ExplicitReference = resOpt.MapOldCtxToNewCtx[lin.ExplicitReference as CdmAttributeContext]; } } } if (ac.Contents == null || ac.Contents.Count == 0) { return; } // look at all children foreach (CdmAttributeContext subSub in ac.Contents) { FixAttCtxNodeLineage(subSub, ac); } }; FixAttCtxNodeLineage(this, null); return(true); }
// the only thing we need this code for is testing!!! public override ResolvedEntityReferenceSet FetchResolvedEntityReferences(ResolveOptions resOpt = null) { if (resOpt == null) { resOpt = new ResolveOptions(this, this.Ctx.Corpus.DefaultResolutionDirectives); } ResolvedTraitSet rtsThisAtt = this.FetchResolvedTraits(resOpt); CdmAttributeResolutionGuidance resGuide = (CdmAttributeResolutionGuidance)this.ResolutionGuidance; // this context object holds all of the info about what needs to happen to resolve these attributes AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuide, rtsThisAtt); RelationshipInfo relInfo = this.GetRelationshipInfo(resOpt, arc); if (relInfo.IsByRef && !relInfo.IsArray) { { // only place this is used, so logic here instead of encapsulated. // make a set and the one ref it will hold ResolvedEntityReferenceSet rers = new ResolvedEntityReferenceSet(resOpt); ResolvedEntityReference rer = new ResolvedEntityReference(); // referencing attribute(s) come from this attribute rer.Referencing.ResolvedAttributeSetBuilder.MergeAttributes(this.FetchResolvedAttributes(resOpt, null)); Func <CdmEntityReference, ResolvedEntityReferenceSide> resolveSide = entRef => { ResolvedEntityReferenceSide sideOther = new ResolvedEntityReferenceSide(null, null); if (entRef != null) { // reference to the other entity, hard part is the attribue name. // by convention, this is held in a trait that identifies the key sideOther.Entity = entRef.FetchObjectDefinition <CdmEntityDefinition>(resOpt); if (sideOther.Entity != null) { CdmAttribute otherAttribute; ResolveOptions otherOpts = new ResolveOptions { WrtDoc = resOpt.WrtDoc, Directives = resOpt.Directives }; ResolvedTrait t = entRef.FetchResolvedTraits(otherOpts).Find(otherOpts, "is.identifiedBy"); if (t?.ParameterValues?.Length > 0) { dynamic otherRef = (t.ParameterValues.FetchParameterValueByName("attribute").Value); if (typeof(CdmObject).IsAssignableFrom(otherRef?.GetType())) { otherAttribute = (otherRef as CdmObject).FetchObjectDefinition <CdmObjectDefinition>(otherOpts) as CdmAttribute; if (otherAttribute != null) { ResolvedAttributeSet resolvedAttributeSet = sideOther.Entity.FetchResolvedAttributes(otherOpts); if (resolvedAttributeSet != null) { sideOther.ResolvedAttributeSetBuilder.OwnOne(resolvedAttributeSet.Get(otherAttribute.GetName()).Copy()); } } } } } } return(sideOther); }; // either several or one entity // for now, a sub for the 'select one' idea if ((this.Entity as CdmEntityReference).ExplicitReference != null) { CdmEntityDefinition entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition <CdmEntityDefinition>(resOpt); CdmCollection <CdmAttributeItem> attsPick = entPickFrom.Attributes; if (attsPick != null && attsPick != null) { for (int i = 0; i < attsPick.Count; i++) { if (attsPick.AllItems[i].ObjectType == CdmObjectType.EntityAttributeDef) { CdmEntityReference er = (attsPick.AllItems[i] as CdmEntityAttributeDefinition).Entity; rer.Referenced.Add(resolveSide(er)); } } } } else { rer.Referenced.Add(resolveSide(this.Entity as CdmEntityReference)); } rers.Set.Add(rer); return(rers); } } return(null); }
internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null) { // find and cache the complete set of attributes // attributes definitions originate from and then get modified by subsequent re-defintions from (in this order): // the entity used as an attribute, traits applied to that entity, // the purpose of the attribute, any traits applied to the attribute. ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder(); CdmEntityReference ctxEnt = this.Entity; CdmAttributeContext underAtt = under; AttributeContextParameters acpEnt = null; var ctxEntObjDef = ctxEnt.FetchObjectDefinition <CdmObjectDefinition>(resOpt); if (ctxEntObjDef.ObjectType == CdmObjectType.ProjectionDef) { // A Projection ProjectionDirective projDirective = new ProjectionDirective(resOpt, this, ownerRef: ctxEnt); CdmProjection projDef = (CdmProjection)ctxEntObjDef; ProjectionContext projCtx = projDef.ConstructProjectionContext(projDirective, under); ResolvedAttributeSet ras = projDef.ExtractResolvedAttributes(projCtx); rasb.ResolvedAttributeSet = ras; } else { // An Entity Reference if (underAtt != null) { // make a context for this attreibute that holds the attributes that come up from the entity acpEnt = new AttributeContextParameters { under = underAtt, type = CdmAttributeContextType.Entity, Name = ctxEnt.FetchObjectDefinitionName(), Regarding = ctxEnt, IncludeTraits = true }; } ResolvedTraitSet rtsThisAtt = this.FetchResolvedTraits(resOpt); // this context object holds all of the info about what needs to happen to resolve these attributes. // make a copy and add defaults if missing CdmAttributeResolutionGuidance resGuideWithDefault; if (this.ResolutionGuidance != null) { resGuideWithDefault = (CdmAttributeResolutionGuidance)this.ResolutionGuidance.Copy(resOpt); } else { resGuideWithDefault = new CdmAttributeResolutionGuidance(this.Ctx); } resGuideWithDefault.UpdateAttributeDefaults(this.Name); AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuideWithDefault, rtsThisAtt); // complete cheating but is faster. // this purpose will remove all of the attributes that get collected here, so dumb and slow to go get them RelationshipInfo relInfo = this.GetRelationshipInfo(arc.ResOpt, arc); if (relInfo.IsByRef) { // make the entity context that a real recursion would have give us if (under != null) { under = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEnt); } // if selecting from one of many attributes, then make a context for each one if (under != null && relInfo.SelectsOne) { // the right way to do this is to get a resolved entity from the embedded entity and then // look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes // that seems like a disaster waiting to happen given endless looping, etc. // for now, just insist that only the top level entity attributes declared in the ref entity will work CdmEntityDefinition entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition <CdmEntityDefinition>(resOpt) as CdmEntityDefinition; CdmCollection <CdmAttributeItem> attsPick = entPickFrom?.Attributes; if (entPickFrom != null && attsPick != null) { for (int i = 0; i < attsPick.Count; i++) { if (attsPick.AllItems[i].ObjectType == CdmObjectType.EntityAttributeDef) { // a table within a table. as expected with a selectsOne attribute // since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key // these are the same contexts that would get created if we recursed // first this attribute AttributeContextParameters acpEntAtt = new AttributeContextParameters { under = under, type = CdmAttributeContextType.AttributeDefinition, Name = attsPick.AllItems[i].FetchObjectDefinitionName(), Regarding = attsPick.AllItems[i], IncludeTraits = true }; CdmAttributeContext pickUnder = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAtt); CdmEntityReference pickEnt = (attsPick.AllItems[i] as CdmEntityAttributeDefinition).Entity as CdmEntityReference; CdmAttributeContextType pickEntType = (pickEnt.FetchObjectDefinition <CdmObjectDefinition>(resOpt).ObjectType == CdmObjectType.ProjectionDef) ? CdmAttributeContextType.Projection : CdmAttributeContextType.Entity; AttributeContextParameters acpEntAttEnt = new AttributeContextParameters { under = pickUnder, type = pickEntType, Name = pickEnt.FetchObjectDefinitionName(), Regarding = pickEnt, IncludeTraits = true }; rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAttEnt); } } } } // if we got here because of the max depth, need to impose the directives to make the trait work as expected if (relInfo.MaxDepthExceeded) { if (arc.ResOpt.Directives == null) { arc.ResOpt.Directives = new AttributeResolutionDirectiveSet(); } arc.ResOpt.Directives.Add("referenceOnly"); } } else { ResolveOptions resLink = CopyResolveOptions(resOpt); resLink.SymbolRefSet = resOpt.SymbolRefSet; resLink.RelationshipDepth = relInfo.NextDepth; rasb.MergeAttributes(this.Entity.FetchResolvedAttributes(resLink, acpEnt)); } // from the traits of purpose and applied here, see if new attributes get generated rasb.ResolvedAttributeSet.AttributeContext = underAtt; rasb.ApplyTraits(arc); rasb.GenerateApplierAttributes(arc, true); // true = apply the prepared traits to new atts // this may have added symbols to the dependencies, so merge them resOpt.SymbolRefSet.Merge(arc.ResOpt.SymbolRefSet); // use the traits for linked entity identifiers to record the actual foreign key links if (rasb.ResolvedAttributeSet?.Set != null && relInfo.IsByRef) { foreach (var att in rasb.ResolvedAttributeSet.Set) { if (att.ResolvedTraits != null) { var reqdTrait = att.ResolvedTraits.Find(resOpt, "is.linkedEntity.identifier"); if (reqdTrait == null) { continue; } if (reqdTrait.ParameterValues == null || reqdTrait.ParameterValues.Length == 0) { Logger.Warning(nameof(CdmEntityAttributeDefinition), this.Ctx as ResolveContext, "is.linkedEntity.identifier does not support arguments"); continue; } var entReferences = new List <string>(); var attReferences = new List <string>(); Action <CdmEntityReference, string> addEntityReference = (CdmEntityReference entRef, string nameSpace) => { var entDef = entRef.FetchObjectDefinition <CdmEntityDefinition>(resOpt); if (entDef != null) { var otherResTraits = entRef.FetchResolvedTraits(resOpt); ResolvedTrait identifyingTrait; if (otherResTraits != null && (identifyingTrait = otherResTraits.Find(resOpt, "is.identifiedBy")) != null) { var attRef = identifyingTrait.ParameterValues.FetchParameterValueByName("attribute").Value; string attNamePath = ((CdmObjectReferenceBase)attRef).NamedReference; string attName = attNamePath.Split('/').Last(); // path should be absolute and without a namespace string relativeEntPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(entDef.AtCorpusPath, entDef.InDocument); if (relativeEntPath.StartsWith($"{nameSpace}:")) { relativeEntPath = relativeEntPath.Substring(nameSpace.Length + 1); } entReferences.Add(relativeEntPath); attReferences.Add(attName); } } }; if (relInfo.SelectsOne) { var entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition <CdmEntityDefinition>(resOpt) as CdmEntityDefinition; var attsPick = entPickFrom?.Attributes.Cast <CdmObject>().ToList(); if (entPickFrom != null && attsPick != null) { for (int i = 0; i < attsPick.Count; i++) { if (attsPick[i].ObjectType == CdmObjectType.EntityAttributeDef) { var entAtt = attsPick[i] as CdmEntityAttributeDefinition; addEntityReference(entAtt.Entity, this.InDocument.Namespace); } } } } else { addEntityReference(this.Entity, this.InDocument.Namespace); } var constantEntity = this.Ctx.Corpus.MakeObject <CdmConstantEntityDefinition>(CdmObjectType.ConstantEntityDef); constantEntity.EntityShape = this.Ctx.Corpus.MakeRef <CdmEntityReference>(CdmObjectType.EntityRef, "entityGroupSet", true); constantEntity.ConstantValues = entReferences.Select((entRef, idx) => new List <string> { entRef, attReferences[idx] }).ToList(); var traitParam = this.Ctx.Corpus.MakeRef <CdmEntityReference>(CdmObjectType.EntityRef, constantEntity, false); reqdTrait.ParameterValues.SetParameterValue(resOpt, "entityReferences", traitParam); } } } // a 'structured' directive wants to keep all entity attributes together in a group if (arc.ResOpt.Directives?.Has("structured") == true) { ResolvedAttribute raSub = new ResolvedAttribute(rtsThisAtt.ResOpt, rasb.ResolvedAttributeSet, this.Name, rasb.ResolvedAttributeSet.AttributeContext); if (relInfo.IsArray) { // put a resolved trait on this att group, yuck, hope I never need to do this again and then need to make a function for this CdmTraitReference tr = this.Ctx.Corpus.MakeObject <CdmTraitReference>(CdmObjectType.TraitRef, "is.linkedEntity.array", true); var t = tr.FetchObjectDefinition <CdmTraitDefinition>(resOpt); ResolvedTrait rt = new ResolvedTrait(t, null, new List <dynamic>(), new List <bool>()); raSub.ResolvedTraits = raSub.ResolvedTraits.Merge(rt, true); } rasb = new ResolvedAttributeSetBuilder(); rasb.ResolvedAttributeSet.AttributeContext = raSub.AttCtx; // this got set to null with the new builder rasb.OwnOne(raSub); } } return(rasb); }