Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
MshParameter.cs530 linesDownload Raw Back to Utilities
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Diagnostics.CodeAnalysis;8using System.Management.Automation;9using System.Management.Automation.Internal;10using System.Text;11 12namespace Microsoft.PowerShell.Commands.Internal.Format13{14    /// <summary>15    /// Normalized parameter class to be constructed from the command line parameters16    /// using the metadata information provided by an instance of CommandParameterDefinition17    /// it's basically the hash table with the normalized values.18    /// </summary>19    internal class MshParameter20    {21        internal Hashtable hash = null;22 23        internal object GetEntry(string key)24        {25            if (this.hash.ContainsKey(key))26                return this.hash[key];27 28            return AutomationNull.Value;29        }30    }31 32    internal class NameEntryDefinition : HashtableEntryDefinition33    {34        internal const string NameEntryKey = "name";35 36        internal NameEntryDefinition()37            : base(NameEntryKey, new string[] { FormatParameterDefinitionKeys.LabelEntryKey }, new Type[] { typeof(string) }, false)38        {39        }40    }41 42    /// <summary>43    /// Metadata base class for hashtable entry definitions44    /// it contains the key name and the allowable types45    /// it also provides hooks for type expansion.46    /// </summary>47    internal class HashtableEntryDefinition48    {49        internal HashtableEntryDefinition(string name, IEnumerable<string> secondaryNames, Type[] types, bool mandatory)50            : this(name, types, mandatory)51        {52            SecondaryNames = secondaryNames;53        }54 55        internal HashtableEntryDefinition(string name, Type[] types, bool mandatory)56        {57            KeyName = name;58            AllowedTypes = types;59            Mandatory = mandatory;60        }61 62        internal HashtableEntryDefinition(string name, Type[] types)63            : this(name, types, false)64        {65        }66 67        internal virtual Hashtable CreateHashtableFromSingleType(object val)68        {69            // NOTE: must override for the default type(s) entry70            // this entry will have to expand the object into a hash table71            throw PSTraceSource.NewNotSupportedException();72        }73 74        internal bool IsKeyMatch(string key)75        {76            if (CommandParameterDefinition.FindPartialMatch(key, this.KeyName))77            {78                return true;79            }80 81            if (this.SecondaryNames != null)82            {83                foreach (string secondaryKey in this.SecondaryNames)84                {85                    if (CommandParameterDefinition.FindPartialMatch(key, secondaryKey))86                    {87                        return true;88                    }89                }90            }91 92            return false;93        }94 95        internal virtual object Verify(object val,96                                        TerminatingErrorContext invocationContext,97                                        bool originalParameterWasHashTable)98        {99            return null;100        }101 102        internal virtual object ComputeDefaultValue()103        {104            return AutomationNull.Value;105        }106 107        internal string KeyName { get; }108 109        internal Type[] AllowedTypes { get; }110 111        internal bool Mandatory { get; }112 113        internal IEnumerable<string> SecondaryNames { get; }114    }115 116    /// <summary>117    /// Metadata abstract base class to contain hash entries definitions.118    /// </summary>119    internal abstract class CommandParameterDefinition120    {121        [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]122        internal CommandParameterDefinition()123        {124            SetEntries();125        }126 127        protected abstract void SetEntries();128 129        internal virtual MshParameter CreateInstance() { return new MshParameter(); }130 131        /// <summary>132        /// For a key name, verify it is a legal entry:133        ///     1. it must match (partial match allowed)134        ///     2. it must be unambiguous (if partial match)135        /// If an error condition occurs, an exception will be thrown.136        /// </summary>137        /// <param name="keyName">Key to verify.</param>138        /// <param name="invocationContext">Invocation context for error reporting.</param>139        /// <returns>Matching hash table entry.</returns>140        /// <exception cref="ArgumentException"></exception>141        internal HashtableEntryDefinition MatchEntry(string keyName, TerminatingErrorContext invocationContext)142        {143            if (string.IsNullOrEmpty(keyName))144                PSTraceSource.NewArgumentNullException(nameof(keyName));145 146            HashtableEntryDefinition matchingEntry = null;147            for (int k = 0; k < this.hashEntries.Count; k++)148            {149                if (this.hashEntries[k].IsKeyMatch(keyName))150                {151                    // we have a match152                    if (matchingEntry == null)153                    {154                        // this is the first match, we save the entry155                        // and we keep going for ambiguity check156                        matchingEntry = this.hashEntries[k];157                    }158                    else159                    {160                        // we already had a match, we have an ambiguous key161                        ProcessAmbiguousKey(invocationContext, keyName, matchingEntry, this.hashEntries[k]);162                    }163                }164            }165 166            if (matchingEntry != null)167            {168                // we found an unambiguous match169                return matchingEntry;170            }171            // we did not have a match172            ProcessIllegalKey(invocationContext, keyName);173            return null;174        }175 176        internal static bool FindPartialMatch(string key, string normalizedKey)177        {178            if (key.Length < normalizedKey.Length)179            {180                // shorter, could be an abbreviation181                if (key.AsSpan().Equals(normalizedKey.AsSpan(0, key.Length), StringComparison.OrdinalIgnoreCase))182                {183                    // found abbreviation184                    return true;185                }186            }187 188            if (string.Equals(key, normalizedKey, StringComparison.OrdinalIgnoreCase))189            {190                // found full match191                return true;192            }193 194            return false;195        }196 197        #region Error Processing198 199        private static void ProcessAmbiguousKey(TerminatingErrorContext invocationContext,200                                            string keyName,201                                            HashtableEntryDefinition matchingEntry,202                                            HashtableEntryDefinition currentEntry)203        {204            string msg = StringUtil.Format(FormatAndOut_MshParameter.AmbiguousKeyError,205                keyName, matchingEntry.KeyName, currentEntry.KeyName);206 207            ParameterProcessor.ThrowParameterBindingException(invocationContext, "DictionaryKeyAmbiguous", msg);208        }209 210        private static void ProcessIllegalKey(TerminatingErrorContext invocationContext,211                                            string keyName)212        {213            string msg = StringUtil.Format(FormatAndOut_MshParameter.IllegalKeyError, keyName);214 215            ParameterProcessor.ThrowParameterBindingException(invocationContext, "DictionaryKeyIllegal", msg);216        }217        #endregion218 219        internal List<HashtableEntryDefinition> hashEntries = new List<HashtableEntryDefinition>();220    }221 222    /// <summary>223    /// Engine to process a generic object[] from the command line and224    /// generate a list of MshParameter objects , given the metadata provided by225    /// a class derived from CommandParameterDefinition.226    /// </summary>227    internal sealed class ParameterProcessor228    {229        #region tracer230        [TraceSource("ParameterProcessor", "ParameterProcessor")]231        internal static readonly PSTraceSource tracer = PSTraceSource.GetTracer("ParameterProcessor", "ParameterProcessor");232        #endregion tracer233 234        internal static void ThrowParameterBindingException(TerminatingErrorContext invocationContext,235                                                            string errorId,236                                                            string msg)237        {238            ErrorRecord errorRecord = new ErrorRecord(239                                new NotSupportedException(),240                                errorId,241                                ErrorCategory.InvalidArgument,242                                null);243 244            errorRecord.ErrorDetails = new ErrorDetails(msg);245            invocationContext.ThrowTerminatingError(errorRecord);246        }247 248        internal ParameterProcessor(CommandParameterDefinition p)249        {250            _paramDef = p;251        }252 253        /// <exception cref="ArgumentException"></exception>254        internal List<MshParameter> ProcessParameters(object[] p, TerminatingErrorContext invocationContext)255        {256            if (p == null || p.Length == 0)257                return null;258 259            List<MshParameter> retVal = new List<MshParameter>();260            MshParameter currParam;261 262            bool originalParameterWasHashTable = false;263            for (int k = 0; k < p.Length; k++)264            {265                // we always copy into a fresh hash table266                currParam = _paramDef.CreateInstance();267                var actualObject = PSObject.Base(p[k]);268                if (actualObject is IDictionary)269                {270                    originalParameterWasHashTable = true;271                    currParam.hash = VerifyHashTable((IDictionary)actualObject, invocationContext);272                }273                else if ((actualObject != null) && MatchesAllowedTypes(actualObject.GetType(), _paramDef.hashEntries[0].AllowedTypes))274                {275                    // a simple type was specified, build a hash with one entry276                    currParam.hash = _paramDef.hashEntries[0].CreateHashtableFromSingleType(actualObject);277                }278                else279                {280                    // unknown type, error281                    // provide error message (the user did not enter a hash table)282                    ProcessUnknownParameterType(invocationContext, actualObject, _paramDef.hashEntries[0].AllowedTypes);283                }284 285                // value range validation and post processing on the hash table286                VerifyAndNormalizeParameter(currParam, invocationContext, originalParameterWasHashTable);287                retVal.Add(currParam);288            }289 290            return retVal;291        }292 293        private static bool MatchesAllowedTypes(Type t, Type[] allowedTypes)294        {295            for (int k = 0; k < allowedTypes.Length; k++)296            {297                if (allowedTypes[k].IsAssignableFrom(t))298                    return true;299            }300 301            return false;302        }303 304        /// <exception cref="ArgumentException"></exception>305        private Hashtable VerifyHashTable(IDictionary hash, TerminatingErrorContext invocationContext)306        {307            // full blown hash, need to:308            // 1. verify names(keys) and expand names if there are partial matches309            // 2. verify value types310            Hashtable retVal = new Hashtable();311 312            foreach (DictionaryEntry e in hash)313            {314                if (e.Key == null)315                {316                    ProcessNullHashTableKey(invocationContext);317                }318 319                string currentStringKey = e.Key as string;320                if (currentStringKey == null)321                {322                    ProcessNonStringHashTableKey(invocationContext, e.Key);323                }324 325                // find a match for the key326                HashtableEntryDefinition def = _paramDef.MatchEntry(currentStringKey, invocationContext);327                if (retVal.Contains(def.KeyName))328                {329                    // duplicate key error330                    ProcessDuplicateHashTableKey(invocationContext, currentStringKey, def.KeyName);331                }332 333                // now the key is verified, need to check the type334                bool matchType = false;335 336                if (def.AllowedTypes == null || def.AllowedTypes.Length == 0)337                {338                    // we match on any type, it will be up to the entry to further check339                    matchType = true;340                }341                else342                {343                    for (int t = 0; t < def.AllowedTypes.Length; t++)344                    {345                        if (e.Value == null)346                        {347                            ProcessMissingKeyValue(invocationContext, currentStringKey);348                        }349 350                        if (def.AllowedTypes[t].IsAssignableFrom(e.Value.GetType()))351                        {352                            matchType = true;353                            break;354                        }355                    }356                }357 358                if (!matchType)359                {360                    // bad type error361                    ProcessIllegalHashTableKeyValue(invocationContext, currentStringKey, e.Value.GetType(), def.AllowedTypes);362                }363 364                retVal.Add(def.KeyName, e.Value);365            }366 367            return retVal;368        }369 370        /// <exception cref="ArgumentException"></exception>371        private void VerifyAndNormalizeParameter(MshParameter parameter,372                                                    TerminatingErrorContext invocationContext,373                                                    bool originalParameterWasHashTable)374        {375            for (int k = 0; k < _paramDef.hashEntries.Count; k++)376            {377                if (parameter.hash.ContainsKey(_paramDef.hashEntries[k].KeyName))378                {379                    // we have a key, just do some post processing normalization380                    // retrieve the value381                    object val = parameter.hash[_paramDef.hashEntries[k].KeyName];382                    object newVal = _paramDef.hashEntries[k].Verify(val, invocationContext, originalParameterWasHashTable);383 384                    if (newVal != null)385                    {386                        // if a new value is provided, we need to update the hash entry387                        parameter.hash[_paramDef.hashEntries[k].KeyName] = newVal;388                    }389                }390                else391                {392                    // we do not have the key, we might want to have a default value393                    object defaultValue = _paramDef.hashEntries[k].ComputeDefaultValue();394 395                    if (defaultValue != AutomationNull.Value)396                    {397                        // we have a default value, add it398                        parameter.hash[_paramDef.hashEntries[k].KeyName] = defaultValue;399                    }400                    else if (_paramDef.hashEntries[k].Mandatory)401                    {402                        // no default value and mandatory: we cannot proceed403                        ProcessMissingMandatoryKey(invocationContext, _paramDef.hashEntries[k].KeyName);404                    }405                }406            }407        }408 409        #region Error Processing410 411        private static void ProcessUnknownParameterType(TerminatingErrorContext invocationContext, object actualObject, Type[] allowedTypes)412        {413            string allowedTypesList = CatenateTypeArray(allowedTypes);414            string msg;415 416            if (actualObject != null)417            {418                msg = StringUtil.Format(FormatAndOut_MshParameter.UnknownParameterTypeError,419                    actualObject.GetType().FullName, allowedTypesList);420            }421            else422            {423                msg = StringUtil.Format(FormatAndOut_MshParameter.NullParameterTypeError,424                    allowedTypesList);425            }426 427            ParameterProcessor.ThrowParameterBindingException(invocationContext, "DictionaryKeyUnknownType", msg);428        }429 430        private static void ProcessDuplicateHashTableKey(TerminatingErrorContext invocationContext, string duplicateKey, string existingKey)431        {432            string msg = StringUtil.Format(FormatAndOut_MshParameter.DuplicateKeyError,433                           duplicateKey, existingKey);434 435            ParameterProcessor.ThrowParameterBindingException(invocationContext, "DictionaryKeyDuplicate", msg);436        }437 438        private static void ProcessNullHashTableKey(TerminatingErrorContext invocationContext)439        {440            string msg = StringUtil.Format(FormatAndOut_MshParameter.DictionaryKeyNullError);441            ParameterProcessor.ThrowParameterBindingException(invocationContext, "DictionaryKeyNull", msg);442        }443 444        private static void ProcessNonStringHashTableKey(TerminatingErrorContext invocationContext, object key)445        {446            string msg = StringUtil.Format(FormatAndOut_MshParameter.DictionaryKeyNonStringError, key.GetType().Name);447            ParameterProcessor.ThrowParameterBindingException(invocationContext, "DictionaryKeyNonString", msg);448        }449 450        private static void ProcessIllegalHashTableKeyValue(TerminatingErrorContext invocationContext, string key, Type actualType, Type[] allowedTypes)451        {452            string msg;453            string errorID;454            if (allowedTypes.Length > 1)455            {456                string legalTypes = CatenateTypeArray(allowedTypes);457 458                msg = StringUtil.Format(FormatAndOut_MshParameter.IllegalTypeMultiError,459                    key,460                    actualType.FullName,461                    legalTypes462                    );463 464                errorID = "DictionaryKeyIllegalValue1";465            }466            else467            {468                msg = StringUtil.Format(FormatAndOut_MshParameter.IllegalTypeSingleError,469                    key,470                    actualType.FullName,471                    allowedTypes[0]472                    );473 474                errorID = "DictionaryKeyIllegalValue2";475            }476 477            ParameterProcessor.ThrowParameterBindingException(invocationContext, errorID, msg);478        }479 480        private static void ProcessMissingKeyValue(TerminatingErrorContext invocationContext, string keyName)481        {482            string msg = StringUtil.Format(FormatAndOut_MshParameter.MissingKeyValueError, keyName);483            ParameterProcessor.ThrowParameterBindingException(invocationContext, "DictionaryKeyMissingValue", msg);484        }485 486        private static void ProcessMissingMandatoryKey(TerminatingErrorContext invocationContext, string keyName)487        {488            string msg = StringUtil.Format(FormatAndOut_MshParameter.MissingKeyMandatoryEntryError, keyName);489            ParameterProcessor.ThrowParameterBindingException(invocationContext, "DictionaryKeyMandatoryEntry", msg);490        }491 492        #endregion493 494        #region Utilities495 496        private static string CatenateTypeArray(Type[] arr)497        {498            string[] strings = new string[arr.Length];499            for (int k = 0; k < arr.Length; k++)500            {501                strings[k] = arr[k].FullName;502            }503 504            return CatenateStringArray(strings);505        }506 507        internal static string CatenateStringArray(string[] arr)508        {509            StringBuilder sb = new StringBuilder();510            sb.Append('{');511            for (int k = 0; k < arr.Length; k++)512            {513                if (k > 0)514                {515                    sb.Append(", ");516                }517 518                sb.Append(arr[k]);519            }520 521            sb.Append('}');522            return sb.ToString();523        }524 525        #endregion526 527        private readonly CommandParameterDefinition _paramDef = null;528    }529}530