Public/Test-SpdxLicenseExpression.ps1

function Test-SpdxLicenseExpression
{
    <#
        .SYNOPSIS
            Tests an SPDX License expression.
        .DESCRIPTION
            Tests an SPDX License expression.
        .PARAMETER Expression
            A string array containing the license expression to test.
        .PARAMETER OsiApproved
            A switch specifying to test that the expression only contains Open Source Initiative approved licenses.
        .PARAMETER FsfLibre
            A switch specifying to test that the expression only contains Free Software Foundation Free/Libre licenses.
        .PARAMETER FsfOrOsi
            A switch specifying to test that the expression only contains Free Software Foundation Free/Libre OR Open Source Initiative approved licenses.
        .PARAMETER FsfAndOsi
            A switch specifying to test that the expression only contains Free Software Foundation Free/Libre AND Open Source Initiative approved licenses (equivalent to specify FsfLibre and OsiApproved).
        .PARAMETER ExcludeDeprecated
            A switch specifying to test that the expression only contains not deprecated licenses and exceptions.
        .INPUTS
            System.String
            This CmdLet accepts a String containing the expression to test.
        .OUTPUTS
            System.Boolean
            This cmdlet returns a Boolean confirming if the expression is valid or not.
        .NOTES
        .LINK
            Get-SpdxLicense
        .LINK
            Get-SpdxLicenseException
        .LINK
            https://spdx.org/licenses/
        .LINK
            https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60
        .LINK
            https://opensource.org/licenses/
        .LINK
            https://www.fsf.org/licensing/
    #>

    [CmdLetBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("e")]
        [string] $Expression,
        [Parameter(Mandatory = $false)]
        [Alias("Osi")]
        [switch] $OsiApproved,
        [Parameter(Mandatory = $false)]
        [Alias("FsfFree", "Fsf")]
        [switch] $FsfLibre,
        [Parameter(Mandatory = $false)]
        [switch] $FsfOrOsi,
        [Parameter(Mandatory = $false)]
        [switch] $FsfAndOsi,
        [Parameter(Mandatory = $false)]
        [Alias("xd")]
        [switch] $ExcludeDeprecated
    )

    try 
    {
        $Return = $true
        if ($Expression.Contains(" "))
        {
            $Return = $false
            Write-Error -Message $global:LocalizedData.TestSpdxLicenseExpression.Error.DoubleWhiteSpace.Message -Category SyntaxError -CategoryActivity $MyInvocation.MyCommand -TargetName $Expression -TargetType $global:LocalizedData.TestSpdxLicenseExpression.Error.DoubleWhiteSpace.Target -Exception SyntaxErrorException
        }
        else
        {
            $OpenParenthesis = ($Expression.ToCharArray() | Where-Object { $_ -eq '(' }).Count
            $CloseParenthesis = ($Expression.ToCharArray() | Where-Object { $_ -eq ')' }).Count
            if ($OpenParenthesis -ne $CloseParenthesis)
            {
                $Return = $false
                Write-Error -Message ($global:LocalizedData.TestSpdxLicenseExpression.Error.Parenthesis.Message -f $OpenParenthesis, $CloseParenthesis) -Category SyntaxError -CategoryActivity $MyInvocation.MyCommand -TargetName $Expression -TargetType $global:LocalizedData.TestSpdxLicenseExpression.Error.Parenthesis.Target -Exception SyntaxErrorException
            }
            else
            {
                $Licenses = Get-SpdxLicense
                $Exceptions = Get-SpdxLicenseException
                Write-Verbose ($global:LocalizedData.TestSpdxLicenseExpression.Verbose.Parsing -f $Expression)
                $Terms = $Expression.Replace("(", "").Replace(")", "") -split " "

                $RecognizedLicenses = @()
                $RecognizedExceptions = @()
                for ($i = 0; $Terms[$i]; $i++)
                {
                    Write-Verbose ($global:LocalizedData.TestSpdxLicenseExpression.Verbose.Member -f $i, $Terms[$i])
                    if ($Terms[$i] -in "AND", "OR")
                    {
                        Write-Verbose ($global:LocalizedData.TestSpdxLicenseExpression.Verbose.Operator -f $Terms[$i].ToUpper())
                        $Preceding = $Terms[$i - 1].Replace("+", "")
                        $Following = $Terms[$i + 1].Replace("+", "")
                        if (($Preceding -notin $Licenses.licenseId) -or ($Following -notin $Licenses.licenseId))
                        {
                            $Return = $false
                            Write-Error -Message $global:LocalizedData.TestSpdxLicenseExpression.Error.NotALicense.MessageOperator -Category SyntaxError -CategoryActivity $MyInvocation.MyCommand -TargetName "$($Preceding) - $($Following)" -TargetType $global:LocalizedData.TestSpdxLicenseExpression.Error.NotALicense.Target -Exception SyntaxErrorException
                        }
                    }
                    elseif ($Terms[$i] -eq "WITH")
                    {
                        Write-Verbose ($global:LocalizedData.TestSpdxLicenseExpression.Verbose.Operator -f $Terms[$i].ToUpper())
                        if ($Terms[$i + 1] -notin $Exceptions.licenseExceptionId)
                        {
                            $Return = $false
                            Write-Error -Message $global:LocalizedData.TestSpdxLicenseExpression.Error.NotAnException.Message -Category SyntaxError -CategoryActivity $MyInvocation.MyCommand -TargetName "$($Terms[$i + 1])" -TargetType $global:LocalizedData.TestSpdxLicenseExpression.Error.NotAnException.Target -Exception SyntaxErrorException
                        }
                    }
                    elseif ($Terms[$i] -like "*+")
                    {
                        $License = $Terms[$i].Replace("+", "")
                        if ($License -in $Licenses.licenseId)
                        {
                            Write-Verbose ($global:LocalizedData.TestSpdxLicenseExpression.Verbose.License -f $License, ($Licenses | Where-Object { $_.licenseId -eq $License }).name)
                            $RecognizedLicenses += $License
                        }
                        else
                        {
                            $Return = $false
                            Write-Error -Message $global:LocalizedData.TestSpdxLicenseExpression.Error.NotALicense.MessageSuffix -Category SyntaxError -CategoryActivity $MyInvocation.MyCommand -TargetName $License -TargetType $global:LocalizedData.TestSpdxLicenseExpression.Error.NotALicense.Target -Exception SyntaxErrorException
                        }
                    }
                    elseif ($Terms[$i] -in $Licenses.licenseId)
                    {
                        Write-Verbose ($global:LocalizedData.TestSpdxLicenseExpression.Verbose.License -f $Terms[$i], ($Licenses | Where-Object { $_.licenseId -eq $Terms[$i] }).name)
                        $RecognizedLicenses += $Terms[$i]
                    }
                    elseif ($Terms[$i] -in $Exceptions.licenseExceptionId)
                    {
                        Write-Verbose ($global:LocalizedData.TestSpdxLicenseExpression.Verbose.Exception -f $Terms[$i], ($Exceptions | Where-Object { $_.licenseExceptionId -eq $Terms[$i] }).name)
                        $RecognizedExceptions += $Terms[$i]
                    }
                    else
                    {
                        $Return = $false
                        Write-Error -Message ($global:LocalizedData.TestSpdxLicenseExpression.Error.UnidentifiedTerm.Message -f $Terms[$i]) -Category SyntaxError -CategoryActivity $MyInvocation.MyCommand -TargetName $Terms[$i] -TargetType $global:LocalizedData.TestSpdxLicenseExpression.Error.UnidentifiedTerm.Target -Exception SyntaxErrorException
                    }
                }

                if ($RecognizedLicenses)
                {
                    if (($FsfLibre -and $OsiApproved) -or $FsfAndOsi) { $TargetLicences = Get-SpdxLicense -OsiApproved -FsfLibre -ExcludeDeprecated:$ExcludeDeprecated }
                    elseif ($FsfOrOsi) { $TargetLicences = (Get-SpdxLicense -OsiApproved -ExcludeDeprecated:$ExcludeDeprecated) + (Get-SpdxLicense -FsfLibre -ExcludeDeprecated:$ExcludeDeprecated) }
                    else { $TargetLicences = Get-SpdxLicense -OsiApproved:$OsiApproved -FsfLibre:$FsfLibre -ExcludeDeprecated:$ExcludeDeprecated }

                    $RecognizedLicenses | ForEach-Object {
                        if ($_ -notin $TargetLicences.licenseId)
                        {
                            $Return = $false
                            Write-Error -Message ($global:LocalizedData.TestSpdxLicenseExpression.Error.LicenseCriteria.Message -f $_) -Category InvalidData -CategoryActivity $MyInvocation.MyCommand -TargetName $_ -TargetType $global:LocalizedData.TestSpdxLicenseExpression.Error.LicenseCriteria.Target -Exception InvalidDataException
                        }
                    }
                }
                if ($RecognizedExceptions)
                {
                    $TargetExceptions = Get-SpdxLicenseException -ExcludeDeprecated:$ExcludeDeprecated

                    $RecognizedExceptions | ForEach-Object {
                        if ($_ -notin $TargetExceptions.licenseExceptionId)
                        {
                            $Return = $false
                            Write-Error -Message ($global:LocalizedData.TestSpdxLicenseExpression.Error.ExceptionCriteria.Message -f $_) -Category InvalidData -CategoryActivity $MyInvocation.MyCommand -TargetName $_ -TargetType $global:LocalizedData.TestSpdxLicenseExpression.Error.ExceptionCriteria.Target -Exception InvalidDataException
                        }
                    }          
                }
            }
        }
    }
    catch
    {
        $Return = $false
        Write-Error $_
    }
    finally
    {
        $Return
    }
}