|
| 1 | +import sys |
| 2 | + |
| 3 | +from .sort_xml import XMLSchemaWalker, namespaces |
| 4 | +from .sheet import Sheet |
| 5 | + |
| 6 | + |
| 7 | +class XMLSchemaWalkerForTemplate(XMLSchemaWalker): |
| 8 | + def attribute_loop(self, element): |
| 9 | + """ |
| 10 | + Returns a list containing a tuple for each attribute the given element |
| 11 | + can have. |
| 12 | + The format of the tuple is (name, is_required) |
| 13 | + """ |
| 14 | + #if element.find("xsd:complexType[@mixed='true']", namespaces=namespaces) is not None: |
| 15 | + # print_column_info('text', indent) |
| 16 | + |
| 17 | + a = element.attrib |
| 18 | + type_attributes = [] |
| 19 | + type_attributeGroups = [] |
| 20 | + if 'type' in a: |
| 21 | + complexType = self.get_schema_element('complexType', a['type']) |
| 22 | + if complexType is not None: |
| 23 | + type_attributes = ( |
| 24 | + complexType.findall('xsd:attribute', namespaces=namespaces) + |
| 25 | + complexType.findall('xsd:simpleContent/xsd:extension/xsd:attribute', namespaces=namespaces) |
| 26 | + ) |
| 27 | + type_attributeGroups = ( |
| 28 | + complexType.findall('xsd:attributeGroup', namespaces=namespaces) + |
| 29 | + complexType.findall('xsd:simpleContent/xsd:extension/xsd:attributeGroup', namespaces=namespaces) |
| 30 | + ) |
| 31 | + |
| 32 | + group_attributes = [] |
| 33 | + for attributeGroup in ( |
| 34 | + element.findall('xsd:complexType/xsd:attributeGroup', namespaces=namespaces) + |
| 35 | + element.findall('xsd:complexType/xsd:simpleContent/xsd:extension/xsd:attributeGroup', namespaces=namespaces) + |
| 36 | + type_attributeGroups |
| 37 | + ): |
| 38 | + group_attributes += self.get_schema_element('attributeGroup', attributeGroup.attrib['ref']).findall('xsd:attribute', namespaces=namespaces) |
| 39 | + |
| 40 | + for attribute in ( |
| 41 | + element.findall('xsd:complexType/xsd:attribute', namespaces=namespaces) + |
| 42 | + element.findall('xsd:complexType/xsd:simpleContent/xsd:extension/xsd:attribute', namespaces=namespaces) + |
| 43 | + type_attributes + group_attributes |
| 44 | + ): |
| 45 | + doc = attribute.find(".//xsd:documentation", namespaces=namespaces) |
| 46 | + if 'ref' in attribute.attrib: |
| 47 | + referenced_attribute = self.get_schema_element('attribute', attribute.get('ref')) |
| 48 | + if referenced_attribute is not None: |
| 49 | + attribute = referenced_attribute |
| 50 | + if doc is None: |
| 51 | + # Only fetch the documentation of the referenced definition |
| 52 | + # if we don't already have documentation. |
| 53 | + doc = attribute.find(".//xsd:documentation", namespaces=namespaces) |
| 54 | + yield attribute.get('name') or attribute.get('ref'), attribute.get('use') == 'required' |
| 55 | + |
| 56 | + def has_simple_content(self, element): |
| 57 | + a = element.attrib |
| 58 | + simple_content = False |
| 59 | + if 'type' in a: |
| 60 | + complexType = self.get_schema_element('complexType', a['type']) |
| 61 | + if complexType is not None: |
| 62 | + simple_content = bool(complexType.findall('xsd:simpleContent', namespaces=namespaces)) |
| 63 | + return simple_content or bool(element.findall('xsd:complexType/xsd:simpleContent', namespaces=namespaces)) |
| 64 | + |
| 65 | + def generate_paths(self, parent_name, parent_element=None, parent_path=''): |
| 66 | + if parent_element is None: |
| 67 | + parent_element = self.get_schema_element('element', parent_name) |
| 68 | + |
| 69 | + for name, required, in self.attribute_loop(parent_element): |
| 70 | + if name == 'xml:lang': |
| 71 | + # Namespaces not supported yet https://github.com/OpenDataServices/flatten-tool/issues/148 |
| 72 | + # And no way to specify two narrative elements anyway https://github.com/OpenDataServices/cove/issues/777 |
| 73 | + continue |
| 74 | + yield parent_path + '@' + name |
| 75 | + |
| 76 | + for name, element, _, minOccurs, maxOccurs in self.element_loop(parent_element): |
| 77 | + if element is None: |
| 78 | + element = self.get_schema_element('element', name) |
| 79 | + path = parent_path + name |
| 80 | + if self.has_simple_content(element): |
| 81 | + yield path |
| 82 | + if maxOccurs == 'unbounded' or int(maxOccurs) > 1: |
| 83 | + path += '/0/' |
| 84 | + else: |
| 85 | + path += '/' |
| 86 | + yield from list(self.generate_paths(name, element, path)) |
| 87 | + |
| 88 | + |
| 89 | +class XMLSchemaParser(object): |
| 90 | + """Parse the fields of a JSON schema into a flattened structure.""" |
| 91 | + |
| 92 | + def __init__(self, xml_schemas=[], root_list_path=None): |
| 93 | + self.sub_sheets = {} |
| 94 | + self.main_sheet = Sheet() |
| 95 | + self.sub_sheet_mapping = {} |
| 96 | + self.xml_schemas = xml_schemas |
| 97 | + assert root_list_path is not None |
| 98 | + self.root_list_path = root_list_path |
| 99 | + |
| 100 | + def parse(self): |
| 101 | + for path in XMLSchemaWalkerForTemplate(self.xml_schemas).generate_paths(self.root_list_path): |
| 102 | + self.main_sheet.append(path) |
| 103 | + |
0 commit comments