Commit 136fe0ff authored by Guillaume Perréal's avatar Guillaume Perréal

Refactoring de la lecture de la configuration des graphes.

Ajout de classes "builders".
Utilise le container du début jusqu'à la fin, même pour les filtres.
parent 07decdd8
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace Irstea\PlantUmlBundle\DependencyInjection\Builder;
use Irstea\PlantUmlBundle\Model\Filter\ClassFilter;
use Irstea\PlantUmlBundle\Model\Filter\Composite\AllFilter;
use Irstea\PlantUmlBundle\Model\Filter\DirectoryFilter;
use Irstea\PlantUmlBundle\Model\Filter\NamespaceFilter;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
/**
* Description of ClassFilterBuilder
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class ClassFilterBuilder
{
/**
* @var ContainerBuilder
*/
private $container;
/**
* @var Reference[]
*/
private $filters = [];
/**
* @var string[]
*/
static private $filterClasses = [
'classes' => ClassFilter::class,
'directories' => DirectoryFilter::class,
'namespaces' => NamespaceFilter::class
];
/**
* @param \Irstea\PlantUmlBundle\DependencyInjection\Builder\ContainerBuilder $container
*/
public function __construct(ContainerBuilder $container)
{
$this->container = $container;
}
/**
* @param array $config
* @return Reference
*/
public function build(array $config)
{
$filters = $this->normalize(array_intersect_key($config, ['include' => true, 'exclude' => true]));
if ($filters === null) {
return null;
}
$hash = sha1(serialize($filters));
if (!key_exists($hash, $this->filters)) {
return $this->filters[$hash] = $this->doBuild("irstea.plant_uml.filters.$hash", $filters);
}
return $this->filters[$hash];
}
/**
* @param mixed $data
* @return mixed
*/
protected function normalize($data)
{
if ($data === null || $data === []) {
return null;
}
if (is_array($data)) {
$res = [];
foreach($data as $k => $v) {
$normalized = $this->normalize($v);
if (!empty($normalized)) {
$res[$k] = $normalized;
}
}
return empty($res) ? null : $res;
}
return $data;
}
/**
* @param type $id
* @param array $config
* @return Reference
*/
protected function doBuild($id, array $config)
{
$filters = [];
if (isset($config['include'])) {
$this->buildSubFilter($filters, "$id.include", $config['include'], false);
}
if (isset($config['exclude'])) {
$this->buildSubFilter($filters, "$id.exclude", $config['exclude'], true);
}
if (empty($filters)) {
return null;
}
if (count($filters) === 1) {
return $filters[0];
}
$this->container->setDefinition($id, new Definition(AllFilter::class, [$filters]));
return new Reference($id);
}
/**
* @param array $filters
* @param string $id
* @param array $config
* @param bool $notFound
*/
protected function buildSubFilter(array &$filters, $id, array $config, $notFound)
{
foreach (self::$filterClasses as $key => $class) {
if (!isset($config[$key])) {
continue;
}
$subId = "$id.$key";
$def = new Definition($class, [$config[$key], $notFound]);
$this->container->setDefinition($subId, $def);
$filters[] = new Reference($subId);
}
}
}
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace Irstea\PlantUmlBundle\DependencyInjection\Builder;
use Irstea\PlantUmlBundle\Doctrine\EntityFinder;
use Irstea\PlantUmlBundle\Finder\ClassFinder;
use Irstea\PlantUmlBundle\Finder\FilteringFinder;
use Irstea\PlantUmlBundle\Model\ClassVisitor;
use Irstea\PlantUmlBundle\Model\Graph;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
/**
* Description of GraphDefinitionBuilder
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class GraphDefinitionBuilder
{
/**
* @var ContainerBuilder
*/
private $container;
/**
* @var string
*/
private $baseId;
/**
* @var array
*/
private $config;
/**
* @var ClassFilterBuilder
*/
private $filterBuilder;
/**
* @var Reference
*/
private $entityManager;
/**
*
* @var Definition
*/
private $definition = null;
/**
*
* @param ContainerBuilder $container
* @param string $baseId
* @param array $config
*/
public function __construct(ContainerBuilder $container, $baseId, array $config, ClassFilterBuilder $filterBuilder)
{
$this->container = $container;
$this->baseId = $baseId;
$this->config = $config;
$this->filterBuilder = $filterBuilder;
$emId = $config['sources']['entity_manager'];
$this->entityManager = new Reference("doctrine.orm.${emId}_entity_manager");
}
/**
* @return Definition
*/
public function build()
{
if (!$this->definition) {
return $this->definition = $this->doBuild();
}
return $this->definition;
}
/**
* @return Definition
*/
protected function doBuild()
{
list($source, $sourceFilter) = $this->buildSources();
$layoutFilter = $this->filterBuilder->build($this->config['layout']) ?: $sourceFilter;
$decorator = $this->buildFilteredDecorator();
$namespace = $this->buildNamespace();
$visitor = $this->setDefinition("visitor", ClassVisitor::class, $decorator, $layoutFilter, $namespace);
return new Definition(Graph::class, [$visitor, $source]);
}
/**
* @return Refernce[]
*/
protected function buildSources()
{
$finder = $this->buildFinder();
$filter = $this->filterBuilder->build($this->config['sources']);
if ($filter) {
$filtered = $this->setDefinition("finder", FilteringFinder::class, $finder, $filter);
return [$filtered, $filter];
}
return [$finder, null];
}
/**
* @return Reference
*/
protected function buildFinder()
{
$config = $this->config['sources'];
switch($config['type']) {
case 'entities':
return $this->setDefinition("finder.entities", EntityFinder::class, $this->entityManager);
case 'classes':
return $this->setDefinition("finder.classes", ClassFinder::class, $config['directories']);
}
}
/**
* @return Reference
*/
protected function buildFilteredDecorator()
{
$decorator = $this->buildDecorator();
if (!$decorator) {
return $decorator;
}
$filter = $this->filterBuilder->build($this->config['decoration']);
if (!$filter) {
return $decorator;
}
return $this->setDefinitionDecorator('decorator', 'irstea.plant_uml.decorator.filtered.template', $decorator, $filter);
}
/**
* @return Reference
*/
protected function buildDecorator()
{
$config = $this->config['decoration']['decorators'];
if (empty($config)) {
return null;
}
if (count($config) === 1) {
return $this->buildTypedDecorator($config[0]);
}
$decorators = [];
foreach ($config as $type) {
$decorators[] = $this->buildTypedDecorator($type);
}
return $this->setDefinitionDecorator(
'decorator.all',
'irstea.plant_uml.decorator.composite.template',
$decorators
);
}
/**
* @param string $type
* @return Reference
*/
protected function buildTypedDecorator($type)
{
if (in_array($type, ['entity', 'associations', 'fields'])) {
return $this->setDefinitionDecorator(
"decorator.$type",
"irstea.plant_uml.decorator.$type.template",
$this->entityManager
);
}
return new Reference("irstea.plant_uml.decorator.$type");
}
/**
œ * @return Reference
*/
protected function buildNamespace()
{
$type = $this->config['layout']['namespaces'];
if ($type === "entities") {
return $this->setDefinitionDecorator(
"namespace.$type",
"irstea.plant_uml.namespaces.$type.template",
$this->entityManager
);
}
return new Reference("irstea.plant_uml.namespaces.$type");
}
/**
* @param string $localId
* @param string|Definition $classOrDef
* @param array ...$arguments
* @return Reference
*/
protected function setDefinition($localId, $classOrDef, ...$arguments)
{
if ($classOrDef instanceof Definition) {
$definition = $classOrDef;
} else {
$definition = new Definition($classOrDef, $arguments);
}
$id = $this->globalId($localId);
$this->container->setDefinition($id, $definition);
return new Reference($id);
}
/**
* @param string $localId
* @param string $templateId
* @param array ...$arguments
* @return Reference
*/
protected function setDefinitionDecorator($localId, $templateId, ...$arguments)
{
$def = new DefinitionDecorator($templateId);
$def->setArguments($arguments);
return $this->setDefinition($localId, $def);
}
/**
* @param string $localId
* @return string
*/
protected function globalId($localId)
{
return "{$this->baseId}.$localId";
}
}
......@@ -8,18 +8,12 @@
namespace Irstea\PlantUmlBundle\DependencyInjection;
use Irstea\PlantUmlBundle\Doctrine\EntityFinder;
use Irstea\PlantUmlBundle\Finder\ClassFinder;
use Irstea\PlantUmlBundle\Finder\FilteringFinder;
use Irstea\PlantUmlBundle\Model\ClassVisitor;
use Irstea\PlantUmlBundle\Model\Graph;
use Irstea\PlantUmlBundle\DependencyInjection\Builder\ClassFilterBuilder;
use Irstea\PlantUmlBundle\DependencyInjection\Builder\GraphDefinitionBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Reference;
/**
* Description of IrsteaPlantUmlExtension
......@@ -41,184 +35,13 @@ class IrsteaPlantUmlExtension extends Extension
$container->setParameter('irstea_plant_uml.binaries.dot', $config['binaries']['dot']);
$container->setParameter('irstea_plant_uml.output.directory', $config['output']['directory']);
$container->setParameter('irstea_plant_uml.output.format', $config['output']['format']);
foreach($config['graphs'] as $key => $graph) {
$this->loadGraph($key, $graph, $container);
}
$container->setParameter('irstea_plant_uml.graph_keys', array_keys($config['graphs']));
}
public function loadGraph($key, array $config, ContainerBuilder $container)
{
$id = "irstea_plant_uml.graph.$key";
$emName = $config['sources']['entity_manager'];
$em = new Reference("doctrine.orm.${emName}_entity_manager");
list($source, $defaultFilter) = $this->loadSources($id, $em, $config['sources'], $container);
$visitor = $this->addService(
$container,
"$id.visitor",
ClassVisitor::class,
[
$this->loadDecorator($id, $em, $config['decoration'], $defaultFilter, $container),
$this->loadFilter($id, $config['layout'], $container),
$this->loadNamespace($id, $em, $config['layout']['namespaces'], $container)
]
);
$container->setDefinition($id, new Definition(Graph::class, [$visitor, $source]))
->setPublic(true)
->setLazy(true);
}
/**
* @param string $id
* @param array $config
* @param ContainerBuilder $container
* @return Refernce[]
*/
protected function loadSources($id, Reference $em, array $config, ContainerBuilder $container)
{
$finderId = "$id.finder";
$filter = $this->loadFilter($finderId, $config, $container);
if ($filter) {
$inner = $this->loadFinder("$finderId.inner", $em, $config, $container);
$finder = $this->addService($container, $finderId, FilteringFinder::class, [$inner, $filter]);
} else {
$finder = $this->loadFinder($finderId, $em, $config, $container);
$filterBuilder = new ClassFilterBuilder($container);
foreach ($config['graphs'] as $key => $graph) {
$id = "irstea_plant_uml.graph.$key";
$builder = new GraphDefinitionBuilder($container, $id, $graph, $filterBuilder);
$container->setDefinition($id, $builder->build());
}
return [$finder, $filter];
}
/**
* @param string $id
* @param Reference $em
* @param array $config
* @param ContainerBuilder $container
* @return Reference
*/
protected function loadFinder($id, Reference $em, array $config, ContainerBuilder $container)
{
switch($config['type']) {
case 'entities':
return $this->addService($container, $id, EntityFinder::class, [$em]);
case 'classes':
return $this->addService($container, $id, ClassFinder::class, [$config['directories']]);
}
}
/**
*
* @param string $id
* @param Reference $em
* @param array $config
* @param Reference $defaultFilter
* @param ContainerBuilder $container
* @return Reference
*/
protected function loadDecorator($id, Reference $em, array $config, Reference $defaultFilter, ContainerBuilder $container)
{
if (empty($config['decorators'])) {
return new Reference('irstea.plant_uml.decorator.null');
}
$decoratorId = "$id.decorator";
$decorators = [];
foreach($config['decorators'] as $i => $type) {
$decorators[] = $this->loadTypedDecorator("$decoratorId.$i", $em, $type, $container);
}
if (count($decorators) === 1) {
$container->setAlias($decoratorId, "$decoratorId.0");
} else {
$container
->setDefinition($decoratorId, new DefinitionDecorator('irstea.plant_uml.decorator.composite.template'))
->setArguments([$decorators]);
}
$filter = $this->loadFilter($decoratorId, $config, $container) ?: $defaultFilter;
if ($filter) {
$container
->setDefinition("$decoratorId.decorator", new DefinitionDecorator('irstea.plant_uml.decorator.filtered.template'))
->setDecoratedService($decoratorId, "inner_decorator")
->setArguments([new Reference("inner_decorator"), $filter]);
}
return new Reference($decoratorId);
}
/**
* @param string $id
* @param Reference $em
* @param string $type
* @param ContainerBuilder $container
* @return Reference
*/
protected function loadTypedDecorator($id, Reference $em, $type, ContainerBuilder $container)
{
if (in_array($type, ['entity', 'associations', 'fields'])) {
$container
->setDefinition($id, new DefinitionDecorator("irstea.plant_uml.decorator.$type.template"))
->setArguments([$em]);
return new Reference($id);
}
return new Reference("irstea.plant_uml.decorator.$type");
}
/**
* @param string $id
* @param Reference $em
* @param string $type
* @param ContainerBuilder $container
* @return Reference
*/
protected function loadNamespace($id, Reference $em, $type, ContainerBuilder $container)
{
if ($type === "entities") {
$namespaceId = "$id.namespace";
$container
->setDefinition($namespaceId, new DefinitionDecorator('irstea.plant_uml.namespaces.entities.template'))
->setArguments([$em]);
return new Reference($namespaceId);
}
return new Reference("irstea.plant_uml.namespaces.$type");
}
/**
* @param string $id
* @param array $config
* @param ContainerBuilder $container
* @return Reference
*/
protected function loadFilter($id, array $config, ContainerBuilder $container)
{
$filterId = "$id.filter";
$container
->setDefinition($filterId, new DefinitionDecorator("irstea.plant_uml.filter.template"))
->setArguments([
isset($config['include']) ? $config['include'] : [],
isset($config['exclude']) ? $config['exclude'] : [],
]);
return new Reference($filterId);
}
/**
*
* @param ContainerBuilder $container
* @param string $id
* @param Definition $def
* @return Reference
*/
protected function addService(ContainerBuilder $container, $id, $class, array $arguments = [])
{
$container->setDefinition($id, new Definition($class, $arguments));
return new Reference($id);
}
}
parameters: ~
services:
irstea.plant_uml.filter.factory:
class: Irstea\PlantUmlBundle\Factory\FilterFactory
arguments: [@kernel]
irstea.plant_uml.filter.template:
class: Irstea\PlantUmlBundle\Model\ClassFilterInterface
abstract: true
factory: ["@irstea.plant_uml.filter.factory", create]
public: false
irstea.plant_uml.finder.classes.template:
class: Irstea\PlantUmlBundle\Finder\ClassFinder
abstract: true
......
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