N°8772 - test driven compiler wip

This commit is contained in:
Eric Espie
2025-12-05 16:53:02 +01:00
parent 8374d12869
commit 0d3f7c5f07
25 changed files with 334 additions and 24 deletions

View File

@@ -41,7 +41,7 @@ use utils;
/**
* Class \Combodo\iTop\DesignDocument
*
* A design document is the DOM tree that modelize behaviors. One of its
* A design document is the DOM tree that models behaviors. One of its
* characteristics is that it can be altered by the mean of the same kind of document.
*
*/

View File

@@ -554,12 +554,16 @@ return array(
'Combodo\\iTop\\PropertyTree\\CollectionOfValues' => $baseDir . '/sources/PropertyTree/CollectionOfValues.php',
'Combodo\\iTop\\PropertyTree\\Property' => $baseDir . '/sources/PropertyTree/Property.php',
'Combodo\\iTop\\PropertyTree\\PropertyTree' => $baseDir . '/sources/PropertyTree/PropertyTree.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeException' => $baseDir . '/sources/PropertyTree/PropertyTreeException.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeFactory' => $baseDir . '/sources/PropertyTree/PropertyTreeFactory.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\AbstractValueType' => $baseDir . '/sources/PropertyTree/ValueType/AbstractValueType.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeAggregateFunction' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoice' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeChoice.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClass' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClass.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttribute' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttribute.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeGroupBy' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeGroupBy.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeValue' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeValue.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeFactory' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeFactory.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeInteger' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeInteger.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeLabel' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeLabel.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeOQL' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeOQL.php',

View File

@@ -940,12 +940,16 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\PropertyTree\\CollectionOfValues' => __DIR__ . '/../..' . '/sources/PropertyTree/CollectionOfValues.php',
'Combodo\\iTop\\PropertyTree\\Property' => __DIR__ . '/../..' . '/sources/PropertyTree/Property.php',
'Combodo\\iTop\\PropertyTree\\PropertyTree' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTree.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeException' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTreeException.php',
'Combodo\\iTop\\PropertyTree\\PropertyTreeFactory' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTreeFactory.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\AbstractValueType' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/AbstractValueType.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeAggregateFunction' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoice' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeChoice.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClass' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClass.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttribute' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttribute.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeGroupBy' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeGroupBy.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeValue' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeValue.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeFactory' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeFactory.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeInteger' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeInteger.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeLabel' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeLabel.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeOQL' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeOQL.php',

View File

@@ -7,12 +7,11 @@
namespace Combodo\iTop\Forms\Compiler;
use Combodo\iTop\DesignDocument;
use Combodo\iTop\Forms\Block\Base\FormBlock;
use Combodo\iTop\ItopSdkFormDemonstrator\Helper\ItopSdkFormDemonstratorHelper;
use Combodo\iTop\PropertyTree\PropertyTreeFactory;
use Combodo\iTop\Service\Cache\DataModelDependantCache;
use Combodo\iTop\Service\DependencyInjection\DIService;
use ModelReflection;
use ModelReflectionRuntime;
use DOMFormatException;
class FormsCompiler
{
@@ -34,9 +33,29 @@ class FormsCompiler
return static::$oInstance;
}
public function CompileFormFromFile(string $filePath): ?FormBlock
/**
* Compile XML property tree into PHP to create the configuration form
*
* @param string $sXMLContent property tree structure in xml
*
* @return string
* @throws \Combodo\iTop\Forms\Compiler\FormsCompilerException
*/
public function CompileFormFromXML(string $sXMLContent): string
{
return null;
$oDoc = new DesignDocument();
libxml_clear_errors();
$oDoc->loadXML($sXMLContent);
$aErrors = libxml_get_errors();
if (count($aErrors) > 0) {
throw new FormsCompilerException('Dashlet properties definition not correctly formatted!');
}
/** @var \Combodo\iTop\DesignElement $oRoot */
$oRoot = $oDoc->firstChild;
$oPropertyTree = PropertyTreeFactory::GetInstance()->CreateNodeFromDom($oRoot);
$sPHP = $oPropertyTree->ToPHP();
return $sPHP;
}
public function StoreFormFromContent(string $sId, string $sPHPContent): void

View File

@@ -7,6 +7,7 @@
namespace Combodo\iTop\PropertyTree;
use Combodo\iTop\DesignElement;
use Combodo\iTop\PropertyTree\ValueType\AbstractValueType;
/**
@@ -14,21 +15,27 @@ use Combodo\iTop\PropertyTree\ValueType\AbstractValueType;
*/
abstract class AbstractProperty
{
protected string $sId;
protected ?string $sLabel;
/** @var array<AbstractProperty> */
protected array $aChildren;
private ?AbstractValueType $oValueType;
protected ?AbstractValueType $oValueType;
public function InitFromDomNode(DesignElement $oDomNode)
{
$this->sId = $oDomNode->getAttribute('id');
$this->sLabel = $oDomNode->GetChildText('label');
}
abstract public function ToPHP(&$aPHPFragments = []): string;
public function GetValueType(): ?AbstractValueType
{
return $this->oValueType;
}
public function SetValueType(AbstractValueType $oValueType): void
{
$this->oValueType = $oValueType;
}
public function AddChild(AbstractValueType $oValueType): void
public function AddChild(AbstractProperty $oValueType): void
{
$this->aChildren[] = $oValueType;
}

View File

@@ -12,4 +12,8 @@ namespace Combodo\iTop\PropertyTree;
*/
class CollectionOfTrees extends AbstractProperty
{
public function ToPHP(&$aPHPFragments = []): string
{
return '';
}
}

View File

@@ -12,4 +12,8 @@ namespace Combodo\iTop\PropertyTree;
*/
class CollectionOfValues extends AbstractProperty
{
public function ToPHP(&$aPHPFragments = []): string
{
return '';
}
}

View File

@@ -7,9 +7,39 @@
namespace Combodo\iTop\PropertyTree;
use Combodo\iTop\DesignElement;
use Combodo\iTop\PropertyTree\ValueType\ValueTypeFactory;
/**
* @since 3.3.0
*/
class Property extends AbstractProperty
{
/**
* @param \Combodo\iTop\DesignElement $oDomNode
*
* @return void
* @throws \Combodo\iTop\PropertyTree\PropertyTreeException
* @throws \DOMFormatException
*/
public function InitFromDomNode(DesignElement $oDomNode): void
{
parent::InitFromDomNode($oDomNode);
$oValueTypeNode = $oDomNode->GetOptionalElement('value-type');
if ($oValueTypeNode) {
$this->oValueType = ValueTypeFactory::GetInstance()->CreateValueTypeFromDomNode($oValueTypeNode);
}
}
public function ToPHP(&$aPHPFragments = []): string
{
$sFormBlockClass = $this->oValueType->GetFormBlockClass();
return <<<PHP
\$this->Add('$this->sId', '$sFormBlockClass', [
'label' => '$this->sLabel',
]);
PHP;
}
}

View File

@@ -7,9 +7,58 @@
namespace Combodo\iTop\PropertyTree;
use Combodo\iTop\DesignElement;
/**
* @since 3.3.0
*/
class PropertyTree extends AbstractProperty
{
/**
* @param \Combodo\iTop\DesignElement $oDomNode
*
* @return void
* @throws \Combodo\iTop\PropertyTree\PropertyTreeException
* @throws \DOMFormatException
*/
public function InitFromDomNode(DesignElement $oDomNode): void
{
parent::InitFromDomNode($oDomNode);
$oPropertyTreeService = PropertyTreeFactory::GetInstance();
// read child properties
foreach ($oDomNode->GetUniqueElement('nodes')->childNodes as $oNode) {
if ($oNode instanceof DesignElement) {
$this->AddChild($oPropertyTreeService->CreateNodeFromDom($oNode));
}
}
}
public function ToPHP(&$aPHPFragments = []): string
{
$bIsRoot = (count($aPHPFragments) === 0);
$sLocalPHP = <<<PHP
class FormFor$this->sId extends Combodo\iTop\Forms\Block\Base\FormBlock
{
protected function BuildForm(): void
{
PHP;
foreach ($this->aChildren as $oProperty) {
$sLocalPHP .= "\n".$oProperty->ToPHP($aPHPFragments);
}
$sLocalPHP .= <<<PHP
}
}
PHP;
$aPHPFragments[] = $sLocalPHP;
if ($bIsRoot) {
return implode("\n", $aPHPFragments);
}
return '';
}
}

View File

@@ -0,0 +1,12 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\PropertyTree;
class PropertyTreeException extends \Exception
{
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\PropertyTree;
use Combodo\iTop\DesignDocument;
use Combodo\iTop\DesignElement;
class PropertyTreeFactory
{
private static PropertyTreeFactory $oInstance;
protected function __construct()
{
}
final public static function GetInstance(): PropertyTreeFactory
{
if (!isset(static::$oInstance)) {
static::$oInstance = new PropertyTreeFactory();
}
return static::$oInstance;
}
/**
* Create a property node from a design element
*
* @param \Combodo\iTop\DesignElement $oDomNode
*
* @return \Combodo\iTop\PropertyTree\AbstractProperty
* @throws \Combodo\iTop\PropertyTree\PropertyTreeException
*/
public function CreateNodeFromDom(DesignElement $oDomNode): AbstractProperty
{
$sNodeType = $oDomNode->getAttribute('xsi:type');
if (is_a($sNodeType, AbstractProperty::class, true)) {
$oNode = new $sNodeType();
$oNode->InitFromDomNode($oDomNode);
return $oNode;
}
throw new PropertyTreeException('Unknown property node class: '.json_encode($sNodeType).' from xpath: '.DesignDocument::GetItopNodePath($oDomNode));
}
}

View File

@@ -7,10 +7,17 @@
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\DesignElement;
/**
* @since 3.3.0
*/
abstract class AbstractValueType
{
abstract public function getFormBlockClass(): string;
abstract public function GetFormBlockClass(): string;
public function InitFromDomNode(DesignElement $oDomNode): void
{
}
}

View File

@@ -14,7 +14,7 @@ use Combodo\iTop\Forms\Block\DataModel\Dashlet\AggregateFunctionFormBlock;
*/
class ValueTypeAggregateFunction extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return AggregateFunctionFormBlock::class;
}

View File

@@ -15,7 +15,7 @@ use Combodo\iTop\Forms\Block\Base\FormBlock;
*/
class ValueTypeChoice extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return ChoiceFormBlock::class;
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\Base\TextFormBlock;
use Combodo\iTop\PropertyTree\ValueType\AbstractValueType;
class ValueTypeClass extends AbstractValueType
{
public function GetFormBlockClass(): string
{
return TextFormBlock::class;
}
}

View File

@@ -14,7 +14,7 @@ use Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock;
*/
class ValueTypeClassAttribute extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return AttributeChoiceFormBlock::class;
}

View File

@@ -14,7 +14,7 @@ use Combodo\iTop\Forms\Block\DataModel\Dashlet\ClassAttributeGroupByFormBlock;
*/
class ValueTypeClassAttributeGroupBy extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return ClassAttributeGroupByFormBlock::class;
}

