Functions/Helper/DynamicParam/New-ValentiaDynamicParamMulti.ps1

#Requires -Version 3.0

#-- function helper for Dynamic Param --#

<#
.SYNOPSIS
This cmdlet will return Dynamic param dictionary
 
.DESCRIPTION
You can use this cmdlet to define Dynamic Param
 
.NOTES
Author: guitrrapc
Created: 02/03/2014
 
.EXAMPLE
function Show-ValentiaDynamicParamMulti
{
    [CmdletBinding()]
    param(
        [parameter(position = 6)]
        $nyao
    )
     
    dynamicParam
    {
        $dynamicParams = (
            @{Mandatory = $true
              name = "hoge"
              Options = "hoge","piyo"
              position = 0
              Type = "System.String[]"
              validateSet = $true
              valueFromPipelineByPropertyName = $true},
               
              @{Mandatory = $true
              name = "foo"
              Options = 1,2,3,4,5
              position = 1
              Type = "System.Int32[]"
              validateSet = $true},
 
              @{DefaultValue = (4,2,5)
              Mandatory = $false
              name = "bar"
              Options = 1,2,3,4,5
              position = 2
              Type = "System.Int32[]"
              validateSet = $false}
        )
 
        $dynamic = New-ValentiaDynamicParamMulti -dynamicParams $dynamicParams
        return $dynamic
    }
 
    begin
    {
    }
    process
    {
        $PSBoundParameters.hoge
        $PSBoundParameters.foo
        if ($PSBoundParameters.ContainsKey('bar'))
        {
            $PSBoundParameters.bar
            $PSBoundParameters.bar.GetType().FullName
        }
        else
        {
            $bar = $dynamic.bar.Value
            $bar
            $bar.GetType().FullName
        }
    }
}
 
"Test 1 ---------------------"
Show-ValentiaDynamicParamMulti -hoge hoge -foo 1,2,3,4
"Test 2 ---------------------"
Show-ValentiaDynamicParamMulti -hoge piyo -foo 2 -bar 2
#>


