MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Management.Automation.Help;6 7namespace System.Management.Automation8{9 /// <summary>10 /// The MamlUtil class.11 /// </summary>12 internal static class MamlUtil13 {14 /// <summary>15 /// Takes Name value from maml2 and overrides it in maml1.16 /// </summary>17 /// <param name="maml1"></param>18 /// <param name="maml2"></param>19 internal static void OverrideName(PSObject maml1, PSObject maml2)20 {21 PrependPropertyValue(maml1, maml2, new string[] { "Name" }, true);22 PrependPropertyValue(maml1, maml2, new string[] { "Details", "Name" }, true);23 }24 25 /// <summary>26 /// Takes Name value from maml2 and overrides it in maml1.27 /// </summary>28 /// <param name="maml1"></param>29 /// <param name="maml2"></param>30 internal static void OverridePSTypeNames(PSObject maml1, PSObject maml2)31 {32 foreach (var typename in maml2.TypeNames)33 {34 if (typename.StartsWith(DefaultCommandHelpObjectBuilder.TypeNameForDefaultHelp, StringComparison.OrdinalIgnoreCase))35 {36 // Win8: 638494 if the original help is auto-generated, let the Provider help decide the format.37 return;38 }39 }40 41 maml1.TypeNames.Clear();42 // User request at the top..43 foreach (string typeName in maml2.TypeNames)44 {45 maml1.TypeNames.Add(typeName);46 }47 }48 49 /// <summary>50 /// Adds common properties like PSSnapIn,ModuleName from maml2 to maml1.51 /// </summary>52 /// <param name="maml1"></param>53 /// <param name="maml2"></param>54 internal static void AddCommonProperties(PSObject maml1, PSObject maml2)55 {56 if (maml1.Properties["PSSnapIn"] == null)57 {58 PSPropertyInfo snapInProperty = maml2.Properties["PSSnapIn"];59 if (snapInProperty != null)60 {61 maml1.Properties.Add(new PSNoteProperty("PSSnapIn", snapInProperty.Value));62 }63 }64 65 if (maml1.Properties["ModuleName"] == null)66 {67 PSPropertyInfo moduleNameProperty = maml2.Properties["ModuleName"];68 if (moduleNameProperty != null)69 {70 maml1.Properties.Add(new PSNoteProperty("ModuleName", moduleNameProperty.Value));71 }72 }73 }74 75 /// <summary>76 /// Prepend - Modify Syntax element in maml1 using the Syntax element from maml2.77 /// </summary>78 internal static void PrependSyntax(PSObject maml1, PSObject maml2)79 {80 PrependPropertyValue(maml1, maml2, new string[] { "Syntax", "SyntaxItem" }, false);81 }82 83 /// <summary>84 /// Prepend - Modify DetailedDescription element in maml1 using the DetailedDescription element from maml2.85 /// </summary>86 internal static void PrependDetailedDescription(PSObject maml1, PSObject maml2)87 {88 PrependPropertyValue(maml1, maml2, new string[] { "Description" }, false);89 }90 91 /// <summary>92 /// Override - Modify Parameters element in maml1 using the Parameters element from maml2.93 /// This will copy parameters from maml2 that are not present in maml1.94 /// </summary>95 internal static void OverrideParameters(PSObject maml1, PSObject maml2)96 {97 string[] parametersPath = new string[] { "Parameters", "Parameter" };98 // Final collection of PSObjects.99 List<object> maml2items = new List<object>();100 101 // Add maml2 first since we are prepending.102 103 // For maml2: Add as collection or single item. No-op if104 PSPropertyInfo propertyInfo2 = GetPropertyInfo(maml2, parametersPath);105 var array = propertyInfo2.Value as Array;106 if (array != null)107 {108 maml2items.AddRange(array as IEnumerable<object>);109 }110 else111 {112 maml2items.Add(PSObject.AsPSObject(propertyInfo2.Value));113 }114 115 // Extend maml1 to make sure the property-path exists - since we'll be modifying it soon.116 EnsurePropertyInfoPathExists(maml1, parametersPath);117 // For maml1: Add as collection or single item. Do nothing if null or some other type.118 PSPropertyInfo propertyInfo1 = GetPropertyInfo(maml1, parametersPath);119 List<object> maml1items = new List<object>();120 array = propertyInfo1.Value as Array;121 if (array != null)122 {123 maml1items.AddRange(array as IEnumerable<object>);124 }125 else126 {127 maml1items.Add(PSObject.AsPSObject(propertyInfo1.Value));128 }129 130 // copy parameters from maml2 that are not present in maml1131 for (int index = 0; index < maml2items.Count; index++)132 {133 PSObject m2paramObj = PSObject.AsPSObject(maml2items[index]);134 string param2Name = string.Empty;135 PSPropertyInfo m2propertyInfo = m2paramObj.Properties["Name"];136 137 if (m2propertyInfo != null)138 {139 if (!LanguagePrimitives.TryConvertTo<string>(m2propertyInfo.Value, out param2Name))140 {141 continue;142 }143 }144 145 bool isParamFoundInMaml1 = false;146 foreach (PSObject m1ParamObj in maml1items)147 {148 string param1Name = string.Empty;149 PSPropertyInfo m1PropertyInfo = m1ParamObj.Properties["Name"];150 151 if (m1PropertyInfo != null)152 {153 if (!LanguagePrimitives.TryConvertTo<string>(m1PropertyInfo.Value, out param1Name))154 {155 continue;156 }157 }158 159 if (param1Name.Equals(param2Name, StringComparison.OrdinalIgnoreCase))160 {161 isParamFoundInMaml1 = true;162 }163 }164 165 if (!isParamFoundInMaml1)166 {167 maml1items.Add(maml2items[index]);168 }169 }170 171 // Now replace in maml1. If items.Count == 0 do nothing since Value is already null.172 if (maml1items.Count == 1)173 {174 propertyInfo1.Value = maml1items[0];175 }176 else if (maml1items.Count >= 2)177 {178 propertyInfo1.Value = maml1items.ToArray();179 }180 }181 182 /// <summary>183 /// Prepend - Modify Notes element in maml1 using the Notes element from maml2.184 /// </summary>185 internal static void PrependNotes(PSObject maml1, PSObject maml2)186 {187 PrependPropertyValue(maml1, maml2, new string[] { "AlertSet", "Alert" }, false);188 }189 190 /// <summary>191 /// Get property info.192 /// </summary>193 internal static PSPropertyInfo GetPropertyInfo(PSObject psObject, string[] path)194 {195 if (path.Length == 0)196 {197 return null;198 }199 200 for (int i = 0; i < path.Length; ++i)201 {202 string propertyName = path[i];203 PSPropertyInfo propertyInfo = psObject.Properties[propertyName];204 if (i == path.Length - 1)205 {206 return propertyInfo;207 }208 209 if (propertyInfo == null || propertyInfo.Value is not PSObject)210 {211 return null;212 }213 214 psObject = (PSObject)propertyInfo.Value;215 }216 217 // We will never reach this line but the compiler needs some reassurance.218 return null;219 }220 221 /// <summary>222 /// Prepend property value.223 /// </summary>224 /// <param name="maml1">225 /// </param>226 /// <param name="maml2">227 /// </param>228 /// <param name="path">229 /// </param>230 /// <param name="shouldOverride">231 /// Should Override the maml1 value from maml2 instead of prepend.232 /// </param>233 internal static void PrependPropertyValue(PSObject maml1, PSObject maml2, string[] path, bool shouldOverride)234 {235 // Final collection of PSObjects.236 List<object> items = new List<object>();237 238 // Add maml2 first since we are prepending.239 240 // For maml2: Add as collection or single item. No-op if241 PSPropertyInfo propertyInfo2 = GetPropertyInfo(maml2, path);242 243 if (propertyInfo2 != null)244 {245 var array = propertyInfo2.Value as Array;246 if (array != null)247 {248 items.AddRange(propertyInfo2.Value as IEnumerable<object>);249 }250 else251 {252 items.Add(propertyInfo2.Value);253 }254 }255 256 // Extend maml1 to make sure the property-path exists - since we'll be modifying it soon.257 EnsurePropertyInfoPathExists(maml1, path);258 // For maml1: Add as collection or single item. Do nothing if null or some other type.259 PSPropertyInfo propertyInfo1 = GetPropertyInfo(maml1, path);260 261 if (propertyInfo1 != null)262 {263 if (!shouldOverride)264 {265 var array = propertyInfo1.Value as Array;266 if (array != null)267 {268 items.AddRange(propertyInfo1.Value as IEnumerable<object>);269 }270 else271 {272 items.Add(propertyInfo1.Value);273 }274 }275 276 // Now replace in maml1. If items.Count == 0 do nothing since Value is already null.277 if (items.Count == 1)278 {279 propertyInfo1.Value = items[0];280 }281 else if (items.Count >= 2)282 {283 propertyInfo1.Value = items.ToArray();284 }285 }286 }287 288 /// <summary>289 /// Ensure property info path exists.290 /// </summary>291 internal static void EnsurePropertyInfoPathExists(PSObject psObject, string[] path)292 {293 if (path.Length == 0)294 {295 return;296 }297 298 // Walk the path and extend it if necessary.299 for (int i = 0; i < path.Length; ++i)300 {301 string propertyName = path[i];302 PSPropertyInfo propertyInfo = psObject.Properties[propertyName];303 304 // Add a property info here if none was found.305 if (propertyInfo == null)306 {307 // Add null on the last one, since we don't need to extend path further.308 object propertyValue = (i < path.Length - 1) ? new PSObject() : null;309 propertyInfo = new PSNoteProperty(propertyName, propertyValue);310 psObject.Properties.Add(propertyInfo);311 }312 313 // If we are on the last path element, we are done. Let's not mess with modifying Value.314 if (i == path.Length - 1)315 {316 return;317 }318 319 // If we are not on the last path element, let's make sure we can extend the path.320 if (propertyInfo.Value == null || propertyInfo.Value is not PSObject)321 {322 propertyInfo.Value = new PSObject();323 }324 325 // Now move one step further along the path.326 psObject = (PSObject)propertyInfo.Value;327 }328 }329 }330}331 