MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Globalization;5using System.Text;6using System.Xml;7 8namespace System.Management.Automation9{10 /// <summary>11 /// Class MamlCommandHelpInfo keeps track of help information to be returned by12 /// command help provider.13 /// </summary>14 internal class MamlCommandHelpInfo : BaseCommandHelpInfo15 {16 /// <summary>17 /// Constructor for custom HelpInfo object construction18 ///19 /// This is used by the CommandHelpProvider class to generate the20 /// default help UX when no help content is present.21 /// </summary>22 /// <param name="helpObject"></param>23 /// <param name="helpCategory"></param>24 internal MamlCommandHelpInfo(PSObject helpObject, HelpCategory helpCategory)25 : base(helpCategory)26 {27 _fullHelpObject = helpObject;28 29 this.ForwardHelpCategory = HelpCategory.Provider;30 31 this.AddCommonHelpProperties();32 // set user defined data33 if (helpObject.Properties["Component"] != null)34 {35 _component = helpObject.Properties["Component"].Value as string;36 }37 38 if (helpObject.Properties["Role"] != null)39 {40 _role = helpObject.Properties["Role"].Value as string;41 }42 43 if (helpObject.Properties["Functionality"] != null)44 {45 _functionality = helpObject.Properties["Functionality"].Value as string;46 }47 }48 49 /// <summary>50 /// Constructor for MamlCommandHelpInfo. This constructor will call the corresponding51 /// constructor in CommandHelpInfo so that xmlNode will be converted a mamlNode.52 /// </summary>53 /// <remarks>54 /// This constructor is intentionally made private so that the only way to create55 /// MamlCommandHelpInfo is through static function56 /// Load(XmlNode node)57 /// where some sanity check is done.58 /// </remarks>59 private MamlCommandHelpInfo(XmlNode xmlNode, HelpCategory helpCategory) : base(helpCategory)60 {61 MamlNode mamlNode = new MamlNode(xmlNode);62 _fullHelpObject = mamlNode.PSObject;63 64 this.Errors = mamlNode.Errors;65 66 // The type name hierarchy for mshObject doesn't necessary67 // reflect the hierarchy in source code. From display's point of68 // view MamlCommandHelpInfo is derived from HelpInfo.69 70 _fullHelpObject.TypeNames.Clear();71 if (helpCategory == HelpCategory.DscResource)72 {73 _fullHelpObject.TypeNames.Add("DscResourceHelpInfo");74 }75 else76 {77 _fullHelpObject.TypeNames.Add("MamlCommandHelpInfo");78 _fullHelpObject.TypeNames.Add("HelpInfo");79 }80 81 this.ForwardHelpCategory = HelpCategory.Provider;82 }83 84 /// <summary>85 /// Override the FullHelp PSObject of this provider-specific HelpInfo with generic help.86 /// </summary>87 internal void OverrideProviderSpecificHelpWithGenericHelp(HelpInfo genericHelpInfo)88 {89 PSObject genericHelpMaml = genericHelpInfo.FullHelp;90 MamlUtil.OverrideName(_fullHelpObject, genericHelpMaml);91 MamlUtil.OverridePSTypeNames(_fullHelpObject, genericHelpMaml);92 MamlUtil.PrependSyntax(_fullHelpObject, genericHelpMaml);93 MamlUtil.PrependDetailedDescription(_fullHelpObject, genericHelpMaml);94 MamlUtil.OverrideParameters(_fullHelpObject, genericHelpMaml);95 MamlUtil.PrependNotes(_fullHelpObject, genericHelpMaml);96 MamlUtil.AddCommonProperties(_fullHelpObject, genericHelpMaml);97 }98 99 #region Basic Help Properties100 101 private readonly PSObject _fullHelpObject;102 103 /// <summary>104 /// Full help object for this help item.105 /// </summary>106 /// <value>Full help object for this help item.</value>107 internal override PSObject FullHelp108 {109 get110 {111 return _fullHelpObject;112 }113 }114 115 /// <summary>116 /// Examples string of this cmdlet help info.117 /// </summary>118 private string Examples119 {120 get121 {122 return ExtractTextForHelpProperty(this.FullHelp, "Examples");123 }124 }125 126 /// <summary>127 /// Parameters string of this cmdlet help info.128 /// </summary>129 private string Parameters130 {131 get132 {133 return ExtractTextForHelpProperty(this.FullHelp, "Parameters");134 }135 }136 137 /// <summary>138 /// Notes string of this cmdlet help info.139 /// </summary>140 private string Notes141 {142 get143 {144 return ExtractTextForHelpProperty(this.FullHelp, "alertset");145 }146 }147 148 #endregion149 150 #region Component, Role, Features151 152 // Component, Role, Functionality are required by exchange for filtering153 // help contents to be returned from help system.154 //155 // Following is how this is going to work,156 // 1. Each command will optionally include component, role and functionality157 // information. This information is discovered from help content158 // from xml tags <component>, <role>, <functionality> respectively159 // as part of command metadata.160 // 2. From command line, end user can request help for commands for161 // particular component, role and functionality using parameters like162 // -component, -role, -functionality.163 // 3. At runtime, help engine will match against component/role/functionality164 // criteria before returning help results.165 //166 167 private string _component = null;168 /// <summary>169 /// Component for this command.170 /// </summary>171 /// <value></value>172 internal override string Component173 {174 get175 {176 return _component;177 }178 }179 180 private string _role = null;181 /// <summary>182 /// Role for this command.183 /// </summary>184 /// <value></value>185 internal override string Role186 {187 get188 {189 return _role;190 }191 }192 193 private string _functionality = null;194 /// <summary>195 /// Functionality for this command.196 /// </summary>197 /// <value></value>198 internal override string Functionality199 {200 get201 {202 return _functionality;203 }204 }205 206 internal void SetAdditionalDataFromHelpComment(string component, string functionality, string role)207 {208 _component = component;209 _functionality = functionality;210 _role = role;211 212 // component,role,functionality is part of common help..213 // Update these properties as we have new data now..214 this.UpdateUserDefinedDataProperties();215 }216 217 /// <summary>218 /// Add user-defined command help data to command help.219 /// </summary>220 /// <param name="userDefinedData">User defined data object.</param>221 internal void AddUserDefinedData(UserDefinedHelpData userDefinedData)222 {223 if (userDefinedData == null)224 return;225 226 string propertyValue;227 if (userDefinedData.Properties.TryGetValue("component", out propertyValue))228 {229 _component = propertyValue;230 }231 232 if (userDefinedData.Properties.TryGetValue("role", out propertyValue))233 {234 _role = propertyValue;235 }236 237 if (userDefinedData.Properties.TryGetValue("functionality", out propertyValue))238 {239 _functionality = propertyValue;240 }241 242 // component,role,functionality is part of common help..243 // Update these properties as we have new data now..244 this.UpdateUserDefinedDataProperties();245 }246 247 #endregion248 249 #region Load250 251 /// <summary>252 /// Create a MamlCommandHelpInfo object from an XmlNode.253 /// </summary>254 /// <param name="xmlNode">XmlNode that contains help info.</param>255 /// <param name="helpCategory">Help category this maml object fits into.</param>256 /// <returns>MamlCommandHelpInfo object created.</returns>257 internal static MamlCommandHelpInfo Load(XmlNode xmlNode, HelpCategory helpCategory)258 {259 MamlCommandHelpInfo mamlCommandHelpInfo = new MamlCommandHelpInfo(xmlNode, helpCategory);260 261 if (string.IsNullOrEmpty(mamlCommandHelpInfo.Name))262 return null;263 264 mamlCommandHelpInfo.AddCommonHelpProperties();265 266 return mamlCommandHelpInfo;267 }268 269 #endregion270 271 #region Provider specific help272 273#if V2274 /// <summary>275 /// Merge the provider specific help with current command help.276 ///277 /// The cmdletHelp and dynamicParameterHelp is normally retrieved from ProviderHelpProvider.278 /// </summary>279 /// <remarks>280 /// A new MamlCommandHelpInfo is created to avoid polluting the provider help cache.281 /// </remarks>282 /// <param name="cmdletHelp">Provider-specific cmdletHelp to merge into current MamlCommandHelpInfo object.</param>283 /// <param name="dynamicParameterHelp">Provider-specific dynamic parameter help to merge into current MamlCommandHelpInfo object.</param>284 /// <returns>Merged command help info object.</returns>285 internal MamlCommandHelpInfo MergeProviderSpecificHelp(PSObject cmdletHelp, PSObject[] dynamicParameterHelp)286 {287 if (this._fullHelpObject == null)288 return null;289 290 MamlCommandHelpInfo result = (MamlCommandHelpInfo)this.MemberwiseClone();291 292 // We will need to use a deep clone of _fullHelpObject293 // to avoid _fullHelpObject being get terminated.294 result._fullHelpObject = this._fullHelpObject.Copy();295 296 if (cmdletHelp != null)297 result._fullHelpObject.Properties.Add(new PSNoteProperty("PS_Cmdlet", cmdletHelp));298 299 if (dynamicParameterHelp != null)300 result._fullHelpObject.Properties.Add(new PSNoteProperty("PS_DynamicParameters", dynamicParameterHelp));301 302 return result;303 }304#endif305 306 #endregion307 308 #region Helper Methods and Overloads309 310 /// <summary>311 /// Extracts text for a given property from the full help object.312 /// </summary>313 /// <param name="psObject">FullHelp object.</param>314 /// <param name="propertyName">315 /// Name of the property for which text needs to be extracted.316 /// </param>317 /// <returns></returns>318 private static string ExtractTextForHelpProperty(PSObject psObject, string propertyName)319 {320 if (psObject == null)321 return string.Empty;322 323 if (psObject.Properties[propertyName] == null ||324 psObject.Properties[propertyName].Value == null)325 {326 return string.Empty;327 }328 329 return ExtractText(PSObject.AsPSObject(psObject.Properties[propertyName].Value));330 }331 332 /// <summary>333 /// Given a PSObject, this method will traverse through the objects properties,334 /// extracts content from properties that are of type System.String, appends them335 /// together and returns.336 /// </summary>337 /// <param name="psObject"></param>338 /// <returns></returns>339 private static string ExtractText(PSObject psObject)340 {341 if (psObject == null)342 {343 return string.Empty;344 }345 346 // I think every cmdlet description should at least have 400 characters...347 // so starting with this assumption..I did an average of all the cmdlet348 // help content available at the time of writing this code and came up349 // with this number.350 StringBuilder result = new StringBuilder(400);351 foreach (PSPropertyInfo propertyInfo in psObject.Properties)352 {353 string typeNameOfValue = propertyInfo.TypeNameOfValue;354 switch (typeNameOfValue.ToLowerInvariant())355 {356 case "system.boolean":357 case "system.int32":358 case "system.object":359 case "system.object[]":360 continue;361 case "system.string":362 result.Append((string)LanguagePrimitives.ConvertTo(propertyInfo.Value,363 typeof(string), CultureInfo.InvariantCulture));364 break;365 case "system.management.automation.psobject[]":366 PSObject[] items = (PSObject[])LanguagePrimitives.ConvertTo(367 propertyInfo.Value,368 typeof(PSObject[]),369 CultureInfo.InvariantCulture);370 foreach (PSObject item in items)371 {372 result.Append(ExtractText(item));373 }374 375 break;376 case "system.management.automation.psobject":377 result.Append(ExtractText(PSObject.AsPSObject(propertyInfo.Value)));378 break;379 default:380 result.Append(ExtractText(PSObject.AsPSObject(propertyInfo.Value)));381 break;382 }383 }384 385 return result.ToString();386 }387 388 /// <summary>389 /// Returns true if help content in help info matches the390 /// pattern contained in <paramref name="pattern"/>.391 /// The underlying code will usually run pattern.IsMatch() on392 /// content it wants to search.393 /// Cmdlet help info looks for pattern in Synopsis and394 /// DetailedDescription.395 /// </summary>396 /// <param name="pattern"></param>397 /// <returns></returns>398 internal override bool MatchPatternInContent(WildcardPattern pattern)399 {400 System.Management.Automation.Diagnostics.Assert(pattern != null, "pattern cannot be null");401 402 string synopsis = Synopsis;403 if ((!string.IsNullOrEmpty(synopsis)) && (pattern.IsMatch(synopsis)))404 {405 return true;406 }407 408 string detailedDescription = DetailedDescription;409 if ((!string.IsNullOrEmpty(detailedDescription)) && (pattern.IsMatch(detailedDescription)))410 {411 return true;412 }413 414 string examples = Examples;415 if ((!string.IsNullOrEmpty(examples)) && (pattern.IsMatch(examples)))416 {417 return true;418 }419 420 string notes = Notes;421 if ((!string.IsNullOrEmpty(notes)) && (pattern.IsMatch(notes)))422 {423 return true;424 }425 426 string parameters = Parameters;427 if ((!string.IsNullOrEmpty(parameters)) && (pattern.IsMatch(parameters)))428 {429 return true;430 }431 432 return false;433 }434 435 internal MamlCommandHelpInfo Copy()436 {437 MamlCommandHelpInfo result = new MamlCommandHelpInfo(_fullHelpObject.Copy(), this.HelpCategory);438 return result;439 }440 441 internal MamlCommandHelpInfo Copy(HelpCategory newCategoryToUse)442 {443 MamlCommandHelpInfo result = new MamlCommandHelpInfo(_fullHelpObject.Copy(), newCategoryToUse);444 result.FullHelp.Properties["Category"].Value = newCategoryToUse.ToString();445 return result;446 }447 448 #endregion449 }450}451 