GraphDefinitionBuilder.php 6.37 KB
Newer Older
<?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";
    }
}