GraphDefinitionBuilder.php 6.39 KB
Newer Older
Guillaume Perréal's avatar
Guillaume Perréal committed
 * Copyright (C) 2016-2017 IRSTEA
 * All rights reserved.
 */

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;

/**
Guillaume Perréal's avatar
Guillaume Perréal committed
 * Description of GraphDefinitionBuilder.
 */
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
Guillaume Perréal's avatar
Guillaume Perréal committed
     * @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();
        }
Guillaume Perréal's avatar
Guillaume Perréal committed

        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();

Guillaume Perréal's avatar
Guillaume Perréal committed
        $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) {
Guillaume Perréal's avatar
Guillaume Perréal committed
            $filtered = $this->setDefinition('finder', FilteringFinder::class, $finder, $filter);

            return [$filtered, $filter];
        }

        return [$finder, null];
    }

    /**
     * @return Reference
     */
    protected function buildFinder()
    {
        $config = $this->config['sources'];

Guillaume Perréal's avatar
Guillaume Perréal committed
        switch ($config['type']) {
Guillaume Perréal's avatar
Guillaume Perréal committed
                return $this->setDefinition('finder.entities', EntityFinder::class, $this->entityManager);
Guillaume Perréal's avatar
Guillaume Perréal committed
                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
Guillaume Perréal's avatar
Guillaume Perréal committed
     *
     * @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'];
Guillaume Perréal's avatar
Guillaume Perréal committed
        if ($type === 'entities') {
            return $this->setDefinitionDecorator(
                "namespace.$type",
                "irstea.plant_uml.namespaces.$type.template",
                $this->entityManager
            );
        }

        return $this->setDefinitionDecorator(
            "namespace.$type",
            "irstea.plant_uml.namespaces.$type.template"
Guillaume Perréal's avatar
Guillaume Perréal committed
     * @param string            $localId
     * @param string|Definition $classOrDef
Guillaume Perréal's avatar
Guillaume Perréal committed
     * @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);
Guillaume Perréal's avatar
Guillaume Perréal committed

        return new Reference($id);
    }

    /**
     * @param string $localId
     * @param string $templateId
Guillaume Perréal's avatar
Guillaume Perréal committed
     * @param array  ...$arguments
     *
     * @return Reference
     */
    protected function setDefinitionDecorator($localId, $templateId, ...$arguments)
    {
        $def = new DefinitionDecorator($templateId);
        $def->setArguments($arguments);
Guillaume Perréal's avatar
Guillaume Perréal committed

        return $this->setDefinition($localId, $def);
    }

    /**
     * @param string $localId
Guillaume Perréal's avatar
Guillaume Perréal committed
     *
     * @return string
     */
    protected function globalId($localId)
    {
        return "{$this->baseId}.$localId";
    }
}