Ejemplo n.º 1
0
        /// <summary>
        /// Projections require a new resolved attribute to be created multiple times
        /// This function allows us to create new resolved attributes based on a input attribute
        /// </summary>
        /// <param name="projCtx"></param>
        /// <param name="attrCtxUnder"></param>
        /// <param name="targetAttr"></param>
        /// <param name="overrideDefaultName"></param>
        /// <param name="addedSimpleRefTraits"></param>
        /// <returns></returns>
        internal static ResolvedAttribute CreateNewResolvedAttribute(
            ProjectionContext projCtx,
            CdmAttributeContext attrCtxUnder,
            CdmAttribute targetAttr,
            string overrideDefaultName         = null,
            List <string> addedSimpleRefTraits = null)
        {
            targetAttr = targetAttr.Copy() as CdmAttribute;

            ResolvedAttribute newResAttr = new ResolvedAttribute(
                projCtx.ProjectionDirective.ResOpt,
                targetAttr,
                !string.IsNullOrWhiteSpace(overrideDefaultName) ? overrideDefaultName : targetAttr.GetName(), attrCtxUnder);

            targetAttr.InDocument = projCtx.ProjectionDirective.Owner.InDocument;

            if (addedSimpleRefTraits != null)
            {
                foreach (string trait in addedSimpleRefTraits)
                {
                    if (targetAttr.AppliedTraits.Item(trait) == null)
                    {
                        targetAttr.AppliedTraits.Add(trait, true);
                    }
                }
            }

            ResolvedTraitSet resTraitSet = targetAttr.FetchResolvedTraits(projCtx.ProjectionDirective.ResOpt);

            // Create deep a copy of traits to avoid conflicts in case of parameters
            if (resTraitSet != null)
            {
                newResAttr.ResolvedTraits = resTraitSet.DeepCopy();
            }

            return(newResAttr);
        }
        /// <inheritdoc />
        internal override ProjectionAttributeStateSet AppendProjectionAttributeState(
            ProjectionContext projCtx,
            ProjectionAttributeStateSet projOutputSet,
            CdmAttributeContext attrCtx)
        {
            // Create new attribute context for the operation
            AttributeContextParameters attrCtxOpFKParam = new AttributeContextParameters
            {
                under = attrCtx,
                type  = CdmAttributeContextType.OperationReplaceAsForeignKey,
                Name  = $"operation/index{Index}/operationReplaceAsForeignKey"
            };
            CdmAttributeContext attrCtxOpFK = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpFKParam);

            // Create new attribute context for the AddedAttributeIdentity
            AttributeContextParameters attrCtxFKParam = new AttributeContextParameters
            {
                under = attrCtxOpFK,
                type  = CdmAttributeContextType.AddedAttributeIdentity,
                Name  = $"_foreignKey"
            };
            CdmAttributeContext attrCtxFK = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxFKParam);

            // get the added attribute and applied trait
            CdmTypeAttributeDefinition subFK = this.ReplaceWith;
            List <string> addTrait           = new List <string>()
            {
                "is.linkedEntity.identifier"
            };

            // Create new resolved attribute, set the new attribute as target, and apply "is.linkedEntity.identifier" trait
            ResolvedAttribute resAttrNewFK = CreateNewResolvedAttribute(projCtx, attrCtxFK, subFK, addedSimpleRefTraits: addTrait);

            ProjectionAttributeStateSet outputFromOpPasSet = CreateNewProjectionAttributeStateSet(projCtx, projOutputSet, resAttrNewFK, this.Reference);

            return(outputFromOpPasSet);
        }
        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);
            AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuideWithDefault, rts);

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

            return rasb;
        }