function New-ValentiaDynamicParamMulti
{
    [CmdletBinding()]
    param
    (
        [parameter(mandatory = $true, position = 0, valueFromPipeline = 1, valueFromPipelineByPropertyName = 1)]
        [hashtable[]]$dynamicParams
    )

    begin
    {
        $dynamicParamLists = New-ValentiaDynamicParamList -dynamicParams $dynamicParams
        $dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    }

    process
    {
        foreach ($dynamicParamList in $dynamicParamLists)
        {
            # create attributes
            $attributes = New-Object System.Management.Automation.ParameterAttribute
            $attributes.ParameterSetName = "__AllParameterSets"
            (
                "helpMessage",
                "mandatory",
                "parameterSetName",
                "position",
                "valueFromPipeline",
                "valueFromPipelineByPropertyName",
                "valueFromRemainingArguments"
            ) `
            | %{
                if($dynamicParamList.$_)
                {
                    $attributes.$_ = $dynamicParamList.$_
                }
            }

            # create attributes Collection
            $attributesCollection = New-Object 'Collections.ObjectModel.Collection[System.Attribute]'
            $attributesCollection.Add($attributes)
        
            # create validation set
            if ($dynamicParamList.validateSet)
            {
                $validateSetAttributes = New-Object System.Management.Automation.ValidateSetAttribute $dynamicParamList.options
                $attributesCollection.Add($validateSetAttributes)
            }

            # Set default type or get from dynamicparam
            # Priority
            # 1. Type KV
            # 2. Type of DefaultValue
            # 3. System.Object[]
            if ($dynamicParamList.type)
            {
                $type = [Type]::GetType($dynamicParamList.Type)
            }
            else
            {
                if ($dynamicParamList.defaultValue)
                {
                    $DefaultValueType = $dynamicParamList.defaultValue.GetType().FullName
                    $type = [Type]::GetType($DefaultValueType)
                }
                else
                {
                    $type = [Type]::GetType("System.Object[]")
                }
            }

            if ($null -eq $type)
            {
                throw "type not defined or Null exception! Make sure you have set fullname for the type : '{0}'" -f $dynamicParamList.type
            }

            # create RuntimeDefinedParameter
            $runtimeDefinedParameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter @($dynamicParamList.name, $type, $attributesCollection)

            # Set Default Value if passed
            if ($dynamicParamList.defaultValue)
            {
                if ($dynamicParamList.defaultValue -is $type)
                {
                    $runtimeDefinedParameter.Value = $dynamicParamList.defaultValue
                }
                elseif ($dynamicParamList.defaultValue -as $type)
                {
                    Write-Verbose ("Convert Type for ParameterName '{0}'. DefaultValue '{1}' convert from '{2}' to '{3}'" `
                        -f 
                            $dynamicParamList.name,
                            $dynamicParamLists.defaultValue,
                            $dynamicParamList.defaultValue.GetType().FullName,
                            $type)
                    $runtimeDefinedParameter.Value = $dynamicParamList.defaultValue -as $type
                }
                else
                {
                    throw "Cannot convert Type for ParameterName '{0}'. DefaultValue '{1}' could not convert from '{2}' to '{3}'" `
                        -f 
                            $dynamicParamList.name,
                            $dynamicParamLists.defaultValue,
                            $dynamicParamList.defaultValue.GetType().FullName,
                            $type
                }
            }

            # create Dictionary
            $dictionary.Add($dynamicParamList.name, $runtimeDefinedParameter)
        }
    }

    end
    {
        # return result
        return $dictionary
    }
}


<#
.SYNOPSIS
This cmdlet will return Dynamic param list item for dictionary
 
.DESCRIPTION
You can pass this list to DynamicPramMulti to create Dynamic Param
#>

function New-ValentiaDynamicParamList
{
    [CmdletBinding()]
    param
    (
        [parameter(
            mandatory = $true,
            position = 0,
            valueFromPipeline = 1,
            valueFromPipelineByPropertyName = 1)]
        [hashtable[]]
        $dynamicParams
    )

    begin
    {
        # create generic list
        $list = New-Object System.Collections.Generic.List[HashTable]

        # create key check array
        [string[]]$keyCheckInputItems = "helpMessage", "mandatory", "name", "parameterSetName", "options", "position", "valueFromPipeline", "valueFromPipelineByPropertyName", "valueFromRemainingArguments", "validateSet", "Type", "DefaultValue"

        $keyCheckList = New-Object System.Collections.Generic.List[String]
        $keyCheckList.AddRange($keyCheckInputItems)

        # sort dynamicParams hashtable by position
        $newDynamicParams = Sort-ValentiaDynamicParamHashTable -dynamicParams $dynamicParams
    }

    process
    {
        foreach ($dynamicParam in $newDynamicParams)
        {
            $invalidParamter = $dynamicParam.Keys | where {$_ -notin $keyCheckList}
            if ($($invalidParamter).count -ne 0)
            {
                throw ("Invalid parameter '{0}' found. Please use parameter from '{1}'" -f $invalidParamter, ("$keyCheckInputItems" -replace " "," ,"))
            }
            else
            {
                if (-not $dynamicParam.Keys.contains("name"))
                {
                    throw ("You must specify mandatory parameter '{0}' to hashtable key." -f "name")
                }
                elseif (-not $dynamicParam.Keys.contains("options"))
                {
                    throw ("You must specify mandatory parameter '{0}' to hashtable key." -f "options")
                }
                else
                {
                    $list.Add($dynamicParam)
                }
            }
        }
    }

    end
    {
        return $list
    }
}


function Sort-ValentiaDynamicParamHashTable
{
    [CmdletBinding()]
    param
    (
        [parameter(
            mandatory = $true,
            position = 0,
            valueFromPipeline = 1,
            valueFromPipelineByPropertyName = 1)]
        [hashtable[]]
        $dynamicParams
    )

    begin
    {
        # get max number of position for null position item
        $max = ($dynamicParams.position | measure -Maximum).Maximum
    }

    process
    {
        # output PSCustomObject[Name<SortedPosition>,Value<DynamicParamHashTable>]. posision is now sorted.
        $h = $dynamicParams `
        | %{
            $history = New-Object System.Collections.Generic.List[int]
            $hash = @{}
            
            # temp posision for null item. This set as (max + number of collection items)
            $num = $max + $parameters.Length
        }{
            Write-Verbose ("position is '{0}'." -f $position)
            $position = $_.position
            
            #region null check
            if ($null -eq $position)
            {
                Write-Verbose ("position is '{0}'. set current max index '{1}'" -f $position, $num)
                $position = $num
                $num++
            }
            #endregion

            #region dupricate check
            if ($position -notin $history)
            {
                Write-Verbose ("position '{0}' not found in '{1}'. Add to history." -f $position, ($history -join ", "))
                $history.Add($position)
            }
            else
            {
                $changed = $false
                while ($position -in $history)
                {
                    Write-Verbose ("position '{0}' found in '{1}'. Start increment." -f $position, ($history -join ", "))
                    $position++
                    $changed = $true
                }
                Write-Verbose (" incremented position '{0}' not found in '{1}'. Add to history." -f $position, ($history -join ", "))
                if ($changed){$history.Add($position)}
            }
            #endregion

            #region set temp hash
            Write-Verbose ("Set position '{0}' as name of temp hash." -f $position)
            $hash."$position" = $_
            #endregion
        }{[PSCustomObject]$hash}
    }

    end
    {
        # get index for each object
        $index = [int[]](($h | Get-Member -MemberType NoteProperty).Name) | sort
        
        # return sorted hash order by index
        return $index | %{$h.$_}
    }
}