Commit 85f81393 authored by Guillaume Perréal's avatar Guillaume Perréal
Browse files

Modélisation propres des namespaces et de writer.

No related merge requests found
Showing with 495 additions and 97 deletions
+495 -97
......@@ -8,7 +8,9 @@ namespace Irstea\PlantUmlBundle\Command;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
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;
......@@ -50,11 +52,9 @@ class EntitySchemaCommand extends ContainerAwareCommand
$visitor->visitClass($metadata->getName());
}
$stream = fopen("php://output", "wt");
fputs($stream, "@startuml\n");
fputs($stream, "set namespaceSeparator .\n");
$visitor->outputTo($stream);
fputs($stream, "@enduml\n");
fclose($stream);
$writer = new OutputWriter($output);
$writer->write("@startuml\n");
$visitor->outputTo($writer);
$writer->write("@enduml\n");
}
}
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace Irstea\PlantUmlBundle\Model;
use Irstea\PlantUmlBundle\Writer\WriterInterface;
/**
* Description of Namespace
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
abstract class AbstractNamespace implements UmlComponentInterface, NamespaceInterface
{
/**
* @var UmlNodeInterface[]
*/
private $nodes = [];
/**
* @var NamespaceInterface
*/
private $children = [];
/**
*
* @param UmlNodeInterface $node
*/
public function addNode(UmlNodeInterface $node)
{
$this->nodes[] = $node;
return $this;
}
/**
* @param type $namespaceName
* @return NamespaceInterface
*/
public function getNamespace($namespaceName)
{
$namespaceName = trim($namespaceName, '\\');
if (!$namespaceName) {
return $this;
}
@list($head, $tail) = explode('\\', $namespaceName, 2);
if (!isset($this->children[$head])) {
$this->children[$head] = new Namespace_($this, $head);
}
if (empty($tail)) {
return $this->children[$head];
}
return $this->children[$head]->getNamespace($tail);
}
/**
* @param WriterInterface $writer
* @return self
*/
protected function outputNodesTo(WriterInterface $writer)
{
foreach ($this->nodes as $node) {
$node->outputTo($writer);
}
return $this;
}
/**
* @param WriterInterface $writer
* @return self
*/
protected function outputChildrenTo(WriterInterface $writer)
{
foreach ($this->children as $child) {
$child->outputTo($writer);
}
return $this;
}
/**
* @return bool
*/
protected function isEmpty()
{
return empty($this->children) && empty($this->nodes);
}
}
......@@ -8,15 +8,16 @@
namespace Irstea\PlantUmlBundle\Model\Arrow;
use Irstea\PlantUmlBundle\Model\UmlComponentInterface;
use Irstea\PlantUmlBundle\Model\UmlArrowInterface;
use Irstea\PlantUmlBundle\Model\UmlNodeInterface;
use Irstea\PlantUmlBundle\Writer\WriterInterface;
/**
* Description of Arrow
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class BaseArrow implements UmlComponentInterface
class BaseArrow implements UmlArrowInterface
{
/**
* @var UmlNodeInterface
......@@ -41,24 +42,27 @@ class BaseArrow implements UmlComponentInterface
$this->label = $label;
}
public function outputTo($stream)
public function outputTo(WriterInterface $writer)
{
$this->source->outputAliasTo($stream);
$this->outputLinkTo($stream);
$this->target->outputAliasTo($stream);
$this->outputLabelTo($stream);
fputs($stream, "\n");
$this->source->outputAliasTo($writer);
$this->outputLinkTo($writer);
$this->target->outputAliasTo($writer);
$this->outputLabelTo($writer);
$writer->write("\n");
return $this;
}
protected function outputLabelTo($stream)
protected function outputLabelTo(WriterInterface $writer)
{
if ($this->label) {
fputs($stream, " : {$this->label}");
$writer->printf(" : %s", $this->label);
}
return $this;
}
protected function outputLinkTo($stream)
protected function outputLinkTo(WriterInterface $writer)
{
fputs($stream, " ".$this->link." ");
$writer->printf(" %s ", $this->link);
return $this;
}
}
......@@ -11,6 +11,7 @@ namespace Irstea\PlantUmlBundle\Model;
use Irstea\PlantUmlBundle\Model\Node\Class_;
use Irstea\PlantUmlBundle\Model\Node\Interface_;
use Irstea\PlantUmlBundle\Model\Node\Trait_;
use Irstea\PlantUmlBundle\Writer\WriterInterface;
use ReflectionClass;
/**
......@@ -21,19 +22,14 @@ use ReflectionClass;
class ClassVisitor implements ClassVisitorInterface, UmlComponentInterface
{
/**
* @var UmlNodeInterface[]
* @var RootNamespace
*/
protected $nodes = [];
protected $rootNamespace;
/**
* @var UmlComponentInterface[]
*/
protected $arrows = [];
/**
* @var string[]
*/
protected $namespaces = [];
public function __construct()
{
$this->rootNamespace = new RootNamespace();
}
public function visitClass($className)
{
......@@ -69,8 +65,11 @@ class ClassVisitor implements ClassVisitorInterface, UmlComponentInterface
} else {
$node = new Class_($class->getName(), $class->isAbstract(), $class->isFinal());
}
$this->nodes[$class->getName()] = $node;
$this->visitNamespace($class->getNamespaceName());
$namespace = $this->rootNamespace->getNamespace($class->getNamespaceName());
$namespace->addNode($node);
$parentClass = $class->getParentClass();
$traitNames = $class->getTraitNames();
......@@ -90,59 +89,25 @@ class ClassVisitor implements ClassVisitorInterface, UmlComponentInterface
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, $namespace, [$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->visitRelations($node, $namespace, $interfaceNames, 'Irstea\PlantUmlBundle\Model\Arrow\ImplementsInterface');
$this->visitRelations($node, $namespace, $traitNames, 'Irstea\PlantUmlBundle\Model\Arrow\UsesTrait');
return $node;
}
protected function visitRelations(UmlNodeInterface $source, array $classNames, $relationClass)
protected function visitRelations(UmlNodeInterface $source, NamespaceInterface $namespace, array $classNames, $relationClass)
{
foreach ($classNames as $className) {
$target = $this->visitClass($className);
$this->arrows[] = new $relationClass($source, $target);
$namespace->addArrow(new $relationClass($source, $target));
}
}
protected function visitNamespace($namespaceName)
public function outputTo(WriterInterface $writer)
{
$current =& $this->namespaces;
foreach(explode('\\', $namespaceName) as $part) {
if (!isset($current[$part])) {
$current[$part] = [];
}
$current = &$current[$part];
}
}
public function outputTo($stream)
{
$this->outputNamespacesTo($stream, $this->namespaces);
usort($this->nodes, 'Irstea\PlantUmlBundle\Model\Node\BaseNode::compare');
foreach ($this->nodes as $node) {
$node->outputTo($stream);
}
foreach ($this->arrows as $arrow) {
$arrow->outputTo($stream);
}
}
protected function outputNamespacesTo($stream, $namespaces)
{
foreach($namespaces as $name => $children) {
fputs($stream, "namespace ");
/*while(count($children) == 1) {
fputs($stream, $name.'.');
list($name, $children) = each($children);
}*/
fputs($stream, "$name {\n");
$this->outputNamespacesTo($stream, $children);
fputs($stream, "}\n");
}
return $this->rootNamespace->outputTo($writer);
}
}
<?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 NamespaceInterface
{
/**
* @param string $namespaceName
* @return NamespaceInterface
*/
public function getNamespace($namespaceName);
/**
* @param UmlNodeInterface $node
* @return self
*/
public function addNode(UmlNodeInterface $node);
/**
* @param UmlArrowInterface $arrow
* @return self
*/
public function addArrow(UmlArrowInterface $arrow);
}
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace Irstea\PlantUmlBundle\Model;
use Irstea\PlantUmlBundle\Writer\WriterInterface;
/**
* Description of Namespace
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class Namespace_ extends AbstractNamespace
{
/**
* @var NamespaceInterface
*/
private $parent;
/**
* @var string
*/
private $name;
public function __construct(NamespaceInterface $parent, $name)
{
$this->parent = $parent;
$this->name = $name;
}
/**
* @param UmlArrowInterface $arrow
* @return self
*/
public function addArrow(UmlArrowInterface $arrow)
{
$this->parent->addArrow($arrow);
return $this;
}
/**
* @param resource WriterInterface $writer
* @return self
*/
public function outputTo(WriterInterface $writer)
{
if ($this->isEmpty()) {
return;
}
$writer
->printf("namespace %s {\n", $this->name)
->indent();
$this
->outputNodesTo($writer)
->outputChildrenTo($writer);
$writer
->dedent()
->write("}\n");
return $this;
}
}
......@@ -9,6 +9,7 @@
namespace Irstea\PlantUmlBundle\Model\Node;
use Irstea\PlantUmlBundle\Model\UmlNodeInterface;
use Irstea\PlantUmlBundle\Writer\WriterInterface;
/**
* Description of Class
......@@ -22,6 +23,11 @@ class BaseNode implements UmlNodeInterface
*/
private $name;
/**
* @var string
*/
private $alias;
/**
* @var string
*/
......@@ -50,57 +56,72 @@ class BaseNode implements UmlNodeInterface
*/
function __construct($name, $nodeType, array $classifiers = [], array $stereotypes = [], $ordering = 0)
{
$this->name = $name;
$pos = strrpos($name, '\\');
$this->name = substr($name, $pos + 1);
$this->alias = str_replace('\\', '.', $name)."_node";
$this->nodeType = $nodeType;
$this->classifiers = $classifiers;
$this->stereotypes = $stereotypes;
$this->ordering = $ordering;
}
public function outputTo($stream)
public function outputTo(WriterInterface $writer)
{
$this->outputClassifiersTo($stream);
$this->outputNodeTypeTo($stream);
$this->outputAliasTo($stream);
$this->outputStereotypesTo($stream);
fputs($stream, " {\n");
$this->outputAttributesTo($stream);
$this->outputMethodsTo($stream);
fputs($stream, "}\n");
$this
->outputClassifiersTo($writer)
->outputNodeTypeTo($writer);
$writer->printf('"%s" as %s', $this->name, $this->alias);
$this
->outputStereotypesTo($writer);
$writer
->write(" {\n")
->indent();
$this
->outputAttributesTo($writer)
->outputMethodsTo($writer);
$writer
->dedent()
->write("}\n");
return $this;
}
public function outputAliasTo($stream)
public function outputAliasTo(WriterInterface $writer)
{
fputs($stream, '"'.str_replace('\\', '.', $this->name).'"');
$writer->write($this->alias);
return $this;
}
protected function outputClassifiersTo($stream)
protected function outputClassifiersTo(WriterInterface $writer)
{
foreach($this->classifiers as $classifier) {
fputs($stream, "$classifier ");
$writer->printf('%s ', $classifier);
}
return $this;
}
protected function outputNodeTypeTo($stream)
protected function outputNodeTypeTo(WriterInterface $writer)
{
fputs($stream, $this->nodeType." ");
$writer->printf('%s ', $this->nodeType);
return $this;
}
protected function outputStereotypesTo($stream)
protected function outputStereotypesTo(WriterInterface $writer)
{
foreach($this->stereotypes as $stereotypes) {
fputs($stream, " <<$stereotypes>>");
foreach($this->stereotypes as $stereotype) {
$writer->printf(' <<%s>>', $stereotype);
}
return $this;
}
protected function outputAttributesTo($stream)
protected function outputAttributesTo(WriterInterface $writer)
{
// NOP
return $this;
}
protected function outputMethodsTo($stream)
protected function outputMethodsTo(WriterInterface $writer)
{
// NOP
return $this;
}
static public function compare(BaseNode $a, BaseNode $b)
......
......@@ -25,6 +25,8 @@ class EntityVisitor extends ClassVisitor
public function __construct(array $metadata)
{
parent::__construct();
foreach($metadata as $md) {
/* @var $md ClassMetadata */
$this->metadata[$md->getReflectionClass()->getName()] = $md;
......
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace Irstea\PlantUmlBundle\Model;
/**
* Description of RootNamespace
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class RootNamespace extends AbstractNamespace
{
/**
* @var UmlArrowInterface[]
*/
private $arrows = [];
public function addArrow(UmlArrowInterface $arrow)
{
$this->arrows[] = $arrow;
return $this;
}
public function outputTo(\Irstea\PlantUmlBundle\Writer\WriterInterface $writer)
{
$this
->outputNodesTo($writer)
->outputChildrenTo($writer)
->outputArrowsTo($writer);
return $this;
}
protected function outputArrowsTo(\Irstea\PlantUmlBundle\Writer\WriterInterface $writer)
{
foreach ($this->arrows as $arrow) {
$arrow->outputTo($writer);
}
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 UmlArrowInterface extends UmlComponentInterface
{
}
......@@ -8,11 +8,16 @@
namespace Irstea\PlantUmlBundle\Model;
use Irstea\PlantUmlBundle\Writer\WriterInterface;
/**
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
interface UmlComponentInterface
{
public function outputTo($stream);
/**
* @param WriterInterface $writer
*/
public function outputTo(WriterInterface $writer);
}
......@@ -8,11 +8,16 @@
namespace Irstea\PlantUmlBundle\Model;
use Irstea\PlantUmlBundle\Writer\WriterInterface;
/**
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
interface UmlNodeInterface extends \Irstea\PlantUmlBundle\Model\UmlComponentInterface
interface UmlNodeInterface extends UmlComponentInterface
{
public function outputAliasTo($stream);
/**
* @param WriterInterface $writer
*/
public function outputAliasTo(WriterInterface $writer);
}
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace Irstea\PlantUmlBundle\Writer;
/**
* Description of AbstractWriter
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
abstract class AbstractWriter implements WriterInterface
{
/**
* @var int
*/
private $indentation = 0;
/**
* @var string
*/
private $buffer;
public function dedent($n = 1)
{
$this->indentation = max(0, $this->indentation - $n);
return $this;
}
public function indent($n = 1)
{
$this->indentation += $n;
return $this;
}
public function printf($format)
{
$args = func_get_args();
array_shift($args);
$this->write(vsprintf($format, $args));
return $this;
}
public function write($data)
{
$buffer = $this->buffer . $data;
$indentation = str_repeat(" ", $this->indentation);
$current = 0;
while($current < strlen($buffer) && false !== ($next = strpos($buffer, "\n", $current))) {
$next++;
$this->doWrite($indentation . substr($buffer, $current, $next - $current));
$current = $next;
}
$this->buffer = substr($buffer, $current);
return $this;
}
/**
* @param string $data
*/
abstract protected function doWrite($data);
}
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace Irstea\PlantUmlBundle\Writer;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Description of OutputWriter
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class OutputWriter extends AbstractWriter
{
/**
* @var OutputInterface
*/
private $output;
public function __construct(OutputInterface $output)
{
$this->output = $output;
}
protected function doWrite($data)
{
$this->output->write($data);
}
}
<?php
/*
* © 2016 IRSTEA
* Guillaume Perréal <guillaume.perreal@irstea.fr>
* Tous droits réservés.
*/
namespace Irstea\PlantUmlBundle\Writer;
/**
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
interface WriterInterface
{
/**
* @param int $n
* @return self
*/
public function indent($n = 1);
/**
* @param int $n
* @return self
*/
public function dedent($n = 1);
/**
* @param string $data
* @return self
*/
public function write($data);
/**
* @param string $fmt
* @param mixed ...
* @return self
*/
public function printf($fmt);
}
Supports Markdown
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