Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collect meta attributes from XSD files #83

Merged
merged 6 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,129 changes: 1,085 additions & 1,044 deletions clover.xml

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/Schema/Attribute/AbstractAttributeItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

namespace GoetasWebservices\XML\XSDReader\Schema\Attribute;

use GoetasWebservices\XML\XSDReader\Schema\CustomAttributeContainerTrait;
use GoetasWebservices\XML\XSDReader\Schema\Item;

abstract class AbstractAttributeItem extends Item implements AttributeSingle
{
use CustomAttributeContainerTrait;

protected ?string $fixed = null;

protected ?string $default = null;
Expand Down
3 changes: 2 additions & 1 deletion src/Schema/Attribute/AttributeSingle.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

namespace GoetasWebservices\XML\XSDReader\Schema\Attribute;

use GoetasWebservices\XML\XSDReader\Schema\CustomAttributeContainerInterface;
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;

interface AttributeSingle extends AttributeItem
interface AttributeSingle extends AttributeItem, CustomAttributeContainerInterface
{
public const USE_OPTIONAL = 'optional';

Expand Down
45 changes: 45 additions & 0 deletions src/Schema/CustomAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace GoetasWebservices\XML\XSDReader\Schema;

/**
* Contains the information of additional custom attributes that are not part of the XSD specification.
*
* For example:
* <xsd:element name="myElement" dfdl:encoding="iso-8859-1" />
*/
class CustomAttribute
{
private string $namespaceURI;

private string $name;

private string $value;

public function __construct(
string $namespaceURI,
string $name,
string $value
) {
$this->namespaceURI = $namespaceURI;
$this->name = $name;
$this->value = $value;
}

public function getNamespaceURI(): string
{
return $this->namespaceURI;
}

public function getName(): string
{
return $this->name;
}

public function getValue(): string
{
return $this->value;
}
}
18 changes: 18 additions & 0 deletions src/Schema/CustomAttributeContainerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace GoetasWebservices\XML\XSDReader\Schema;

interface CustomAttributeContainerInterface
{
/**
* @param list<CustomAttribute> $customAttributes
*/
public function setCustomAttributes(array $customAttributes): void;

/**
* @return list<CustomAttribute>
*/
public function getCustomAttributes(): array;
}
29 changes: 29 additions & 0 deletions src/Schema/CustomAttributeContainerTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace GoetasWebservices\XML\XSDReader\Schema;

trait CustomAttributeContainerTrait
{
/**
* @var list<CustomAttribute>
*/
protected array $customAttributes = [];

/**
* @return list<CustomAttribute>
*/
public function getCustomAttributes(): array
{
return $this->customAttributes;
}

/**
* @param list<CustomAttribute> $customAttributes
*/
public function setCustomAttributes(array $customAttributes): void
{
$this->customAttributes = $customAttributes;
}
}
3 changes: 3 additions & 0 deletions src/Schema/Element/AbstractElementSingle.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

namespace GoetasWebservices\XML\XSDReader\Schema\Element;

use GoetasWebservices\XML\XSDReader\Schema\CustomAttributeContainerTrait;
use GoetasWebservices\XML\XSDReader\Schema\Item;

class AbstractElementSingle extends Item implements ElementSingle, InterfaceSetAbstract
{
use CustomAttributeContainerTrait;

protected int $min = 1;

protected int $max = 1;
Expand Down
3 changes: 2 additions & 1 deletion src/Schema/Element/ElementSingle.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

namespace GoetasWebservices\XML\XSDReader\Schema\Element;

use GoetasWebservices\XML\XSDReader\Schema\CustomAttributeContainerInterface;
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;

interface ElementSingle extends ElementItem, InterfaceSetMinMax, InterfaceSetFixed, InterfaceSetDefault
interface ElementSingle extends ElementItem, InterfaceSetMinMax, InterfaceSetFixed, InterfaceSetDefault, CustomAttributeContainerInterface
{
public function getType(): ?Type;

Expand Down
24 changes: 24 additions & 0 deletions src/SchemaReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeRef;
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeSingle;
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
use GoetasWebservices\XML\XSDReader\Schema\CustomAttribute;
use GoetasWebservices\XML\XSDReader\Schema\Element\AbstractElementSingle;
use GoetasWebservices\XML\XSDReader\Schema\Element\Choice;
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
Expand Down Expand Up @@ -236,6 +237,27 @@ private function fillAttribute(AttributeSingle $attribute, \DOMElement $node): v
if ($node->hasAttribute('use')) {
$attribute->setUse($node->getAttribute('use'));
}

$attribute->setCustomAttributes($this->loadCustomAttributesForElement($attribute, $node));
}

/**
* @return list<CustomAttribute>
*/
private function loadCustomAttributesForElement(SchemaItem $item, \DOMElement $node): array
{
$customAttributes = [];
foreach ($node->attributes as $attr) {
if (null !== $attr->namespaceURI && self::XSD_NS !== $attr->namespaceURI) {
goetas marked this conversation as resolved.
Show resolved Hide resolved
$customAttributes[] = new CustomAttribute(
$attr->namespaceURI,
$attr->name,
$attr->value
);
}
}

return $customAttributes;
}

private function loadAttributeOrElementDef(
Expand Down Expand Up @@ -1456,6 +1478,8 @@ private function fillElement(AbstractElementSingle $element, \DOMElement $node):
$element->setLocal(true);
}
}

$element->setCustomAttributes($this->loadCustomAttributesForElement($element, $node));
}

private function addAttributeFromAttributeOrRef(
Expand Down
78 changes: 78 additions & 0 deletions tests/AttributesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,82 @@ public function testAttributeUseOverriding(): void
self::assertEquals('language', $attribute->getType()->getName());
self::assertEquals(AttributeSingle::USE_REQUIRED, $attribute->getUse());
}

public function testCustomAttributesInformation(): void
{
$schema = $this->reader->readString(
'
<xs:schema targetNamespace="http://www.example.com" xmlns:tns="http://www.example.com" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="myAttribute" type="xs:string" tns:customAttributes="hello" />
</xs:schema>'
);

$myAttribute = $schema->findAttribute('myAttribute', 'http://www.example.com');
self::assertInstanceOf(AttributeDef::class, $myAttribute);

$customAttributes = $myAttribute->getCustomAttributes();
self::assertCount(1, $customAttributes);
self::assertEquals('customAttributes', $customAttributes[0]->getName());
self::assertEquals('hello', $customAttributes[0]->getValue());
self::assertEquals('http://www.example.com', $customAttributes[0]->getNamespaceURI());
}

public function testExternalSchemaReferencingCustomAttributesInformationPrefixed(): void
{
$dom = new \DOMDocument();
$dom->loadXML(
'
<types xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:schema targetNamespace="http://www.ref.com">
<xs:attribute name="customAttributesType" type="xs:string" />
</xs:schema>
<xs:schema targetNamespace="http://www.example.com" xmlns:ref="http://www.ref.com">
<xs:import namespace="http://www.ref.com" />
<xs:attribute name="myAttribute" type="xs:string" ref:customAttributesType="xs:string" />
</xs:schema>
</types>
');
$schema = $this->reader->readNodes(iterator_to_array($dom->documentElement->childNodes), 'file.xsd');

$myAttribute = $schema->findAttribute('myAttribute', 'http://www.example.com');
self::assertInstanceOf(AttributeDef::class, $myAttribute);

$customAttributes = $myAttribute->getCustomAttributes();
self::assertCount(1, $customAttributes);
self::assertEquals('customAttributesType', $customAttributes[0]->getName());
self::assertEquals('xs:string', $customAttributes[0]->getValue());

$refAttr = $schema->findAttribute('customAttributesType', 'http://www.ref.com');
self::assertSame($refAttr->getSchema()->getTargetNamespace(), $customAttributes[0]->getNamespaceURI());
}

public function testExternalSchemaReferencingCustomAttributesInformationUnprefixed(): void
{
$dom = new \DOMDocument();
$dom->loadXML(
'
<types xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:schema targetNamespace="http://www.ref.com">
<xs:attribute name="customAttributesType" type="xs:string" />
</xs:schema>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.com" xmlns:ref="http://www.ref.com">
<import namespace="http://www.ref.com" />
<attribute name="myAttribute" type="string" ref:customAttributesType="string" />
</schema>
</types>
');
$schema = $this->reader->readNodes(iterator_to_array($dom->documentElement->childNodes), 'file.xsd');

$myAttribute = $schema->findAttribute('myAttribute', 'http://www.example.com');
self::assertInstanceOf(AttributeDef::class, $myAttribute);

$customAttributes = $myAttribute->getCustomAttributes();

self::assertCount(1, $customAttributes);
self::assertEquals('customAttributesType', $customAttributes[0]->getName());
self::assertEquals('string', $customAttributes[0]->getValue());

$refAttr = $schema->findAttribute('customAttributesType', 'http://www.ref.com');
self::assertSame($refAttr->getSchema()->getTargetNamespace(), $customAttributes[0]->getNamespaceURI());
}
}
65 changes: 65 additions & 0 deletions tests/ElementsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,69 @@ public function testSequenceElementDocs(): void
$aloneElement = $myGroup->getElements()[0];
self::assertSame('Alone description', $aloneElement->getDoc());
}

public function testCustomAttributesInformation(): void
{
$schema = $this->reader->readString(
'
<xs:schema targetNamespace="http://www.example.com" xmlns:tns="http://www.example.com" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="myElement" type="xs:string" tns:customAttributes="hello" />
</xs:schema>'
);

$myElement = $schema->findElement('myElement', 'http://www.example.com');
self::assertInstanceOf(ElementDef::class, $myElement);

$customAttributes = $myElement->getCustomAttributes();
self::assertCount(1, $customAttributes);
self::assertEquals('customAttributes', $customAttributes[0]->getName());
self::assertEquals('hello', $customAttributes[0]->getValue());
self::assertEquals('http://www.example.com', $customAttributes[0]->getNamespaceURI());
}

public function testDfdlElementCustomAttributesInformation(): void
{
$schema = $this->reader->readString(
'
<xs:schema targetNamespace="http://www.example.com" xmlns:tns="http://www.example.com" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element
xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/extensions"
dfdl:encoding="iso-8859-1"
dfdl:initiator="UNA"
dfdl:length="6"
dfdl:lengthKind="explicit"
dfdl:terminator="%NL;%WSP*; %WSP*;"
minOccurs="0"
name="myElement"
type="xs:string" />
</xs:schema>'
);

$myElement = $schema->findElement('myElement', 'http://www.example.com');
self::assertInstanceOf(ElementDef::class, $myElement);

$customAttributes = $myElement->getCustomAttributes();
$namespaceUri = 'http://www.ogf.org/dfdl/dfdl-1.0/extensions';

self::assertCount(5, $customAttributes);
self::assertEquals($namespaceUri, $customAttributes[0]->getNamespaceURI());
self::assertEquals('encoding', $customAttributes[0]->getName());
self::assertEquals('iso-8859-1', $customAttributes[0]->getValue());

self::assertEquals($namespaceUri, $customAttributes[1]->getNamespaceURI());
self::assertEquals('initiator', $customAttributes[1]->getName());
self::assertEquals('UNA', $customAttributes[1]->getValue());

self::assertEquals($namespaceUri, $customAttributes[2]->getNamespaceURI());
self::assertEquals('length', $customAttributes[2]->getName());
self::assertEquals('6', $customAttributes[2]->getValue());

self::assertEquals($namespaceUri, $customAttributes[3]->getNamespaceURI());
self::assertEquals('lengthKind', $customAttributes[3]->getName());
self::assertEquals('explicit', $customAttributes[3]->getValue());

self::assertEquals($namespaceUri, $customAttributes[4]->getNamespaceURI());
self::assertEquals('terminator', $customAttributes[4]->getName());
self::assertEquals('%NL;%WSP*; %WSP*;', $customAttributes[4]->getValue());
}
}