averbraeck / opentrafficsim

Open Source Multi-Level Traffic Simulator
BSD 3-Clause "New" or "Revised" License
28 stars 8 forks source link

Simplify aOneOrTwoOrThree in generated XML parsing code #82

Closed WJSchakel closed 12 months ago

WJSchakel commented 12 months ago

xsd:choice elements get parsed to either List<JAXBElement<?>> or List<Serializable>. For example the network elements become:

    public List<Serializable> getNodeOrLinkOrCentroid() {
        if (nodeOrLinkOrCentroid == null) {
            nodeOrLinkOrCentroid = new ArrayList<Serializable>();
        }
        return this.nodeOrLinkOrCentroid;
    }

In case of List<Serializable> we can use ParseUtil.getObjectsOfType(...) to return all the elements of a certain type. In case of List<JAXBElement<?>> it is not possible to use ParseUtil.getObjectsOfType(...). This limits the possibilities of what we can specify in XSD, without burdening ourselves with elaborate parsing code to deal with the low-level JAXBElement<?> objects.

For example we have the voluntary incentives below. The outer xsd:sequence results in a List<Incentive> (Incentive being a generated type).

                  <xsd:element name="VoluntaryIncentives" minOccurs="0">
                    <xsd:complexType>
                      <xsd:sequence>
                        <xsd:element name="Incentive" minOccurs="0" maxOccurs="unbounded">
                          <xsd:complexType>
                            <xsd:choice>
                              <xsd:element name="Keep" type="ots:EmptyType" />
                              ...
                              <xsd:element name="Class" type="ots:ClassNameType" />
                            </xsd:choice>
                          </xsd:complexType>
                        </xsd:element>
                      </xsd:sequence>
                    </xsd:complexType>
                  </xsd:element>

But, now we have a list of elements, inside of which there is a choice. This is an unnecessary layer, and it obscures and convolutes the tree in the editor. What we want is the following, with the set of chosen incentives directly under the VoluntaryIncentives element.

                  <xsd:element name="VoluntaryIncentives" minOccurs="0">
                    <xsd:complexType>
                      <xsd:choice maxOccurs="unbounded">
                        <xsd:element name="Keep" type="ots:EmptyType" />
                        ...
                        <xsd:element name="Class" type="ots:ClassNameType" />
                      </xsd:choice>
                    </xsd:complexType>
                  </xsd:element>

This however results in a List<JAXBElement<?>>. To solve this we can use jax2 simplify plugin, see https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Simplify-Plugin.

Within the jaxb configuration in the pom file of ots-parser-xml we add (with maven-jaxb2-basics.version defined elsewhere):

          <args>
            <arg>-Xsimplify</arg>
          </args>
          <plugins>
            <plugin>
              <groupId>org.jvnet.jaxb2_commons</groupId>
              <artifactId>jaxb2-basics</artifactId>
              <version>${maven-jaxb2-basics.version}</version>
            </plugin>
          </plugins>

In bindings.xml we add in the main tag:

jaxb:extensionBindingPrefixes="xjc simplify" <-- add 'simplify'
xmlns:simplify="http://jaxb2-commons.dev.java.net/basic/simplify"

And the following bindings:

  <jaxb:bindings schemaLocation="ots-network.xsd">
    <jaxb:bindings node="//xsd:element[@name='Network']/xsd:complexType/xsd:sequence/xsd:choice">
      <simplify:as-element-property />
    </jaxb:bindings>
  </jaxb:bindings>

This results in the following generated code in Network:

List<Node> getNode()
List<Link> getLink()
List<Centroid> getCentroid()
List<Connector> getConnector()

This is much better than the List<Serializable> of before. In case of List<JAXBElement<?>> the XPath string needs to point to one of the elements. For example:

    <jaxb:bindings
      node="//xsd:element[@name='VoluntaryIncentives']/xsd:complexType/xsd:choice/xsd:element[@name='Keep']">
      <simplify:as-element-property />
    </jaxb:bindings>

We can scan usage of ParseUtil.getObjectsOfType(...) and replace these with appropriate bindings. Moreover various XSD types may be simplified and also get a binding.

averbraeck commented 12 months ago

This makes the code a lot more clear.

WJSchakel commented 12 months ago

The following tags were simplified:

Two tags are not simplified: