diff --git a/generated/xcore/contab/test.html b/generated/xcore/contab/test.html index 45b20ed..ccc221e 100644 --- a/generated/xcore/contab/test.html +++ b/generated/xcore/contab/test.html @@ -1,70 +1,57 @@ - - + - NeTEx + NeTEx + + + + + - +
-
-

1. StopPlace.xsd

-

element StopPlace

-
-

complexType AlternativeNameType

-
-

complexType AlternativeTextType

-
-

complexType CentroidType

-
-

complexType ExtensionsType

-
-

complexType KeyListType

-
-

complexType KeyValueType

-
-

complexType LocalisedStringType

-
-

complexType QuayType

-
-

complexType StopPlaceType

-
-

complexType TopographicPlaceRefType

-
-

complexType ValidBetweenType

-
-

complexType

-
-

complexType

-
-

complexType

-
-

complexType

-
-

complexType

-
-

complexType

-
+
+

1.18. The complex type complexType[test:StopPlaceType]/quays#complexType (typedef-1.6)

+
+ + + + + + + + + + + + + + + + + + + + + + + +
+

complexType[test:StopPlaceType]
  /quays #complexType
  (typedef-1.6)

+
+

+
+

-

+
+

test:Quay

+
+

0:*

+
+

+test:QuayType

+
+

+
+
+
\ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/AlternativeNameType.html b/generated/xcore/contab/test/complexTypes/AlternativeNameType.html index 65f593e..ff6e453 100644 --- a/generated/xcore/contab/test/complexTypes/AlternativeNameType.html +++ b/generated/xcore/contab/test/complexTypes/AlternativeNameType.html @@ -1,9 +1,20 @@ - -
-

1.2. The complex type test:AlternativeNameType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.2. The complex type test:AlternativeNameType

+
+
+ @@ -11,86 +22,88 @@

1.2. The complex type test:AlternativeNameType

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

test:AlternativeNameType

-
-

-
-

-

-
-

@id

-
-

1:1

-
-

-
-

@version

-
-

1:1

-
-

-
-

test:NameType

-
-

1:1

-
-

-
-

test:TypeOfName

-
-

1:1

-
-

-
-

test:Name

-
-

1:1

-
-

>test:LocalisedStringType

-
-

-
-
-
\ No newline at end of file + + + +

test:AlternativeNameType

+ + +

+ + +

-

+ + + + + +

@id

+ + +

1:1

+ + + +

+ + + + + +

@version

+ + +

1:1

+ + + +

+ + + + + +

test:NameType

+ + +

1:1

+ + + +

+ + + + + +

test:TypeOfName

+ + +

1:1

+ + + +

+ + + + + +

test:Name

+ + +

1:1

+ + +

>test:LocalisedStringType

+ + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/AlternativeTextType.html b/generated/xcore/contab/test/complexTypes/AlternativeTextType.html index 81c2320..176d579 100644 --- a/generated/xcore/contab/test/complexTypes/AlternativeTextType.html +++ b/generated/xcore/contab/test/complexTypes/AlternativeTextType.html @@ -1,9 +1,20 @@ - -
-

1.3. The complex type test:AlternativeTextType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.3. The complex type test:AlternativeTextType

+
+
+ @@ -11,47 +22,49 @@

1.3. The complex type test:AlternativeTextType

- - - - - - - - - - - - - - - - - - - - - -
-

test:AlternativeTextType

-
-

-
-

-

-
-

@attributeName

-
-

1:1

-
-

-
-

test:Text

-
-

1:1

-
-

>test:LocalisedStringType

-
-

-
-
-
\ No newline at end of file + + + +

test:AlternativeTextType

+ + +

+ + +

-

+ + + + + +

@attributeName

+ + +

1:1

+ + + +

+ + + + + +

test:Text

+ + +

1:1

+ + +

>test:LocalisedStringType

+ + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/CentroidType.html b/generated/xcore/contab/test/complexTypes/CentroidType.html index b257998..fbeb55f 100644 --- a/generated/xcore/contab/test/complexTypes/CentroidType.html +++ b/generated/xcore/contab/test/complexTypes/CentroidType.html @@ -1,9 +1,20 @@ - -
-

1.4. The complex type test:CentroidType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.4. The complex type test:CentroidType

+
+
+ @@ -11,34 +22,36 @@

1.4. The complex type test:CentroidType

- - - - - - - - - - - - - - -
-

test:CentroidType

-
-

-
-

-

-
-

test:Location

-
-

1:1

-
-

+local-type: typedef-1.3

-
-

-
-
-
\ No newline at end of file + + + +

test:CentroidType

+ + +

+ + +

-

+ + + + + +

test:Location

+ + +

1:1

+ + +

+local-type: typedef-1.3

+ + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/ExtensionsType.html b/generated/xcore/contab/test/complexTypes/ExtensionsType.html index 56b6c57..df08c6a 100644 --- a/generated/xcore/contab/test/complexTypes/ExtensionsType.html +++ b/generated/xcore/contab/test/complexTypes/ExtensionsType.html @@ -1,9 +1,20 @@ - -
-

1.5. The complex type test:ExtensionsType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.5. The complex type test:ExtensionsType

+
+
+ @@ -11,49 +22,51 @@

1.5. The complex type test:ExtensionsType

- - - - - - - - - - - - - - - - - - - - - -
-

test:ExtensionsType

-
-

-
-

-

-
-

test:HafasPriority

-
-

0:1

-
-

+local-type: typedef-1.1

-
-

-
-

test:HafasKMInfo

-
-

0:1

-
-

+local-type: typedef-1.2

-
-

-
-
-
\ No newline at end of file + + + +

test:ExtensionsType

+ + +

+ + +

-

+ + + + + +

test:HafasPriority

+ + +

0:1

+ + +

+local-type: typedef-1.1

+ + +

+ + + + + +

test:HafasKMInfo

+ + +

0:1

+ + +

+local-type: typedef-1.2

+ + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/KeyListType.html b/generated/xcore/contab/test/complexTypes/KeyListType.html index dcd6446..76a62f6 100644 --- a/generated/xcore/contab/test/complexTypes/KeyListType.html +++ b/generated/xcore/contab/test/complexTypes/KeyListType.html @@ -1,9 +1,20 @@ - -
-

1.6. The complex type test:KeyListType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.6. The complex type test:KeyListType

+
+
+ @@ -11,34 +22,36 @@

1.6. The complex type test:KeyListType

- - - - - - - - - - - - - - -
-

test:KeyListType

-
-

-
-

-

-
-

test:KeyValue

-
-

1:*

-
-

+test:KeyValueType

-
-

-
-
-
\ No newline at end of file + + + +

test:KeyListType

+ + +

+ + +

-

+ + + + + +

test:KeyValue

+ + +

1:*

+ + +

+test:KeyValueType

+ + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/KeyValueType.html b/generated/xcore/contab/test/complexTypes/KeyValueType.html index 6d16961..07962df 100644 --- a/generated/xcore/contab/test/complexTypes/KeyValueType.html +++ b/generated/xcore/contab/test/complexTypes/KeyValueType.html @@ -1,9 +1,20 @@ - -
-

1.7. The complex type test:KeyValueType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.7. The complex type test:KeyValueType

+
+
+ @@ -11,45 +22,47 @@

1.7. The complex type test:KeyValueType

- - - - - - - - - - - - - - - - - - - - - -
-

test:KeyValueType

-
-

-
-

-

-
-

test:Key

-
-

1:1

-
-

-
-

test:Value

-
-

1:1

-
-

-
-
-
\ No newline at end of file + + + +

test:KeyValueType

+ + +

+ + +

-

+ + + + + +

test:Key

+ + +

1:1

+ + + +

+ + + + + +

test:Value

+ + +

1:1

+ + + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/LocalisedStringType.html b/generated/xcore/contab/test/complexTypes/LocalisedStringType.html index b381f1e..2ea697d 100644 --- a/generated/xcore/contab/test/complexTypes/LocalisedStringType.html +++ b/generated/xcore/contab/test/complexTypes/LocalisedStringType.html @@ -1,9 +1,20 @@ - -
-

1.8. The complex type test:LocalisedStringType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.8. The complex type test:LocalisedStringType

+
+
+ @@ -11,37 +22,39 @@

1.8. The complex type test:LocalisedStringType

- - - - - - - - - - - - - - - - - -
-

test:LocalisedStringType

-
-

xs:string

-
-

-

-
-

Inherited content is followed by own content:

-
-

@lang

-
-

1:1

-
-

-
-
-
\ No newline at end of file + + + +

test:LocalisedStringType

+ + +

xs:string

+ + +

-

+ + + + +

Inherited content is followed by own content:

+ + + + + +

@lang

+ + +

1:1

+ + + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/QuayType.html b/generated/xcore/contab/test/complexTypes/QuayType.html index 755f185..ee15c12 100644 --- a/generated/xcore/contab/test/complexTypes/QuayType.html +++ b/generated/xcore/contab/test/complexTypes/QuayType.html @@ -1,9 +1,20 @@ - -
-

1.9. The complex type test:QuayType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.9. The complex type test:QuayType

+
+
+ @@ -11,73 +22,75 @@

1.9. The complex type test:QuayType

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

test:QuayType

-
-

-
-

The QUAYs contained in the STOP PLACE, that is platforms, jetties, bays, taxi ranks, and other points of physical access to VEHICLEs.

-
-

@id

-
-

1:1

-
-

-
-

@version

-
-

1:1

-
-

-
-

test:PublicCode

-
-

1:1

-
-

-
-

test:keyList

-
-

0:1

-
-

+test:KeyListType

-
-

-
-
-
\ No newline at end of file + + + +

test:QuayType

+ + +

+ + +

The QUAYs contained in the STOP PLACE, that is platforms, jetties, bays, taxi ranks, and other points of physical access to VEHICLEs.

+ + + + + +

@id

+ + +

1:1

+ + + +

+ + + + + +

@version

+ + +

1:1

+ + + +

+ + + + + +

test:PublicCode

+ + +

1:1

+ + + +

+ + + + + +

test:keyList

+ + +

0:1

+ + +

+test:KeyListType

+ + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/StopPlaceType.html b/generated/xcore/contab/test/complexTypes/StopPlaceType.html index 3456c30..84dc90d 100644 --- a/generated/xcore/contab/test/complexTypes/StopPlaceType.html +++ b/generated/xcore/contab/test/complexTypes/StopPlaceType.html @@ -1,9 +1,20 @@ - -
-

1.10. The complex type test:StopPlaceType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.10. The complex type test:StopPlaceType

+
+
+ @@ -11,219 +22,221 @@

1.10. The complex type test:StopPlaceType

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

test:StopPlaceType

-
-

-
-

-

-
-

@id

-
-

1:1

-
-

-
-

@version

-
-

1:1

-
-

-
-

test:ValidBetween

-
-

1:1

-
-

+test:ValidBetweenType

-
-

Validity of the StopPlace

-
-

test:alternativeTexts

-
-

0:1

-
-

+local-type: typedef-1.4

-
-

Alternative texts for the StopPlace

-
-

test:keyList

-
-

0:1

-
-

+test:KeyListType

-
-

KEY LIST with the KEY VALUEs related to the STOP PLACE. SKI use KeyValues: one for the Didok number one for the SLOID For delivery to SKI only one Value is necessary.

-
-

test:Extensions

-
-

0:1

-
-

+test:ExtensionsType

-
-

See description of extensions

-
-

test:Name

-
-

1:1

-
-

The name of the StopPlace

-
-

test:ShortName

-
-

0:1

-
-

>test:LocalisedStringType

-
-

Description of TYPE OF VALUE. Is used to transmit the abbreviation of the StopPlace. There is not one abbreviation for all StopPlaces

-
-

test:PrivateCode

-
-

0:1

-
-

Private Code of STOP PLACE. Field must be filled. In Switzerland it is the DiDok number.

-
-

test:Centroid

-
-

0:1

-
-

+test:CentroidType

-
-

Global or national location of STOP PLACE.

-
-

test:alternativeNames

-
-

0:1

-
-

+local-type: typedef-1.5

-
-

Alternative names for SITE ELEMENT. We will also use these for synonyms. From INFO+ the synonyms are used on the StopPlace.

