annotate vendor/consolidation/output-formatters/src/Transformations/Wrap/ColumnWidths.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2 namespace Consolidation\OutputFormatters\Transformations\Wrap;
Chris@0 3
Chris@0 4 use Symfony\Component\Console\Helper\TableStyle;
Chris@0 5
Chris@0 6 /**
Chris@0 7 * Calculate the width of data in table cells in preparation for word wrapping.
Chris@0 8 */
Chris@0 9 class ColumnWidths
Chris@0 10 {
Chris@0 11 protected $widths;
Chris@0 12
Chris@0 13 public function __construct($widths = [])
Chris@0 14 {
Chris@0 15 $this->widths = $widths;
Chris@0 16 }
Chris@0 17
Chris@0 18 public function paddingSpace(
Chris@0 19 $paddingInEachCell,
Chris@0 20 $extraPaddingAtEndOfLine = 0,
Chris@0 21 $extraPaddingAtBeginningOfLine = 0
Chris@0 22 ) {
Chris@0 23 return ($extraPaddingAtBeginningOfLine + $extraPaddingAtEndOfLine + (count($this->widths) * $paddingInEachCell));
Chris@0 24 }
Chris@0 25
Chris@0 26 /**
Chris@0 27 * Find all of the columns that are shorter than the specified threshold.
Chris@0 28 */
Chris@0 29 public function findShortColumns($thresholdWidth)
Chris@0 30 {
Chris@0 31 $thresholdWidths = array_fill_keys(array_keys($this->widths), $thresholdWidth);
Chris@0 32
Chris@0 33 return $this->findColumnsUnderThreshold($thresholdWidths);
Chris@0 34 }
Chris@0 35
Chris@0 36 /**
Chris@0 37 * Find all of the columns that are shorter than the corresponding minimum widths.
Chris@0 38 */
Chris@0 39 public function findUndersizedColumns($minimumWidths)
Chris@0 40 {
Chris@0 41 return $this->findColumnsUnderThreshold($minimumWidths->widths());
Chris@0 42 }
Chris@0 43
Chris@0 44 protected function findColumnsUnderThreshold(array $thresholdWidths)
Chris@0 45 {
Chris@0 46 $shortColWidths = [];
Chris@0 47 foreach ($this->widths as $key => $maxLength) {
Chris@0 48 if (isset($thresholdWidths[$key]) && ($maxLength <= $thresholdWidths[$key])) {
Chris@0 49 $shortColWidths[$key] = $maxLength;
Chris@0 50 }
Chris@0 51 }
Chris@0 52
Chris@0 53 return new ColumnWidths($shortColWidths);
Chris@0 54 }
Chris@0 55
Chris@0 56 /**
Chris@0 57 * If the widths specified by this object do not fit within the
Chris@0 58 * provided avaiable width, then reduce them all proportionally.
Chris@0 59 */
Chris@0 60 public function adjustMinimumWidths($availableWidth, $dataCellWidths)
Chris@0 61 {
Chris@0 62 $result = $this->selectColumns($dataCellWidths->keys());
Chris@0 63 if ($result->isEmpty()) {
Chris@0 64 return $result;
Chris@0 65 }
Chris@0 66 $numberOfColumns = $dataCellWidths->count();
Chris@0 67
Chris@0 68 // How many unspecified columns are there?
Chris@0 69 $unspecifiedColumns = $numberOfColumns - $result->count();
Chris@0 70 $averageWidth = $this->averageWidth($availableWidth);
Chris@0 71
Chris@0 72 // Reserve some space for the columns that have no minimum.
Chris@0 73 // Make sure they collectively get at least half of the average
Chris@0 74 // width for each column. Or should it be a quarter?
Chris@0 75 $reservedSpacePerColumn = ($averageWidth / 2);
Chris@0 76 $reservedSpace = $reservedSpacePerColumn * $unspecifiedColumns;
Chris@0 77
Chris@0 78 // Calculate how much of the available space is remaining for use by
Chris@0 79 // the minimum column widths after the reserved space is accounted for.
Chris@0 80 $remainingAvailable = $availableWidth - $reservedSpace;
Chris@0 81
Chris@0 82 // Don't do anything if our widths fit inside the available widths.
Chris@0 83 if ($result->totalWidth() <= $remainingAvailable) {
Chris@0 84 return $result;
Chris@0 85 }
Chris@0 86
Chris@0 87 // Shrink the minimum widths if the table is too compressed.
Chris@0 88 return $result->distribute($remainingAvailable);
Chris@0 89 }
Chris@0 90
Chris@0 91 /**
Chris@0 92 * Return proportional weights
Chris@0 93 */
Chris@0 94 public function distribute($availableWidth)
Chris@0 95 {
Chris@0 96 $result = [];
Chris@0 97 $totalWidth = $this->totalWidth();
Chris@0 98 $lastColumn = $this->lastColumn();
Chris@0 99 $widths = $this->widths();
Chris@0 100
Chris@0 101 // Take off the last column, and calculate proportional weights
Chris@0 102 // for the first N-1 columns.
Chris@0 103 array_pop($widths);
Chris@0 104 foreach ($widths as $key => $width) {
Chris@0 105 $result[$key] = round(($width / $totalWidth) * $availableWidth);
Chris@0 106 }
Chris@0 107
Chris@0 108 // Give the last column the rest of the available width
Chris@0 109 $usedWidth = $this->sumWidth($result);
Chris@0 110 $result[$lastColumn] = $availableWidth - $usedWidth;
Chris@0 111
Chris@0 112 return new ColumnWidths($result);
Chris@0 113 }
Chris@0 114
Chris@0 115 public function lastColumn()
Chris@0 116 {
Chris@0 117 $keys = $this->keys();
Chris@0 118 return array_pop($keys);
Chris@0 119 }
Chris@0 120
Chris@0 121 /**
Chris@0 122 * Return the number of columns.
Chris@0 123 */
Chris@0 124 public function count()
Chris@0 125 {
Chris@0 126 return count($this->widths);
Chris@0 127 }
Chris@0 128
Chris@0 129 /**
Chris@0 130 * Calculate how much space is available on average for all columns.
Chris@0 131 */
Chris@0 132 public function averageWidth($availableWidth)
Chris@0 133 {
Chris@0 134 if ($this->isEmpty()) {
Chris@0 135 debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
Chris@0 136 }
Chris@0 137 return $availableWidth / $this->count();
Chris@0 138 }
Chris@0 139
Chris@0 140 /**
Chris@0 141 * Return the available keys (column identifiers) from the calculated
Chris@0 142 * data set.
Chris@0 143 */
Chris@0 144 public function keys()
Chris@0 145 {
Chris@0 146 return array_keys($this->widths);
Chris@0 147 }
Chris@0 148
Chris@0 149 /**
Chris@0 150 * Set the length of the specified column.
Chris@0 151 */
Chris@0 152 public function setWidth($key, $width)
Chris@0 153 {
Chris@0 154 $this->widths[$key] = $width;
Chris@0 155 }
Chris@0 156
Chris@0 157 /**
Chris@0 158 * Return the length of the specified column.
Chris@0 159 */
Chris@0 160 public function width($key)
Chris@0 161 {
Chris@0 162 return isset($this->widths[$key]) ? $this->widths[$key] : 0;
Chris@0 163 }
Chris@0 164
Chris@0 165 /**
Chris@0 166 * Return all of the lengths
Chris@0 167 */
Chris@0 168 public function widths()
Chris@0 169 {
Chris@0 170 return $this->widths;
Chris@0 171 }
Chris@0 172
Chris@0 173 /**
Chris@0 174 * Return true if there is no data in this object
Chris@0 175 */
Chris@0 176 public function isEmpty()
Chris@0 177 {
Chris@0 178 return empty($this->widths);
Chris@0 179 }
Chris@0 180
Chris@0 181 /**
Chris@0 182 * Return the sum of the lengths of the provided widths.
Chris@0 183 */
Chris@0 184 public function totalWidth()
Chris@0 185 {
Chris@0 186 return static::sumWidth($this->widths());
Chris@0 187 }
Chris@0 188
Chris@0 189 /**
Chris@0 190 * Return the sum of the lengths of the provided widths.
Chris@0 191 */
Chris@0 192 public static function sumWidth($widths)
Chris@0 193 {
Chris@0 194 return array_reduce(
Chris@0 195 $widths,
Chris@0 196 function ($carry, $item) {
Chris@0 197 return $carry + $item;
Chris@0 198 }
Chris@0 199 );
Chris@0 200 }
Chris@0 201
Chris@0 202 /**
Chris@0 203 * Ensure that every item in $widths that has a corresponding entry
Chris@0 204 * in $minimumWidths is as least as large as the minimum value held there.
Chris@0 205 */
Chris@0 206 public function enforceMinimums($minimumWidths)
Chris@0 207 {
Chris@0 208 $result = [];
Chris@0 209 if ($minimumWidths instanceof ColumnWidths) {
Chris@0 210 $minimumWidths = $minimumWidths->widths();
Chris@0 211 }
Chris@0 212 $minimumWidths += $this->widths;
Chris@0 213
Chris@0 214 foreach ($this->widths as $key => $value) {
Chris@0 215 $result[$key] = max($value, $minimumWidths[$key]);
Chris@0 216 }
Chris@0 217
Chris@0 218 return new ColumnWidths($result);
Chris@0 219 }
Chris@0 220
Chris@0 221 /**
Chris@0 222 * Remove all of the specified columns from this data structure.
Chris@0 223 */
Chris@0 224 public function removeColumns($columnKeys)
Chris@0 225 {
Chris@0 226 $widths = $this->widths();
Chris@0 227
Chris@0 228 foreach ($columnKeys as $key) {
Chris@0 229 unset($widths[$key]);
Chris@0 230 }
Chris@0 231
Chris@0 232 return new ColumnWidths($widths);
Chris@0 233 }
Chris@0 234
Chris@0 235 /**
Chris@0 236 * Select all columns that exist in the provided list of keys.
Chris@0 237 */
Chris@0 238 public function selectColumns($columnKeys)
Chris@0 239 {
Chris@0 240 $widths = [];
Chris@0 241
Chris@0 242 foreach ($columnKeys as $key) {
Chris@0 243 if (isset($this->widths[$key])) {
Chris@0 244 $widths[$key] = $this->width($key);
Chris@0 245 }
Chris@0 246 }
Chris@0 247
Chris@0 248 return new ColumnWidths($widths);
Chris@0 249 }
Chris@0 250
Chris@0 251 /**
Chris@0 252 * Combine this set of widths with another set, and return
Chris@0 253 * a new set that contains the entries from both.
Chris@0 254 */
Chris@0 255 public function combine(ColumnWidths $combineWith)
Chris@0 256 {
Chris@0 257 // Danger: array_merge renumbers numeric keys; that must not happen here.
Chris@0 258 $combined = $combineWith->widths();
Chris@0 259 foreach ($this->widths() as $key => $value) {
Chris@0 260 $combined[$key] = $value;
Chris@0 261 }
Chris@0 262 return new ColumnWidths($combined);
Chris@0 263 }
Chris@0 264 }