MegaBites-AI/Windows-powershell
0308
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 