private static string MapEnumToTypeName(CdmAttributeContextType enumVal) { switch (enumVal) { case CdmAttributeContextType.Entity: return("entity"); case CdmAttributeContextType.EntityReferenceExtends: return("entityReferenceExtends"); case CdmAttributeContextType.AttributeGroup: return("attributeGroup"); case CdmAttributeContextType.AttributeDefinition: return("attributeDefinition"); case CdmAttributeContextType.AddedAttributeSupporting: return("addedAttributeSupporting"); case CdmAttributeContextType.AddedAttributeIdentity: return("addedAttributeIdentity"); case CdmAttributeContextType.AddedAttributeExpansionTotal: return("addedAttributeExpansionTotal"); case CdmAttributeContextType.AddedAttributeSelectedType: return("addedAttributeSelectedType"); case CdmAttributeContextType.GeneratedRound: return("generatedRound"); case CdmAttributeContextType.GeneratedSet: return("generatedSet"); default: return("unknown"); } }
private static string MapEnumToTypeName(CdmAttributeContextType enumVal) { switch (enumVal) { case CdmAttributeContextType.Entity: return("entity"); case CdmAttributeContextType.EntityReferenceExtends: return("entityReferenceExtends"); case CdmAttributeContextType.AttributeGroup: return("attributeGroup"); case CdmAttributeContextType.AttributeDefinition: return("attributeDefinition"); case CdmAttributeContextType.AddedAttributeSupporting: return("addedAttributeSupporting"); case CdmAttributeContextType.AddedAttributeIdentity: return("addedAttributeIdentity"); case CdmAttributeContextType.AddedAttributeExpansionTotal: return("addedAttributeExpansionTotal"); case CdmAttributeContextType.AddedAttributeSelectedType: return("addedAttributeSelectedType"); case CdmAttributeContextType.GeneratedRound: return("generatedRound"); case CdmAttributeContextType.GeneratedSet: return("generatedSet"); case CdmAttributeContextType.Projection: return("projection"); case CdmAttributeContextType.Source: return("source"); case CdmAttributeContextType.Operations: return("operations"); case CdmAttributeContextType.OperationAddCountAttribute: return("operationAddCountAttribute"); case CdmAttributeContextType.OperationAddSupportingAttribute: return("operationAddSupportingAttribute"); case CdmAttributeContextType.OperationAddTypeAttribute: return("operationAddTypeAttribute"); case CdmAttributeContextType.OperationExcludeAttributes: return("operationExcludeAttributes"); case CdmAttributeContextType.OperationArrayExpansion: return("operationArrayExpansion"); case CdmAttributeContextType.OperationCombineAttributes: return("operationCombineAttributes"); case CdmAttributeContextType.OperationRenameAttributes: return("operationRenameAttributes"); case CdmAttributeContextType.OperationReplaceAsForeignKey: return("operationReplaceAsForeignKey"); case CdmAttributeContextType.OperationIncludeAttributes: return("operationIncludeAttributes"); case CdmAttributeContextType.OperationAddAttributeGroup: return("operationAddAttributeGroup"); default: return("unknown"); } }
/// <summary> /// Creates the attribute context parameters for the searchFor, found, and action nodes and then stores them in different maps. /// The maps are used when constructing the actual attribute context tree. /// </summary> /// <param name="searchFor">The "search for" string</param> /// <param name="found">The projection attribute state that contains the "found" attribute</param> /// <param name="resAttrFromAction">The resolved attribute that resulted from the action</param> /// <param name="attrCtxType">The attribute context type to give the "action" attribute context parameter</param> internal void CreateAndStoreAttributeContextParameters(string searchFor, ProjectionAttributeState found, ResolvedAttribute resAttrFromAction, CdmAttributeContextType attrCtxType) { // searchFor is null when we have to construct attribute contexts for the excluded attributes in Include or the included attributes in Exclude, // as these attributes weren't searched for with a searchFor name. // If searchFor is null, just set it to have the same name as found so that it'll collapse in the final tree. if (searchFor == null) { searchFor = found.CurrentResolvedAttribute.ResolvedName; } // Create the attribute context parameter for the searchFor node and store it in the map as [searchFor name]:[attribute context parameter] AttributeContextParameters searchForAttrCtxParam = null; if (!searchForToSearchForAttrCtxParam.ContainsKey(searchFor)) { searchForAttrCtxParam = new AttributeContextParameters { under = root, type = CdmAttributeContextType.AttributeDefinition, Name = searchFor }; searchForToSearchForAttrCtxParam.Add(searchFor, searchForAttrCtxParam); } else { searchForToSearchForAttrCtxParam.TryGetValue(searchFor, out searchForAttrCtxParam); } // Create the attribute context parameter for the found node AttributeContextParameters foundAttrCtxParam = new AttributeContextParameters { under = root, // Set this to be under the root for now, as we may end up collapsing this node type = CdmAttributeContextType.AttributeDefinition, Name = $"{found.CurrentResolvedAttribute.ResolvedName}{(found.Ordinal != null ? "@" + found.Ordinal : "")}" }; // Store this in the map as [searchFor attribute context parameter]:[found attribute context parameters] // We store it this way so that we can create the found nodes under their corresponding searchFor nodes. if (!searchForAttrCtxParamToFoundAttrCtxParam.ContainsKey(searchForAttrCtxParam)) { searchForAttrCtxParamToFoundAttrCtxParam.Add(searchForAttrCtxParam, new List <AttributeContextParameters> { foundAttrCtxParam }); } else { List <AttributeContextParameters> foundAttrCtxParams = null; searchForAttrCtxParamToFoundAttrCtxParam.TryGetValue(searchForAttrCtxParam, out foundAttrCtxParams); foundAttrCtxParams.Add(foundAttrCtxParam); searchForAttrCtxParamToFoundAttrCtxParam[searchForAttrCtxParam] = foundAttrCtxParams; } // Create the attribute context parameter for the action node AttributeContextParameters actionAttrCtxParam = new AttributeContextParameters { under = root, // Set this to be under the root for now, as we may end up collapsing this node type = attrCtxType, // This type will be updated once we implement the new attribute context types Name = resAttrFromAction.ResolvedName }; // Store this in the map as [found attribute context parameter]:[action attribute context parameter] // We store it this way so that we can create the action nodes under their corresponding found nodes. foundAttrCtxParamToActionAttrCtxParam[foundAttrCtxParam] = actionAttrCtxParam; // Store the action attribute context parameter with the resolved attribute resulting out of the action. // This is so that we can point the action attribute context to the correct resolved attribute once the attribute context is created. actionAttrCtxParamToResAttr[actionAttrCtxParam] = resAttrFromAction; }
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); }
/// <summary> /// Creates the attribute context parameters for the searchFor, found, and action nodes and then stores them in different maps. /// The maps are used when constructing the actual attribute context tree. /// </summary> /// <param name="searchFor">The "search for" string</param> /// <param name="found">The projection attribute state that contains the "found" attribute</param> /// <param name="resAttrFromAction">The resolved attribute that resulted from the action</param> /// <param name="attrCtxType">The attribute context type to give the "action" attribute context parameter</param> /// <param name="lineageOut">normally lineage goes from new context to the found. false means don't and maybe even flip it</param> /// <param name="lineageIn"></param> internal void CreateAndStoreAttributeContextParameters(string searchFor, ProjectionAttributeState found, ResolvedAttribute resAttrFromAction, CdmAttributeContextType attrCtxType, CdmAttributeContext lineageOut, CdmAttributeContext lineageIn) { // searchFor is null when we have to construct attribute contexts for the excluded attributes in Include or the included attributes in Exclude, // as these attributes weren't searched for with a searchFor name. // If searchFor is null, just set it to have the same name as found so that it'll collapse in the final tree. if (searchFor == null) { searchFor = found.CurrentResolvedAttribute.ResolvedName; } // Create the attribute context parameter for the searchFor node and store it in the map as [searchFor name]:[attribute context parameter] AttributeContextParameters searchForAttrCtxParam = null; if (!searchForToSearchForAttrCtxParam.ContainsKey(searchFor)) { searchForAttrCtxParam = new AttributeContextParameters { under = root, type = CdmAttributeContextType.AttributeDefinition, Name = searchFor }; searchForToSearchForAttrCtxParam.Add(searchFor, searchForAttrCtxParam); } else { searchForToSearchForAttrCtxParam.TryGetValue(searchFor, out searchForAttrCtxParam); } // Create the attribute context parameter for the found node AttributeContextParameters foundAttrCtxParam = new AttributeContextParameters { under = root, // Set this to be under the root for now, as we may end up collapsing this node type = CdmAttributeContextType.AttributeDefinition, Name = $"{found.CurrentResolvedAttribute.ResolvedName}{(found.Ordinal != null ? "@" + found.Ordinal : "")}" }; // Store this in the map as [searchFor attribute context parameter]:[found attribute context parameters] // We store it this way so that we can create the found nodes under their corresponding searchFor nodes. if (!searchForAttrCtxParamToFoundAttrCtxParam.ContainsKey(searchForAttrCtxParam)) { searchForAttrCtxParamToFoundAttrCtxParam.Add(searchForAttrCtxParam, new List <AttributeContextParameters>()); } List <AttributeContextParameters> foundAttrCtxParams = searchForAttrCtxParamToFoundAttrCtxParam[searchForAttrCtxParam]; foundAttrCtxParams.Add(foundAttrCtxParam); // Create the attribute context parameter for the action node AttributeContextParameters actionAttrCtxParam = new AttributeContextParameters { under = root, // Set this to be under the root for now, as we may end up collapsing this node type = attrCtxType, // This type will be updated once we implement the new attribute context types Name = resAttrFromAction.ResolvedName }; // Store this in the map as [found attribute context parameter]:[action attribute context parameter] // We store it this way so that we can create the action nodes under their corresponding found nodes. foundAttrCtxParamToActionAttrCtxParam[foundAttrCtxParam] = actionAttrCtxParam; // Store the action attribute context parameter with the resolved attribute resulting out of the action. // This is so that we can point the action attribute context to the correct resolved attribute once the attribute context is created. actionAttrCtxParamToResAttr[actionAttrCtxParam] = resAttrFromAction; // store the current resAtt as the lineage of the new one // of note, if no lineage is stored AND the resolved Att associated above holds an existing context? we will // flip the lineage when we make a new context and point 'back' to this new node. this means this new node should // point 'back' to the context of the source attribute if (lineageOut != null) { actionAttrCtxParamToLineageOut[actionAttrCtxParam] = lineageOut; } if (lineageIn != null) { actionAttrCtxParamToLineageIn[actionAttrCtxParam] = lineageIn; } }