# encoding: ascii
# api: powershell
# title: Convert-TextObject.ps1
# description: From Windows PowerShell Cookbook (O’Reilly) by Lee Holmes
# version: 0.1
# type: script
# author: Lee Holmes
# license: CC0
# x-poshcode-id: 2134
# x-archived: 2017-04-30T11:43:21
# x-published: 2011-09-09T21:40:00
#
#
##############################################################################
##
## Convert-TextObject
##
## From Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################

<#

.SYNOPSIS

Convert a simple string into a custom PowerShell object.

.EXAMPLE

"Hello World" | Convert-TextObject
Generates an Object with "P1=Hello" and "P2=World"

.EXAMPLE

"Hello World" | Convert-TextObject -Delimiter "ll"
Generates an Object with "P1=He" and "P2=o World"

.EXAMPLE

"Hello World" | Convert-TextObject -Pattern "He(ll.*o)r(ld)"
Generates an Object with "P1=llo Wo" and "P2=ld"

.EXAMPLE

"Hello World" | Convert-TextObject -PropertyName FirstWord,SecondWord
Generates an Object with "FirstWord=Hello" and "SecondWord=World

.EXAMPLE

"123 456" | Convert-TextObject -PropertyType $([string],[int])
Generates an Object with "Property1=123" and "Property2=456"
The second property is an integer, as opposed to a string

.EXAMPLE

PS >$ipAddress = (ipconfig | Convert-TextObject -Delim ": ")[2].P2
PS >$ipAddress
192.168.1.104

#>

[CmdletBinding(DefaultParameterSetName = "ByDelimiter")]
param(
    ## If specified, gives the .NET Regular Expression with which to
    ## split the string. The script generates properties for the
    ## resulting object out of the elements resulting from this split.
    ## If not specified, defaults to splitting on the maximum amount
    ## of whitespace: "\s+", as long as Pattern is not
    ## specified either.
    [Parameter(ParameterSetName = "ByDelimiter", Position = 0)]
    [string] $Delimiter = "\s+",

    ## If specified, gives the .NET Regular Expression with which to
    ## parse the string. The script generates properties for the
    ## resulting object out of the groups captured by this regular
    ## expression.
    [Parameter(Mandatory = $true,
        ParameterSetName = "ByPattern",
        Position = 0)]
    [string] $Pattern,

    ## If specified, the script will pair the names from this object
    ## definition with the elements from the parsed string.  If not
    ## specified (or the generated object contains more properties
    ## than you specify,) the script uses property names in the
    ## pattern of P1,P2,...,PN
    [Parameter(Position = 1)]
    [Alias("PN")]
    [string[]] $PropertyName = @(),

    ## If specified, the script will pair the types from this list with
    ## the properties from the parsed string.  If not specified (or the
    ## generated object contains more properties than you specify,) the
    ## script sets the properties to be of type [string]
    [Parameter(Position = 2)]
    [Alias("PT")]
    [type[]] $PropertyType = @(),

    ## The input object to process
    [Parameter(ValueFromPipeline = $true)]
    [string] $InputObject
)

begin {
    Set-StrictMode -Version Latest
}

process {
    $returnObject = New-Object PSObject

    $matches = $null
    $matchCount = 0

    if($PSBoundParameters["Pattern"])
    {
        ## Verify that the input contains the pattern
        ## Populates the matches variable by default
        if(-not ($InputObject -match $pattern))
        {
            return
        }

        $matchCount = $matches.Count
    $startIndex = 1
    }
    else
    {
        ## Verify that the input contains the delimiter
        if(-not ($InputObject -match $delimiter))
        {
            return
        }

        ## If so, split the input on that delimiter
        $matches = $InputObject -split $delimiter
        $matchCount = $matches.Length
        $startIndex = 0
    }

    ## Go through all of the matches, and add them as notes to the output
    ## object.
    for($counter = $startIndex; $counter -lt $matchCount; $counter++)
    {
        $currentPropertyName = "P$($counter - $startIndex + 1)"
        $currentPropertyType = [string]

        ## Get the property name
        if($counter -lt $propertyName.Length)
        {
            if($propertyName[$counter])
            {
                $currentPropertyName = $propertyName[$counter - 1]
            }
        }

        ## Get the property value
        if($counter -lt $propertyType.Length)
        {
            if($propertyType[$counter])
            {
                $currentPropertyType = $propertyType[$counter - 1]
            }
        }

        Add-Member -InputObject $returnObject NoteProperty `
            -Name $currentPropertyName `
            -Value ($matches[$counter].Trim() -as $currentPropertyType)
    }

    $returnObject
}
