/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Management.Automation.Language; using System.IO; using Dbg = System.Management.Automation.Diagnostics; using System.Management.Automation.Internal; namespace System.Management.Automation { /// /// Searcher class for finding PS classes on the system. /// internal class PSClassSearcher : IEnumerable, IEnumerator { internal PSClassSearcher( string className, bool useWildCards, ExecutionContext context) { Diagnostics.Assert(context != null, "caller to verify context is not null"); _context = context; Diagnostics.Assert(className != null, "caller to verify className is not null"); _className = className; _useWildCards = useWildCards; _moduleInfoCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } #region private properties private string _className = null; private ExecutionContext _context = null; private PSClassInfo _currentMatch = null; private IEnumerator _matchingClass = null; private Collection _matchingClassList = null; private bool _useWildCards = false; private Dictionary _moduleInfoCache = null; private object _lockObject = new Object(); #endregion #region public methods /// /// Reset the Iterator. /// public void Reset() { _currentMatch = null; _matchingClass = null; } /// /// Reset and dispose the Iterator. /// public void Dispose() { Reset(); GC.SuppressFinalize(this); } /// /// Get the Enumerator. /// /// IEnumerator IEnumerable.GetEnumerator() { return this; } /// /// Get the Enumerator /// /// IEnumerator IEnumerable.GetEnumerator() { return this; } /// /// Move to the Next value in the enumerator. /// /// public bool MoveNext() { _currentMatch = GetNextClass(); if (_currentMatch != null) return true; return false; } /// /// Return the current PSClassInfo /// PSClassInfo IEnumerator.Current { get { return _currentMatch; } } /// /// Return the current PSClassInfo as object /// object IEnumerator.Current { get { return ((IEnumerator)this).Current; } } #endregion #region private methods /// /// Get all modules and find the matching type /// When found add them to the enumerator. If we have already got it, return the next resource. /// /// Next PSClassInfo object or null if none are found. private PSClassInfo GetNextClass() { PSClassInfo returnValue = null; WildcardPattern classNameMatcher = WildcardPattern.Get(_className, WildcardOptions.IgnoreCase); if (_matchingClassList == null) { _matchingClassList = new Collection(); if (FindTypeByModulePath(classNameMatcher)) _matchingClass = _matchingClassList.GetEnumerator(); else return null; } if (!_matchingClass.MoveNext()) { _matchingClass = null; } else { returnValue = _matchingClass.Current; } return returnValue; } private bool FindTypeByModulePath(WildcardPattern classNameMatcher) { bool matchFound = false; var moduleList = ModuleUtils.GetDefaultAvailableModuleFiles(false, false, _context); foreach (var modulePath in moduleList) { string expandedModulePath = IO.Path.GetFullPath(modulePath); var cachedClasses = AnalysisCache.GetExportedClasses(expandedModulePath, _context); if (cachedClasses != null) { //Exact match if (!_useWildCards) { if (cachedClasses.ContainsKey(_className)) { var classInfo = CachedItemToPSClassInfo(classNameMatcher, modulePath); if (classInfo != null) { _matchingClassList.Add(classInfo); matchFound = true; } } } else { foreach (var className in cachedClasses.Keys) { if (classNameMatcher.IsMatch(className)) { var classInfo = CachedItemToPSClassInfo(classNameMatcher, modulePath); if (classInfo != null) { _matchingClassList.Add(classInfo); matchFound = true; } } } } } } return matchFound; } /// /// Convert the cacheItem to a PSClassInfo object. /// For this, we call Get-Module -List with module name. /// /// Wildcard pattern matcher for comparing class name. /// Path to the module where the class is defined. /// Converted PSClassInfo object. private PSClassInfo CachedItemToPSClassInfo(WildcardPattern classNameMatcher, string modulePath) { foreach (var module in GetPSModuleInfo(modulePath)) { var exportedTypes = module.GetExportedTypeDefinitions(); ScriptBlockAst ast = null; TypeDefinitionAst typeAst = null; if (!_useWildCards) { if (exportedTypes.TryGetValue(_className, out typeAst)) { ast = typeAst.Parent.Parent as ScriptBlockAst; if (ast != null) return ConvertToClassInfo(module, ast, typeAst); } } else { foreach (var exportedType in exportedTypes) { if (exportedType.Value != null && classNameMatcher.IsMatch(exportedType.Value.Name) && exportedType.Value.IsClass) { ast = exportedType.Value.Parent.Parent as ScriptBlockAst; if (ast != null) return ConvertToClassInfo(module, ast, exportedType.Value); } } } } return null; } private Collection GetPSModuleInfo(string modulePath) { PSModuleInfo moduleInfo = null; lock (_lockObject) { _moduleInfoCache.TryGetValue(modulePath, out moduleInfo); } if (moduleInfo != null) { var returnValue = new Collection(); returnValue.Add(moduleInfo); return returnValue; } CommandInfo commandInfo = new CmdletInfo("Get-Module", typeof(Microsoft.PowerShell.Commands.GetModuleCommand), null, null, _context); System.Management.Automation.Runspaces.Command getModuleCommand = new System.Management.Automation.Runspaces.Command(commandInfo); string moduleName = Path.GetFileNameWithoutExtension(modulePath); var modules = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace) .AddCommand(getModuleCommand) .AddParameter("List", true) .AddParameter("Name", moduleName) .AddParameter("ErrorAction", ActionPreference.Ignore) .AddParameter("WarningAction", ActionPreference.Ignore) .AddParameter("InformationAction", ActionPreference.Ignore) .AddParameter("Verbose", false) .AddParameter("Debug", false) .Invoke(); lock (_lockObject) { foreach (var module in modules) { _moduleInfoCache.Add(module.Path, module); } } return modules; } private PSClassInfo ConvertToClassInfo(PSModuleInfo module, ScriptBlockAst ast, TypeDefinitionAst statement) { PSClassInfo classInfo = new PSClassInfo(statement.Name); Dbg.Assert(statement.Name != null, "statement should have a name."); classInfo.Module = module; Collection properties = new Collection(); foreach (var member in statement.Members) { PropertyMemberAst propAst = member as PropertyMemberAst; if (propAst != null) { Dbg.Assert(propAst.Name != null, "PropName cannot be null"); Dbg.Assert(propAst.PropertyType != null, "PropertyType cannot be null"); Dbg.Assert(propAst.PropertyType.TypeName != null, "Property TypeName cannot be null"); Dbg.Assert(propAst.Extent != null, "Property Extent cannot be null"); Dbg.Assert(propAst.Extent.Text != null, "Property ExtentText cannot be null"); PSClassMemberInfo classProperty = new PSClassMemberInfo(propAst.Name, propAst.PropertyType.TypeName.FullName, propAst.Extent.Text); properties.Add(classProperty); } } classInfo.UpdateMembers(properties); string mamlHelpFile = null; if (ast.GetHelpContent() != null) mamlHelpFile = ast.GetHelpContent().MamlHelpFile; if (!String.IsNullOrEmpty(mamlHelpFile)) classInfo.HelpFile = mamlHelpFile; return classInfo; } #endregion } }