-
-

test:TopographicPlaceRef

-
-

0:1

-
-

test:TopographicPlaceRefType

-
-

Reference to TopographicPlace. Link to TopographicPlace of type county or country

-
-

test:Weighting

-
-

0:1

-
-

Default relative weighting to be used for stop place. The STOP PLACE element WEIGHTING basically accomplishes this feature but only allows the following values: noInterchange interchangeAllowed recommendedInterchange preferredInterchange. To incorporate the desired value range, we will add an EXTENSION element “HafasPriority” that contains the full information.

-
-

test:quays

-
-

0:1

-
-

+local-type: typedef-1.6

-
-

The QUAYs contained in the STOP PLACE, that is platforms, jetties, bays, taxi ranks, and other points of physical access to VEHICLEs.

-
-
-
\ No newline at end of file + + + +

test:StopPlaceType

+ + +

+ + +

-

+ + + + + +

@id

+ + +

1:1

+ + + +

+ + + + + +

@version

+ + +

1:1

+ + + +

+ + + + + +

test:ValidBetween

+ + +

1:1

+ + +

+test:ValidBetweenType

+ + +

Validity of the StopPlace

+ + + + + +

test:alternativeTexts

+ + +

0:1

+ + +

+local-type: typedef-1.4

+ + +

Alternative texts for the StopPlace

+ + + + + +

test:keyList

+ + +

0:1

+ + +

+test:KeyListType

+ + +

KEY LIST with the KEY VALUEs related to the STOP PLACE. SKI use KeyValues: one for the Didok number one for the SLOID For delivery to SKI only one Value is necessary.

+ + + + + +

test:Extensions

+ + +

0:1

+ + +

+test:ExtensionsType

+ + +

See description of extensions

+ + + + + +

test:Name

+ + +

1:1

+ + + +

The name of the StopPlace

+ + + + + +

test:ShortName

+ + +

0:1

+ + +

>test:LocalisedStringType

+ + +

Description of TYPE OF VALUE. Is used to transmit the abbreviation of the StopPlace. There is not one abbreviation for all StopPlaces

+ + + + + +

test:PrivateCode

+ + +

0:1

+ + + +

Private Code of STOP PLACE. Field must be filled. In Switzerland it is the DiDok number.

+ + + + + +

test:Centroid

+ + +

0:1

+ + +

+test:CentroidType

+ + +

Global or national location of STOP PLACE.

+ + + + + +

test:alternativeNames

+ + +

0:1

+ + +

+local-type: typedef-1.5

+ + +

Alternative names for SITE ELEMENT. We will also use these for synonyms. From INFO+ the synonyms are used on the StopPlace.

+ + + + + +

test:TopographicPlaceRef

+ + +

0:1

+ + +

test:TopographicPlaceRefType

+ + +

Reference to TopographicPlace. Link to TopographicPlace of type county or country

+ + + + + +

test:Weighting

+ + +

0:1

+ + + +

Default relative weighting to be used for stop place. The STOP PLACE element WEIGHTING basically accomplishes this feature but only allows the following values: noInterchange interchangeAllowed recommendedInterchange preferredInterchange. To incorporate the desired value range, we will add an EXTENSION element “HafasPriority” that contains the full information.

+ + + + + +

test:quays

+ + +

0:1

+ + +

+local-type: typedef-1.6

+ + +

The QUAYs contained in the STOP PLACE, that is platforms, jetties, bays, taxi ranks, and other points of physical access to VEHICLEs.

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/TopographicPlaceRefType.html b/generated/xcore/contab/test/complexTypes/TopographicPlaceRefType.html index e3dbf24..0683d6b 100644 --- a/generated/xcore/contab/test/complexTypes/TopographicPlaceRefType.html +++ b/generated/xcore/contab/test/complexTypes/TopographicPlaceRefType.html @@ -1,9 +1,20 @@ - -
-

1.11. The complex type test:TopographicPlaceRefType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.11. The complex type test:TopographicPlaceRefType

+
+
+ @@ -11,45 +22,47 @@

1.11. The complex type test:TopographicPlaceRefType

- - - - - - - - - - - - - - - - - - - - - -
-

