acrn-hypervisor/misc/config_tools/scenario_config/scenario_transformer.py
Junjie Mao 5bd3e96426 config_tools: add default values to vBDF of vUART and IVSHMEM
Different from other nodes which have static default values, the virtual
BDF nodes of virtual UART endpoints or IVSHMEM interfaces have a set of
default values and should be used with uniqueness guaranteed. The existing
default value mechanism in XSD does not include support for this.

This patch adds two more ACRN-specific annotations, namely `acrn:defaults`
and `acrn:unique-among`, that have the following semantics.

  - `acrn:defaults` is a Python expression that evaluates to an iterable.

  - `acrn:unique-among` is an xpath that specifies the nodes. Each node
    within the select ones shall have a value unique among them. The xpath
    is evaluated with the variable `parent` defined as the parent node
    under which the vBDF node is to be appended.

Tracked-On: #7330
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
2022-04-21 10:08:53 +08:00

104 lines
4.3 KiB
Python

#!/usr/bin/env python3
#
# Copyright (C) 2022 Intel Corporation.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import elementpath
class ScenarioTransformer:
xpath_ns = {
"xs": "http://www.w3.org/2001/XMLSchema",
}
@classmethod
def get_node(cls, element, xpath):
return element.find(xpath, namespaces=cls.xpath_ns)
def __init__(self, xsd_etree, visit_optional_node=False):
self.xsd_etree = xsd_etree
self.xml_etree = None
self._visit_optional_node = visit_optional_node
def type_of_element(self, type_tag, xsd_element_node, xml_node):
xsd_alternative_node = xsd_element_node
if xml_node is not None:
for alternative in xsd_element_node.findall("xs:alternative", namespaces=self.xpath_ns):
if elementpath.select(xml_node, alternative.get("test")):
xsd_alternative_node = alternative
break
type_node = xsd_alternative_node.find(type_tag, namespaces=self.xpath_ns)
if type_node is None:
type_name = xsd_alternative_node.get("type")
if type_name:
type_node = self.get_node(self.xsd_etree, f".//{type_tag}[@name='{type_name}']")
return type_node
def simple_type_of_element(self, xsd_element_node, xml_node = None):
return self.type_of_element("xs:simpleType", xsd_element_node, xml_node)
def complex_type_of_element(self, xsd_element_node, xml_node = None):
return self.type_of_element("xs:complexType", xsd_element_node, xml_node)
def transform_node(self, xsd_element_node, xml_node):
complex_type_node = self.complex_type_of_element(xsd_element_node, xml_node)
if complex_type_node is not None:
xsd_sequence_node = complex_type_node.find("xs:sequence", namespaces=self.xpath_ns)
if xsd_sequence_node is not None:
self.transform_sequence(xsd_sequence_node, xml_node)
xsd_all_node = complex_type_node.find("xs:all", namespaces=self.xpath_ns)
if xsd_all_node is not None:
self.transform_all(xsd_all_node, xml_node)
def transform_sequence(self, xsd_sequence_node, xml_node):
children = list(enumerate(list(xml_node)))
for xsd_element_node in xsd_sequence_node.findall("xs:element", namespaces=self.xpath_ns):
element_name = xsd_element_node.get("name")
element_min_occurs = xsd_element_node.get("minOccurs")
if len(children) == 0 or children[0][1].tag != element_name:
if self._visit_optional_node or element_min_occurs != "0":
index = children[0][0] if len(children) > 0 else None
self.add_and_transform_missing_node(xsd_element_node, xml_node, new_node_index=index)
else:
while len(children) > 0 and children[0][1].tag == element_name:
self.transform_node(xsd_element_node, children.pop(0)[1])
def transform_all(self, xsd_all_node, xml_node):
for xsd_element_node in xsd_all_node.findall("xs:element", namespaces=self.xpath_ns):
element_name = xsd_element_node.get("name")
if not element_name:
continue
xml_children = xml_node.findall(element_name)
if len(xml_children) == 0:
element_min_occurs = xsd_element_node.get("minOccurs")
if self._visit_optional_node or element_min_occurs != "0":
self.add_and_transform_missing_node(xsd_element_node, xml_node)
else:
for xml_child_node in xml_children:
self.transform_node(xsd_element_node, xml_child_node)
def add_and_transform_missing_node(self, xsd_element_node, xml_parent_node, new_node_index=None):
for new_node in self.add_missing_nodes(xsd_element_node, xml_parent_node, new_node_index):
self.transform_node(xsd_element_node, new_node)
def add_missing_nodes(self, xsd_element_node, xml_parent_node, new_node_index):
return []
def transform(self, xml_etree):
self.xml_etree = xml_etree
xml_root_node = xml_etree.getroot()
xsd_root_node = self.get_node(self.xsd_etree, f".//xs:element[@name='{xml_root_node.tag}']")
if xsd_root_node is not None:
self.transform_node(xsd_root_node, xml_root_node)
self.xml_etree = None