Ejemplo n.º 4
0
        /// <inheritdoc />
        internal override ProjectionAttributeStateSet AppendProjectionAttributeState(
            ProjectionContext projCtx,
            ProjectionAttributeStateSet projOutputSet,
            CdmAttributeContext attrCtx)
        {
            // Create a new attribute context for the operation
            AttributeContextParameters attrCtxOpCombineAttrsParam = new AttributeContextParameters
            {
                under = attrCtx,
                type  = CdmAttributeContextType.OperationCombineAttributes,
                Name  = $"operation/index{Index}/operationCombineAttributes"
            };
            CdmAttributeContext attrCtxOpCombineAttrs = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpCombineAttrsParam);

            // Initialize a projection attribute context tree builder with the created attribute context for the operation
            ProjectionAttributeContextTreeBuilder attrCtxTreeBuilder = new ProjectionAttributeContextTreeBuilder(attrCtxOpCombineAttrs);

            // Get all the leaf level PAS nodes from the tree for each selected attribute and cache to a dictionary
            Dictionary <string, List <ProjectionAttributeState> > leafLevelCombineAttributeNames = new Dictionary <string, List <ProjectionAttributeState> >();
            // Also, create a single list of leaf level PAS
            List <ProjectionAttributeState> leafLevelMergePASList = new List <ProjectionAttributeState>();

            foreach (string select in this.Select)
            {
                List <ProjectionAttributeState> leafLevelListForCurrentSelect = ProjectionResolutionCommonUtil.GetLeafList(projCtx, select);
                if (leafLevelListForCurrentSelect != null &&
                    leafLevelListForCurrentSelect.Count > 0 &&
                    !leafLevelCombineAttributeNames.ContainsKey(select))
                {
                    leafLevelCombineAttributeNames.Add(select, leafLevelListForCurrentSelect);

                    leafLevelMergePASList.AddRange(leafLevelListForCurrentSelect);
                }
            }

            // Create a List of top-level PAS objects that will be get merged based on the selected attributes
            List <ProjectionAttributeState> pasMergeList = new List <ProjectionAttributeState>();

            // Run through the top-level PAS objects
            foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States)
            {
                if (leafLevelCombineAttributeNames.ContainsKey(currentPAS.CurrentResolvedAttribute.ResolvedName))
                {
                    // Attribute to Merge

                    if (!pasMergeList.Contains(currentPAS))
                    {
                        pasMergeList.Add(currentPAS);
                    }
                }
                else
                {
                    // Attribute to Pass Through

                    // Create a projection attribute state for the non-selected / pass-through attribute 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();

                    projOutputSet.Add(newPAS);
                }
            }

            if (pasMergeList.Count > 0)
            {
                CdmTypeAttributeDefinition mergeIntoAttribute = this.MergeInto as CdmTypeAttributeDefinition;

                // the merged attribute needs one new place to live, so here it is
                AttributeContextParameters mergedAttrCtxParam = new AttributeContextParameters
                {
                    under = attrCtxOpCombineAttrs,
                    type  = CdmAttributeContextType.AttributeDefinition,
                    Name  = mergeIntoAttribute.GetName()
                };
                CdmAttributeContext mergedAttrCtx = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, mergedAttrCtxParam);

                // Create new resolved attribute, set the new attribute as target
                ResolvedAttribute raNewMergeInto = CreateNewResolvedAttribute(projCtx, mergedAttrCtx, mergeIntoAttribute, null);

                // Create new output projection attribute state set
                ProjectionAttributeState newMergeIntoPAS = new ProjectionAttributeState(projOutputSet.Ctx)
                {
                    CurrentResolvedAttribute = raNewMergeInto,
                    PreviousStateList        = pasMergeList
                };

                // Create the attribute context parameters and just store it in the builder for now
                // We will create the attribute contexts at the end
                foreach (string select in leafLevelCombineAttributeNames.Keys)
                {
                    if (leafLevelCombineAttributeNames.ContainsKey(select) &&
                        leafLevelCombineAttributeNames[select] != null &&
                        leafLevelCombineAttributeNames[select].Count > 0)
                    {
                        foreach (ProjectionAttributeState leafLevelForSelect in leafLevelCombineAttributeNames[select])
                        {
                            attrCtxTreeBuilder.CreateAndStoreAttributeContextParameters(select, leafLevelForSelect, newMergeIntoPAS.CurrentResolvedAttribute,
                                                                                        CdmAttributeContextType.AttributeDefinition,
                                                                                        leafLevelForSelect.CurrentResolvedAttribute.AttCtx, // lineage is the source att
                                                                                        newMergeIntoPAS.CurrentResolvedAttribute.AttCtx);   // merge into points back here
                        }
                    }
                }

                projOutputSet.Add(newMergeIntoPAS);
            }

            // Create all the attribute contexts and construct the tree
            attrCtxTreeBuilder.ConstructAttributeContextTree(projCtx);

            return(projOutputSet);
        }
