Skip to content

Commit

Permalink
Merge pull request #9 from miqwit/ddex411
Browse files Browse the repository at this point in the history
Support DDEX 4.1.1 and *List elemens
  • Loading branch information
miqwit authored Oct 27, 2021
2 parents 1fd56ec + 8f9209b commit 9e9759f
Show file tree
Hide file tree
Showing 341 changed files with 85,004 additions and 131 deletions.
4 changes: 3 additions & 1 deletion CONTRIBUTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ I used the superb `xsd2php` package to generate classes based on the XSD provide

The parser is "filling" these objects while parsing the XML file line by line.

Example for generating DDEX 411 entity classes:

```
vendor/bin/xsd2php convert config.yml /home/my/ota/OTA_Air*.xsd
./vendor/goetas-webservices/xsd2php/bin/xsd2php convert config/xsd2php.yaml xsd/release_notification/411/*.xsd
```
3 changes: 3 additions & 0 deletions config/xsd2php.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ xsd2php:
namespaces:
'http://ddex.net/xml/ern/382': 'DedexBundle/Entity/Ern382'
'http://ddex.net/xml/ern/41': 'DedexBundle/Entity/Ern41'
'http://ddex.net/xml/ern/411': 'DedexBundle/Entity/Ern411'
'http://ddex.net/xml/avs/avs': 'DedexBundle/Entity/Avs'

destinations_php:
'DedexBundle\Entity\Ern382': src/Entity/Ern382
'DedexBundle\Entity\Ern41': src/Entity/Ern41
'DedexBundle\Entity\Ern411': src/Entity/Ern411
'DedexBundle\Entity\Avs': src/Entity/Avs

destinations_jms:
'DedexBundle\Entity\Ern382': src/Entity/Ern382/metadata
'DedexBundle\Entity\Ern41': src/Entity/Ern41/metadata
'DedexBundle\Entity\Ern411': src/Entity/Ern411/metadata
'DedexBundle\Entity\Avs': src/Entity/Avs/metadata

# Uncomment this section if you want to have also symfony/validator metadata to be generated from XSD
Expand Down
130 changes: 81 additions & 49 deletions src/Controller/ErnParserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ public function setXsdValidation(bool $val) {
"xs:schemaLocation",
"xsi:schemaLocation",
"xmlns:avs",
"ReleaseResourceReferenceList" // TODO generic
];

