Skip to content

Commit

Permalink
Add table extractor
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicky Gerritsen committed Mar 27, 2015
1 parent 1ed86a5 commit 1cfc721
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 1 deletion.
7 changes: 6 additions & 1 deletion Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<parameter key="jms_translation.extractor.file.translation_container_extractor">JMS\TranslationBundle\Translation\Extractor\File\TranslationContainerExtractor</parameter>
<parameter key="jms_translation.extractor.file.twig_extractor">JMS\TranslationBundle\Translation\Extractor\File\TwigFileExtractor</parameter>
<parameter key="jms_translation.extractor.file.form_extractor.class">JMS\TranslationBundle\Translation\Extractor\File\FormExtractor</parameter>
<parameter key="jms_translation.extractor.file.table_extractor.class">JMS\TranslationBundle\Translation\Extractor\File\TableExtractor</parameter>
<parameter key="jms_translation.extractor.file.validation_extractor.class">JMS\TranslationBundle\Translation\Extractor\File\ValidationExtractor</parameter>
<parameter key="jms_translation.extractor.file.authentication_message_extractor.class">JMS\TranslationBundle\Translation\Extractor\File\AuthenticationMessagesExtractor</parameter>

Expand Down Expand Up @@ -85,6 +86,10 @@
<argument type="service" id="jms_translation.doc_parser" />
<tag name="jms_translation.file_visitor" />
</service>
<service id="jms_translation.extractor.file.table_extractor" class="%jms_translation.extractor.file.table_extractor.class%" public="false">
<argument type="service" id="jms_translation.doc_parser" />
<tag name="jms_translation.file_visitor" />
</service>
<service id="jms_translation.extractor.file.translation_container_extractor" class="%jms_translation.extractor.file.translation_container_extractor%" public="false">
<tag name="jms_translation.file_visitor" />
</service>
Expand Down Expand Up @@ -121,4 +126,4 @@
<tag name="twig.extension" />
</service>
</services>
</container>
</container>
60 changes: 60 additions & 0 deletions Tests/Translation/Extractor/File/Fixture/DummyTableDefinition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace StreamOne\DummyBundle\Table;

/**
* Table definition for the dummy list table
*/
class DummyTableDefinition implements TableDefinitionInterface
{
/**
* {@inheritdoc}
*/
public function buildTable(TableBuilder $builder)
{
$builder
->setTableId('dummy')
->setDefaultSortColumn('title')
->setPaginationEnabled()
->addColumn('', 'xyz', array())
->addColumn('dummy.xyz', 'xyz', array())
->addColumn(
/** @Desc("This is abc") */
'dummy.abc', 'abc', array(
'formatter' => array(
'formatter' => 'link',
'component' => 'dummy',
'page' => 'detail',
'route_params' => array(
'dummy' => '%id%',
),
'name_formatter' => array(
'formatter' => 'date',
'date_format' => \IntlDateFormatter::SHORT,
'time_format' => \IntlDateFormatter::SHORT,
),
),
'sortable' => true,
'sort_field' => 'created',
)
)
->addColumn(
/** @Desc("This is a test") */
'dummy.def', 'def', array(
'formatter' => array(
'formatter' => 'link',
'component' => 'dummy',
'page' => 'detail',
'route_params' => array(
'dummy' => '%id%',
)
),
'sortable' => true,
)
)
->addColumn(
/** @Desc("Ghi") */
'dummy.ghi', 'ghi'
);
}
}
94 changes: 94 additions & 0 deletions Tests/Translation/Extractor/File/TableExtractorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Added by Nicky Gerritsen in march 2015 for the StreamOne manager
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace JMS\TranslationBundle\Tests\Translation\Extractor\File;

use JMS\TranslationBundle\Exception\RuntimeException;
use Doctrine\Common\Annotations\DocParser;

use JMS\TranslationBundle\Translation\Extractor\File\TableExtractor;
use JMS\TranslationBundle\Model\FileSource;
use JMS\TranslationBundle\Model\Message;
use JMS\TranslationBundle\Model\MessageCatalogue;

class TableExtractorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var TableExtractor
*/
private $extractor;

public function testExtract()
{
$expected = new MessageCatalogue();
$path = __DIR__.'/Fixture/DummyTableDefinition.php';

$message = new Message('dummy.xyz');
$message->addSource(new FileSource($path, 20));
$expected->add($message);

$message = new Message('dummy.abc');
$message->setDesc('This is abc');
$message->addSource(new FileSource($path, 23));
$expected->add($message);

$message = new Message('dummy.def');
$message->setDesc('This is a test');
$message->addSource(new FileSource($path, 43));
$expected->add($message);

$message = new Message('dummy.ghi');
$message->setDesc('Ghi');
$message->addSource(new FileSource($path, 57));
$expected->add($message);

$this->assertEquals($expected, $this->extract('DummyTableDefinition.php'));
}