Ejemplo n.º 5
0
        /// <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;
        }
Ejemplo n.º 6
0
        /// <inheritdoc />
        internal override ProjectionAttributeStateSet AppendProjectionAttributeState(
            ProjectionContext projCtx,
            ProjectionAttributeStateSet projOutputSet,
            CdmAttributeContext attrCtx)
        {
            // Create a new attribute context for the operation
            AttributeContextParameters attrCtxOpArrayExpansionParam = new AttributeContextParameters
            {
                under = attrCtx,
                type  = CdmAttributeContextType.OperationArrayExpansion,
                Name  = $"operation/index{Index}/operationArrayExpansion"
            };
            CdmAttributeContext attrCtxOpArrayExpansion = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpArrayExpansionParam);

            // Expansion steps start at round 0
            int round = 0;
            List <ProjectionAttributeState> projAttrStatesFromRounds = new List <ProjectionAttributeState>();

            // Ordinal validation
            if (this.StartOrdinal > this.EndOrdinal)
            {
                Logger.Warning(TAG, this.Ctx, $"startOrdinal {this.StartOrdinal} should not be greater than endOrdinal {this.EndOrdinal}", nameof(AppendProjectionAttributeState));
            }
            else
            {
                // Ordinals should start at startOrdinal or 0, whichever is larger.
                int startingOrdinal = Math.Max(0, (int)this.StartOrdinal);

                // Ordinals should end at endOrdinal or the maximum ordinal allowed (set in resolve options), whichever is smaller.
                if (this.EndOrdinal > projCtx.ProjectionDirective.ResOpt.MaxOrdinalForArrayExpansion)
                {
                    Logger.Warning(TAG, this.Ctx, $"endOrdinal {this.EndOrdinal} is greater than the maximum allowed ordinal of {projCtx.ProjectionDirective.ResOpt.MaxOrdinalForArrayExpansion}. Using the maximum allowed ordinal instead.", nameof(AppendProjectionAttributeState));
                }
                int endingOrdinal = Math.Min(projCtx.ProjectionDirective.ResOpt.MaxOrdinalForArrayExpansion, (int)this.EndOrdinal);

                // For each ordinal, create a copy of the input resolved attribute
                for (int i = startingOrdinal; i <= endingOrdinal; i++)
                {
                    // Create a new attribute context for the round
                    AttributeContextParameters attrCtxRoundParam = new AttributeContextParameters
                    {
                        under = attrCtxOpArrayExpansion,
                        type  = CdmAttributeContextType.GeneratedRound,
                        Name  = $"_generatedAttributeRound{round}"
                    };
                    CdmAttributeContext attrCtxRound = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxRoundParam);

                    // Iterate through all the projection attribute states generated from the source's resolved attributes
                    // Each projection attribute state contains a resolved attribute that it is corresponding to
                    foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States)
                    {
                        // Create a new attribute context for the expanded attribute with the current ordinal
                        AttributeContextParameters attrCtxExpandedAttrParam = new AttributeContextParameters
                        {
                            under = attrCtxRound,
                            type  = CdmAttributeContextType.AttributeDefinition,
                            Name  = $"{currentPAS.CurrentResolvedAttribute.ResolvedName}@{i}"
                        };
                        CdmAttributeContext attrCtxExpandedAttr = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxExpandedAttrParam);

                        if (currentPAS.CurrentResolvedAttribute.Target is ResolvedAttributeSet)
                        {
                            Logger.Error(TAG, this.Ctx, "Array expansion operation does not support attribute groups.");
                            projAttrStatesFromRounds.Clear();
                            break;
                        }

                        // Create a new resolved attribute for the expanded attribute
                        ResolvedAttribute newResAttr = CreateNewResolvedAttribute(projCtx, attrCtxExpandedAttr, currentPAS.CurrentResolvedAttribute.Target, currentPAS.CurrentResolvedAttribute.ResolvedName);
                        newResAttr.AttCtx.AddLineage(currentPAS.CurrentResolvedAttribute.AttCtx);

                        // Create a projection attribute state for the expanded attribute
                        ProjectionAttributeState newPAS = new ProjectionAttributeState(projOutputSet.Ctx)
                        {
                            CurrentResolvedAttribute = newResAttr,
                            PreviousStateList        = new List <ProjectionAttributeState> {
                                currentPAS
                            },
                            Ordinal = i
                        };

                        projAttrStatesFromRounds.Add(newPAS);
                    }

                    if (i == endingOrdinal)
                    {
                        break;
                    }

                    // Increment the round
                    round++;
                }
            }

            if (projAttrStatesFromRounds.Count == 0)
            {
                // No rounds were produced from the array expansion - input passes through
                foreach (ProjectionAttributeState pas in projCtx.CurrentAttributeStateSet.States)
                {
                    projOutputSet.Add(pas);
                }
            }
            else
            {
                // Add all the projection attribute states containing the expanded attributes to the output
                foreach (ProjectionAttributeState pas in projAttrStatesFromRounds)
                {
                    projOutputSet.Add(pas);
                }
            }

            return(projOutputSet);
        }