View File

@@ -14,7 +14,7 @@ use Combodo\iTop\Forms\Block\DataModel\AttributeValueChoiceFormBlock;
*/
class ValueTypeClassAttributeValue extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return AttributeValueChoiceFormBlock::class;
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\DesignElement;
use Combodo\iTop\PropertyTree\AbstractProperty;
use Combodo\iTop\PropertyTree\PropertyTreeException;
use DOMElement;
class ValueTypeFactory
{
private static ValueTypeFactory $oInstance;
protected function __construct()
{
}
final public static function GetInstance(): ValueTypeFactory
{
if (!isset(static::$oInstance)) {
static::$oInstance = new ValueTypeFactory();
}
return static::$oInstance;
}
public function CreateValueTypeFromDomNode(DesignElement $oDomNode): AbstractValueType
{
$sNodeType = $oDomNode->getAttribute('xsi:type');
if (is_a($sNodeType, AbstractValueType::class, true)) {
$oNode = new $sNodeType();
$oNode->InitFromDomNode($oDomNode);
return $oNode;
}
throw new PropertyTreeException('Unknown value-type node class: '.json_encode($sNodeType));
}
}

View File

@@ -14,7 +14,7 @@ use Combodo\iTop\Forms\Block\Base\IntegerFormBlock;
*/
class ValueTypeInteger extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return IntegerFormBlock::class;
}