/**
Expand All @@ -223,6 +222,7 @@ private function callbackStartElement($parser, string $name, array $attrs) {
$name = "NewReleaseMessage";
} else {
$parent = end($this->pile);

$class_name = $this->getTypeOfElementFromDoc(get_class($parent), $name);

if (!class_exists($class_name)) {
Expand All @@ -240,7 +240,7 @@ private function callbackStartElement($parser, string $name, array $attrs) {
} else {
$this->pile[$name . "##" . random_int(2, 99999)] = $elem;
}

// Will process attributes later
$this->attrs_to_process[count($this->pile)] = $attrs;
}
Expand Down Expand Up @@ -279,8 +279,15 @@ private function callbackEndElement($parser, string $name) {
if (in_array($name, $this->ignore_these_tags_or_attributes)) {
return;
}

if (!$this->set_to_parent) {

$properties = array_filter(array_values((array) end($this->pile)));
if (count($properties) == 0 && !$this->set_to_parent) {
// If we are leaving an element that is completely empty (the object
// at the end of the pile, converted to an array, only contains emtpy
// values), then to not add this element to parent. It's an empty List
// element in DDEX, like ReleaseResourceReferenceList
array_pop($this->pile);
} else if (!$this->set_to_parent) {
// Process attributes now.
// Consider each attribute as a setter of current element
// if attribute is MessageSchemaVersionId on ern:NewReleaseMessage
Expand Down Expand Up @@ -327,19 +334,10 @@ private function attachToParent() {
}

$keys = array_keys($this->pile);
$parent_tag = $keys[count($keys) - 2];
$child_tag = end($keys);

$parent = $this->pile[$parent_tag];
$child = $this->pile[$child_tag];

$func_names = $this->listPossibleFunctionNames("set", $child_tag);
foreach ($func_names as $func_name) {
if (!method_exists($parent, $func_name)) {
continue;
}
break;
}

[$func_name, $parent] = $this->getValidFunctionName("set", $child_tag, $child);

$parent->$func_name($child);
}
Expand Down Expand Up @@ -368,23 +366,45 @@ private function listPossibleFunctionNames($prefix, $tag) {
if (strpos($tag, "##") !== false) {
$tag = preg_replace("/##\d+/", "", $tag);
}

$func_names = [];

switch ($prefix) {
case "get":
$func_names[] = $prefix . $tag;
$func_names[] = $prefix . $tag . "s";
$func_names[] = $prefix . $tag . "List";
// $prefix . $tag . "Type",

// Hack for release deals. DDEX 4.1.1 is not consistent. It has a DealList
// and ReleaseDeals in it. This parser would expect a ReleaseDealList instead
// of the actual DealList
if ($tag == "ReleaseDeal") {
$func_names[] = $prefix . "DealList";
}

$func_names = [
$prefix . $tag,
$prefix . $tag . "s",
$prefix . $tag . "Type",
$prefix . $tag . "List"
];

// Add to func names also the addTo, because can be array. Will be tested first
if ($prefix == "set") {
$func_names = array_merge([
"addTo" . $tag,
"addTo" . $tag . "List",
"create" . $tag,
"create" . $tag . "List"
],
$func_names);
break;
case "set":
// order is important
$func_names[] = "addTo" . $tag;
$func_names[] = "addTo" . $tag . "List";
$func_names[] = "create" . $tag;
$func_names[] = "create" . $tag . "List";
$func_names[] = $prefix . $tag;
$func_names[] = $prefix . $tag;
$func_names[] = $prefix . $tag . "s";
$func_names[] = $prefix . $tag . "List";

// Hack for release deals. DDEX 4.1.1 is not consistent. It has a DealList
// and ReleaseDeals in it. This parser would expect a ReleaseDealList instead
// of the actual DealList
if ($tag == "ReleaseDeal") {
$func_names[] = "addToDealList";
}

break;
default:
throw new \Exception("Prefix must be get or set");
}

return $func_names;
Expand Down Expand Up @@ -439,27 +459,38 @@ private function setCurrentElement($value) {
*
* @param string $prefix "set" or "get"
* @param type $tag
* @param type $elem
* @return type
* @throws Exception
*/
private function getValidFunctionName($prefix, $tag, $elem) {
private function getValidFunctionName($prefix, $tag, $value = null) {
$func_names = $this->listPossibleFunctionNames($prefix, $tag);

$function_used = false;
foreach ($func_names as $func_name) {
if (!method_exists($elem, $func_name)) {
continue;
}
$function_used = true;
break;

$elem = end($this->pile);

// If type is complex, always start at previous than end,
// as end will be itself
if ($value != null && !in_array(get_class($value), ["string", "int", "bool", "float", "mixed"])) {
$elem = prev($this->pile);
}

while (true) {
$function_used = false;
foreach ($func_names as $func_name) {
if (!method_exists($elem, $func_name)) {
continue;
}
$function_used = true;
break 2;
}

if (!$function_used) {
throw new Exception("No functions found for this tag: $tag. Path is " . implode(",", array_keys($this->pile)));
// Continue with previous element if exists
$elem = prev($this->pile);
if ($elem === false) {
throw new Exception("No functions found for this tag: $tag. Path is " . implode(",", array_keys($this->pile)));
}
}

return $func_name;
return array($func_name, $elem);
}

private function expectedParamIsArray($class, $func_name) {
Expand All @@ -486,16 +517,17 @@ private function expectedParamIsArray($class, $func_name) {
* @return string
*/
private function getTypeOfElementFromDoc($class, $tag) {
$function = $this->getValidFunctionName("get", $tag, $class);

$rc = new ReflectionMethod($class, $function);
[$function_name, $class] = $this->getValidFunctionName("get", $tag);
$rc = new ReflectionMethod($class, $function_name);
$doc = $rc->getDocComment();
preg_match("/@return (\S+).*/", $doc, $matches);
if (count($matches) > 1) {
$type = str_replace("[]", "", $matches[1]);
} else {
$type = "\\DedexBundle\\Entity\\Ern{$this->version}\\{$tag}Type";
$type = "\\DedexBundle\\Entity\\Ern{$this->version}\\{$tag}Type";
}

return $type;
}

Expand Down Expand Up @@ -598,7 +630,7 @@ private function newXmlParser(string $file) {
*/
private function detectVersion($fp) {
$version = null;
$supported_versions = ["41", "382"];
$supported_versions = ["411", "41", "382"];

while (($buffer = fgets($fp, 4096)) !== false) {
$trimed = trim($buffer);
Expand Down
Loading

0 comments on commit 9e9759f

Please sign in to comment.