CalculateWidths   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 15
c 0
b 0
f 0
lcom 1
cbo 1
dl 0
loc 131
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A calculate() 0 26 3
A calculateLongestCell() 0 9 1
A calculateLongestWord() 0 9 1
A calculateColumnWidths() 0 17 5
A getShortColumns() 0 6 1
A distributeLongColumns() 0 20 2
A longestWordLength() 0 8 1
1
<?php
2
namespace Consolidation\OutputFormatters\Transformations\Wrap;
3
4
use Symfony\Component\Console\Helper\TableStyle;
5
6
/**
7
 * Calculate column widths for table cells.
8
 *
9
 * Influenced by Drush and webmozart/console.
10
 */
11
class CalculateWidths
12
{
13
    public function __construct()
14
    {
15
    }
16
17
    /**
18
     * Given the total amount of available space, and the width of
19
     * the columns to place, calculate the optimum column widths to use.
20
     */
21
    public function calculate($availableWidth, ColumnWidths $dataWidths, ColumnWidths $minimumWidths)
22
    {
23
        // First, check to see if all columns will fit at their full widths.
24
        // If so, do no further calculations. (This may be redundant with
25
        // the short column width calculation.)
26
        if ($dataWidths->totalWidth() <= $availableWidth) {
27
            return $dataWidths->enforceMinimums($minimumWidths);
28
        }
29
30
        // Get the short columns first. If there are none, then distribute all
31
        // of the available width among the remaining columns.
32
        $shortColWidths = $this->getShortColumns($availableWidth, $dataWidths, $minimumWidths);
33
        if ($shortColWidths->isEmpty()) {
34
            return $this->distributeLongColumns($availableWidth, $dataWidths, $minimumWidths);
35
        }
36
37
        // If some short columns were removed, then account for the length
38
        // of the removed columns and make a recursive call (since the average
39
        // width may be higher now, if the removed columns were shorter in
40
        // length than the previous average).
41
        $availableWidth -= $shortColWidths->totalWidth();
42
        $remainingWidths = $dataWidths->removeColumns($shortColWidths->keys());
43
        $remainingColWidths = $this->calculate($availableWidth, $remainingWidths, $minimumWidths);
44
45
        return $shortColWidths->combine($remainingColWidths);
46
    }
47
48
    /**
49
     * Calculate the longest cell data from any row of each of the cells.
50
     */
51
    public function calculateLongestCell($rows)
52
    {
53
        return $this->calculateColumnWidths(
54
            $rows,
55
            function ($cell) {
56
                return strlen($cell);
57
            }
58
        );
59
    }
60
61
    /**
62
     * Calculate the longest word and longest line in the provided data.
63
     */
64
    public function calculateLongestWord($rows)
65
    {
66
        return $this->calculateColumnWidths(
67
            $rows,
68
            function ($cell) {
69
                return static::longestWordLength($cell);
70
            }
71
        );
72
    }
73
74
    protected function calculateColumnWidths($rows, callable $fn)
75
    {
76
        $widths = [];
77
78
        // Examine each row and find the longest line length and longest
79
        // word in each column.
80
        foreach ($rows as $rowkey => $row) {
81
            foreach ($row as $colkey => $cell) {
82
                $value = $fn($cell);
83
                if ((!isset($widths[$colkey]) || ($widths[$colkey] < $value))) {
84
                    $widths[$colkey] = $value;
85
                }
86
            }
87
        }
88
89
        return new ColumnWidths($widths);
90
    }
91
92
    /**
93
     * Return all of the columns whose longest line length is less than or
94
     * equal to the average width.
95
     */
96
    public function getShortColumns($availableWidth, ColumnWidths $dataWidths, ColumnWidths $minimumWidths)
97
    {
98
        $averageWidth = $dataWidths->averageWidth($availableWidth);
99
        $shortColWidths = $dataWidths->findShortColumns($averageWidth);
100
        return $shortColWidths->enforceMinimums($minimumWidths);
101
    }
102
103
    /**
104
     * Distribute the remainig space among the columns that were not
105
     * included in the list of "short" columns.
106
     */
107
    public function distributeLongColumns($availableWidth, ColumnWidths $dataWidths, ColumnWidths $minimumWidths)
108
    {
109
        // First distribute the remainder without regard to the minimum widths.
110
        $result = $dataWidths->distribute($availableWidth);
111
112
        // Find columns that are shorter than their minimum width.
113
        $undersized = $result->findUndersizedColumns($minimumWidths);
114
115
        // Nothing too small? Great, we're done!
116
        if ($undersized->isEmpty()) {
117
            return $result;
118
        }
119
120
        // Take out the columns that are too small and redistribute the rest.
121
        $availableWidth -= $undersized->totalWidth();
122
        $remaining = $dataWidths->removeColumns($undersized->keys());
123
        $distributeRemaining = $this->distributeLongColumns($availableWidth, $remaining, $minimumWidths);
124
125
        return $undersized->combine($distributeRemaining);
126
    }
127
128
    /**
129
     * Return the length of the longest word in the string.
130
     * @param string $str
131
     * @return int
132
     */
133
    protected static function longestWordLength($str)
134
    {
135
        $words = preg_split('#[ /-]#', $str);
136
        $lengths = array_map(function ($s) {
137
            return strlen($s);
138
        }, $words);
139
        return max($lengths);
140
    }
141
}
142