Commit 051af78f authored by Guillaume Perréal's avatar Guillaume Perréal
Browse files

Mise en place d'un framework de décorateurs.

parent 709da3ad
......@@ -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);
......
......@@ -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);
......
<?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;
}
}
<?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;
}
}
<?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));
}
}
}
}
<?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;
}
}
<?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);
}
......@@ -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;
}
}
<?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());
}
}
<?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;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment