MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Management.Automation.Language;5 6using Microsoft.Management.Infrastructure;7 8namespace System.Management.Automation.Runspaces9{10 using System;11 using System.Collections.ObjectModel;12 using Debug = System.Management.Automation.Diagnostics;13 14 /// <summary>15 /// Define a parameter for <see cref="Command"/>16 /// </summary>17 public sealed class CommandParameter18 {19 #region Public constructors20 21 /// <summary>22 /// Create a named parameter with a null value.23 /// </summary>24 /// <param name="name">Parameter name.</param>25 /// <exception cref="ArgumentNullException">26 /// name is null.27 /// </exception>28 /// <exception cref="ArgumentException">29 /// Name length is zero after trimming whitespace.30 /// </exception>31 public CommandParameter(string name)32 : this(name, null)33 {34 if (name == null)35 {36 throw PSTraceSource.NewArgumentNullException(nameof(name));37 }38 }39 40 /// <summary>41 /// Create a named parameter.42 /// </summary>43 /// <param name="name">Parameter name.</param>44 /// <param name="value">Parameter value.</param>45 /// <exception cref="ArgumentException">46 /// Name is non null and name length is zero after trimming whitespace.47 /// </exception>48 public CommandParameter(string name, object value)49 {50 if (name != null)51 {52 if (string.IsNullOrWhiteSpace(name))53 {54 throw PSTraceSource.NewArgumentException(nameof(name));55 }56 57 Name = name;58 }59 else60 {61 Name = null;62 }63 64 Value = value;65 }66 67 #endregion Public constructors68 69 #region Public properties70 71 /// <summary>72 /// Gets the parameter name.73 /// </summary>74 public string Name { get; }75 76 /// <summary>77 /// Gets the value of the parameter.78 /// </summary>79 public object Value { get; }80 81 #endregion Public properties82 83 /// <summary>84 /// Gets whether the parameter was from splatting a Hashtable.85 /// </summary>86 private bool FromHashtableSplatting { get; set; }87 88 #region Conversion from and to CommandParameterInternal89 90 internal static CommandParameter FromCommandParameterInternal(CommandParameterInternal internalParameter)91 {92 if (internalParameter == null)93 {94 throw PSTraceSource.NewArgumentNullException(nameof(internalParameter));95 }96 97 // we want the name to preserve 1) dashes, 2) colons, 3) followed-by-space information98 string name = null;99 if (internalParameter.ParameterNameSpecified)100 {101 name = internalParameter.ParameterText;102 if (internalParameter.SpaceAfterParameter)103 {104 name += " ";105 }106 107 Diagnostics.Assert(name != null, "'name' variable should be initialized at this point");108 Diagnostics.Assert(name[0].IsDash(), "first character in parameter name must be a dash");109 Diagnostics.Assert(name.Trim().Length != 1, "Parameter name has to have some non-whitespace characters in it");110 }111 112 CommandParameter result = internalParameter.ParameterAndArgumentSpecified113 ? new CommandParameter(name, internalParameter.ArgumentValue)114 : name != null115 ? new CommandParameter(name)116 : new CommandParameter(name: null, internalParameter.ArgumentValue);117 118 result.FromHashtableSplatting = internalParameter.FromHashtableSplatting;119 return result;120 }121 122 internal static CommandParameterInternal ToCommandParameterInternal(CommandParameter publicParameter, bool forNativeCommand)123 {124 if (publicParameter == null)125 {126 throw PSTraceSource.NewArgumentNullException(nameof(publicParameter));127 }128 129 string name = publicParameter.Name;130 object value = publicParameter.Value;131 132 Debug.Assert((name == null) || (name.Trim().Length != 0), "Parameter name has to null or have some non-whitespace characters in it");133 134 if (name == null)135 {136 return CommandParameterInternal.CreateArgument(value);137 }138 139 string parameterText;140 if (!name[0].IsDash())141 {142 parameterText = forNativeCommand ? name : "-" + name;143 return CommandParameterInternal.CreateParameterWithArgument(144 parameterAst: null,145 parameterName: name,146 parameterText: parameterText,147 argumentAst: null,148 value: value,149 spaceAfterParameter: true);150 }151 152 // if first character of name is '-', then we try to fake the original token153 // reconstructing dashes, colons and followed-by-space information154 155 // find the last non-whitespace character156 bool spaceAfterParameter = false;157 int endPosition = name.Length;158 while ((endPosition > 0) && char.IsWhiteSpace(name[endPosition - 1]))159 {160 spaceAfterParameter = true;161 endPosition--;162 }163 164 Debug.Assert(endPosition > 0, "parameter name should have some non-whitespace characters in it");165 166 // now make sure that parameterText doesn't have whitespace at the end,167 parameterText = name.Substring(0, endPosition);168 169 // parameterName should contain only the actual name of the parameter (no whitespace, colons, dashes)170 bool hasColon = (name[endPosition - 1] == ':');171 var parameterName = parameterText.Substring(1, parameterText.Length - (hasColon ? 2 : 1));172 173 // At this point we have rebuilt the token. There are 3 strings that might be different:174 // name = nameToken.Script = "-foo: " <- needed to fake FollowedBySpace=true (i.e. for "testecho.exe -a:b -c: d")175 // tokenString = nameToken.TokenText = "-foo:" <- needed to preserve full token text (i.e. for write-output)176 // nameToken.Data = "foo" <- needed to preserve name of parameter so parameter binding works177 // Now we just need to use the token to build appropriate CommandParameterInternal object178 179 // is this a name+value pair, or is it just a name (of a parameter)?180 if (!hasColon && value == null)181 {182 // just a name183 return CommandParameterInternal.CreateParameter(parameterName, parameterText);184 }185 186 // name+value pair187 return CommandParameterInternal.CreateParameterWithArgument(188 parameterAst: null,189 parameterName,190 parameterText,191 argumentAst: null,192 value,193 spaceAfterParameter,194 publicParameter.FromHashtableSplatting);195 }196 197 #endregion198 199 #region Serialization / deserialization for remoting200 201 /// <summary>202 /// Creates a CommandParameter object from a PSObject property bag.203 /// PSObject has to be in the format returned by ToPSObjectForRemoting method.204 /// </summary>205 /// <param name="parameterAsPSObject">PSObject to rehydrate.</param>206 /// <returns>207 /// CommandParameter rehydrated from a PSObject property bag208 /// </returns>209 /// <exception cref="ArgumentNullException">210 /// Thrown if the PSObject is null.211 /// </exception>212 /// <exception cref="System.Management.Automation.Remoting.PSRemotingDataStructureException">213 /// Thrown when the PSObject is not in the expected format214 /// </exception>215 internal static CommandParameter FromPSObjectForRemoting(PSObject parameterAsPSObject)216 {217 if (parameterAsPSObject == null)218 {219 throw PSTraceSource.NewArgumentNullException(nameof(parameterAsPSObject));220 }221 222 string name = RemotingDecoder.GetPropertyValue<string>(parameterAsPSObject, RemoteDataNameStrings.ParameterName);223 object value = RemotingDecoder.GetPropertyValue<object>(parameterAsPSObject, RemoteDataNameStrings.ParameterValue);224 return new CommandParameter(name, value);225 }226 227 /// <summary>228 /// Returns this object as a PSObject property bag229 /// that can be used in a remoting protocol data object.230 /// </summary>231 /// <returns>This object as a PSObject property bag.</returns>232 internal PSObject ToPSObjectForRemoting()233 {234 PSObject parameterAsPSObject = RemotingEncoder.CreateEmptyPSObject();235 parameterAsPSObject.Properties.Add(new PSNoteProperty(RemoteDataNameStrings.ParameterName, this.Name));236 parameterAsPSObject.Properties.Add(new PSNoteProperty(RemoteDataNameStrings.ParameterValue, this.Value));237 return parameterAsPSObject;238 }239 240 #endregion241 }242 243 /// <summary>244 /// Defines a collection of parameters.245 /// </summary>246 public sealed class CommandParameterCollection : Collection<CommandParameter>247 {248 // TODO: this class needs a mechanism to lock further changes249 250 /// <summary>251 /// Create a new empty instance of this collection type.252 /// </summary>253 public CommandParameterCollection()254 {255 }256 257 /// <summary>258 /// Add a parameter with given name and default null value.259 /// </summary>260 /// <param name="name">Name of the parameter.</param>261 /// <exception cref="ArgumentNullException">262 /// name is null.263 /// </exception>264 /// <exception cref="ArgumentException">265 /// Name length is zero after trimming whitespace.266 /// </exception>267 public void Add(string name)268 {269 Add(new CommandParameter(name));270 }271 272 /// <summary>273 /// Add a parameter with given name and value.274 /// </summary>275 /// <param name="name">Name of the parameter.</param>276 /// <param name="value">Value of the parameter.</param>277 /// <exception cref="ArgumentNullException">278 /// Both name and value are null. One of these must be non-null.279 /// </exception>280 /// <exception cref="ArgumentException">281 /// Name is non null and name length is zero after trimming whitespace.282 /// </exception>283 public void Add(string name, object value)284 {285 Add(new CommandParameter(name, value));286 }287 }288}289 