TableNode::getRowAsStringWithWrappedValues()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 6
cp 0
rs 9.9
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 6
1
<?php
2
3
/*
4
 * This file is part of the Behat Gherkin.
5
 * (c) Konstantin Kudryashov <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Behat\Gherkin\Node;
12
13
use ArrayIterator;
14
use Behat\Gherkin\Exception\NodeException;
15
use Iterator;
16
use IteratorAggregate;
17
18
/**
19
 * Represents Gherkin Table argument.
20
 *
21
 * @author Konstantin Kudryashov <[email protected]>
22
 */
23
class TableNode implements ArgumentInterface, IteratorAggregate
24
{
25
    /**
26
     * @var array
27
     */
28
    private $table;
29
    /**
30
     * @var integer
31
     */
32
    private $maxLineLength = array();
33
34
    /**
35
     * Initializes table.
36
     *
37
     * @param array $table Table in form of [$rowLineNumber => [$val1, $val2, $val3]]
38
     *
39
     * @throws NodeException If the given table is invalid
40
     */
41 251
    public function __construct(array $table)
42
    {
43 251
        $this->table = $table;
44 251
        $columnCount = null;
45
46 251
        foreach ($this->getRows() as $ridx => $row) {
47
48 236
            if (!is_array($row)) {
49 1
                throw new NodeException(sprintf(
50 1
                    "Table row '%s' is expected to be array, got %s",
51 1
                    $ridx,
52 1
                    gettype($row)
53
                ));
54
            }
55
56 235
            if ($columnCount === null) {
57 235
                $columnCount = count($row);
58
            }
59
60 235
            if (count($row) !== $columnCount) {
61 2
                throw new NodeException(sprintf(
62 2
                    "Table row '%s' is expected to have %s columns, got %s",
63 2
                    $ridx,
64 2
                    $columnCount,
65 2
                    count($row)
66
                ));
67
            }
68
69 235
            foreach ($row as $column => $string) {
70 235
                if (!isset($this->maxLineLength[$column])) {
71 235
                    $this->maxLineLength[$column] = 0;
72
                }
73
74 235
                if (!is_scalar($string)) {
75 1
                    throw new NodeException(sprintf(
76 1
                        "Table cell at row '%s', col '%s' is expected to be scalar, got %s",
77 1
                        $ridx,
78 1
                        $column,
79 1
                        gettype($string)
80
                    ));
81
                }
82
83 234
                $this->maxLineLength[$column] = max($this->maxLineLength[$column], mb_strlen($string, 'utf8'));
84
            }
85
        }
86 247
    }
87
88
    /**
89
     * Creates a table from a given list.
90
     *
91
     * @param array $list One-dimensional array
92
     *
93
     * @return TableNode
94
     *
95
     * @throws NodeException If the given list is not a one-dimensional array
96
     */
97 2
    public static function fromList(array $list)
98
    {
99 2
        if (count($list) !== count($list, COUNT_RECURSIVE)) {
100 1
            throw new NodeException('List is not a one-dimensional array.');
101
        }
102
103
        array_walk($list, function (&$item) {
104 1
            $item = array($item);
105 1
        });
106 1
        return new self($list);
107
    }
108
109
    /**
110
     * Returns node type.
111
     *
112
     * @return string
113
     */
114
    public function getNodeType()
115
    {
116
        return 'Table';
117
    }
118
119
    /**
120
     * Returns table hash, formed by columns (ColumnsHash).
121
     *
122
     * @return array
123
     */
124 4
    public function getHash()
125
    {
126 4
        return $this->getColumnsHash();
127
    }
128
129
    /**
130
     * Returns table hash, formed by columns.
131
     *
132
     * @return array
133
     */
134 10
    public function getColumnsHash()
135
    {
136 10
        $rows = $this->getRows();
137 10
        $keys = array_shift($rows);
138
139 10
        $hash = array();
140 10
        foreach ($rows as $row) {
141 8
            $hash[] = array_combine($keys, $row);
142
        }
143
144 10
        return $hash;
145
    }
146
147
    /**
148
     * Returns table hash, formed by rows.
149
     *
150
     * @return array
151
     */
152 2
    public function getRowsHash()
153
    {
154 2
        $hash = array();
155
156 2
        foreach ($this->getRows() as $row) {
157 2
            $hash[array_shift($row)] = (1 == count($row)) ? $row[0] : $row;
158
        }
159
160 2
        return $hash;
161
    }
162
163
    /**
164
     * Returns numerated table lines.
165
     * Line numbers are keys, lines are values.
166
     *
167
     * @return array
168
     */
169 14
    public function getTable()
170
    {
171 14
        return $this->table;
172
    }
173
174
    /**
175
     * Returns table rows.
176
     *
177
     * @return array
178
     */
179 251
    public function getRows()
180
    {
181 251
        return array_values($this->table);
182
    }
183
184
    /**
185
     * Returns table definition lines.
186
     *
187
     * @return array
188
     */
189 11
    public function getLines()
190
    {
191 11
        return array_keys($this->table);
192
    }
193
194
    /**
195
     * Returns specific row in a table.
196
     *
197
     * @param integer $index Row number
198
     *
199
     * @return array
200
     *
201
     * @throws NodeException If row with specified index does not exist
202
     */
203 17 View Code Duplication
    public function getRow($index)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
204
    {
205 17
        $rows = $this->getRows();
206
207 17
        if (!isset($rows[$index])) {
208
            throw new NodeException(sprintf('Rows #%d does not exist in table.', $index));
209
        }
210
211 17
        return $rows[$index];
212
    }
213
214
    /**
215
     * Returns specific column in a table.
216
     *
217
     * @param integer $index Column number
218
     *
219
     * @return array
220
     *
221
     * @throws NodeException If column with specified index does not exist
222
     */
223 1
    public function getColumn($index)
224
    {
225 1
        if ($index >= count($this->getRow(0))) {
226
            throw new NodeException(sprintf('Column #%d does not exist in table.', $index));
227
        }
228
229 1
        $rows = $this->getRows();
230 1
        $column = array();
231
232 1
        foreach ($rows as $row) {
233 1
            $column[] = $row[$index];
234
        }
235
236 1
        return $column;
237
    }
238
239
    /**
240
     * Returns line number at which specific row was defined.
241
     *
242
     * @param integer $index
243
     *
244
     * @return integer
245
     *
246
     * @throws NodeException If row with specified index does not exist
247
     */
248 10 View Code Duplication
    public function getRowLine($index)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
249
    {
250 10
        $lines = array_keys($this->table);
251
252 10
        if (!isset($lines[$index])) {
253
            throw new NodeException(sprintf('Rows #%d does not exist in table.', $index));
254
        }
255
256 10
        return $lines[$index];
257
    }
258
259
    /**
260
     * Converts row into delimited string.
261
     *
262
     * @param integer $rowNum Row number
263
     *
264
     * @return string
265
     */
266 6
    public function getRowAsString($rowNum)
267
    {
268 6
        $values = array();
269 6
        foreach ($this->getRow($rowNum) as $column => $value) {
270 6
            $values[] = $this->padRight(' ' . $value . ' ', $this->maxLineLength[$column] + 2);
271
        }
272
273 6
        return sprintf('|%s|', implode('|', $values));
274
    }
275
276
    /**
277
     * Converts row into delimited string.
278
     *
279
     * @param integer  $rowNum  Row number
280
     * @param callable $wrapper Wrapper function
281
     *
282
     * @return string
283
     */
284
    public function getRowAsStringWithWrappedValues($rowNum, $wrapper)
285
    {
286
        $values = array();
287
        foreach ($this->getRow($rowNum) as $column => $value) {
288
            $value = $this->padRight(' ' . $value . ' ', $this->maxLineLength[$column] + 2);
289
290
            $values[] = call_user_func($wrapper, $value, $column);
291
        }
292
293
        return sprintf('|%s|', implode('|', $values));
294
    }
295
296
    /**
297
     * Converts entire table into string
298
     *
299
     * @return string
300
     */
301 2
    public function getTableAsString()
302
    {
303 2
        $lines = array();
304 2
        for ($i = 0; $i < count($this->getRows()); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
305 2
            $lines[] = $this->getRowAsString($i);
306
        }
307
308 2
        return implode("\n", $lines);
309
    }
310
311
    /**
312
     * Returns line number at which table was started.
313
     *
314
     * @return integer
315
     */
316 5
    public function getLine()
317
    {
318 5
        return $this->getRowLine(0);
319
    }
320
321
    /**
322
     * Converts table into string
323
     *
324
     * @return string
325
     */
326
    public function __toString()
327
    {
328
        return $this->getTableAsString();
329
    }
330
331
    /**
332
     * Retrieves a hash iterator.
333
     *
334
     * @return Iterator
335
     */
336 1
    public function getIterator()
337
    {
338 1
        return new ArrayIterator($this->getHash());
339
    }
340
341
    /**
342
     * Obtains and adds rows from another table to the current table.
343
     * The second table should have the same structure as the current one.
344
     * @param TableNode $node
345
     *
346
     * @deprecated remove together with OutlineNode::getExampleTable
347
     */
348 8
    public function mergeRowsFromTable(TableNode $node)
349
    {
350
        // check structure
351 8
        if ($this->getRow(0) !== $node->getRow(0)) {
352 3
            throw new NodeException("Tables have different structure. Cannot merge one into another");
353
        }
354
355 5
        $firstLine = $node->getLine();
356 5
        foreach ($node->getTable() as $line => $value) {
357 5
            if ($line === $firstLine) {
358 5
                continue;
359
            }
360
361 5
            $this->table[$line] = $value;
362
        }
363 5
    }
364
365
    /**
366
     * Pads string right.
367
     *
368
     * @param string  $text   Text to pad
369
     * @param integer $length Length
370
     *
371
     * @return string
372
     */
373 6
    protected function padRight($text, $length)
374
    {
375 6
        while ($length > mb_strlen($text, 'utf8')) {
376 6
            $text = $text . ' ';
377
        }
378
379 6
        return $text;
380
    }
381
}
382