test:TopographicPlaceRefType

-
-

-
-

-

-
-

@ref

-
-

1:1

-
-

-
-

@version

-
-

1:1

-
-

-
-
-
\ No newline at end of file + + + +

test:TopographicPlaceRefType

+ + +

+ + +

-

+ + + + + +

@ref

+ + +

1:1

+ + + +

+ + + + + +

@version

+ + +

1:1

+ + + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/complexTypes/ValidBetweenType.html b/generated/xcore/contab/test/complexTypes/ValidBetweenType.html index 82d37da..2b84f90 100644 --- a/generated/xcore/contab/test/complexTypes/ValidBetweenType.html +++ b/generated/xcore/contab/test/complexTypes/ValidBetweenType.html @@ -1,9 +1,20 @@ - -
-

1.12. The complex type test:ValidBetweenType

-
- - + + + + NeTEx + + + + + + + +
+
+

1.12. The complex type test:ValidBetweenType

+
+
+ @@ -11,45 +22,47 @@

1.12. The complex type test:ValidBetweenType

- - - - - - - - - - - - - - - - - - - - - -
-

test:ValidBetweenType

-
-

-
-

-

-
-

test:FromDate

-
-

1:1

-
-

-
-

test:ToDate

-
-

1:1

-
-

-
-
-
\ No newline at end of file + + + +

test:ValidBetweenType

+ + +

+ + +

-

+ + + + + +

test:FromDate

+ + +

1:1

+ + + +

+ + + + + +

test:ToDate

+ + +

1:1

+ + + +

+ + + + + + + + \ No newline at end of file diff --git a/generated/xcore/contab/test/elements/StopPlace.html b/generated/xcore/contab/test/elements/StopPlace.html index 21f61da..ff1cf7d 100644 --- a/generated/xcore/contab/test/elements/StopPlace.html +++ b/generated/xcore/contab/test/elements/StopPlace.html @@ -1,9 +1,20 @@ - -
-

1.1. The toplevel element test:StopPlace

-
- - + + + + NeTEx + + + + + + + +
+
+

1.1. The toplevel element test:StopPlace

+
+
+ @@ -11,19 +22,21 @@

1.1. The toplevel element test:StopPlace

- - - - - - - -
-

test:StopPlace

-
-

+test:StopPlaceType

-
-

-
-
-
\ No newline at end of file + + + +

test:StopPlace

+ + +

+test:StopPlaceType

+ + +