Ejemplo n.º 7
0
        /// <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);
        }
Ejemplo n.º 8
0
        /// <inheritdoc />
        internal override ProjectionAttributeStateSet AppendProjectionAttributeState(
            ProjectionContext projCtx,
            ProjectionAttributeStateSet projOutputSet,
            CdmAttributeContext attrCtx)
        {
            // Create a new attribute context for the operation
            AttributeContextParameters attrCtxOpRenameAttrsParam = new AttributeContextParameters
            {
                under = attrCtx,
                type  = CdmAttributeContextType.OperationRenameAttributes,
                Name  = $"operation/index{Index}/operationRenameAttributes"
            };
            CdmAttributeContext attrCtxOpRenameAttrs = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, attrCtxOpRenameAttrsParam);

            // Get the list of attributes that will be renamed
            List <string> renameAttributes;

            if (this.ApplyTo != null)
            {
                renameAttributes = this.ApplyTo;
            }
            else
            {
                renameAttributes = new List <string>();
                foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States)
                {
                    renameAttributes.Add(currentPAS.CurrentResolvedAttribute.ResolvedName);
                }
            }

            // Get the top-level attribute names of the attributes to rename
            // We use the top-level names because the rename list may contain a previous name our current resolved attributes had
            Dictionary <string, string> topLevelRenameAttributeNames = ProjectionResolutionCommonUtil.GetTopList(projCtx, renameAttributes);

            string sourceAttributeName = projCtx.ProjectionDirective.OriginalSourceEntityAttributeName;

            // Initialize a projection attribute context tree builder with the created attribute context for the operation
            ProjectionAttributeContextTreeBuilder attrCtxTreeBuilder = new ProjectionAttributeContextTreeBuilder(attrCtxOpRenameAttrs);

            // Iterate through all the projection attribute states generated from the source's resolved attributes
            // Each projection attribute state contains a resolved attribute that it is corresponding to
            foreach (ProjectionAttributeState currentPAS in projCtx.CurrentAttributeStateSet.States)
            {
                // Check if the current projection attribute state's resolved attribute is in the list of attributes to rename
                // If this attribute is not in the rename list, then we are including it in the output without changes
                if (topLevelRenameAttributeNames.ContainsKey(currentPAS.CurrentResolvedAttribute.ResolvedName))
                {
                    if (currentPAS.CurrentResolvedAttribute.Target is CdmAttribute)
                    {
                        // The current attribute should be renamed

                        string newAttributeName = GetNewAttributeName(currentPAS, sourceAttributeName);

                        // Create new resolved attribute with the new name, set the new attribute as target
                        ResolvedAttribute resAttrNew = CreateNewResolvedAttribute(projCtx, null, currentPAS.CurrentResolvedAttribute.Target, newAttributeName);

                        // Get the attribute name the way it appears in the applyTo list
                        string applyToName = topLevelRenameAttributeNames[currentPAS.CurrentResolvedAttribute.ResolvedName];

                        // Create the attribute context parameters and just store it in the builder for now
                        // We will create the attribute contexts at the end
                        attrCtxTreeBuilder.CreateAndStoreAttributeContextParameters(applyToName, currentPAS, resAttrNew,
                                                                                    CdmAttributeContextType.AttributeDefinition,
                                                                                    currentPAS.CurrentResolvedAttribute.AttCtx, // lineage is the original attribute
                                                                                    null);                                      // don't know who will point here yet

                        // Create a projection attribute state for the renamed attribute by creating a copy of the current state
                        // Copy() sets the current state as the previous state for the new one
                        // We only create projection attribute states for attributes that are in the rename list
                        ProjectionAttributeState newPAS = currentPAS.Copy();

                        // Update the resolved attribute to be the new renamed attribute we created
                        newPAS.CurrentResolvedAttribute = resAttrNew;

                        projOutputSet.Add(newPAS);
                    }
                    else
                    {
                        Logger.Warning(this.Ctx, Tag, nameof(AppendProjectionAttributeState), this.AtCorpusPath, CdmLogCode.WarnProjRenameAttrNotSupported);
                        // Add the attribute without changes
                        projOutputSet.Add(currentPAS);
                    }
                }
                else
                {
                    // Pass through
                    projOutputSet.Add(currentPAS);
                }
            }

            // Create all the attribute contexts and construct the tree
            attrCtxTreeBuilder.ConstructAttributeContextTree(projCtx);

            return(projOutputSet);
        }
