From 051af78feb396b96489a65dd83d4c47e3f9b2364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20Perr=C3=A9al?= <guillaume.perreal@irstea.fr> Date: Wed, 9 Mar 2016 15:04:15 +0100 Subject: [PATCH] =?UTF-8?q?Mise=20en=20place=20d'un=20framework=20de=20d?= =?UTF-8?q?=C3=A9corateurs.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Command/EntitySchemaCommand.php | 18 ++++-- Model/ClassVisitor.php | 73 ++++++++---------------- Model/Decorator/CompositeDecorator.php | 45 +++++++++++++++ Model/Decorator/FilteringDecorator.php | 50 ++++++++++++++++ Model/Decorator/InheritanceDecorator.php | 65 +++++++++++++++++++++ Model/Decorator/NullDecorator.php | 28 +++++++++ Model/DecoratorInterface.php | 18 ++++++ Model/Filter/AcceptAllFilter.php | 18 +----- Model/Orm/EntityVisitor.php | 53 ----------------- Utils/Singleton.php | 33 +++++++++++ 10 files changed, 278 insertions(+), 123 deletions(-) create mode 100644 Model/Decorator/CompositeDecorator.php create mode 100644 Model/Decorator/FilteringDecorator.php create mode 100644 Model/Decorator/InheritanceDecorator.php create mode 100644 Model/Decorator/NullDecorator.php create mode 100644 Model/DecoratorInterface.php delete mode 100644 Model/Orm/EntityVisitor.php create mode 100644 Utils/Singleton.php diff --git a/Command/EntitySchemaCommand.php b/Command/EntitySchemaCommand.php index e290e0a..be44af3 100644 --- a/Command/EntitySchemaCommand.php +++ b/Command/EntitySchemaCommand.php @@ -8,16 +8,18 @@ namespace Irstea\PlantUmlBundle\Command; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; +use Irstea\PlantUmlBundle\Model\ClassVisitor; +use Irstea\PlantUmlBundle\Model\Decorator\CompositeDecorator; +use Irstea\PlantUmlBundle\Model\Decorator\FilteringDecorator; +use Irstea\PlantUmlBundle\Model\Decorator\InheritanceDecorator; +use Irstea\PlantUmlBundle\Model\Decorator\NullDecorator; use Irstea\PlantUmlBundle\Model\Filter\Whitelist; -use Irstea\PlantUmlBundle\Model\Namespace_; -use Irstea\PlantUmlBundle\Model\Orm\EntityVisitor; use Irstea\PlantUmlBundle\Writer\OutputWriter; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; + /** * Description of ImportAffiliationCommand * @@ -52,7 +54,13 @@ class EntitySchemaCommand extends ContainerAwareCommand $allMetadata ); - $visitor = new EntityVisitor($allMetadata); + $decorator = new FilteringDecorator( + new InheritanceDecorator(), + new Whitelist($classes) + ); + + $visitor = new ClassVisitor($decorator); + array_walk($classes, [$visitor, 'visitClass']); $writer = new OutputWriter($output); diff --git a/Model/ClassVisitor.php b/Model/ClassVisitor.php index 0ee2cba..d39a4f4 100644 --- a/Model/ClassVisitor.php +++ b/Model/ClassVisitor.php @@ -35,10 +35,16 @@ class ClassVisitor implements ClassVisitorInterface, WritableInterface */ protected $filter; - public function __construct(ClassFilterInterface $filter = null) + /** + * @var DecoratorInterface + */ + protected $decorator; + + public function __construct(DecoratorInterface $decorator = null, ClassFilterInterface $filter = null) { $this->rootNamespace = new RootNamespace(); $this->filter = $filter ?: AcceptAllFilter::instance(); + $this->decorator = $decorator ?: NullDecorator::instance(); } /** @@ -51,6 +57,16 @@ class ClassVisitor implements ClassVisitorInterface, WritableInterface return $this; } + /** + * @param DecoratorInterface $decorator + * @return self + */ + public function setDecorator(DecoratorInterface $decorator) + { + $this->decorator = $decorator; + return $this; + } + public function visitClass($className) { assert('is_string($className)', $className); @@ -61,7 +77,7 @@ class ClassVisitor implements ClassVisitorInterface, WritableInterface return $this->nodes[$className] = false; } - return $this->doVisitClass($className); + return $this->createNode($className); } /** @@ -69,68 +85,27 @@ class ClassVisitor implements ClassVisitorInterface, WritableInterface * @param string $className * @return NodeInterface */ - protected function doVisitClass($className) - { - $reflection = new ReflectionClass($className); - return $this->visitClassReflection($reflection); - } - - /** - * @param ReflectionClass $class - * @return NodeInterface - */ - protected function visitClassReflection(ReflectionClass $class) + protected function createNode($className) { + $class = new ReflectionClass($className); $namespace = $this->rootNamespace->getNamespace($class->getNamespaceName()); if ($class->isTrait()) { - $node = new Trait_($namespace, $class->getName()); + $node = new Trait_($namespace, $className); } elseif ($class->isInterface()) { - $node = new Interface_($namespace, $class->getName()); + $node = new Interface_($namespace, $className); } else { - $node = new Class_($namespace, $class->getName(), $class->isAbstract(), $class->isFinal()); + $node = new Class_($namespace, $className, $class->isAbstract(), $class->isFinal()); } $this->nodes[$class->getName()] = $node; $namespace->addNode($node); - $parentClass = $class->getParentClass(); - $traitNames = $class->getTraitNames(); - - $indirectInterfaces = array_filter( - array_map( - function ($i) { return $i->getInterfaceNames(); }, - $class->getInterfaces() - ) - ); - $interfaceNames = $class->getInterfaceNames(); - if (!empty($indirectInterfaces)) { - $indirectInterfaces = call_user_func_array('array_merge', $indirectInterfaces); - $interfaceNames = array_diff($interfaceNames, $indirectInterfaces); - } - - if ($parentClass) { - $traitNames = array_diff($traitNames, $parentClass->getTraitNames()); - $interfaceNames = array_diff($interfaceNames, $parentClass->getInterfaceNames()); - $this->visitRelations($node, [$parentClass->getName()], 'Irstea\PlantUmlBundle\Model\Arrow\ExtendsClass'); - } - - $this->visitRelations($node, $interfaceNames, 'Irstea\PlantUmlBundle\Model\Arrow\ImplementsInterface'); - $this->visitRelations($node, $traitNames, 'Irstea\PlantUmlBundle\Model\Arrow\UsesTrait'); + $this->decorator->decorate($className, $node, $this); return $node; } - protected function visitRelations(NodeInterface $source, array $classNames, $relationClass) - { - foreach ($classNames as $className) { - $target = $this->visitClass($className); - if ($target) { - $source->addArrow(new $relationClass($source, $target)); - } - } - } - public function outputTo(WriterInterface $writer) { return $this->rootNamespace->outputTo($writer); diff --git a/Model/Decorator/CompositeDecorator.php b/Model/Decorator/CompositeDecorator.php new file mode 100644 index 0000000..788210a --- /dev/null +++ b/Model/Decorator/CompositeDecorator.php @@ -0,0 +1,45 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Model\Decorator; + +use Irstea\PlantUmlBundle\Model\ClassVisitorInterface; +use Irstea\PlantUmlBundle\Model\DecoratorInterface; +use Irstea\PlantUmlBundle\Model\NodeInterface; + +/** + * Description of CompositeDecorator + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +class CompositeDecorator implements DecoratorInterface +{ + /** + * @var DecoratorInterface[] + */ + private $decorators = []; + + public function __construct(array $decorators = []) + { + $this->decorators = $decorators; + } + + public function addDecorator(DecoratorInterface $decorator) + { + $this->decorators[] = $decorator; + return $this; + } + + public function decorate($className, NodeInterface $node, ClassVisitorInterface $visitor) + { + foreach($this->decorators as $decorator) { + $decorator->decorate($className, $node, $visitor); + } + return $this; + } +} diff --git a/Model/Decorator/FilteringDecorator.php b/Model/Decorator/FilteringDecorator.php new file mode 100644 index 0000000..6025de1 --- /dev/null +++ b/Model/Decorator/FilteringDecorator.php @@ -0,0 +1,50 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Model\Decorator; + +use Irstea\PlantUmlBundle\Model\ClassFilterInterface; +use Irstea\PlantUmlBundle\Model\ClassVisitorInterface; +use Irstea\PlantUmlBundle\Model\DecoratorInterface; +use Irstea\PlantUmlBundle\Model\NodeInterface; + +/** + * Description of FilteringDecorator + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +class FilteringDecorator implements DecoratorInterface +{ + /** + * @var DecoratorInterface + */ + private $next; + + /** + * @var ClassFilterInterface + */ + private $filter; + + /** + * @param DecoratorInterface $next + * @param ClassFilterInterface $filter + */ + public function __construct(DecoratorInterface $next, ClassFilterInterface $filter) + { + $this->next = $next; + $this->filter = $filter; + } + + public function decorate($className, NodeInterface $node, ClassVisitorInterface $visitor) + { + if ($this->filter->accept($className)) { + $this->next->decorate($className, $node, $visitor); + } + return $this; + } +} diff --git a/Model/Decorator/InheritanceDecorator.php b/Model/Decorator/InheritanceDecorator.php new file mode 100644 index 0000000..d3b52c4 --- /dev/null +++ b/Model/Decorator/InheritanceDecorator.php @@ -0,0 +1,65 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Model\Decorator; + +use Irstea\PlantUmlBundle\Model\ClassVisitor; +use Irstea\PlantUmlBundle\Model\ClassVisitorInterface; +use Irstea\PlantUmlBundle\Model\DecoratorInterface; +use Irstea\PlantUmlBundle\Model\NodeInterface; +use ReflectionClass; + +/** + * Description of InheritanceDecorator + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +class InheritanceDecorator implements DecoratorInterface +{ + public function decorate($className, NodeInterface $node, ClassVisitorInterface $visitor) + { + $class = new ReflectionClass($className); + + $parent = $class->getParentClass(); + $traits = $class->getTraitNames(); + $interfaces = $class->getInterfaceNames(); + + $indirectInterfaces = array_filter( + array_map( + function ($i) { return $i->getInterfaceNames(); }, + $class->getInterfaces() + ) + ); + + if (!empty($indirectInterfaces)) { + $indirectInterfaces = call_user_func_array('array_merge', $indirectInterfaces); + $interfaces = array_diff($interfaces, $indirectInterfaces); + } + + if ($parent) { + $traits = array_diff($traits, $parent->getTraitNames()); + $interfaces = array_diff($interfaces, $parent->getInterfaceNames()); + $this->visitRelations($node, [$parent->getName()], 'Irstea\PlantUmlBundle\Model\Arrow\ExtendsClass', $visitor); + } + + $this->visitRelations($node, $interfaces, 'Irstea\PlantUmlBundle\Model\Arrow\ImplementsInterface', $visitor); + $this->visitRelations($node, $traits, 'Irstea\PlantUmlBundle\Model\Arrow\UsesTrait', $visitor); + + return $this; + } + + protected function visitRelations(NodeInterface $source, array $classNames, $relationClass, ClassVisitorInterface $visitor) + { + foreach ($classNames as $className) { + $target = $visitor->visitClass($className); + if ($target) { + $source->addArrow(new $relationClass($source, $target)); + } + } + } +} diff --git a/Model/Decorator/NullDecorator.php b/Model/Decorator/NullDecorator.php new file mode 100644 index 0000000..e55976f --- /dev/null +++ b/Model/Decorator/NullDecorator.php @@ -0,0 +1,28 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Model\Decorator; + +use Irstea\PlantUmlBundle\Model\ClassVisitorInterface; +use Irstea\PlantUmlBundle\Model\DecoratorInterface; +use Irstea\PlantUmlBundle\Model\NodeInterface; + +/** + * Description of NullDecorator + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +class NullDecorator implements DecoratorInterface +{ + use \Irstea\PlantUmlBundle\Utils\Singleton; + + public function decorate($className, NodeInterface $node, ClassVisitorInterface $visitor) + { + return $this; + } +} diff --git a/Model/DecoratorInterface.php b/Model/DecoratorInterface.php new file mode 100644 index 0000000..72c530b --- /dev/null +++ b/Model/DecoratorInterface.php @@ -0,0 +1,18 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Model; + +/** + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +interface DecoratorInterface +{ + public function decorate($className, NodeInterface $node, ClassVisitorInterface $visitor); +} diff --git a/Model/Filter/AcceptAllFilter.php b/Model/Filter/AcceptAllFilter.php index 287052d..7ec74ee 100644 --- a/Model/Filter/AcceptAllFilter.php +++ b/Model/Filter/AcceptAllFilter.php @@ -17,24 +17,10 @@ use Irstea\PlantUmlBundle\Model\ClassFilterInterface; */ class AcceptAllFilter implements ClassFilterInterface { + use \Irstea\PlantUmlBundle\Utils\Singleton; + public function accept($className) { return true; } - - /** - * @var self - */ - static private $instance; - - /** - * @return self - */ - static public function instance() - { - if (!static::$instance) { - static::$instance = new static(); - } - return static::$instance; - } } diff --git a/Model/Orm/EntityVisitor.php b/Model/Orm/EntityVisitor.php deleted file mode 100644 index 48342e1..0000000 --- a/Model/Orm/EntityVisitor.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/* - * © 2016 IRSTEA - * Guillaume Perréal <guillaume.perreal@irstea.fr> - * Tous droits réservés. - */ - -namespace Irstea\PlantUmlBundle\Model\Orm; - -use Doctrine\ORM\Mapping\ClassMetadata; -use Irstea\PlantUmlBundle\Model\ClassVisitor; - -/** - * Description of Visitor - * - * @author Guillaume Perréal <guillaume.perreal@irstea.fr> - */ -class EntityVisitor extends ClassVisitor -{ - /** - * @var ClassMetadata[] - */ - protected $metadata = []; - - public function __construct(array $metadata, \Irstea\PlantUmlBundle\Model\ClassFilterInterface $filter = null) - { - parent::__construct($filter); - - foreach($metadata as $md) { - /* @var $md ClassMetadata */ - $this->metadata[$md->getReflectionClass()->getName()] = $md; - } - } - - public function doVisitClass($className) - { - if (isset($this->metadata[$className])) { - return $this->visitClassMetadata($this->metadata[$className]); - } - - return parent::doVisitClass($className); - } - - /** - * @param ClassMetadata $metadata - * @return AbstractNode - */ - protected function visitClassMetadata(ClassMetadata $metadata) - { - return $this->visitClassReflection($metadata->getReflectionClass()); - } -} diff --git a/Utils/Singleton.php b/Utils/Singleton.php new file mode 100644 index 0000000..d09a44e --- /dev/null +++ b/Utils/Singleton.php @@ -0,0 +1,33 @@ +<?php + +/* + * © 2016 IRSTEA + * Guillaume Perréal <guillaume.perreal@irstea.fr> + * Tous droits réservés. + */ + +namespace Irstea\PlantUmlBundle\Utils; + +/** + * Description of Singleton + * + * @author Guillaume Perréal <guillaume.perreal@irstea.fr> + */ +trait Singleton +{ + /** + * @var self + */ + static private $instance; + + /** + * @return self + */ + static public function instance() + { + if (!static::$instance) { + static::$instance = new static(); + } + return static::$instance; + } +} -- GitLab