protected function setUp()
{
$docParser = new DocParser();
$docParser->setImports(array(
'desc' => 'JMS\TranslationBundle\Annotation\Desc',
'meaning' => 'JMS\TranslationBundle\Annotation\Meaning',
'ignore' => 'JMS\TranslationBundle\Annotation\Ignore',
));
$docParser->setIgnoreNotImportedAnnotations(true);

$this->extractor = new TableExtractor($docParser);
}

private function extract($file)
{
if (!is_file($file = __DIR__.'/Fixture/'.$file)) {
throw new RuntimeException(sprintf('The file "%s" does not exist.', $file));
}
$file = new \SplFileInfo($file);

$lexer = new \PHPParser_Lexer(file_get_contents($file));
$parser = new \PHPParser_Parser();
$ast = $parser->parse($lexer);

$catalogue = new MessageCatalogue();
$this->extractor->visitPhpFile($file, $catalogue, $ast);

return $catalogue;
}
}
150 changes: 150 additions & 0 deletions Translation/Extractor/File/TableExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Added by Nicky Gerritsen in march 2015 for the StreamOne manager
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace JMS\TranslationBundle\Translation\Extractor\File;

use JMS\TranslationBundle\Exception\RuntimeException;
use JMS\TranslationBundle\Model\FileSource;
use JMS\TranslationBundle\Model\Message;
use JMS\TranslationBundle\Annotation\Meaning;
use JMS\TranslationBundle\Annotation\Desc;
use JMS\TranslationBundle\Annotation\Ignore;
use Doctrine\Common\Annotations\DocParser;
use JMS\TranslationBundle\Model\MessageCatalogue;
use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface;
use JMS\TranslationBundle\Logger\LoggerAwareInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;

class TableExtractor implements FileVisitorInterface, \PHPParser_NodeVisitor
{
private $docParser;
private $traverser;
private $file;
private $catalogue;
private $logger;

public function __construct(DocParser $docParser)
{
$this->docParser = $docParser;

$this->traverser = new \PHPParser_NodeTraverser();
$this->traverser->addVisitor($this);
}


public function enterNode(\PHPParser_Node $node)
{
if ($node instanceof \PHPParser_Node_Expr_MethodCall) {

if (!is_string($node->name)) {
return;
}

$name = strtolower($node->name);
if ('addcolumn' === $name) {
$this->parseNode($node);
}
}
}

private function parseNode(\PHPParser_Node_Expr_MethodCall $node)
{
if (count($node->args) >= 1) {
$first_argument = $node->args[0];

$ignore = false;
$desc = $meaning = null;
$docComment = $first_argument->getDocComment();

if ($docComment) {
foreach ($this->docParser->parse($docComment, 'file ' . $this->file . ' near line ' . $first_argument->value->getLine()) as $annot) {
if ($annot instanceof Ignore) {
$ignore = true;
} else if ($annot instanceof Desc) {
$desc = $annot->text;
} else if ($annot instanceof Meaning) {
$meaning = $annot->text;
}
}
}

if (!$first_argument->value instanceof \PHPParser_Node_Scalar_String) {
if ($ignore) {
return;
}

$message = sprintf('Unable to extract translation id for table column name from non-string values, but got "%s" in %s on line %d. Please refactor your code to pass a string, or add "/** @Ignore */".', get_class($first_argument->value), $this->file, $first_argument->value->getLine());
if ($this->logger) {
$this->logger->err($message);

return;
}

throw new RuntimeException($message);
}

$source = new FileSource((string)$this->file, $first_argument->value->getLine());
$value = $first_argument->value->value;

if (empty($value)) {
// Empty columns should not be translated
return;
}

$this->addToCatalogue($value, $source, $desc, $meaning);
}
}

private function addToCatalogue($id, $source, $desc = null, $meaning = null)
{
$message = new Message($id);

$message->addSource($source);

if ($desc) {
$message->setDesc($desc);
}

if ($meaning) {
$message->setMeaning($meaning);
}

$this->catalogue->add($message);
}

public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, array $ast)
{
$this->file = $file;
$this->catalogue = $catalogue;
$this->traverser->traverse($ast);
}

public function leaveNode(\PHPParser_Node $node) { }

public function beforeTraverse(array $nodes) { }
public function afterTraverse(array $nodes) { }
public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { }
public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) { }

public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
}

0 comments on commit 1cfc721

Please sign in to comment.