Gumby.String.psm1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
using module Debug

<#
.SYNOPSIS
If necessary, abbreviates a string by replacing part of it with the ellipsis.
 
.PARAMETER Text
String to abbreviate.
 
.PARAMETER MaxLength
Maximum length of abbreviated string.
 
.PARAMETER Ellipsis
Ellipsis string (optional, defaults to "...").
 
.OUTPUTS
Abbreviated string.
#>

function Abbreviate([string] $Text, [uint32] $MaxLength, [string] $Ellipsis = "...") {
    if ($Text.Length -le $MaxLength) {
        return $Text
    } else {
        # assert ($MaxLength -gt $Ellipsis.length)
        # PS converts floats to ints by rounding, not by truncation of the fractional part

        # 01234567890123456789
        # abcdefghij
        #
        # p q
        # | |
        # l v v p q
        # 9 abc... hij 3 7 x = (9-3)/2 = 3 p = round(x, 0, 1) q = l - trunc(x)
        # 8 abc... ij 3 8 x = (8-3)/2 = 2.5 p = round(x, 0, 1) q = l - trunc(x)
        # 7 ab... ij 2 8 x = (7-3)/2 = 2 p = round(x, 0, 1) q = l - trunc(x)
        # 6 ab... j 2 9 x = (6-3)/2 = 1.5 p = round(x, 0, 1) q = l - trunc(x)
        # 5 a... j 1 9 x = (5-3)/2 = 1 p = round(x, 0, 1) q = l - trunc(x)
        # 4 a... 1 10 x = (4-3)/2 = 0.5

        $x = ($MaxLength - $Ellipsis.Length ) / 2
        return $Text.Substring(0, [Math]::Round($x, 0, 1)) + $Ellipsis + $Text.Substring($Text.Length - [Math]::Truncate($x))
    }
}

<#
.SYNOPSIS
Ensures a string has the specified length.
 
.PARAMETER Text
Text to ensure length of.
 
.PARAMETER Length
Desired length of the text.
 
.PARAMETER FillChar
Character to pad the text with if it is shorter than the specified length (optional, defaults to
' ').
 
.OUTPUTS
Text with the specified length.
#>

function EnsureStringLength([string] $Text, [int] $Length, [string] $FillChar = ' ') {
    if ($Text.Length -lt $Length) {
        return $Text.PadRight($Length, $FillChar)
    } else {
        return $Text.Substring(0, $Length)
    }
}

<#
.SYNOPSIS
Expand macro references ('$MacroName') with macro values.
 
.PARAMETER Text
String containing macro references.
 
.PARAMETER Macros
Hashtable with macro names as keys and macro values as values.
 
.OUTPUTS
String in which macro references have been replaced with macro values.
#>

function ExpandMacros($Text, $Macros) {
    [regex] $macroPattern = '(?<!`)\$(?<MacroName>[a-zA-Z_]\w*)'
    return $macroPattern.Replace($Text, {
        param ($match)
        $macroName = $match.Groups['MacroName'].Value
        if ($Macros.ContainsKey($macroName)) {
            return $Macros[$macroName]
        } else {
            return $match.Value
        }
    })
}

<#
.SYNOPSIS
Normalizes a string by removing whitespace characters from its beginning and end, and by reducing
whitespace character sequences in the middle of the string to a single whitespace character.
 
.PARAMETER Text
String to normalize.
 
.OUTPUTS
Normalized string.
#>

function NormalizeWhitespace([string] $Text) {
    $([regex] '\s+').Replace($Text.Trim().Replace('`r`n', ' ').Replace('`r', ' ').Replace('`n', ' '), ' ')
}

function SpaceWords([string[]] $Words, [int] $Width, [string] $SpacingChar = ' ') {
    # The code below is a sketch that only works correctly for 2 words. A proper version needs
    # to provide heterogenous spacing lengths according to the integer divisibility of the
    # total spacing length.

    assert ($Words.Count -ge 2)
    assert ($SpacingChar.Length -eq 1)

    $totalSpacingLength = $Width
    foreach ($word in $Words) { $totalSpacingLength -= $word.Length }

    assert ($totalSpacingLength -ge 0)

    $spacingLength = $totalSpacingLength / ($Words.Count - 1)

    $sb = [Text.StringBuilder]::new($Width)
    for ($i = 0; $i -lt $Words.Count; ++$i) {
        $sb.Append($Words[$i]) | Out-Null
        if ($i -lt $Words.Count - 1) { $sb.Append($SpacingChar * $spacingLength) | Out-Null }
    }

    return $sb.ToString()
}

<#
.SYNOPSIS
Splits a string of comma-separated, quoted sub-strings (e.g. a line from a CSV file).
 
.PARAMETER Line
String to split.
 
.OUTPUTS
List of substrings.
#>

function SplitCSVLine([string] $Line) {
    [bool] $inString = $false;

    $fields = @()
    $accu = ""

    for ($i = 0; $i -lt $Line.Length; $i++) {
        switch ($Line[$i]) {
            #{$_ -eq '"' -and -not $inString} {$inString = $true; break}
            #{$_ -eq '"' -and $inString} {$inString = $false; break}
            {$_ -eq '"'} {$inString = !$inString; break}
            {($_ -in ',', ';') -and -not $inString} {$fields += $accu; $accu = ""; break}
            default {$accu += $_}
        }
    }

    $fields += $accu
    return $fields
}

<#
.SYNOPSIS
Integrates an array of names and an array of values into a hash table.
 
.PARAMETER Names
Array of names. The items therein become the hash table keys.
 
.PARAMETER Values
Array of values. The items therein become the hash table values.
 
.OUTPUTS
Hash table.
#>

function Zip($Names, $Values) {
    if ($Names.Length -ne $Values.Length) { throw "Number of names and values does not line up." }

    $hash = @{}

    for ($i = 0; $i -lt $Names.Length; $i++) {
        $hash.Add($Names[$i], $Values[$i])
    }

    return $hash
}