Ejemplo n.º 9
0
        internal override ResolvedAttributeSetBuilder ConstructResolvedAttributes(ResolveOptions resOpt, CdmAttributeContext under = null)
        {
            // find and cache the complete set of attributes
            // attributes definitions originate from and then get modified by subsequent re-defintions from (in this order):
            // the entity used as an attribute, traits applied to that entity,
            // the purpose of the attribute, any traits applied to the attribute.
            ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder();
            CdmEntityReference ctxEnt = this.Entity as CdmEntityReference;
            CdmAttributeContext underAtt = (CdmAttributeContext)under;
            AttributeContextParameters acpEnt = null;
            if (underAtt != null)
            {
                // make a context for this attreibute that holds the attributes that come up from the entity
                acpEnt = new AttributeContextParameters
                {
                    under = underAtt,
                    type = CdmAttributeContextType.Entity,
                    Name = ctxEnt.FetchObjectDefinitionName(),
                    Regarding = ctxEnt,
                    IncludeTraits = true
                };
            }

            ResolvedTraitSet rtsThisAtt = this.FetchResolvedTraits(resOpt);

            // this context object holds all of the info about what needs to happen to resolve these attributes.
            // make a copy and add defaults if missing
            CdmAttributeResolutionGuidance resGuideWithDefault;
            if (this.ResolutionGuidance != null)
                resGuideWithDefault = (CdmAttributeResolutionGuidance)this.ResolutionGuidance.Copy(resOpt);
            else
                resGuideWithDefault = new CdmAttributeResolutionGuidance(this.Ctx);
            resGuideWithDefault.UpdateAttributeDefaults(this.Name);

            AttributeResolutionContext arc = new AttributeResolutionContext(resOpt, resGuideWithDefault, rtsThisAtt);

            // complete cheating but is faster.
            // this purpose will remove all of the attributes that get collected here, so dumb and slow to go get them
            RelationshipInfo relInfo = this.GetRelationshipInfo(arc.ResOpt, arc);
            if (relInfo.IsByRef)
            {
                // make the entity context that a real recursion would have give us
                if (under != null)
                    under = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEnt);
                // if selecting from one of many attributes, then make a context for each one
                if (under != null && relInfo.SelectsOne)
                {
                    // the right way to do this is to get a resolved entity from the embedded entity and then 
                    // look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes
                    // that seems like a disaster waiting to happen given endless looping, etc.
                    // for now, just insist that only the top level entity attributes declared in the ref entity will work
                    CdmEntityDefinition entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt) as CdmEntityDefinition;
                    CdmCollection<CdmAttributeItem> attsPick = entPickFrom?.GetAttributeDefinitions();
                    if (entPickFrom != null && attsPick != null)
                    {
                        for (int i = 0; i < attsPick.Count; i++)
                        {
                            if (attsPick.AllItems[i].ObjectType == CdmObjectType.EntityAttributeDef)
                            {
                                // a table within a table. as expected with a selectsOne attribute
                                // since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key
                                // these are the same contexts that would get created if we recursed
                                // first this attribute
                                AttributeContextParameters acpEntAtt = new AttributeContextParameters
                                {
                                    under = under,
                                    type = CdmAttributeContextType.AttributeDefinition,
                                    Name = attsPick.AllItems[i].FetchObjectDefinitionName(),
                                    Regarding = attsPick.AllItems[i],
                                    IncludeTraits = true
                                };
                                CdmAttributeContext pickUnder = rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAtt);
                                CdmEntityReference pickEnt = (attsPick.AllItems[i] as CdmEntityAttributeDefinition).Entity as CdmEntityReference;
                                AttributeContextParameters acpEntAttEnt = new AttributeContextParameters
                                {
                                    under = pickUnder,
                                    type = CdmAttributeContextType.Entity,
                                    Name = pickEnt.FetchObjectDefinitionName(),
                                    Regarding = pickEnt,
                                    IncludeTraits = true
                                };
                                rasb.ResolvedAttributeSet.CreateAttributeContext(resOpt, acpEntAttEnt);
                            }
                        }
                    }
                }

                // if we got here because of the max depth, need to impose the directives to make the trait work as expected
                if (relInfo.MaxDepthExceeded)
                {
                    if (arc.ResOpt.Directives == null)
                        arc.ResOpt.Directives = new AttributeResolutionDirectiveSet();
                    arc.ResOpt.Directives.Add("referenceOnly");
                }
            }
            else
            {
                ResolveOptions resLink = CopyResolveOptions(resOpt);
                resLink.SymbolRefSet = resOpt.SymbolRefSet;
                resLink.RelationshipDepth = relInfo.NextDepth;
                rasb.MergeAttributes((this.Entity as CdmEntityReference).FetchResolvedAttributes(resLink, acpEnt));
            }

            // from the traits of purpose and applied here, see if new attributes get generated
            rasb.ResolvedAttributeSet.AttributeContext = underAtt;
            rasb.ApplyTraits(arc);
            rasb.GenerateApplierAttributes(arc, true); // true = apply the prepared traits to new atts
            // this may have added symbols to the dependencies, so merge them
            resOpt.SymbolRefSet.Merge(arc.ResOpt.SymbolRefSet);

            // use the traits for linked entity identifiers to record the actual foreign key links
            if (rasb.ResolvedAttributeSet?.Set != null && relInfo.IsByRef)
            {
                foreach (var att in rasb.ResolvedAttributeSet.Set)
                {
                    var reqdTrait = att.ResolvedTraits.Find(resOpt, "is.linkedEntity.identifier");
                    if (reqdTrait == null)
                    {
                        continue;
                    }

                    if (reqdTrait.ParameterValues == null || reqdTrait.ParameterValues.Length == 0)
                    {
                        Logger.Warning(nameof(CdmEntityAttributeDefinition), this.Ctx as ResolveContext, "is.linkedEntity.identifier does not support arguments");
                        continue;
                    }

                    var entReferences = new List<string>();
                    var attReferences = new List<string>();
                    Action<CdmEntityReference, string> addEntityReference = (CdmEntityReference entRef, string nameSpace) =>
                        {
                            var entDef = entRef.FetchObjectDefinition<CdmEntityDefinition>(resOpt);
                            var identifyingTrait = entRef.FetchResolvedTraits(resOpt).Find(resOpt, "is.identifiedBy");
                            if (identifyingTrait != null && entDef != null)
                            {
                                var attRef = identifyingTrait.ParameterValues.FetchParameterValueByName("attribute").Value;
                                string attNamePath = ((CdmObjectReferenceBase)attRef).NamedReference;
                                string attName = attNamePath.Split('/').Last();                                // path should be absolute and without a namespace
                                string relativeEntPath = Ctx.Corpus.Storage.CreateAbsoluteCorpusPath(entDef.AtCorpusPath, entDef.InDocument);
                                if (relativeEntPath.StartsWith($"{nameSpace}:"))
                                {
                                    relativeEntPath = relativeEntPath.Substring(nameSpace.Length + 1);
                                }
                                entReferences.Add(relativeEntPath);
                                attReferences.Add(attName);
                            }
                        };
                    if (relInfo.SelectsOne)
                    {
                        var entPickFrom = (this.Entity as CdmEntityReference).FetchObjectDefinition<CdmEntityDefinition>(resOpt) as CdmEntityDefinition;
                        var attsPick = entPickFrom?.GetAttributeDefinitions()?.Cast<CdmObject>().ToList();
                        if (entPickFrom != null && attsPick != null)
                        {
                            for (int i = 0; i < attsPick.Count; i++)
                            {
                                if (attsPick[i].ObjectType == CdmObjectType.EntityAttributeDef)
                                {
                                    var entAtt = attsPick[i] as CdmEntityAttributeDefinition;
                                    addEntityReference(entAtt.Entity, this.InDocument.Namespace);
                                }
                            }
                        }
                    }
                    else
                    {
                        addEntityReference(this.Entity, this.InDocument.Namespace);
                    }

                    var constantEntity = this.Ctx.Corpus.MakeObject<CdmConstantEntityDefinition>(CdmObjectType.ConstantEntityDef);
                    constantEntity.EntityShape = this.Ctx.Corpus.MakeRef<CdmEntityReference>(CdmObjectType.EntityRef, "entityGroupSet", true);
                    constantEntity.ConstantValues = entReferences.Select((entRef, idx) => new List<string> { entRef, attReferences[idx] }).ToList();
                    var traitParam = this.Ctx.Corpus.MakeRef<CdmEntityReference>(CdmObjectType.EntityRef, constantEntity, false);
                    reqdTrait.ParameterValues.SetParameterValue(resOpt, "entityReferences", traitParam);
                }
            }

            // a 'structured' directive wants to keep all entity attributes together in a group
            if (arc.ResOpt.Directives?.Has("structured") == true)
            {
                ResolvedAttribute raSub = new ResolvedAttribute(rtsThisAtt.ResOpt, rasb.ResolvedAttributeSet, this.Name, (CdmAttributeContext)rasb.ResolvedAttributeSet.AttributeContext);
                if (relInfo.IsArray)
                {
                    // put a resolved trait on this att group, yuck, hope I never need to do this again and then need to make a function for this
                    CdmTraitReference tr = this.Ctx.Corpus.MakeObject<CdmTraitReference>(CdmObjectType.TraitRef, "is.linkedEntity.array", true);
                    var t = tr.FetchObjectDefinition<CdmTraitDefinition>(resOpt);
                    ResolvedTrait rt = new ResolvedTrait(t, null, new List<dynamic>(), new List<bool>());
                    raSub.ResolvedTraits = raSub.ResolvedTraits.Merge(rt, true);
                }
                rasb = new ResolvedAttributeSetBuilder();
                rasb.OwnOne(raSub);
            }

            return rasb;
        }
        /// <summary>
        /// Takes all the stored attribute context parameters, creates attribute contexts from them, and then constructs the tree.
        /// </summary>
        /// <param name="projCtx">The projection context</param>
        internal void ConstructAttributeContextTree(ProjectionContext projCtx)
        {
            // Iterate over all the searchFor attribute context parameters
            foreach (AttributeContextParameters searchForAttrCtxParam in this.searchForToSearchForAttrCtxParam.Values)
            {
                CdmAttributeContext searchForAttrCtx = null;

                // Fetch all the found attribute context parameters associated with this searchFor
                List <AttributeContextParameters> foundAttrCtxParams = null;
                searchForAttrCtxParamToFoundAttrCtxParam.TryGetValue(searchForAttrCtxParam, out foundAttrCtxParams);

                // Iterate over all the found attribute context parameters
                foreach (AttributeContextParameters foundAttrCtxParam in foundAttrCtxParams)
                {
                    // We should only create the searchFor node when searchFor and found have different names. Else collapse the nodes together.
                    if (!StringUtils.EqualsWithCase(searchForAttrCtxParam.Name, foundAttrCtxParam.Name))
                    {
                        // Create the attribute context for searchFor if it hasn't been created already and set it as the parent of found
                        if (searchForAttrCtx == null)
                        {
                            searchForAttrCtx = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, searchForAttrCtxParam);
                        }
                        foundAttrCtxParam.under = searchForAttrCtx;
                    }

                    // Fetch the action attribute context parameter associated with this found
                    AttributeContextParameters actionAttrCtxParam = null;
                    foundAttrCtxParamToActionAttrCtxParam.TryGetValue(foundAttrCtxParam, out actionAttrCtxParam);

                    // We should only create the found node when found and action have different names. Else collapse the nodes together.
                    if (!StringUtils.EqualsWithCase(foundAttrCtxParam.Name, actionAttrCtxParam.Name))
                    {
                        // Create the attribute context for found and set it as the parent of action
                        CdmAttributeContext foundAttrCtx = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, foundAttrCtxParam);
                        actionAttrCtxParam.under = foundAttrCtx;
                    }

                    // Create the attribute context for action
                    CdmAttributeContext actionAttrCtx = CdmAttributeContext.CreateChildUnder(projCtx.ProjectionDirective.ResOpt, actionAttrCtxParam);

                    // Fetch the resolved attribute that should now point at this action attribute context
                    ResolvedAttribute resAttrFromAction = null;
                    actionAttrCtxParamToResAttr.TryGetValue(actionAttrCtxParam, out resAttrFromAction);

                    // make sure the lineage of the attribute stays linked up
                    // there can be either (or both) a lineageOut and a lineageIn.
                    // out lineage is where this attribute came from
                    // in lineage should be pointing back at this context as a source
                    CdmAttributeContext lineageOut = null;
                    if (actionAttrCtxParamToLineageOut.TryGetValue(actionAttrCtxParam, out lineageOut) == true)
                    {
                        if (actionAttrCtx != null && lineageOut != null)
                        {
                            actionAttrCtx.AddLineage(lineageOut);
                        }
                        resAttrFromAction.AttCtx = actionAttrCtx; // probably the right context for this resAtt, unless ...
                    }
                    CdmAttributeContext lineageIn = null;
                    if (actionAttrCtxParamToLineageIn.TryGetValue(actionAttrCtxParam, out lineageIn) == true)
                    {
                        if (actionAttrCtx != null && lineageIn != null)
                        {
                            lineageIn.AddLineage(actionAttrCtx);
                        }
                        resAttrFromAction.AttCtx = lineageIn; // if there is a lineageIn. it points to us as lineage, so it is best
                    }
                }
            }
        }
        /// <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;
            }
        }