From 1cfc721b377cbb0076f3f030a03bebeaa6cfe2cb Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 27 Mar 2015 16:06:15 +0100 Subject: [PATCH] Add table extractor --- Resources/config/services.xml | 7 +- .../File/Fixture/DummyTableDefinition.php | 60 +++++++ .../Extractor/File/TableExtractorTest.php | 94 +++++++++++ Translation/Extractor/File/TableExtractor.php | 150 ++++++++++++++++++ 4 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 Tests/Translation/Extractor/File/Fixture/DummyTableDefinition.php create mode 100644 Tests/Translation/Extractor/File/TableExtractorTest.php create mode 100644 Translation/Extractor/File/TableExtractor.php diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 8b8c6cd9..ea26a7cd 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -13,6 +13,7 @@ JMS\TranslationBundle\Translation\Extractor\File\TranslationContainerExtractor JMS\TranslationBundle\Translation\Extractor\File\TwigFileExtractor JMS\TranslationBundle\Translation\Extractor\File\FormExtractor + JMS\TranslationBundle\Translation\Extractor\File\TableExtractor JMS\TranslationBundle\Translation\Extractor\File\ValidationExtractor JMS\TranslationBundle\Translation\Extractor\File\AuthenticationMessagesExtractor @@ -85,6 +86,10 @@ + + + + @@ -121,4 +126,4 @@ - \ No newline at end of file + diff --git a/Tests/Translation/Extractor/File/Fixture/DummyTableDefinition.php b/Tests/Translation/Extractor/File/Fixture/DummyTableDefinition.php new file mode 100644 index 00000000..483a1a04 --- /dev/null +++ b/Tests/Translation/Extractor/File/Fixture/DummyTableDefinition.php @@ -0,0 +1,60 @@ +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' + ); + } +} diff --git a/Tests/Translation/Extractor/File/TableExtractorTest.php b/Tests/Translation/Extractor/File/TableExtractorTest.php new file mode 100644 index 00000000..d77df54a --- /dev/null +++ b/Tests/Translation/Extractor/File/TableExtractorTest.php @@ -0,0 +1,94 @@ + + * + * 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; + } +} diff --git a/Translation/Extractor/File/TableExtractor.php b/Translation/Extractor/File/TableExtractor.php new file mode 100644 index 00000000..7004cc99 --- /dev/null +++ b/Translation/Extractor/File/TableExtractor.php @@ -0,0 +1,150 @@ + + * + * 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; + } +}