View File

@@ -14,7 +14,7 @@ use Combodo\iTop\Forms\Block\DataModel\LabelFormBlock;
*/
class ValueTypeLabel extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return LabelFormBlock::class;
}

View File

@@ -14,7 +14,7 @@ use Combodo\iTop\Forms\Block\DataModel\OqlFormBlock;
*/
class ValueTypeOQL extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return OqlFormBlock::class;
}

View File

@@ -12,7 +12,7 @@ namespace Combodo\iTop\PropertyTree\ValueType;
*/
class ValueTypeProfileName extends AbstractValueType
{
public function getFormBlockClass(): string
public function GetFormBlockClass(): string
{
return '';
}

View File

@@ -108,3 +108,4 @@ class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeClassAttributeGroupBy:
class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeChoice::class, 'Combodo-ValueTypeChoice');
class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeClassAttribute::class, 'Combodo-ValueTypeClassAttribute');
class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeInteger::class, 'Combodo-ValueTypeInteger');
class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeClass::class, 'Combodo-ValueTypeClass');

View File

@@ -0,0 +1,54 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Forms\Compiler\FormsCompiler;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
class TestFormsCompiler extends ItopDataTestCase
{
public function testCompileFormFromXML()
{
$sXMLContent = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<node id="DashletTest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Combodo-PropertyTree">
<nodes>
<node id="title" xsi:type="Combodo-Property">
<label>UI:DashletTest:Prop-Title</label>
<value-type xsi:type="Combodo-ValueTypeLabel">
</value-type>
</node>
<node id="class" xsi:type="Combodo-Property">
<label>UI:DashletTest:Prop-Class</label>
<value-type xsi:type="Combodo-ValueTypeClass">
</value-type>
</node>
</nodes>
</node>
XML;
$sExpectedPHP = <<<PHP
class FormForDashletTest extends Combodo\iTop\Forms\Block\Base\FormBlock
{
protected function BuildForm(): void
{
\$this->Add('title', 'Combodo\iTop\Forms\Block\DataModel\LabelFormBlock', [
'label' => 'UI:DashletTest:Prop-Title',
]);
\$this->Add('class', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [
'label' => 'UI:DashletTest:Prop-Class',
]);
}
}
PHP;
$sProducedPHP = FormsCompiler::GetInstance()->CompileFormFromXML($sXMLContent);
eval($sProducedPHP);
$this->assertEquals($sExpectedPHP, $sProducedPHP);
}
}