Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
MergedCommandParameterMetadata.cs718 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Globalization;7using System.Management.Automation.Language;8using System.Text;9 10namespace System.Management.Automation11{12    internal class MergedCommandParameterMetadata13    {14        /// <summary>15        /// Replaces any existing metadata in this object with the metadata specified.16        ///17        /// Note that this method should NOT be called after a MergedCommandParameterMetadata18        /// instance is made read only by calling MakeReadOnly(). This is because MakeReadOnly()19        /// will turn 'bindableParameters', 'aliasedParameters' and 'parameterSetMap' into20        /// ReadOnlyDictionary and ReadOnlyCollection.21        /// </summary>22        /// <param name="metadata">23        /// The metadata to replace in this object.24        /// </param>25        /// <returns>26        /// A list of the merged parameter metadata that was added.27        /// </returns>28        internal List<MergedCompiledCommandParameter> ReplaceMetadata(MergedCommandParameterMetadata metadata)29        {30            var result = new List<MergedCompiledCommandParameter>();31 32            // Replace bindable parameters33            _bindableParameters.Clear();34            foreach (KeyValuePair<string, MergedCompiledCommandParameter> entry in metadata.BindableParameters)35            {36                _bindableParameters.Add(entry.Key, entry.Value);37                result.Add(entry.Value);38            }39 40            _aliasedParameters.Clear();41            foreach (KeyValuePair<string, MergedCompiledCommandParameter> entry in metadata.AliasedParameters)42            {43                _aliasedParameters.Add(entry.Key, entry.Value);44            }45 46            // Replace additional meta info47            _defaultParameterSetName = metadata._defaultParameterSetName;48            _nextAvailableParameterSetIndex = metadata._nextAvailableParameterSetIndex;49 50            _parameterSetMap.Clear();51            var parameterSetMapInList = (List<string>)_parameterSetMap;52            parameterSetMapInList.AddRange(metadata._parameterSetMap);53 54            Diagnostics.Assert(ParameterSetCount == _nextAvailableParameterSetIndex,55                "After replacement with the metadata of the new parameters, ParameterSetCount should be equal to nextAvailableParameterSetIndex");56 57            return result;58        }59 60        /// <summary>61        /// Merges the specified metadata with the other metadata already defined62        /// in this object.63        /// </summary>64        /// <param name="parameterMetadata">65        /// The compiled metadata for the type to be merged.66        /// </param>67        /// <param name="binderAssociation">68        /// The type of binder that the CommandProcessor will use to bind69        /// the parameters for <paramref name="parameterMetadata"/>70        /// </param>71        /// <returns>72        /// A collection of the merged parameter metadata that was added.73        /// </returns>74        /// <exception cref="MetadataException">75        /// If a parameter name or alias described in the <paramref name="parameterMetadata"/> already76        /// exists.77        /// </exception>78        internal Collection<MergedCompiledCommandParameter> AddMetadataForBinder(79            InternalParameterMetadata parameterMetadata,80            ParameterBinderAssociation binderAssociation)81        {82            if (parameterMetadata == null)83            {84                throw PSTraceSource.NewArgumentNullException(nameof(parameterMetadata));85            }86 87            Collection<MergedCompiledCommandParameter> result =88                new Collection<MergedCompiledCommandParameter>();89 90            // Merge in the bindable parameters91 92            foreach (KeyValuePair<string, CompiledCommandParameter> bindableParameter in parameterMetadata.BindableParameters)93            {94                if (_bindableParameters.ContainsKey(bindableParameter.Key))95                {96                    MetadataException exception =97                        new MetadataException(98                            "ParameterNameAlreadyExistsForCommand",99                            null,100                            Metadata.ParameterNameAlreadyExistsForCommand,101                            bindableParameter.Key);102                    throw exception;103                }104 105                // NTRAID#Windows Out Of Band Releases-926371-2005/12/27-JonN106                if (_aliasedParameters.ContainsKey(bindableParameter.Key))107                {108                    MetadataException exception =109                        new MetadataException(110                            "ParameterNameConflictsWithAlias",111                            null,112                            Metadata.ParameterNameConflictsWithAlias,113                            bindableParameter.Key,114                            RetrieveParameterNameForAlias(bindableParameter.Key, _aliasedParameters));115                    throw exception;116                }117 118                MergedCompiledCommandParameter mergedParameter =119                    new MergedCompiledCommandParameter(bindableParameter.Value, binderAssociation);120 121                _bindableParameters.Add(bindableParameter.Key, mergedParameter);122                result.Add(mergedParameter);123 124                // Merge in the aliases125 126                foreach (string aliasName in bindableParameter.Value.Aliases)127                {128                    if (_aliasedParameters.ContainsKey(aliasName))129                    {130                        MetadataException exception =131                            new MetadataException(132                                "AliasParameterNameAlreadyExistsForCommand",133                                null,134                                Metadata.AliasParameterNameAlreadyExistsForCommand,135                                aliasName);136                        throw exception;137                    }138 139                    // NTRAID#Windows Out Of Band Releases-926371-2005/12/27-JonN140                    if (_bindableParameters.ContainsKey(aliasName))141                    {142                        MetadataException exception =143                            new MetadataException(144                                "ParameterNameConflictsWithAlias",145                                null,146                                Metadata.ParameterNameConflictsWithAlias,147                                RetrieveParameterNameForAlias(aliasName, _bindableParameters),148                                bindableParameter.Value.Name);149                        throw exception;150                    }151 152                    _aliasedParameters.Add(aliasName, mergedParameter);153                }154            }155 156            return result;157        }158 159        /// <summary>160        /// The next available parameter set bit. This number increments but the parameter161        /// set bit is really 1 shifted left this number of times. This number also acts162        /// as the index for the parameter set map.163        /// </summary>164        private uint _nextAvailableParameterSetIndex;165 166        /// <summary>167        /// The maximum number of parameter sets allowed. Limit is set by the use168        /// of a uint bitmask to store which parameter sets a parameter is included in.169        /// See <see cref="ParameterSetSpecificMetadata.ParameterSetFlag"/>.170        /// </summary>171        private const uint MaxParameterSetCount = 32;172 173        /// <summary>174        /// Gets the number of parameter sets that were declared for the command.175        /// </summary>176        internal int ParameterSetCount177        {178            get179            {180                return _parameterSetMap.Count;181            }182        }183 184        /// <summary>185        /// Gets a bit-field representing all valid parameter sets.186        /// </summary>187        internal uint AllParameterSetFlags188        {189            get190            {191                return (1u << ParameterSetCount) - 1;192            }193        }194 195        /// <summary>196        /// This is the parameter set map. The index is the number of times 1 gets shifted197        /// left to specify the bit field marker for the parameter set.198        /// The value is the parameter set name.199        /// New parameter sets are added at the nextAvailableParameterSetIndex.200        /// </summary>201        private IList<string> _parameterSetMap = new List<string>();202 203        /// <summary>204        /// The name of the default parameter set.205        /// </summary>206        private string _defaultParameterSetName;207 208        /// <summary>209        /// Adds the parameter set name to the parameter set map and returns the210        /// index. If the parameter set name was already in the map, the index to211        /// the existing parameter set name is returned.212        /// </summary>213        /// <param name="parameterSetName">214        /// The name of the parameter set to add.215        /// </param>216        /// <returns>217        /// The index of the parameter set name. If the name didn't already exist the218        /// name gets added and the new index is returned. If the name already exists219        /// the index of the existing name is returned.220        /// </returns>221        /// <remarks>222        /// The nextAvailableParameterSetIndex is incremented if the parameter set name223        /// is added.224        /// </remarks>225        /// <exception cref="ParsingMetadataException">226        /// If more than uint.MaxValue parameter-sets are defined for the command.227        /// </exception>228        private int AddParameterSetToMap(string parameterSetName)229        {230            int index = -1;231            if (!string.IsNullOrEmpty(parameterSetName))232            {233                index = _parameterSetMap.IndexOf(parameterSetName);234 235                // A parameter set name should only be added once236                if (index == -1)237                {238                    if (_nextAvailableParameterSetIndex >= MaxParameterSetCount)239                    {240                        // Don't let the parameter set index overflow241                        ParsingMetadataException parsingException =242                            new ParsingMetadataException(243                                "ParsingTooManyParameterSets",244                                null,245                                Metadata.ParsingTooManyParameterSets);246 247                        throw parsingException;248                    }249 250                    _parameterSetMap.Add(parameterSetName);251                    index = _parameterSetMap.IndexOf(parameterSetName);252 253                    Diagnostics.Assert(254                        index == _nextAvailableParameterSetIndex,255                        "AddParameterSetToMap should always add the parameter set name to the map at the nextAvailableParameterSetIndex");256 257                    _nextAvailableParameterSetIndex++;258                }259            }260 261            return index;262        }263 264        /// <summary>265        /// Loops through all the parameters and retrieves the parameter set names.  In the process266        /// it generates a mapping of parameter set names to the bits in the bit-field and sets267        /// the parameter set flags for the parameter.268        /// </summary>269        /// <param name="defaultParameterSetName">270        /// The default parameter set name.271        /// </param>272        /// <returns>273        /// The bit flag for the default parameter set.274        /// </returns>275        /// <exception cref="ParsingMetadataException">276        /// If more than uint.MaxValue parameter-sets are defined for the command.277        /// </exception>278        internal uint GenerateParameterSetMappingFromMetadata(string defaultParameterSetName)279        {280            // First clear the parameter set map281            _parameterSetMap.Clear();282            _nextAvailableParameterSetIndex = 0;283 284            uint defaultParameterSetFlag = 0;285 286            if (!string.IsNullOrEmpty(defaultParameterSetName))287            {288                _defaultParameterSetName = defaultParameterSetName;289 290                // Add the default parameter set to the parameter set map291 292                int index = AddParameterSetToMap(defaultParameterSetName);293                defaultParameterSetFlag = (uint)1 << index;294            }295 296            // Loop through all the parameters and then each parameter set for each parameter297            foreach (MergedCompiledCommandParameter parameter in BindableParameters.Values)298            {299                // For each parameter we need to generate a bit-field for the parameter sets300                // that the parameter is a part of.301 302                uint parameterSetBitField = 0;303 304                foreach (var keyValuePair in parameter.Parameter.ParameterSetData)305                {306                    var parameterSetName = keyValuePair.Key;307                    var parameterSetData = keyValuePair.Value;308                    if (string.Equals(parameterSetName, ParameterAttribute.AllParameterSets, StringComparison.OrdinalIgnoreCase))309                    {310                        // Don't add the parameter set name but assign the bit field zero and then mark the bool311                        parameterSetData.ParameterSetFlag = 0;312                        parameterSetData.IsInAllSets = true;313                        parameter.Parameter.IsInAllSets = true;314                    }315                    else316                    {317                        // Add the parameter set name and/or get the index in the map318 319                        int index = AddParameterSetToMap(parameterSetName);320 321                        Diagnostics.Assert(322                            index >= 0,323                            "AddParameterSetToMap should always be able to add the parameter set name, if not it should throw");324 325                        // Calculate the bit for this parameter set326                        uint parameterSetBit = (uint)1 << index;327 328                        // Add the bit to the bit-field329                        parameterSetBitField |= parameterSetBit;330 331                        // Add the bit to the parameter set specific data332                        parameterSetData.ParameterSetFlag = parameterSetBit;333                    }334                }335 336                // Set the bit field in the parameter337                parameter.Parameter.ParameterSetFlags = parameterSetBitField;338            }339 340            return defaultParameterSetFlag;341        }342 343        /// <summary>344        /// Gets the parameter set name for the specified parameter set.345        /// </summary>346        /// <param name="parameterSet">347        /// The parameter set to get the name for.348        /// </param>349        /// <returns>350        /// The name of the specified parameter set.351        /// </returns>352        internal string GetParameterSetName(uint parameterSet)353        {354            string result = _defaultParameterSetName;355 356            if (string.IsNullOrEmpty(result))357            {358                result = ParameterAttribute.AllParameterSets;359            }360 361            if (parameterSet != uint.MaxValue && parameterSet != 0)362            {363                // Count the number of right shifts it takes to hit the parameter set364                // This is the index into the parameter set map.365                int index = 0;366 367                while (((parameterSet >> index) & 0x1) == 0)368                {369                    ++index;370                }371 372                // Now check to see if there are any remaining sets passed this bit.373                // If so return string.Empty374 375                if (((parameterSet >> (index + 1)) & 0x1) == 0)376                {377                    // Ensure that the bit found was within the map, if not return an empty string378                    if (index < _parameterSetMap.Count)379                    {380                        result = _parameterSetMap[index];381                    }382                    else383                    {384                        result = string.Empty;385                    }386                }387                else388                {389                    result = string.Empty;390                }391            }392 393            return result;394        }395 396        /// <summary>397        /// Helper function to retrieve the name of the parameter398        /// which defined an alias.399        /// </summary>400        /// <param name="key"></param>401        /// <param name="dict"></param>402        /// <returns></returns>403        private static string RetrieveParameterNameForAlias(404            string key,405            IDictionary<string, MergedCompiledCommandParameter> dict)406        {407            MergedCompiledCommandParameter mergedParam = dict[key];408            if (mergedParam != null)409            {410                CompiledCommandParameter compiledParam = mergedParam.Parameter;411                if (compiledParam != null)412                {413                    if (!string.IsNullOrEmpty(compiledParam.Name))414                        return compiledParam.Name;415                }416            }417 418            return string.Empty;419        }420 421        /// <summary>422        /// Gets the parameters by matching its name.423        /// </summary>424        /// <param name="name">425        /// The name of the parameter.426        /// </param>427        /// <param name="throwOnParameterNotFound">428        /// If true and a matching parameter is not found, an exception will be429        /// throw. If false and a matching parameter is not found, null is returned.430        /// </param>431        /// <param name="tryExactMatching">432        /// If true we do exact matching, otherwise we do not.433        /// </param>434        /// <param name="invocationInfo">435        /// The invocation information about the code being run.436        /// </param>437        /// <returns>438        /// The a collection of the metadata associated with the parameters that439        /// match the specified name. If no matches were found, an empty collection440        /// is returned.441        /// </returns>442        /// <exception cref="ArgumentException">443        /// If <paramref name="name"/> is null or empty.444        /// </exception>445        internal MergedCompiledCommandParameter GetMatchingParameter(446            string name,447            bool throwOnParameterNotFound,448            bool tryExactMatching,449            InvocationInfo invocationInfo)450        {451            if (string.IsNullOrEmpty(name))452            {453                throw PSTraceSource.NewArgumentException(nameof(name));454            }455 456            Collection<MergedCompiledCommandParameter> matchingParameters =457                new Collection<MergedCompiledCommandParameter>();458 459            // Skip the leading '-' if present460            if (name.Length > 0 && CharExtensions.IsDash(name[0]))461            {462                name = name.Substring(1);463            }464 465            // First try to match the bindable parameters466 467            foreach (string parameterName in _bindableParameters.Keys)468            {469                if (CultureInfo.InvariantCulture.CompareInfo.IsPrefix(parameterName, name, CompareOptions.IgnoreCase))470                {471                    // If it is an exact match then only return the exact match472                    // as the result473 474                    if (tryExactMatching && string.Equals(parameterName, name, StringComparison.OrdinalIgnoreCase))475                    {476                        return _bindableParameters[parameterName];477                    }478                    else479                    {480                        matchingParameters.Add(_bindableParameters[parameterName]);481                    }482                }483            }484 485            // Now check the aliases486 487            foreach (string parameterName in _aliasedParameters.Keys)488            {489                if (CultureInfo.InvariantCulture.CompareInfo.IsPrefix(parameterName, name, CompareOptions.IgnoreCase))490                {491                    // If it is an exact match then only return the exact match492                    // as the result493 494                    if (tryExactMatching && string.Equals(parameterName, name, StringComparison.OrdinalIgnoreCase))495                    {496                        return _aliasedParameters[parameterName];497                    }498                    else499                    {500                        if (!matchingParameters.Contains(_aliasedParameters[parameterName]))501                        {502                            matchingParameters.Add(_aliasedParameters[parameterName]);503                        }504                    }505                }506            }507 508            if (matchingParameters.Count > 1)509            {510                // Prefer parameters in the cmdlet over common parameters511                Collection<MergedCompiledCommandParameter> filteredParameters =512                    new Collection<MergedCompiledCommandParameter>();513 514                foreach (MergedCompiledCommandParameter matchingParameter in matchingParameters)515                {516                    if ((matchingParameter.BinderAssociation == ParameterBinderAssociation.DeclaredFormalParameters) ||517                        (matchingParameter.BinderAssociation == ParameterBinderAssociation.DynamicParameters))518                    {519                        filteredParameters.Add(matchingParameter);520                    }521                }522 523                if (tryExactMatching && filteredParameters.Count == 1)524                {525                    matchingParameters = filteredParameters;526                }527                else528                {529                    StringBuilder possibleMatches = new StringBuilder();530 531                    foreach (MergedCompiledCommandParameter matchingParameter in matchingParameters)532                    {533                        possibleMatches.Append(" -");534                        possibleMatches.Append(matchingParameter.Parameter.Name);535                    }536 537                    ParameterBindingException exception =538                        new ParameterBindingException(539                            ErrorCategory.InvalidArgument,540                            invocationInfo,541                            null,542                            name,543                            null,544                            null,545                            ParameterBinderStrings.AmbiguousParameter,546                            "AmbiguousParameter",547                            possibleMatches);548 549                    throw exception;550                }551            }552            else if (matchingParameters.Count == 0)553            {554                if (throwOnParameterNotFound)555                {556                    ParameterBindingException exception =557                        new ParameterBindingException(558                            ErrorCategory.InvalidArgument,559                            invocationInfo,560                            null,561                            name,562                            null,563                            null,564                            ParameterBinderStrings.NamedParameterNotFound,565                            "NamedParameterNotFound");566 567                    throw exception;568                }569            }570 571            MergedCompiledCommandParameter result = null;572            if (matchingParameters.Count > 0)573            {574                result = matchingParameters[0];575            }576 577            return result;578        }579 580        /// <summary>581        /// Gets a collection of all the parameters that are allowed in the parameter set.582        /// </summary>583        /// <param name="parameterSetFlag">584        /// The bit representing the parameter set from which the parameters should be retrieved.585        /// </param>586        /// <returns>587        /// A collection of all the parameters in the specified parameter set.588        /// </returns>589        internal Collection<MergedCompiledCommandParameter> GetParametersInParameterSet(uint parameterSetFlag)590        {591            Collection<MergedCompiledCommandParameter> result =592                new Collection<MergedCompiledCommandParameter>();593 594            foreach (MergedCompiledCommandParameter parameter in BindableParameters.Values)595            {596                if ((parameterSetFlag & parameter.Parameter.ParameterSetFlags) != 0 ||597                    parameter.Parameter.IsInAllSets)598                {599                    result.Add(parameter);600                }601            }602 603            return result;604        }605 606        /// <summary>607        /// Gets a dictionary of the compiled parameter metadata for this Type.608        /// The dictionary keys are the names of the parameters and609        /// the values are the compiled parameter metadata.610        /// </summary>611        internal IDictionary<string, MergedCompiledCommandParameter> BindableParameters { get { return _bindableParameters; } }612 613        private IDictionary<string, MergedCompiledCommandParameter> _bindableParameters =614            new Dictionary<string, MergedCompiledCommandParameter>(StringComparer.OrdinalIgnoreCase);615 616        /// <summary>617        /// Gets a dictionary of the parameters that have been aliased to other names. The key is618        /// the alias name and the value is the MergedCompiledCommandParameter metadata.619        /// </summary>620        internal IDictionary<string, MergedCompiledCommandParameter> AliasedParameters { get { return _aliasedParameters; } }621 622        private IDictionary<string, MergedCompiledCommandParameter> _aliasedParameters =623            new Dictionary<string, MergedCompiledCommandParameter>(StringComparer.OrdinalIgnoreCase);624 625        internal void MakeReadOnly()626        {627            _bindableParameters = new ReadOnlyDictionary<string, MergedCompiledCommandParameter>(_bindableParameters);628            _aliasedParameters = new ReadOnlyDictionary<string, MergedCompiledCommandParameter>(_aliasedParameters);629            _parameterSetMap = new ReadOnlyCollection<string>(_parameterSetMap);630        }631 632        internal void ResetReadOnly()633        {634            _bindableParameters = new Dictionary<string, MergedCompiledCommandParameter>(_bindableParameters, StringComparer.OrdinalIgnoreCase);635            _aliasedParameters = new Dictionary<string, MergedCompiledCommandParameter>(_aliasedParameters, StringComparer.OrdinalIgnoreCase);636        }637    }638 639    /// <summary>640    /// Makes an association between a CompiledCommandParameter and the type641    /// of the parameter binder used to bind the parameter.642    /// </summary>643    internal class MergedCompiledCommandParameter644    {645        /// <summary>646        /// Constructs an association between the CompiledCommandParameter and the647        /// binder that should be used to bind it.648        /// </summary>649        /// <param name="parameter">650        /// The metadata for a parameter.651        /// </param>652        /// <param name="binderAssociation">653        /// The type of binder that should be used to bind the parameter.654        /// </param>655        internal MergedCompiledCommandParameter(656                CompiledCommandParameter parameter,657                ParameterBinderAssociation binderAssociation)658        {659            Diagnostics.Assert(parameter != null, "caller to verify parameter is not null");660            this.Parameter = parameter;661            this.BinderAssociation = binderAssociation;662        }663 664        /// <summary>665        /// Gets the compiled command parameter for the association.666        /// </summary>667        internal CompiledCommandParameter Parameter { get; }668 669        /// <summary>670        /// Gets the type of binder that the compiled command parameter should be bound with.671        /// </summary>672        internal ParameterBinderAssociation BinderAssociation { get; }673 674        public override string ToString()675        {676            return Parameter.ToString();677        }678    }679 680    /// <summary>681    /// This enum is used in the MergedCompiledCommandParameter class682    /// to associate a particular CompiledCommandParameter with the683    /// appropriate ParameterBinder.684    /// </summary>685    internal enum ParameterBinderAssociation686    {687        /// <summary>688        /// The parameter was declared as a formal parameter in the command type.689        /// </summary>690        DeclaredFormalParameters,691 692        /// <summary>693        /// The parameter was declared as a dynamic parameter for the command.694        /// </summary>695        DynamicParameters,696 697        /// <summary>698        /// The parameter is a common parameter found in the CommonParameters class.699        /// </summary>700        CommonParameters,701 702        /// <summary>703        /// The parameter is a ShouldProcess parameter found in the ShouldProcessParameters class.704        /// </summary>705        ShouldProcessParameters,706 707        /// <summary>708        /// The parameter is a transactions parameter found in the TransactionParameters class.709        /// </summary>710        TransactionParameters,711 712        /// <summary>713        /// The parameter is a Paging parameter found in the PagingParameters class.714        /// </summary>715        PagingParameters,716    }717}718