Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
PSClassHelpProvider.cs372 linesDownload Raw Back to help
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Diagnostics;8using System.IO;9using System.Xml;10 11using Dbg = System.Management.Automation.Diagnostics;12 13namespace System.Management.Automation14{15    internal class PSClassHelpProvider : HelpProviderWithCache16    {17        /// <summary>18        /// Constructor for PSClassHelpProvider.19        /// </summary>20        internal PSClassHelpProvider(HelpSystem helpSystem)21            : base(helpSystem)22        {23            _context = helpSystem.ExecutionContext;24        }25 26        /// <summary>27        /// Execution context of the HelpSystem.28        /// </summary>29        private readonly ExecutionContext _context;30 31        /// <summary>32        /// This is a hashtable to track which help files are loaded already.33        ///34        /// This will avoid one help file getting loaded again and again.35        /// </summary>36        private readonly Hashtable _helpFiles = new Hashtable();37 38        [TraceSource("PSClassHelpProvider", "PSClassHelpProvider")]39        private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("PSClassHelpProvider", "PSClassHelpProvider");40 41        #region common properties42 43        /// <summary>44        /// Name of the Help Provider.45        /// </summary>46        internal override string Name47        {48            get { return "Powershell Class Help Provider"; }49        }50 51        /// <summary>52        /// Supported Help Categories.53        /// </summary>54        internal override HelpCategory HelpCategory55        {56            get { return Automation.HelpCategory.Class; }57        }58 59        #endregion60 61        /// <summary>62        /// Override SearchHelp to find a class module with help matching a pattern.63        /// </summary>64        /// <param name="helpRequest">Help request.</param>65        /// <param name="searchOnlyContent">Not used.</param>66        /// <returns></returns>67        internal override IEnumerable<HelpInfo> SearchHelp(HelpRequest helpRequest, bool searchOnlyContent)68        {69            Debug.Assert(helpRequest != null, "helpRequest cannot be null.");70 71            string target = helpRequest.Target;72            Collection<string> patternList = new Collection<string>();73 74            bool decoratedSearch = !WildcardPattern.ContainsWildcardCharacters(helpRequest.Target);75 76            if (decoratedSearch)77            {78                patternList.Add("*" + target + "*");79            }80            else81                patternList.Add(target);82 83            foreach (string pattern in patternList)84            {85                PSClassSearcher searcher = new PSClassSearcher(pattern, useWildCards: true, _context);86 87                foreach (var helpInfo in GetHelpInfo(searcher))88                {89                    if (helpInfo != null)90                        yield return helpInfo;91                }92            }93        }94 95        /// <summary>96        /// Override ExactMatchHelp to find the matching class module matching help request.97        /// </summary>98        /// <param name="helpRequest">Help Request for the search.</param>99        /// <returns>Enumerable of HelpInfo objects.</returns>100        internal override IEnumerable<HelpInfo> ExactMatchHelp(HelpRequest helpRequest)101        {102            Debug.Assert(helpRequest != null, "helpRequest cannot be null.");103 104            if ((helpRequest.HelpCategory & Automation.HelpCategory.Class) == 0)105            {106                yield return null;107            }108 109            PSClassSearcher searcher = new PSClassSearcher(helpRequest.Target, useWildCards: false, _context);110 111            foreach (var helpInfo in GetHelpInfo(searcher))112            {113                if (helpInfo != null)114                {115                    yield return helpInfo;116                }117            }118        }119 120        /// <summary>121        /// Get the help in for the PS Class Info.        ///122        /// </summary>123        /// <param name="searcher">Searcher for PS Classes.</param>124        /// <returns>Next HelpInfo object.</returns>125        private IEnumerable<HelpInfo> GetHelpInfo(PSClassSearcher searcher)126        {127            while (searcher.MoveNext())128            {129                PSClassInfo current = ((IEnumerator<PSClassInfo>)searcher).Current;130 131                string moduleName = current.Module.Name;132                string moduleDir = current.Module.ModuleBase;133 134                if (!string.IsNullOrEmpty(moduleName) && !string.IsNullOrEmpty(moduleDir))135                {136                    string helpFileToFind = moduleName + "-Help.xml";137 138                    string helpFileName = null;139 140                    Collection<string> searchPaths = new Collection<string>();141                    searchPaths.Add(moduleDir);142 143                    string externalHelpFile = current.HelpFile;144 145                    if (!string.IsNullOrEmpty(externalHelpFile))146                    {147                        FileInfo helpFileInfo = new FileInfo(externalHelpFile);148                        DirectoryInfo dirToSearch = helpFileInfo.Directory;149 150                        if (dirToSearch.Exists)151                        {152                            searchPaths.Add(dirToSearch.FullName);153                            helpFileToFind = helpFileInfo.Name; // If external help file is specified. Then use it.154                        }155                    }156 157                    HelpInfo helpInfo = GetHelpInfoFromHelpFile(current, helpFileToFind, searchPaths, true, out helpFileName);158 159                    if (helpInfo != null)160                    {161                        yield return helpInfo;162                    }163                }164            }165        }166 167        /// <summary>168        /// Check whether a HelpItems node indicates that the help content is169        /// authored using maml schema.170        ///171        /// This covers two cases:172        ///     a. If the help file has an extension .maml.173        ///     b. If HelpItems node (which should be the top node of any command help file)174        ///        has an attribute "schema" with value "maml", its content is in maml175        ///        schema.176        /// </summary>177        /// <param name="helpFile">File name.</param>178        /// <param name="helpItemsNode">Nodes to check.</param>179        /// <returns></returns>180        internal static bool IsMamlHelp(string helpFile, XmlNode helpItemsNode)181        {182            Debug.Assert(!string.IsNullOrEmpty(helpFile), "helpFile cannot be null.");183 184            if (helpFile.EndsWith(".maml", StringComparison.OrdinalIgnoreCase))185                return true;186 187            if (helpItemsNode.Attributes == null)188                return false;189 190            foreach (XmlNode attribute in helpItemsNode.Attributes)191            {192                if (attribute.Name.Equals("schema", StringComparison.OrdinalIgnoreCase)193                    && attribute.Value.Equals("maml", StringComparison.OrdinalIgnoreCase))194                {195                    return true;196                }197            }198 199            return false;200        }201 202        #region private methods203 204        private HelpInfo GetHelpInfoFromHelpFile(PSClassInfo classInfo, string helpFileToFind, Collection<string> searchPaths, bool reportErrors, out string helpFile)205        {206            Dbg.Assert(classInfo != null, "Caller should verify that classInfo != null");207            Dbg.Assert(helpFileToFind != null, "Caller should verify that helpFileToFind != null");208 209            helpFile = MUIFileSearcher.LocateFile(helpFileToFind, searchPaths);210 211            if (!File.Exists(helpFile))212                return null;213 214            if (!string.IsNullOrEmpty(helpFile))215            {216                // Load the help file only once. Then use it from the cache.217                if (!_helpFiles.Contains(helpFile))218                {219                    LoadHelpFile(helpFile, helpFile, classInfo.Name, reportErrors);220                }221 222                return GetFromPSClassHelpCache(helpFile, Automation.HelpCategory.Class);223            }224 225            return null;226        }227 228        /// <summary>229        /// Gets the HelpInfo object corresponding to the command.230        /// </summary>231        /// <param name="helpFileIdentifier">Help file identifier (either name of PSSnapIn or simply full path to help file).</param>232        /// <param name="helpCategory">Help Category for search.</param>233        /// <returns>HelpInfo object.</returns>234        private HelpInfo GetFromPSClassHelpCache(string helpFileIdentifier, HelpCategory helpCategory)235        {236            Debug.Assert(!string.IsNullOrEmpty(helpFileIdentifier), "helpFileIdentifier should not be null or empty.");237 238            HelpInfo result = GetCache(helpFileIdentifier);239 240            if (result != null)241            {242                MamlClassHelpInfo original = (MamlClassHelpInfo)result;243                result = original.Copy(helpCategory);244            }245 246            return result;247        }248 249        private void LoadHelpFile(string helpFile, string helpFileIdentifier, string commandName, bool reportErrors)250        {251            Exception e = null;252            try253            {254                LoadHelpFile(helpFile, helpFileIdentifier);255            }256            catch (IOException ioException)257            {258                e = ioException;259            }260            catch (System.Security.SecurityException securityException)261            {262                e = securityException;263            }264            catch (XmlException xmlException)265            {266                e = xmlException;267            }268            catch (NotSupportedException notSupportedException)269            {270                e = notSupportedException;271            }272            catch (UnauthorizedAccessException unauthorizedAccessException)273            {274                e = unauthorizedAccessException;275            }276            catch (InvalidOperationException invalidOperationException)277            {278                e = invalidOperationException;279            }280 281            if (e != null)282                s_tracer.WriteLine("Error occurred in PSClassHelpProvider {0}", e.Message);283 284            if (reportErrors && (e != null))285            {286                ReportHelpFileError(e, commandName, helpFile);287            }288        }289 290        /// <summary>291        /// Load help file for HelpInfo objects. The HelpInfo objects will be292        /// put into help cache.293        /// </summary>294        /// <remarks>295        /// 1. Needs to pay special attention about error handling in this function.296        /// Common errors include: file not found and invalid xml. None of these error297        /// should cause help search to stop.298        /// 2. a helpfile cache is used to avoid same file got loaded again and again.299        /// </remarks>300        private void LoadHelpFile(string helpFile, string helpFileIdentifier)301        {302            Dbg.Assert(!string.IsNullOrEmpty(helpFile), "HelpFile cannot be null or empty.");303            Dbg.Assert(!string.IsNullOrEmpty(helpFileIdentifier), "helpFileIdentifier cannot be null or empty.");304 305            XmlDocument doc = InternalDeserializer.LoadUnsafeXmlDocument(306                new FileInfo(helpFile),307                false, /* ignore whitespace, comments, etc. */308                null); /* default maxCharactersInDocument */309 310            // Add this file into _helpFiles hashtable to prevent it to be loaded again.311            _helpFiles[helpFile] = 0;312 313            XmlNode helpItemsNode = null;314 315            if (doc.HasChildNodes)316            {317                for (int i = 0; i < doc.ChildNodes.Count; i++)318                {319                    XmlNode node = doc.ChildNodes[i];320                    if (node.NodeType == XmlNodeType.Element && string.Equals(node.LocalName, "helpItems", StringComparison.OrdinalIgnoreCase))321                    {322                        helpItemsNode = node;323                        break;324                    }325                }326            }327 328            if (helpItemsNode == null)329            {330                s_tracer.WriteLine("Unable to find 'helpItems' element in file {0}", helpFile);331                return;332            }333 334            bool isMaml = IsMamlHelp(helpFile, helpItemsNode);335 336            using (this.HelpSystem.Trace(helpFile))337            {338                if (helpItemsNode.HasChildNodes)339                {340                    for (int i = 0; i < helpItemsNode.ChildNodes.Count; i++)341                    {342                        XmlNode node = helpItemsNode.ChildNodes[i];343 344                        string nodeLocalName = node.LocalName;345 346                        bool isClass = (string.Equals(nodeLocalName, "class", StringComparison.OrdinalIgnoreCase));347 348                        if (node.NodeType == XmlNodeType.Element && isClass)349                        {350                            MamlClassHelpInfo helpInfo = null;351 352                            if (isMaml)353                            {354                                if (isClass)355                                    helpInfo = MamlClassHelpInfo.Load(node, HelpCategory.Class);356                            }357 358                            if (helpInfo != null)359                            {360                                this.HelpSystem.TraceErrors(helpInfo.Errors);361                                AddCache(helpFileIdentifier, helpInfo);362                            }363                        }364                    }365                }366            }367        }368 369        #endregion370    }371}372