classes/TreeNode.ps1
### ### Everything to represent tree nodes ### enum ColumnAlignment { Left Right Centered } class TreeNodeColumn { # Text to display in the column [String] $Text # Text lengh [int] $TextLength # Column alignment [ColumnAlignment] $Alignment # Children column alignment [ColumnAlignment] $ChildrenAlignment # Column length (which may include padding) [int] $ColumnLength } class TreeNode { ### ### Properties ### # Columns [TreeNodeColumn[]] $Columns # Label to display (i.e. columns formatted one a single line) hidden [String] $Label # List of children for this node hidden [System.Collections.Generic.List[TreeNode]] $Children # Depth at which the tree node lives hidden [int] $Depth # Alignment group for the tree node hidden [int] $AlignmentGroup # Alignment group for the children hidden [int] $ChildrenAlignmentGroup ### ### Constructor ### # Build a new tree node TreeNode([String[]] $Columns) { # Init properties $columnsCount = $null -ne $Columns ? $Columns.Count : 0 $this.Columns = $columnsCount -lt 1 ? @() : ($Columns | ForEach-Object { $trimmedText = $_.Trim() [TreeNodeColumn] @{ Text = $trimmedText TextLength = Get-FormattedStringLength $trimmedText Alignment = [ColumnAlignment]::Left ChildrenAlignment = [ColumnAlignment]::Left } }) $this.Label = '' $this.Children = New-Object 'System.Collections.Generic.List[TreeNode]' } ### ### Methods ### # Add a new child to the current node and return it [TreeNode] AddChild([String[]] $Columns) { if ($null -eq $Columns) { throw "Columns cannot be null" } # Create the child node $child = [TreeNode]::New($Columns) # Inherit the column alignment from the parent for ($i = 0; $i -lt [Math]::Min($Columns.Count, $this.Columns.Count); $i++) { $child.SetColumnAlignment($i, $this.Columns[$i].ChildrenAlignment) $child.SetChildrenColumnAlignment($i, $this.Columns[$i].ChildrenAlignment) } # Inherit alignment group $child.AlignmentGroup = $this.ChildrenAlignmentGroup $child.ChildrenAlignmentGroup = $this.ChildrenAlignmentGroup # Set depth $child.Depth = $this.Depth + 1 # Add the child node and return it $this.Children.Add($child) return $child } # Create a column if needed for a given index hidden [void] EnsureColumn([int] $ColumnIndex) { # If the index is out of bound, create new empty columns while ($ColumnIndex -ge $this.Columns.Count) { $this.Columns += [TreeNodeColumn] @{ Text = '' TextLength = 0 Alignment = [ColumnAlignment]::Left ChildrenAlignment = [ColumnAlignment]::Left } } } # Set column alignment for a given column [void] SetColumnAlignment([int] $ColumnIndex, [ColumnAlignment] $Alignment) { # Ignore negative index if ($ColumnIndex -lt 0) { return } # Set the alignment $this.EnsureColumn($ColumnIndex) $this.Columns[$ColumnIndex].Alignment = $Alignment } # Set children column alignment for a given column (i.e. this is the alignment that is inherited for children) [void] SetChildrenColumnAlignment([int] $ColumnIndex, [ColumnAlignment] $Alignment) { # Ignore negative index if ($ColumnIndex -lt 0) { return } # Set the alignment $this.EnsureColumn($ColumnIndex) $this.Columns[$ColumnIndex].ChildrenAlignment = $Alignment } # Recursively format the label of every column-based node in the tree hidden [void] FormatChildren([int] $SpacesBetweenColumns) { # Format the children that have columns $this.Children | Where-Object { $_.Columns.Count -gt 0 } | ForEach-Object { $node = $_ $node.Label = (@(0..($node.Columns.Count - 1) | ForEach-Object { $columnIndex = $_ $column = $node.Columns[$columnIndex] # Pad the column $invisibleCharacters = $column.Text.Length - $column.TextLength switch ($column.Alignment) { 'Left' { $column.Text.PadRight($column.ColumnLength + $invisibleCharacters, ' ') } 'Right' { $column.Text.PadLeft($column.ColumnLength + $invisibleCharacters, ' ') } 'Centered' { $col = $column.Text $col = $col.PadRight(($column.ColumnLength - $column.TextLength) / 2 + $column.TextLength + $invisibleCharacters, ' ') $col = $col.PadLeft($column.ColumnLength + $invisibleCharacters, ' ') $col } } }) -Join (' ' * $SpacesBetweenColumns)).TrimEnd() } # Continue recursively $this.Children | ForEach-Object { $_.FormatChildren($SpacesBetweenColumns) } } } |