883ca98dd7
* Seal private classes * Fix CS0509 * Fix CS0628
860 lines
31 KiB
C#
860 lines
31 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Management.Automation;
|
|
using System.Management.Automation.Internal;
|
|
|
|
using Microsoft.PowerShell.Commands.Internal.Format;
|
|
|
|
namespace Microsoft.PowerShell.Commands
|
|
{
|
|
/// <summary>
|
|
/// Helper class to do wildcard matching on PSPropertyExpressions.
|
|
/// </summary>
|
|
internal sealed class PSPropertyExpressionFilter
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PSPropertyExpressionFilter"/> class
|
|
/// with the specified array of patterns.
|
|
/// </summary>
|
|
/// <param name="wildcardPatternsStrings">Array of pattern strings to use.</param>
|
|
internal PSPropertyExpressionFilter(string[] wildcardPatternsStrings)
|
|
{
|
|
if (wildcardPatternsStrings == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(wildcardPatternsStrings));
|
|
}
|
|
|
|
_wildcardPatterns = new WildcardPattern[wildcardPatternsStrings.Length];
|
|
for (int k = 0; k < wildcardPatternsStrings.Length; k++)
|
|
{
|
|
_wildcardPatterns[k] = WildcardPattern.Get(wildcardPatternsStrings[k], WildcardOptions.IgnoreCase);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to match the expression against the array of wildcard patterns.
|
|
/// The first match shortcircuits the search.
|
|
/// </summary>
|
|
/// <param name="expression">PSPropertyExpression to test against.</param>
|
|
/// <returns>True if there is a match, else false.</returns>
|
|
internal bool IsMatch(PSPropertyExpression expression)
|
|
{
|
|
for (int k = 0; k < _wildcardPatterns.Length; k++)
|
|
{
|
|
if (_wildcardPatterns[k].IsMatch(expression.ToString()))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private readonly WildcardPattern[] _wildcardPatterns;
|
|
}
|
|
|
|
internal class SelectObjectExpressionParameterDefinition : CommandParameterDefinition
|
|
{
|
|
protected override void SetEntries()
|
|
{
|
|
this.hashEntries.Add(new ExpressionEntryDefinition());
|
|
this.hashEntries.Add(new NameEntryDefinition());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
[Cmdlet(VerbsCommon.Select, "Object", DefaultParameterSetName = "DefaultParameter",
|
|
HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096716", RemotingCapability = RemotingCapability.None)]
|
|
public sealed class SelectObjectCommand : PSCmdlet
|
|
{
|
|
#region Command Line Switches
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(ValueFromPipeline = true)]
|
|
public PSObject InputObject { get; set; } = AutomationNull.Value;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(Position = 0, ParameterSetName = "DefaultParameter")]
|
|
[Parameter(Position = 0, ParameterSetName = "SkipLastParameter")]
|
|
public object[] Property { get; set; }
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(ParameterSetName = "DefaultParameter")]
|
|
[Parameter(ParameterSetName = "SkipLastParameter")]
|
|
public string[] ExcludeProperty { get; set; }
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(ParameterSetName = "DefaultParameter")]
|
|
[Parameter(ParameterSetName = "SkipLastParameter")]
|
|
public string ExpandProperty { get; set; }
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter]
|
|
public SwitchParameter Unique
|
|
{
|
|
get { return _unique; }
|
|
|
|
set { _unique = value; }
|
|
}
|
|
|
|
private bool _unique;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(ParameterSetName = "DefaultParameter")]
|
|
// NTRAID#Windows Out Of Band Releases-927878-2006/03/02
|
|
// Allow zero
|
|
[ValidateRange(0, int.MaxValue)]
|
|
public int Last
|
|
{
|
|
get { return _last; }
|
|
|
|
set { _last = value; _firstOrLastSpecified = true; }
|
|
}
|
|
|
|
private int _last = 0;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(ParameterSetName = "DefaultParameter")]
|
|
// NTRAID#Windows Out Of Band Releases-927878-2006/03/02
|
|
// Allow zero
|
|
[ValidateRange(0, int.MaxValue)]
|
|
public int First
|
|
{
|
|
get { return _first; }
|
|
|
|
set { _first = value; _firstOrLastSpecified = true; }
|
|
}
|
|
|
|
private int _first = 0;
|
|
private bool _firstOrLastSpecified;
|
|
|
|
/// <summary>
|
|
/// Skips the specified number of items from top when used with First, from end when used with Last.
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(ParameterSetName = "DefaultParameter")]
|
|
[ValidateRange(0, int.MaxValue)]
|
|
public int Skip { get; set; }
|
|
|
|
/// <summary>
|
|
/// Skip the specified number of items from end.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = "SkipLastParameter")]
|
|
[ValidateRange(0, int.MaxValue)]
|
|
public int SkipLast { get; set; }
|
|
|
|
/// <summary>
|
|
/// With this switch present, the cmdlet won't "short-circuit"
|
|
/// (i.e. won't stop upstream cmdlets after it knows that no further objects will be emitted downstream).
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = "DefaultParameter")]
|
|
[Parameter(ParameterSetName = "IndexParameter")]
|
|
public SwitchParameter Wait { get; set; }
|
|
|
|
/// <summary>
|
|
/// Used to display the object at the specified index.
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(ParameterSetName = "IndexParameter")]
|
|
[ValidateRangeAttribute(0, int.MaxValue)]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public int[] Index
|
|
{
|
|
get
|
|
{
|
|
return _index;
|
|
}
|
|
|
|
set
|
|
{
|
|
_index = value;
|
|
_indexSpecified = true;
|
|
_isIncludeIndex = true;
|
|
Array.Sort(_index);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to display all objects at the specified indices.
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(ParameterSetName = "SkipIndexParameter")]
|
|
[ValidateRangeAttribute(0, int.MaxValue)]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public int[] SkipIndex
|
|
{
|
|
get
|
|
{
|
|
return _index;
|
|
}
|
|
|
|
set
|
|
{
|
|
_index = value;
|
|
_indexSpecified = true;
|
|
_isIncludeIndex = false;
|
|
Array.Sort(_index);
|
|
}
|
|
}
|
|
|
|
private int[] _index;
|
|
private bool _indexSpecified;
|
|
private bool _isIncludeIndex;
|
|
|
|
#endregion
|
|
|
|
private SelectObjectQueue _selectObjectQueue;
|
|
|
|
private sealed class SelectObjectQueue : Queue<PSObject>
|
|
{
|
|
internal SelectObjectQueue(int first, int last, int skip, int skipLast, bool firstOrLastSpecified)
|
|
{
|
|
_first = first;
|
|
_last = last;
|
|
_skip = skip;
|
|
_skipLast = skipLast;
|
|
_firstOrLastSpecified = firstOrLastSpecified;
|
|
}
|
|
|
|
public bool AllRequestedObjectsProcessed
|
|
{
|
|
get
|
|
{
|
|
return _firstOrLastSpecified && _last == 0 && _first != 0 && _streamedObjectCount >= _first;
|
|
}
|
|
}
|
|
|
|
public new void Enqueue(PSObject obj)
|
|
{
|
|
if (_last > 0 && this.Count >= (_last + _skip) && _first == 0)
|
|
{
|
|
base.Dequeue();
|
|
}
|
|
else if (_last > 0 && this.Count >= _last && _first != 0)
|
|
{
|
|
base.Dequeue();
|
|
}
|
|
|
|
base.Enqueue(obj);
|
|
}
|
|
|
|
public PSObject StreamingDequeue()
|
|
{
|
|
// if skip parameter is not mentioned or there are no more objects to skip
|
|
if (_skip == 0)
|
|
{
|
|
if (_skipLast > 0)
|
|
{
|
|
// We are going to skip some items from end, but it's okay to process
|
|
// the early input objects once we have more items in queue than the
|
|
// specified 'skipLast' value.
|
|
if (this.Count > _skipLast)
|
|
{
|
|
return Dequeue();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_streamedObjectCount < _first || !_firstOrLastSpecified)
|
|
{
|
|
Diagnostics.Assert(this.Count > 0, "Streaming an empty queue");
|
|
_streamedObjectCount++;
|
|
return Dequeue();
|
|
}
|
|
|
|
if (_last == 0)
|
|
{
|
|
Dequeue();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if last parameter is not mentioned,remove the objects and decrement the skip
|
|
if (_last == 0)
|
|
{
|
|
Dequeue();
|
|
_skip--;
|
|
}
|
|
else if (_first != 0)
|
|
{
|
|
_skip--;
|
|
Dequeue();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private int _streamedObjectCount;
|
|
private readonly int _first;
|
|
private readonly int _last;
|
|
private int _skip;
|
|
private readonly int _skipLast;
|
|
private readonly bool _firstOrLastSpecified;
|
|
}
|
|
|
|
/// <summary>
|
|
/// List of processed parameters obtained from the Expression array.
|
|
/// </summary>
|
|
private List<MshParameter> _propertyMshParameterList;
|
|
|
|
/// <summary>
|
|
/// Singleton list of process parameters obtained from ExpandProperty.
|
|
/// </summary>
|
|
private List<MshParameter> _expandMshParameterList;
|
|
|
|
private PSPropertyExpressionFilter _exclusionFilter;
|
|
|
|
private sealed class UniquePSObjectHelper
|
|
{
|
|
internal UniquePSObjectHelper(PSObject o, int notePropertyCount)
|
|
{
|
|
WrittenObject = o;
|
|
NotePropertyCount = notePropertyCount;
|
|
}
|
|
|
|
internal readonly PSObject WrittenObject;
|
|
|
|
internal int NotePropertyCount { get; }
|
|
}
|
|
|
|
private List<UniquePSObjectHelper> _uniques = null;
|
|
|
|
private void ProcessExpressionParameter()
|
|
{
|
|
TerminatingErrorContext invocationContext = new(this);
|
|
ParameterProcessor processor =
|
|
new(new SelectObjectExpressionParameterDefinition());
|
|
if ((Property != null) && (Property.Length != 0))
|
|
{
|
|
// Build property list taking into account the wildcards and @{name=;expression=}
|
|
|
|
_propertyMshParameterList = processor.ProcessParameters(Property, invocationContext);
|
|
}
|
|
else
|
|
{
|
|
// Property don't exist
|
|
_propertyMshParameterList = new List<MshParameter>();
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(ExpandProperty))
|
|
{
|
|
_expandMshParameterList = processor.ProcessParameters(new string[] { ExpandProperty }, invocationContext);
|
|
}
|
|
|
|
if (ExcludeProperty != null)
|
|
{
|
|
_exclusionFilter = new PSPropertyExpressionFilter(ExcludeProperty);
|
|
// ExcludeProperty implies -Property * for better UX
|
|
if ((Property == null) || (Property.Length == 0))
|
|
{
|
|
Property = new object[] { "*" };
|
|
_propertyMshParameterList = processor.ProcessParameters(Property, invocationContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ProcessObject(PSObject inputObject)
|
|
{
|
|
if ((Property == null || Property.Length == 0) && string.IsNullOrEmpty(ExpandProperty))
|
|
{
|
|
FilteredWriteObject(inputObject, new List<PSNoteProperty>());
|
|
return;
|
|
}
|
|
|
|
// If property parameter is mentioned
|
|
List<PSNoteProperty> matchedProperties = new();
|
|
foreach (MshParameter p in _propertyMshParameterList)
|
|
{
|
|
ProcessParameter(p, inputObject, matchedProperties);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(ExpandProperty))
|
|
{
|
|
PSObject result = new();
|
|
if (matchedProperties.Count != 0)
|
|
{
|
|
HashSet<string> propertyNames = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (PSNoteProperty noteProperty in matchedProperties)
|
|
{
|
|
try
|
|
{
|
|
if (!propertyNames.Contains(noteProperty.Name))
|
|
{
|
|
propertyNames.Add(noteProperty.Name);
|
|
result.Properties.Add(noteProperty);
|
|
}
|
|
else
|
|
{
|
|
WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject,
|
|
"AlreadyExistingUserSpecifiedPropertyNoExpand");
|
|
}
|
|
}
|
|
catch (ExtendedTypeSystemException)
|
|
{
|
|
WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject,
|
|
"AlreadyExistingUserSpecifiedPropertyNoExpand");
|
|
}
|
|
}
|
|
}
|
|
|
|
FilteredWriteObject(result, matchedProperties);
|
|
}
|
|
else
|
|
{
|
|
ProcessExpandParameter(_expandMshParameterList[0], inputObject, matchedProperties);
|
|
}
|
|
}
|
|
|
|
private void ProcessParameter(MshParameter p, PSObject inputObject, List<PSNoteProperty> result)
|
|
{
|
|
string name = p.GetEntry(NameEntryDefinition.NameEntryKey) as string;
|
|
|
|
PSPropertyExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as PSPropertyExpression;
|
|
List<PSPropertyExpressionResult> expressionResults = new();
|
|
foreach (PSPropertyExpression resolvedName in ex.ResolveNames(inputObject))
|
|
{
|
|
if (_exclusionFilter == null || !_exclusionFilter.IsMatch(resolvedName))
|
|
{
|
|
List<PSPropertyExpressionResult> tempExprResults = resolvedName.GetValues(inputObject);
|
|
if (tempExprResults == null) continue;
|
|
foreach (PSPropertyExpressionResult mshExpRes in tempExprResults)
|
|
{
|
|
expressionResults.Add(mshExpRes);
|
|
}
|
|
}
|
|
}
|
|
|
|
// allow 'Select-Object -Property noexist-name' to return a PSObject with property noexist-name,
|
|
// unless noexist-name itself contains wildcards
|
|
if (expressionResults.Count == 0 && !ex.HasWildCardCharacters)
|
|
{
|
|
expressionResults.Add(new PSPropertyExpressionResult(null, ex, null));
|
|
}
|
|
|
|
// if we have an expansion, renaming is not acceptable
|
|
else if (!string.IsNullOrEmpty(name) && expressionResults.Count > 1)
|
|
{
|
|
string errorMsg = SelectObjectStrings.RenamingMultipleResults;
|
|
ErrorRecord errorRecord = new(
|
|
new InvalidOperationException(errorMsg),
|
|
"RenamingMultipleResults",
|
|
ErrorCategory.InvalidOperation,
|
|
inputObject);
|
|
WriteError(errorRecord);
|
|
return;
|
|
}
|
|
|
|
foreach (PSPropertyExpressionResult r in expressionResults)
|
|
{
|
|
// filter the exclusions, if any
|
|
if (_exclusionFilter != null && _exclusionFilter.IsMatch(r.ResolvedExpression))
|
|
continue;
|
|
|
|
PSNoteProperty mshProp;
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
string resolvedExpressionName = r.ResolvedExpression.ToString();
|
|
if (string.IsNullOrEmpty(resolvedExpressionName))
|
|
{
|
|
PSArgumentException mshArgE = PSTraceSource.NewArgumentException(
|
|
"Property",
|
|
SelectObjectStrings.EmptyScriptBlockAndNoName);
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(
|
|
mshArgE,
|
|
"EmptyScriptBlockAndNoName",
|
|
ErrorCategory.InvalidArgument, null));
|
|
}
|
|
|
|
mshProp = new PSNoteProperty(resolvedExpressionName, r.Result);
|
|
}
|
|
else
|
|
{
|
|
mshProp = new PSNoteProperty(name, r.Result);
|
|
}
|
|
|
|
result.Add(mshProp);
|
|
}
|
|
}
|
|
|
|
private void ProcessExpandParameter(MshParameter p, PSObject inputObject,
|
|
List<PSNoteProperty> matchedProperties)
|
|
{
|
|
PSPropertyExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as PSPropertyExpression;
|
|
List<PSPropertyExpressionResult> expressionResults = ex.GetValues(inputObject);
|
|
|
|
if (expressionResults.Count == 0)
|
|
{
|
|
ErrorRecord errorRecord = new(
|
|
PSTraceSource.NewArgumentException("ExpandProperty", SelectObjectStrings.PropertyNotFound, ExpandProperty),
|
|
"ExpandPropertyNotFound",
|
|
ErrorCategory.InvalidArgument,
|
|
inputObject);
|
|
throw new SelectObjectException(errorRecord);
|
|
}
|
|
|
|
if (expressionResults.Count > 1)
|
|
{
|
|
ErrorRecord errorRecord = new(
|
|
PSTraceSource.NewArgumentException("ExpandProperty", SelectObjectStrings.MutlipleExpandProperties, ExpandProperty),
|
|
"MutlipleExpandProperties",
|
|
ErrorCategory.InvalidArgument,
|
|
inputObject);
|
|
throw new SelectObjectException(errorRecord);
|
|
}
|
|
|
|
PSPropertyExpressionResult r = expressionResults[0];
|
|
if (r.Exception == null)
|
|
{
|
|
// ignore the property value if it's null
|
|
if (r.Result == null) { return; }
|
|
|
|
System.Collections.IEnumerable results = LanguagePrimitives.GetEnumerable(r.Result);
|
|
if (results == null)
|
|
{
|
|
// add NoteProperties if there is any
|
|
// If r.Result is a base object, we don't want to associate the NoteProperty
|
|
// directly with it. We want the NoteProperty to be associated only with this
|
|
// particular PSObject, so that when the user uses the base object else where,
|
|
// its members remain the same as before the Select-Object command run.
|
|
PSObject expandedObject = PSObject.AsPSObject(r.Result, true);
|
|
AddNoteProperties(expandedObject, inputObject, matchedProperties);
|
|
|
|
FilteredWriteObject(expandedObject, matchedProperties);
|
|
return;
|
|
}
|
|
|
|
foreach (object expandedValue in results)
|
|
{
|
|
// ignore the element if it's null
|
|
if (expandedValue == null) { continue; }
|
|
|
|
// add NoteProperties if there is any
|
|
// If expandedValue is a base object, we don't want to associate the NoteProperty
|
|
// directly with it. We want the NoteProperty to be associated only with this
|
|
// particular PSObject, so that when the user uses the base object else where,
|
|
// its members remain the same as before the Select-Object command run.
|
|
PSObject expandedObject = PSObject.AsPSObject(expandedValue, true);
|
|
AddNoteProperties(expandedObject, inputObject, matchedProperties);
|
|
|
|
FilteredWriteObject(expandedObject, matchedProperties);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new(
|
|
r.Exception,
|
|
"PropertyEvaluationExpand",
|
|
ErrorCategory.InvalidResult,
|
|
inputObject);
|
|
throw new SelectObjectException(errorRecord);
|
|
}
|
|
}
|
|
|
|
private void AddNoteProperties(PSObject expandedObject, PSObject inputObject, IEnumerable<PSNoteProperty> matchedProperties)
|
|
{
|
|
foreach (PSNoteProperty noteProperty in matchedProperties)
|
|
{
|
|
try
|
|
{
|
|
if (expandedObject.Properties[noteProperty.Name] != null)
|
|
{
|
|
WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, "AlreadyExistingUserSpecifiedPropertyExpand");
|
|
}
|
|
else
|
|
{
|
|
expandedObject.Properties.Add(noteProperty);
|
|
}
|
|
}
|
|
catch (ExtendedTypeSystemException)
|
|
{
|
|
WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, "AlreadyExistingUserSpecifiedPropertyExpand");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void WriteAlreadyExistingPropertyError(string name, object inputObject, string errorId)
|
|
{
|
|
ErrorRecord errorRecord = new(
|
|
PSTraceSource.NewArgumentException("Property", SelectObjectStrings.AlreadyExistingProperty, name),
|
|
errorId,
|
|
ErrorCategory.InvalidOperation,
|
|
inputObject);
|
|
WriteError(errorRecord);
|
|
}
|
|
|
|
private void FilteredWriteObject(PSObject obj, List<PSNoteProperty> addedNoteProperties)
|
|
{
|
|
Diagnostics.Assert(obj != null, "This command should never write null");
|
|
|
|
if (!_unique)
|
|
{
|
|
if (obj != AutomationNull.Value)
|
|
{
|
|
SetPSCustomObject(obj, newPSObject: addedNoteProperties.Count > 0);
|
|
WriteObject(obj);
|
|
}
|
|
|
|
return;
|
|
}
|
|
// if only unique is mentioned
|
|
else if ((_unique))
|
|
{
|
|
bool isObjUnique = true;
|
|
foreach (UniquePSObjectHelper uniqueObj in _uniques)
|
|
{
|
|
ObjectCommandComparer comparer = new(true, CultureInfo.CurrentCulture, true);
|
|
if ((comparer.Compare(obj.BaseObject, uniqueObj.WrittenObject.BaseObject) == 0) &&
|
|
(uniqueObj.NotePropertyCount == addedNoteProperties.Count))
|
|
{
|
|
bool found = true;
|
|
foreach (PSNoteProperty note in addedNoteProperties)
|
|
{
|
|
PSMemberInfo prop = uniqueObj.WrittenObject.Properties[note.Name];
|
|
if (prop == null || comparer.Compare(prop.Value, note.Value) != 0)
|
|
{
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
isObjUnique = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (isObjUnique)
|
|
{
|
|
SetPSCustomObject(obj, newPSObject: addedNoteProperties.Count > 0);
|
|
_uniques.Add(new UniquePSObjectHelper(obj, addedNoteProperties.Count));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SetPSCustomObject(PSObject psObj, bool newPSObject)
|
|
{
|
|
if (psObj.ImmediateBaseObject is PSCustomObject)
|
|
{
|
|
var typeName = "Selected." + InputObject.BaseObject.GetType().ToString();
|
|
if (newPSObject || !psObj.TypeNames.Contains(typeName))
|
|
{
|
|
psObj.TypeNames.Insert(0, typeName);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ProcessObjectAndHandleErrors(PSObject pso)
|
|
{
|
|
Diagnostics.Assert(pso != null, "Caller should verify pso != null");
|
|
|
|
try
|
|
{
|
|
ProcessObject(pso);
|
|
}
|
|
catch (SelectObjectException e)
|
|
{
|
|
WriteError(e.ErrorRecord);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
ProcessExpressionParameter();
|
|
|
|
if (_unique)
|
|
{
|
|
_uniques = new List<UniquePSObjectHelper>();
|
|
}
|
|
|
|
_selectObjectQueue = new SelectObjectQueue(_first, _last, Skip, SkipLast, _firstOrLastSpecified);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles processing of InputObject.
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
if (InputObject != AutomationNull.Value && InputObject != null)
|
|
{
|
|
if (_indexSpecified)
|
|
{
|
|
ProcessIndexed();
|
|
}
|
|
else
|
|
{
|
|
_selectObjectQueue.Enqueue(InputObject);
|
|
PSObject streamingInputObject = _selectObjectQueue.StreamingDequeue();
|
|
if (streamingInputObject != null)
|
|
{
|
|
ProcessObjectAndHandleErrors(streamingInputObject);
|
|
}
|
|
|
|
if (_selectObjectQueue.AllRequestedObjectsProcessed && !this.Wait)
|
|
{
|
|
this.EndProcessing();
|
|
throw new StopUpstreamCommandsException(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The index of the active index filter.
|
|
/// </summary>
|
|
private int _currentFilterIndex;
|
|
|
|
/// <summary>
|
|
/// The index of the object being processed.
|
|
/// </summary>
|
|
private int _currentObjectIndex;
|
|
|
|
/// <summary>
|
|
/// Handles processing of InputObject if -Index or -SkipIndex is specified.
|
|
/// </summary>
|
|
private void ProcessIndexed()
|
|
{
|
|
if (_isIncludeIndex)
|
|
{
|
|
if (_currentFilterIndex < _index.Length)
|
|
{
|
|
int nextIndexToOutput = _index[_currentFilterIndex];
|
|
if (_currentObjectIndex == nextIndexToOutput)
|
|
{
|
|
ProcessObjectAndHandleErrors(InputObject);
|
|
while ((_currentFilterIndex < _index.Length) && (_index[_currentFilterIndex] == nextIndexToOutput))
|
|
{
|
|
_currentFilterIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Wait && _currentFilterIndex >= _index.Length)
|
|
{
|
|
EndProcessing();
|
|
throw new StopUpstreamCommandsException(this);
|
|
}
|
|
|
|
_currentObjectIndex++;
|
|
}
|
|
else
|
|
{
|
|
if (_currentFilterIndex < _index.Length)
|
|
{
|
|
int nextIndexToSkip = _index[_currentFilterIndex];
|
|
if (_currentObjectIndex != nextIndexToSkip)
|
|
{
|
|
ProcessObjectAndHandleErrors(InputObject);
|
|
}
|
|
else
|
|
{
|
|
while ((_currentFilterIndex < _index.Length) && (_index[_currentFilterIndex] == nextIndexToSkip))
|
|
{
|
|
_currentFilterIndex++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProcessObjectAndHandleErrors(InputObject);
|
|
}
|
|
|
|
_currentObjectIndex++;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Completes the processing of Input.
|
|
/// </summary>
|
|
protected override void EndProcessing()
|
|
{
|
|
// We can skip this part for 'IndexParameter' and 'SkipLastParameter' sets because:
|
|
// 1. 'IndexParameter' set doesn't use selectObjectQueue.
|
|
// 2. 'SkipLastParameter' set should have processed all valid input in the ProcessRecord.
|
|
if (ParameterSetName == "DefaultParameter")
|
|
{
|
|
if (_first != 0)
|
|
{
|
|
while ((_selectObjectQueue.Count > 0))
|
|
{
|
|
ProcessObjectAndHandleErrors(_selectObjectQueue.Dequeue());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ((_selectObjectQueue.Count > 0))
|
|
{
|
|
int lenQueue = _selectObjectQueue.Count;
|
|
if (lenQueue > Skip)
|
|
{
|
|
ProcessObjectAndHandleErrors(_selectObjectQueue.Dequeue());
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_uniques != null)
|
|
{
|
|
foreach (UniquePSObjectHelper obj in _uniques)
|
|
{
|
|
if (obj.WrittenObject == null || obj.WrittenObject == AutomationNull.Value)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
WriteObject(obj.WrittenObject);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used only internally for select-object.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "This exception is internal and never thrown by any public API")]
|
|
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "This exception is internal and never thrown by any public API")]
|
|
[SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This exception is internal and never thrown by any public API")]
|
|
internal class SelectObjectException : SystemException
|
|
{
|
|
internal ErrorRecord ErrorRecord { get; }
|
|
|
|
internal SelectObjectException(ErrorRecord errorRecord)
|
|
{
|
|
ErrorRecord = errorRecord;
|
|
}
|
|
}
|
|
}
|