+ + + + + + + + \ No newline at end of file diff --git a/tools/pycore/pycore.py b/tools/pycore/pycore.py new file mode 100644 index 0000000..1cb3c1c --- /dev/null +++ b/tools/pycore/pycore.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python3 +import argparse +import os +import sys +from typing import List, Optional, Tuple, Any + +import xmlschema +from xmlschema import XsdElement, XsdType, XMLSchema10, XsdComponent +from xmlschema.validators import XsdGroup, XsdComplexType + +XS_NS = "http://www.w3.org/2001/XMLSchema" + +def local_name_from_qname(qname: Optional[str]) -> str: + if not qname: + return "" + # Clark notation: {ns}local + if qname.startswith("{"): + return qname.split("}", 1)[1] + # Already a local or prefixed name; best effort + return qname.split(":")[-1] + + +def get_component_namespace(component) -> Optional[str]: + # xmlschema components usually have .target_namespace + return getattr(component, "target_namespace", None) + + +def get_component_local_name(component) -> str: + # Try local_name if present, else from qname/name + ln = getattr(component, "local_name", None) + if ln: + return ln + # name is usually local name for named comps + name = getattr(component, "name", None) or getattr(component, "qname", None) + return local_name_from_qname(name) + +def get_namespace_prefix(name: str) -> str: + return name.split(":",1)[0] + +def read_documentation_from_elem(elem) -> str: + """ + Given the underlying XML element (lxml/Element or ElementTree), extract xs:annotation/xs:documentation text. + """ + if elem is None: + return "" + # elem can be Element or may lack namespace prefixes; rely on Clark-notation + docs = [] + # Walk children; avoid deep nested traversal beyond annotation + for child in list(elem): + # annotation may be in XS namespace + if child.tag.endswith("annotation") or child.tag == f"{{{XS_NS}}}annotation": + for doc in list(child): + if doc.tag.endswith("documentation") or doc.tag == f"{{{XS_NS}}}documentation": + text = (doc.text or "").strip() + if text: + docs.append(text) + return " ".join(docs).strip() + + +def get_best_documentation_for_element(el: XsdElement) -> str: + """ + Try documentation in order: + - The occurrence (local declaration with its own annotation) + - The referenced global element (if ref=) + - The element's type + """ + # 1) From this element node + doc = read_documentation_from_elem(getattr(el, "elem", None)) + if doc: + return doc + + # 2) From referenced global + ref = getattr(el, "ref", None) + if ref is not None: + doc = read_documentation_from_elem(getattr(ref, "elem", None)) + if doc: + return doc + + # 3) From type + el_type = getattr(el, "type", None) + if el_type is not None: + doc = read_documentation_from_elem(getattr(el_type, "elem", None)) + if doc: + return doc + + return "" + + +def get_best_documentation_for_component(component) -> str: + """ + For element/complexType/group: get top-level documentation. + """ + # Primary: this component's elem + doc = read_documentation_from_elem(getattr(component, "elem", None)) + if doc: + return doc + + # For elements, try ref/type like above + if isinstance(component, XsdElement): + return get_best_documentation_for_element(component) + + return "" + +def is_builtin_type(xsd_type: XsdType) -> bool: + ns = getattr(xsd_type, "target_namespace", None) + return ns == XS_NS + + +def retrieve_type_name(xsd_type: Optional[XsdType]) -> str: + if xsd_type is None: + return "anyType" + # xmlschema XsdType usually has .name (local) and .qname + name = getattr(xsd_type, "local_name", None) # local name, if named + if is_builtin_type(xsd_type) and name: + return name # e.g., string, int, date + if name: + return name # named complex/simple type + # Anonymous type: try base type or fallback + base_type = getattr(xsd_type, "base_type", None) + if base_type is not None and getattr(base_type, "name", None): + return getattr(base_type, "name") + # Last resort + qname = getattr(xsd_type, "qname", None) + if qname: + return local_name_from_qname(qname) + return "anonymousType" + +def build_type_path(xsd_type: Optional[XsdType], name: str) -> str | None: + if xsd_type is None: + return None + # xmlschema XsdType usually has .name (local) and .qname + if is_builtin_type(xsd_type): + return None # e.g., string, int, date + if name: + return f"../types/{name}.md" # named complex/simple type + return None + +def format_usage(min_occurs: Optional[int], max_occurs) -> str: + def as_int(value, default): + if value is None: + return default + if isinstance(value, str): + if value.lower() == "unbounded": + return "n" + try: + return int(value) + except Exception: + return default + + mino = as_int(min_occurs, 1) + maxo = as_int(max_occurs, 1) + if maxo == "n": + return f"{mino}..n" + return f"{mino}..{maxo}" + + +def build_linked_type_name(xsd_type: Optional[XsdType], max_occurs) -> str: + tname = retrieve_type_name(xsd_type) + tpath = build_type_path(xsd_type, tname) + if isinstance(max_occurs, str) and max_occurs.lower() == "unbounded": + tname = f"{tname}[]" + if int(max_occurs) > 1: + tname = f"{tname}[]" + if tpath: + return f"[{tname}]({tpath})" + return tname + +def collect_child_elements_from_element(node: XsdElement) -> List[Tuple[str, str, str, str]]: + """ + Returns rows for table: + - Element (local name) + - Usage (min..max) + - Type (with [] suffix if max > 1) + - Description (documentation) + We use deep iteration over content model to include nested groups/choices. + """ + rows = [] + content = getattr(node, "content_type", None) + if content is None: + return rows + + # xmlschema's content_type for complex types often supports iter_elements() + iter_elems = getattr(content, "iter_elements", None) + if iter_elems is None: + return rows + + # We keep a sequence; duplicates may appear if the model repeats; it's usually fine + for el in iter_elems(): + if not isinstance(el, XsdElement): + continue + name = get_component_local_name(el) + usage = format_usage(getattr(el, "min_occurs", 1), getattr(el, "max_occurs", 1)) + type_name = build_linked_type_name(getattr(el, "type", None), getattr(el, "max_occurs", 1)) + desc = get_best_documentation_for_element(el) + rows.append((name, usage, type_name, desc)) + return rows + +def collect_child_elements_from_complex_type(node: XsdComplexType) -> List[Tuple[str, str, str, str]]: + """ + Returns rows for table: + - Element (local name) + - Usage (min..max) + - Type (with [] suffix if max > 1) + - Description (documentation) + We use deep iteration over content model to include nested groups/choices. + """ + rows = [] + content = getattr(node, "content", None) + if content is None: + return rows + + # xmlschema's content_type for complex types often supports iter_elements() + elements = getattr(content, "elements", None) + if elements is None: + return rows + + # We keep a sequence; duplicates may appear if the model repeats; it's usually fine + for el in elements: + if not isinstance(el, XsdElement): + continue + name = get_component_local_name(el) + usage = format_usage(getattr(el, "min_occurs", 1), getattr(el, "max_occurs", 1)) + type_name = build_linked_type_name(getattr(el, "type", None), getattr(el, "max_occurs", 1)) + desc = get_best_documentation_for_element(el) + rows.append((name, usage, type_name, desc)) + return rows + + +def collect_child_elements_from_group(node: XsdGroup) -> List[Tuple[str, str, str, str]]: + rows = [] + iter_elems = getattr(node, "iter_elements", None) + if iter_elems is None: + return rows + for el in iter_elems(): + if not isinstance(el, XsdElement): + continue + name = get_component_local_name(el) + usage = format_usage(getattr(el, "min_occurs", 1), getattr(el, "max_occurs", 1)) + type_name = build_linked_type_name(getattr(el, "type", None), getattr(el, "max_occurs", 1)) + desc = get_best_documentation_for_element(el) + rows.append((name, usage, type_name, desc)) + return rows + + +def render_md_table(rows: List[Tuple[str, str, str, str]]) -> str: + header = "| Element | Usage | Type | Description |\n|---|---|---|---|" + if not rows: + return header + "\n" + lines = [header] + for name, usage, type_name, desc in rows: + # Escape pipes in description + safe_desc = desc.replace("|", "\\|") + lines.append(f"| {name} | {usage} | {type_name} | {safe_desc} |") + return "\n".join(lines) + "\n" + + +def generate_markdown_for_node(node: XsdComponent) -> str: + name = get_component_local_name(node) + top_desc = get_best_documentation_for_component(node) + # Determine element's type and collect its child elements if complex + el_type = getattr(node, "type", None) + rows = [] + if isinstance(el_type, XsdElement): + rows = collect_child_elements_from_element(el_type) + elif isinstance(el_type, XsdComplexType): + rows = collect_child_elements_from_complex_type(el_type) + elif isinstance(el_type, XsdGroup): + rows = collect_child_elements_from_group(el_type) + + # Heading + parts = [f"### {name}"] + if top_desc: + parts.append(top_desc.strip()) + + parts.append(render_md_table(rows)) + # Optionally: include attributes table (commented out) + # attrs_rows = [] + # if isinstance(el_type, XsdComplexType): + # for attr in getattr(el_type, "attributes", []): + # # Implement attribute documentation if desired + # pass + # if attrs_rows: + # parts.append("#### Attributes\n") + # parts.append(render_attrs_table(attrs_rows)) + return "\n\n".join(parts).strip() + "\n" + +def write_file(path: str, content: str): + with open(path, "w", encoding="utf-8") as f: + f.write(content) + +def parse_args(): + parser = argparse.ArgumentParser( + description="Generate Markdown tables from an XSD schema (elements, complexTypes, groups)" + ) + parser.add_argument("xsd", help="Path to the main XSD file") + parser.add_argument( + "--output", + "-o", + default=".", + help="Path to the output directory.", + ) + return parser.parse_args() + + # def get_ns_dir(ns: Optional[str]) -> str: + # if ns not in ns_dirs: + # safe = sanitize_namespace(ns) + # ns_dirs[ns] = safe + # ensure_dir(os.path.join(out_root, safe)) + # return os.path.join(out_root, ns_dirs[ns]) + +def main(): + args = parse_args() + xsd_path = args.xsd + + + # Load schema (resolves includes/imports) + try: + schema = xmlschema.XMLSchema(xsd_path) + except Exception as e: + print(f"Failed to load XSD schema: {e}", file=sys.stderr) + sys.exit(2) + + out_dir = args.output + write_md_of_elements(out_dir, schema) + write_md_of_complexTypes(out_dir, schema) + write_md_of_groups(out_dir, schema) + + print(f"Documentation generated under: {out_dir}") + +def write_md_of_nodes(out_dir: str, nodes_name: str, node_type: type, schema: XMLSchema10): + # Collect and write Elements + nodes_map = getattr(schema, nodes_name, None) + # Normalize to dict-like + if hasattr(nodes_map, "items"): + items = nodes_map.items() + else: + items = [] + + for _qname, node in items: + if not isinstance(node, node_type): + continue + if not getattr(node, "name", None): + continue + md = generate_markdown_for_node(node) + ns_prefix = get_namespace_prefix(node.prefixed_name) + nodes_path = os.path.join(out_dir, ns_prefix, nodes_name) + os.makedirs(nodes_path, exist_ok=True) + name = get_component_local_name(node) + write_file(os.path.join(nodes_path, f"{name}.md"), md) + +def write_md_of_elements(out_dir: str, schema: XMLSchema10): + write_md_of_nodes(out_dir, "elements", XsdElement, schema) + +def write_md_of_complexTypes(out_dir, schema: XMLSchema10): + write_md_of_nodes(out_dir, "types", XsdComplexType, schema) + +def write_md_of_groups(out_dir, schema: XMLSchema10): + write_md_of_nodes(out_dir, "groups", XsdGroup, schema) + + +if __name__ == "__main__": + main() diff --git a/tools/xcore/xquery/xco-html.xqm b/tools/xcore/xquery/xco-html.xqm index 9128eee..f3546e5 100644 --- a/tools/xcore/xquery/xco-html.xqm +++ b/tools/xcore/xquery/xco-html.xqm @@ -136,7 +136,8 @@ declare function hl:contabReport_domain( let $compKind := $comp/local-name(.) let $compPathPart := "/" || $compKind || "s/" let $complexTypePath := dm:getReportPartPath('contab', $compPathPart, $compName, $domain , $options) - let $written := u:writeXhtmlDoc($complexTypePath, $table) + let $tableHtml := hl:create_html($head, $table) + let $written := u:writeXhtmlDoc($complexTypePath, $tableHtml) let $domainName := $domain/processing/reportFileBaseName (: TODO refactor to function compHref :) let $compHref := $domainName || $compPathPart || $compName || '.html' @@ -157,27 +158,44 @@ declare function hl:contabReport_domain( } let $title := ($domain/processing/title/

{node()}

,

API Content

)[1] - let $htmlReport := - { + let $htmlReport := hl:create_html_with_toc($head, $xsdDivs, $title, $toc) + | hu:finalizeHtmlReport(., $options) + | u:prettyNode(.) + let $_WRITE := + let $reportPath := dm:getReportPath('contab', $domain, $options) + where ($reportPath) + return u:writeXhtmlDoc($reportPath, $htmlReport) + return $htmlReport +}; + +declare function hl:create_html($head as element(), $content as element()) as element() { + { + $head, + { + , +
{ + $content + }
, + + } + } +}; + +declare function hl:create_html_with_toc($head as element(), $content as element(), $title as element(), $toc as element()) as element() { + { $head, { ,
{ - $xsdDivs + $content }
, } - } - ! hu:finalizeHtmlReport(., $options) - ! u:prettyNode(.) - let $_WRITE := - let $reportPath := dm:getReportPath('contab', $domain, $options) - where ($reportPath) - return u:writeXhtmlDoc($reportPath, $htmlReport) - return $htmlReport + } }; (: NeTEx: Removes the namespace prefix:) @@ -277,7 +295,7 @@ declare function hl:contabReport_head( - + };