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.Linq;8using System.Text.RegularExpressions;9 10using Microsoft.PowerShell;11 12using Dbg = System.Management.Automation.Diagnostics;13 14namespace System.Management.Automation15{16 /// <summary>17 /// The information about a parameter set and its parameters for a cmdlet.18 /// </summary>19 public class CommandParameterSetInfo20 {21 #region ctor22 23 /// <summary>24 /// Constructs the parameter set information using the specified parameter name,25 /// and type metadata.26 /// </summary>27 /// <param name="name">28 /// The formal name of the parameter.29 /// </param>30 /// <param name="isDefaultParameterSet">31 /// True if the parameter set is the default parameter set, or false otherwise.32 /// </param>33 /// <param name="parameterSetFlag">34 /// The bit that specifies the parameter set in the type metadata.35 /// </param>36 /// <param name="parameterMetadata">37 /// The type metadata about the cmdlet.38 /// </param>39 /// <exception cref="ArgumentException">40 /// If <paramref name="name"/> is null or empty.41 /// </exception>42 /// <exception cref="ArgumentNullException">43 /// If <paramref name="parameterMetadata"/> is null.44 /// </exception>45 internal CommandParameterSetInfo(46 string name,47 bool isDefaultParameterSet,48 uint parameterSetFlag,49 MergedCommandParameterMetadata parameterMetadata)50 {51 IsDefault = true;52 Name = string.Empty;53 if (string.IsNullOrEmpty(name))54 {55 throw PSTraceSource.NewArgumentException(nameof(name));56 }57 58 if (parameterMetadata == null)59 {60 throw PSTraceSource.NewArgumentNullException(nameof(parameterMetadata));61 }62 63 this.Name = name;64 this.IsDefault = isDefaultParameterSet;65 66 Initialize(parameterMetadata, parameterSetFlag);67 }68 #endregion ctor69 70 #region public members71 72 /// <summary>73 /// Gets the name of the parameter set.74 /// </summary>75 public string Name { get; }76 77 /// <summary>78 /// Gets whether the parameter set is the default parameter set.79 /// </summary>80 public bool IsDefault { get; }81 82 /// <summary>83 /// Gets the parameter information for the parameters in this parameter set.84 /// </summary>85 public ReadOnlyCollection<CommandParameterInfo> Parameters { get; private set; }86 87 /// <summary>88 /// Gets the synopsis for the cmdlet as a string.89 /// </summary>90 public override string ToString()91 {92 Text.StringBuilder result = new Text.StringBuilder();93 94 GenerateParametersInDisplayOrder(95 parameter => AppendFormatCommandParameterInfo(parameter, result),96 (string str) =>97 {98 if (result.Length > 0)99 {100 result.Append(' ');101 }102 103 result.Append('[');104 result.Append(str);105 result.Append(']');106 });107 108 return result.ToString();109 }110 111 /// <summary>112 /// GenerateParameters parameters in display order113 /// ie., Positional followed by114 /// Named Mandatory (in alpha numeric) followed by115 /// Named (in alpha numeric).116 ///117 /// Callers use <paramref name="parameterAction"/> and118 /// <paramref name="commonParameterAction"/> to handle119 /// syntax generation etc.120 /// </summary>121 /// <param name="parameterAction"></param>122 /// <param name="commonParameterAction"></param>123 /// <returns></returns>124 internal void GenerateParametersInDisplayOrder(125 Action<CommandParameterInfo> parameterAction,126 Action<string> commonParameterAction)127 {128 // First figure out the positions129 130 List<CommandParameterInfo> sortedPositionalParameters = new List<CommandParameterInfo>();131 List<CommandParameterInfo> namedMandatoryParameters = new List<CommandParameterInfo>();132 List<CommandParameterInfo> namedParameters = new List<CommandParameterInfo>();133 134 foreach (CommandParameterInfo parameter in Parameters)135 {136 if (parameter.Position == int.MinValue)137 {138 // The parameter is a named parameter139 if (parameter.IsMandatory)140 {141 namedMandatoryParameters.Add(parameter);142 }143 else144 {145 namedParameters.Add(parameter);146 }147 }148 else149 {150 // The parameter is positional so add it at the correct151 // index (note we have to pad the list if the position is152 // higher than the list count since we don't have any requirements153 // that positional parameters start at zero and are consecutive.154 155 if (parameter.Position >= sortedPositionalParameters.Count)156 {157 for (int fillerIndex = sortedPositionalParameters.Count;158 fillerIndex <= parameter.Position;159 ++fillerIndex)160 {161 sortedPositionalParameters.Add(null);162 }163 }164 165 sortedPositionalParameters[parameter.Position] = parameter;166 }167 }168 169 foreach (CommandParameterInfo parameter in sortedPositionalParameters)170 {171 if (parameter == null)172 {173 continue;174 }175 176 parameterAction(parameter);177 }178 179 // Now convert the named mandatory parameters into a string180 foreach (CommandParameterInfo parameter in namedMandatoryParameters)181 {182 if (parameter == null)183 {184 continue;185 }186 187 parameterAction(parameter);188 }189 190 List<CommandParameterInfo> commonParameters = new List<CommandParameterInfo>();191 192 // Now convert the named parameters into a string193 foreach (CommandParameterInfo parameter in namedParameters)194 {195 if (parameter == null)196 {197 continue;198 }199 200 // Hold off common parameters201 bool isCommon = Cmdlet.CommonParameters.Contains(parameter.Name, StringComparer.OrdinalIgnoreCase);202 if (!isCommon)203 {204 parameterAction(parameter);205 }206 else207 {208 commonParameters.Add(parameter);209 }210 }211 212 // If all common parameters are present, group them together213 if (commonParameters.Count == Cmdlet.CommonParameters.Count)214 {215 commonParameterAction(HelpDisplayStrings.CommonParameters);216 }217 // Else, convert to string as before218 else219 {220 foreach (CommandParameterInfo parameter in commonParameters)221 {222 parameterAction(parameter);223 }224 }225 }226 227 #endregion public members228 229 #region private members230 231 private static void AppendFormatCommandParameterInfo(CommandParameterInfo parameter, Text.StringBuilder result)232 {233 if (result.Length > 0)234 {235 // Add a space between parameters236 result.Append(' ');237 }238 239 if (parameter.ParameterType == typeof(SwitchParameter))240 {241 result.AppendFormat(CultureInfo.InvariantCulture, parameter.IsMandatory ? "-{0}" : "[-{0}]", parameter.Name);242 }243 else244 {245 string parameterTypeString = GetParameterTypeString(parameter.ParameterType, parameter.Attributes);246 247 if (parameter.IsMandatory)248 {249 result.AppendFormat(250 CultureInfo.InvariantCulture,251 parameter.Position != int.MinValue ? "[-{0}] <{1}>" : "-{0} <{1}>",252 parameter.Name,253 parameterTypeString);254 }255 else256 {257 result.AppendFormat(258 CultureInfo.InvariantCulture,259 parameter.Position != int.MinValue ? "[[-{0}] <{1}>]" : "[-{0} <{1}>]",260 parameter.Name,261 parameterTypeString);262 }263 }264 }265 266 internal static string GetParameterTypeString(Type type, IEnumerable<Attribute> attributes)267 {268 string parameterTypeString;269 PSTypeNameAttribute typeName;270 if (attributes != null && (typeName = attributes.OfType<PSTypeNameAttribute>().FirstOrDefault()) != null)271 {272 // If we have a PSTypeName specified on the class, we assume it has a more useful type than the actual273 // parameter type. This is a reasonable assumption, the parameter binder does honor this attribute.274 //275 // This typename might be long, e.g.:276 // Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process277 // System.Management.ManagementObject#root\cimv2\Win32_Process278 // To shorten this, we will drop the namespaces, both on the .Net side and the CIM/WMI side:279 // CimInstance#Win32_Process280 // If our regex doesn't match, we'll just use the full name.281 var match = Regex.Match(typeName.PSTypeName, "(.*\\.)?(?<NetTypeName>.*)#(.*[/\\\\])?(?<CimClassName>.*)");282 if (match.Success)283 {284 parameterTypeString = match.Groups["NetTypeName"].Value + "#" + match.Groups["CimClassName"].Value;285 }286 else287 {288 parameterTypeString = typeName.PSTypeName;289 290 // Drop the namespace from the typename, if any.291 var lastDotIndex = parameterTypeString.LastIndexOf('.');292 if (lastDotIndex != -1 && lastDotIndex + 1 < parameterTypeString.Length)293 {294 parameterTypeString = parameterTypeString.Substring(lastDotIndex + 1);295 }296 }297 298 // If the type is really an array, but the typename didn't include [], then add it.299 if (type.IsArray && !parameterTypeString.Contains("[]", StringComparison.Ordinal))300 {301 var t = type;302 while (t.IsArray)303 {304 parameterTypeString += "[]";305 t = t.GetElementType();306 }307 }308 }309 else310 {311 Type parameterType = Nullable.GetUnderlyingType(type) ?? type;312 parameterTypeString = ToStringCodeMethods.Type(parameterType, true);313 }314 315 return parameterTypeString;316 }317 318 private void Initialize(MergedCommandParameterMetadata parameterMetadata, uint parameterSetFlag)319 {320 Diagnostics.Assert(321 parameterMetadata != null,322 "The parameterMetadata should never be null");323 324 Collection<CommandParameterInfo> processedParameters =325 new Collection<CommandParameterInfo>();326 327 // Get the parameters in the parameter set328 Collection<MergedCompiledCommandParameter> compiledParameters =329 parameterMetadata.GetParametersInParameterSet(parameterSetFlag);330 331 foreach (MergedCompiledCommandParameter parameter in compiledParameters)332 {333 if (parameter != null)334 {335 processedParameters.Add(336 new CommandParameterInfo(parameter.Parameter, parameterSetFlag));337 }338 }339 340 Parameters = new ReadOnlyCollection<CommandParameterInfo>(processedParameters);341 }342 343 #endregion private members344 }345}346 