IrsteaPlantUmlExtension.php 7.81 KB
Newer Older
<?php

/*
 * © 2016 IRSTEA
 * Guillaume Perréal <guillaume.perreal@irstea.fr>
 * Tous droits réservés.
 */

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 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
 *
 * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
 */
class IrsteaPlantUmlExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');

        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $container->setParameter('irstea_plant_uml.binaries.java', $config['binaries']['java']);
        $container->setParameter('irstea_plant_uml.binaries.plamtuml_jar', $config['binaries']['plamtuml_jar']);
        $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);
        }

        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"))
            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'))
            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);