* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Behat\Gherkin\Node; use ArrayIterator; use Behat\Gherkin\Exception\NodeException; use Iterator; use IteratorAggregate; /** * Represents Gherkin Table argument. * * @author Konstantin Kudryashov */ class TableNode implements ArgumentInterface, IteratorAggregate { /** * @var array */ private $table; /** * @var integer */ private $maxLineLength = array(); /** * Initializes table. * * @param array $table Table in form of [$rowLineNumber => [$val1, $val2, $val3]] * * @throws NodeException If the given table is invalid */ public function __construct(array $table) { $this->table = $table; $columnCount = null; foreach ($this->getRows() as $row) { if ($columnCount === null) { $columnCount = count($row); } if (count($row) !== $columnCount) { throw new NodeException('Table does not have same number of columns in every row.'); } if (!is_array($row)) { throw new NodeException('Table is not two-dimensional.'); } foreach ($row as $column => $string) { if (!isset($this->maxLineLength[$column])) { $this->maxLineLength[$column] = 0; } if (!is_scalar($string)) { throw new NodeException('Table is not two-dimensional.'); } $this->maxLineLength[$column] = max($this->maxLineLength[$column], mb_strlen($string, 'utf8')); } } } /** * Creates a table from a given list. * * @param array $list One-dimensional array * * @return TableNode * * @throws NodeException If the given list is not a one-dimensional array */ public static function fromList(array $list) { if (count($list) !== count($list, COUNT_RECURSIVE)) { throw new NodeException('List is not a one-dimensional array.'); } array_walk($list, function (&$item) { $item = array($item); }); return new self($list); } /** * Returns node type. * * @return string */ public function getNodeType() { return 'Table'; } /** * Returns table hash, formed by columns (ColumnsHash). * * @return array */ public function getHash() { return $this->getColumnsHash(); } /** * Returns table hash, formed by columns. * * @return array */ public function getColumnsHash() { $rows = $this->getRows(); $keys = array_shift($rows); $hash = array(); foreach ($rows as $row) { $hash[] = array_combine($keys, $row); } return $hash; } /** * Returns table hash, formed by rows. * * @return array */ public function getRowsHash() { $hash = array(); foreach ($this->getRows() as $row) { $hash[array_shift($row)] = (1 == count($row)) ? $row[0] : $row; } return $hash; } /** * Returns numerated table lines. * Line numbers are keys, lines are values. * * @return array */ public function getTable() { return $this->table; } /** * Returns table rows. * * @return array */ public function getRows() { return array_values($this->table); } /** * Returns table definition lines. * * @return array */ public function getLines() { return array_keys($this->table); } /** * Returns specific row in a table. * * @param integer $index Row number * * @return array * * @throws NodeException If row with specified index does not exist */ public function getRow($index) { $rows = $this->getRows(); if (!isset($rows[$index])) { throw new NodeException(sprintf('Rows #%d does not exist in table.', $index)); } return $rows[$index]; } /** * Returns specific column in a table. * * @param integer $index Column number * * @return array * * @throws NodeException If column with specified index does not exist */ public function getColumn($index) { if ($index >= count($this->getRow(0))) { throw new NodeException(sprintf('Column #%d does not exist in table.', $index)); } $rows = $this->getRows(); $column = array(); foreach ($rows as $row) { $column[] = $row[$index]; } return $column; } /** * Returns line number at which specific row was defined. * * @param integer $index * * @return integer * * @throws NodeException If row with specified index does not exist */ public function getRowLine($index) { $lines = array_keys($this->table); if (!isset($lines[$index])) { throw new NodeException(sprintf('Rows #%d does not exist in table.', $index)); } return $lines[$index]; } /** * Converts row into delimited string. * * @param integer $rowNum Row number * * @return string */ public function getRowAsString($rowNum) { $values = array(); foreach ($this->getRow($rowNum) as $column => $value) { $values[] = $this->padRight(' ' . $value . ' ', $this->maxLineLength[$column] + 2); } return sprintf('|%s|', implode('|', $values)); } /** * Converts row into delimited string. * * @param integer $rowNum Row number * @param callable $wrapper Wrapper function * * @return string */ public function getRowAsStringWithWrappedValues($rowNum, $wrapper) { $values = array(); foreach ($this->getRow($rowNum) as $column => $value) { $value = $this->padRight(' ' . $value . ' ', $this->maxLineLength[$column] + 2); $values[] = call_user_func($wrapper, $value, $column); } return sprintf('|%s|', implode('|', $values)); } /** * Converts entire table into string * * @return string */ public function getTableAsString() { $lines = array(); for ($i = 0; $i < count($this->getRows()); $i++) { $lines[] = $this->getRowAsString($i); } return implode("\n", $lines); } /** * Returns line number at which table was started. * * @return integer */ public function getLine() { return $this->getRowLine(0); } /** * Converts table into string * * @return string */ public function __toString() { return $this->getTableAsString(); } /** * Retrieves a hash iterator. * * @return Iterator */ public function getIterator() { return new ArrayIterator($this->getHash()); } /** * Pads string right. * * @param string $text Text to pad * @param integer $length Length * * @return string */ protected function padRight($text, $length) { while ($length > mb_strlen($text, 'utf8')) { $text = $text . ' '; } return $text; } }