PowerShell/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetMember.cs
xtqqczze 883ca98dd7
Seal private classes (#15725)
* Seal private classes

* Fix CS0509

* Fix CS0628
2021-07-19 14:09:12 +05:00

284 lines
10 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Management.Automation;
using System.Management.Automation.Internal;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Class with member information that this cmdlet writes to the pipeline.
/// </summary>
public class MemberDefinition
{
/// <summary>
/// Returns the member definition.
/// </summary>
public override string ToString()
{
return Definition;
}
/// <summary>
/// Initializes a new instance of the <see cref="MemberDefinition"/> class.
/// </summary>
public MemberDefinition(string typeName, string name, PSMemberTypes memberType, string definition)
{
Name = name;
Definition = definition;
MemberType = memberType;
TypeName = typeName;
}
/// <summary>
/// Type name.
/// </summary>
public string TypeName { get; }
/// <summary>
/// Member name.
/// </summary>
public string Name { get; }
/// <summary>
/// Member type.
/// </summary>
public PSMemberTypes MemberType { get; }
/// <summary>
/// Member definition.
/// </summary>
public string Definition { get; }
}
/// <summary>
/// This class implements get-member command.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Member", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096704", RemotingCapability = RemotingCapability.None)]
[OutputType(typeof(MemberDefinition))]
public class GetMemberCommand : PSCmdlet
{
/// <summary>
/// The object to retrieve properties from.
/// </summary>
[Parameter(ValueFromPipeline = true)]
public PSObject InputObject { get; set; }
/// <summary>
/// The member names to be retrieved.
/// </summary>
[Parameter(Position = 0)]
[ValidateNotNullOrEmpty]
public string[] Name { get; set; } = new string[] { "*" };
/// <summary>
/// The member types to be retrieved.
/// </summary>
[Parameter]
[Alias("Type")]
public PSMemberTypes MemberType { get; set; } = PSMemberTypes.All;
/// <summary>
/// View from which the members are retrieved.
/// </summary>
[Parameter]
public PSMemberViewTypes View { get; set; } = PSMemberViewTypes.Adapted | PSMemberViewTypes.Extended;
private bool _staticParameter = false;
/// <summary>
/// True if we should return static members.
/// </summary>
[Parameter]
public SwitchParameter Static
{
get { return _staticParameter; }
set { _staticParameter = value; }
}
/// <summary>
/// Gets or sets the force property.
/// </summary>
/// <remarks>
/// Gives the Member matcher guidance on how vigorous the Match should be.
/// If set to true all members in a given view + membertype are displayed.
/// This parameter is added to hide Get/Set property accessor methods by default.
/// If a user wants to see these methods, -force should be set to true.
/// </remarks>
[Parameter]
public SwitchParameter Force
{
get
{
return (_matchOptions == MshMemberMatchOptions.IncludeHidden);
}
set
{
if (value)
{
// Include hidden members if force parameter is set
_matchOptions = MshMemberMatchOptions.IncludeHidden;
}
else
{
_matchOptions = MshMemberMatchOptions.None;
}
}
}
private MshMemberMatchOptions _matchOptions = MshMemberMatchOptions.None;
private readonly HybridDictionary _typesAlreadyDisplayed = new();
/// <summary>
/// This method implements the ProcessRecord method for get-member command.
/// </summary>
protected override void ProcessRecord()
{
if (this.InputObject == null || this.InputObject == AutomationNull.Value)
{
return;
}
Type baseObjectAsType = null;
string typeName;
Adapter staticAdapter = null;
if (this.Static == true)
{
staticAdapter = PSObject.DotNetStaticAdapter;
object baseObject = this.InputObject.BaseObject;
baseObjectAsType = baseObject as System.Type ?? baseObject.GetType();
typeName = baseObjectAsType.FullName;
}
else
{
var typeNames = this.InputObject.InternalTypeNames;
if (typeNames.Count != 0)
{
typeName = typeNames[0];
}
else
{
// This is never used for display. It is used only as a key to typesAlreadyDisplayed
typeName = "<null>";
}
}
if (_typesAlreadyDisplayed.Contains(typeName))
{
return;
}
else
{
_typesAlreadyDisplayed.Add(typeName, string.Empty);
}
PSMemberTypes memberTypeToSearch = MemberType;
PSMemberViewTypes viewToSearch = View;
if (((View & PSMemberViewTypes.Extended) == 0) &&
(!typeof(PSMemberSet).ToString().Equals(typeName, StringComparison.OrdinalIgnoreCase)))
{
// PSMemberSet is an internal memberset and its properties/methods are populated differently.
// PSMemberSet instance is created to represent PSExtended, PSAdapted, PSBase, PSObject hidden
// properties. We should honor extended properties for such case.
// request is to search dotnet or adapted or both members.
// dotnet,adapted members cannot be Script*,Note*,Code*
memberTypeToSearch ^= (PSMemberTypes.AliasProperty | PSMemberTypes.CodeMethod | PSMemberTypes.CodeProperty
| PSMemberTypes.MemberSet | PSMemberTypes.NoteProperty | PSMemberTypes.PropertySet | PSMemberTypes.ScriptMethod
| PSMemberTypes.ScriptProperty);
}
if (((View & PSMemberViewTypes.Adapted) == 0) && (View & PSMemberViewTypes.Base) == 0)
{
// base and adapted are not mentioned in the view so ignore respective properties
memberTypeToSearch ^= (PSMemberTypes.Property | PSMemberTypes.ParameterizedProperty | PSMemberTypes.Method);
}
if (((View & PSMemberViewTypes.Base) == PSMemberViewTypes.Base) &&
(InputObject.InternalBaseDotNetAdapter == null))
{
// the input object don't have a custom adapter..
// for this case adapted view and base view are the same.
viewToSearch |= PSMemberViewTypes.Adapted;
}
PSMemberInfoCollection<PSMemberInfo> membersToSearch;
if (this.Static == true)
{
membersToSearch = staticAdapter.BaseGetMembers<PSMemberInfo>(baseObjectAsType);
}
else
{
Collection<CollectionEntry<PSMemberInfo>> memberCollection = PSObject.GetMemberCollection(viewToSearch);
membersToSearch = new PSMemberInfoIntegratingCollection<PSMemberInfo>(this.InputObject, memberCollection);
}
foreach (string nameElement in this.Name)
{
ReadOnlyPSMemberInfoCollection<PSMemberInfo> readOnlyMembers;
readOnlyMembers = membersToSearch.Match(nameElement, memberTypeToSearch, _matchOptions);
MemberDefinition[] members = new MemberDefinition[readOnlyMembers.Count];
int resultCount = 0;
foreach (PSMemberInfo member in readOnlyMembers)
{
if (!Force)
{
PSMethod memberAsPSMethod = member as PSMethod;
if ((memberAsPSMethod != null) && (memberAsPSMethod.IsSpecial))
{
continue;
}
}
members[resultCount] = new MemberDefinition(typeName, member.Name, member.MemberType, member.ToString());
resultCount++;
}
Array.Sort<MemberDefinition>(members, 0, resultCount, new MemberComparer());
for (int index = 0; index < resultCount; index++)
{
this.WriteObject(members[index]);
}
}
}
private sealed class MemberComparer : System.Collections.Generic.IComparer<MemberDefinition>
{
public int Compare(MemberDefinition first, MemberDefinition second)
{
int result = string.Compare(first.MemberType.ToString(), second.MemberType.ToString(),
StringComparison.OrdinalIgnoreCase);
if (result != 0)
{
return result;
}
return string.Compare(first.Name, second.Name, StringComparison.OrdinalIgnoreCase);
}
}
/// <summary>
/// This method implements the End method for get-member command.
/// </summary>
protected override void EndProcessing()
{
if (_typesAlreadyDisplayed.Count == 0)
{
ErrorRecord errorRecord = new(
new InvalidOperationException(GetMember.NoObjectSpecified),
"NoObjectInGetMember",
ErrorCategory.CloseError,
null);
WriteError(errorRecord);
}
}
}
}