/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Numerics;
using System.Threading;
using Debug = System.Management.Automation.Diagnostics;
using System.Security.Cryptography;
namespace Microsoft.PowerShell.Commands
{
///
/// This class implements get-random cmdlet.
///
///
[Cmdlet(VerbsCommon.Get, "Random", DefaultParameterSetName = GetRandomCommand.RandomNumberParameterSet,
HelpUri = "http://go.microsoft.com/fwlink/?LinkID=113446", RemotingCapability = RemotingCapability.None)]
[OutputType(typeof(Int32), typeof(Int64), typeof(Double))]
public class GetRandomCommand : PSCmdlet
{
#region Parameter set handling
private const string RandomNumberParameterSet = "RandomNumberParameterSet";
private const string RandomListItemParameterSet = "RandomListItemParameterSet";
private enum MyParameterSet
{
Unknown,
RandomNumber,
RandomListItem
}
private MyParameterSet _effectiveParameterSet;
private MyParameterSet EffectiveParameterSet
{
get
{
// cache MyParameterSet enum instead of doing string comparison every time
if (_effectiveParameterSet == MyParameterSet.Unknown)
{
if ((this.MyInvocation.ExpectingInput) && (this.Maximum == null) && (this.Minimum == null))
{
_effectiveParameterSet = MyParameterSet.RandomListItem;
}
else if (ParameterSetName.Equals(GetRandomCommand.RandomListItemParameterSet, StringComparison.OrdinalIgnoreCase))
{
_effectiveParameterSet = MyParameterSet.RandomListItem;
}
else if (this.ParameterSetName.Equals(GetRandomCommand.RandomNumberParameterSet, StringComparison.OrdinalIgnoreCase))
{
if ((this.Maximum != null) && (this.Maximum.GetType().IsArray))
{
this.InputObject = (object[])this.Maximum;
_effectiveParameterSet = MyParameterSet.RandomListItem;
}
else
{
_effectiveParameterSet = MyParameterSet.RandomNumber;
}
}
else
{
Debug.Assert(false, "Unrecognized parameter set");
}
}
return _effectiveParameterSet;
}
}
#endregion Parameter set handling
#region Error handling
private void ThrowMinGreaterThanOrEqualMax(object min, object max)
{
if (min == null)
{
throw PSTraceSource.NewArgumentNullException("min");
}
if (max == null)
{
throw PSTraceSource.NewArgumentNullException("max");
}
ErrorRecord errorRecord = new ErrorRecord(
new ArgumentException(String.Format(
CultureInfo.InvariantCulture, GetRandomCommandStrings.MinGreaterThanOrEqualMax, min, max)),
"MinGreaterThanOrEqualMax",
ErrorCategory.InvalidArgument,
null);
this.ThrowTerminatingError(errorRecord);
}
#endregion
#region Random generator state
private static ReaderWriterLockSlim s_runspaceGeneratorMapLock = new ReaderWriterLockSlim();
// 1-to-1 mapping of runspaces and random number generators
private static Dictionary s_runspaceGeneratorMap = new Dictionary();
private static void CurrentRunspace_StateChanged(object sender, RunspaceStateEventArgs e)
{
switch (e.RunspaceStateInfo.State)
{
case RunspaceState.Broken:
case RunspaceState.Closed:
try
{
GetRandomCommand.s_runspaceGeneratorMapLock.EnterWriteLock();
GetRandomCommand.s_runspaceGeneratorMap.Remove(((Runspace)sender).InstanceId);
}
finally
{
GetRandomCommand.s_runspaceGeneratorMapLock.ExitWriteLock();
}
break;
}
}
private PolymorphicRandomNumberGenerator _generator;
///
/// Gets and sets generator associated with the current runspace
///
private PolymorphicRandomNumberGenerator Generator
{
get
{
if (_generator == null)
{
Guid runspaceId = this.Context.CurrentRunspace.InstanceId;
bool needToInitialize = false;
try
{
GetRandomCommand.s_runspaceGeneratorMapLock.EnterReadLock();
needToInitialize = !GetRandomCommand.s_runspaceGeneratorMap.TryGetValue(runspaceId, out _generator);
}
finally
{
GetRandomCommand.s_runspaceGeneratorMapLock.ExitReadLock();
}
if (needToInitialize)
{
this.Generator = new PolymorphicRandomNumberGenerator();
}
}
return _generator;
}
set
{
_generator = value;
Runspace myRunspace = this.Context.CurrentRunspace;
try
{
GetRandomCommand.s_runspaceGeneratorMapLock.EnterWriteLock();
if (!GetRandomCommand.s_runspaceGeneratorMap.ContainsKey(myRunspace.InstanceId))
{
// make sure we won't leave the generator around after runspace exits
myRunspace.StateChanged += CurrentRunspace_StateChanged;
}
GetRandomCommand.s_runspaceGeneratorMap[myRunspace.InstanceId] = _generator;
}
finally
{
GetRandomCommand.s_runspaceGeneratorMapLock.ExitWriteLock();
}
}
}
#endregion
#region Common parameters
///
/// Seed used to reinitialize random numbers generator
///
[Parameter]
[ValidateNotNull]
public int? SetSeed { get; set; }
#endregion Common parameters
#region Parameters for RandomNumberParameterSet
///
/// Maximum number to generate
///
[Parameter(ParameterSetName = RandomNumberParameterSet, Position = 0)]
public object Maximum { get; set; }
///
/// Minimum number to generate
///
[Parameter(ParameterSetName = RandomNumberParameterSet)]
public object Minimum { get; set; }
private bool IsInt(object o)
{
if (o == null || o is int)
{
return true;
}
return false;
}
private bool IsInt64(object o)
{
if (o == null || o is Int64)
{
return true;
}
return false;
}
private object ProcessOperand(object o)
{
if (o == null)
{
return null;
}
PSObject pso = PSObject.AsPSObject(o);
object baseObject = pso.BaseObject;
if (baseObject is string)
{
// The type argument passed in does not decide the number type we want to convert to. ScanNumber will return
// int/long/double based on the string form number passed in.
baseObject = System.Management.Automation.Language.Parser.ScanNumber((string)baseObject, typeof(int));
}
return baseObject;
}
private double ConvertToDouble(object o, double defaultIfNull)
{
if (o == null)
{
return defaultIfNull;
}
double result = (double)LanguagePrimitives.ConvertTo(o, typeof(double), CultureInfo.InvariantCulture);
return result;
}
#endregion
#region Parameters and variables for RandomListItemParameterSet
private List