content: /*
   *  Licensed to the Apache Software Foundation (ASF) under one
   *  or more contributor license agreements.  See the NOTICE file
   *  distributed with this work for additional information
   *  regarding copyright ownership.  The ASF licenses this file
   *  to you under the Apache License, Version 2.0 (the
   *  "License"); you may not use this file except in compliance
   *  with the License.  You may obtain a copy of the License at
   *
   *        http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing,
   *  software distributed under the License is distributed on an
   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   *  KIND, either express or implied.  See the License for the
   *  specific language governing permissions and limitations
   *  under the License.
   */
  package org.apache.causeway.core.metamodel.util.snapshot;
  
  import java.util.function.BiConsumer;
  
  import org.w3c.dom.Attr;
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  import org.w3c.dom.NamedNodeMap;
  import org.w3c.dom.NodeList;
  
  import org.apache.causeway.applib.services.xmlsnapshot.XmlSnapshotService.Snapshot;
  
  /**
   * Represents the schema for the derived snapshot.
   */
  public final class XmlSchema {
  
      private final String prefix;
      private final String uriBase;
      private String uri;
  
      private final CausewaySchema causewayMeta;
      private final XsMetaModel xsMeta;
      private final Helper helper;
  
      /**
       * The base part of the namespace prefix to use if none explicitly supplied
       * in the constructor.
       */
      public static final String DEFAULT_PREFIX = "app";
  
      public static interface ExtensionData<T> {
          public int size();
          public void visit(BiConsumer<Class<T>, T> elementConsumer);
      }
  
      public XmlSchema() {
          this(CausewaySchema.DEFAULT_URI_BASE, XmlSchema.DEFAULT_PREFIX);
      }
  
      /**
       * @param uriBase
       *            the prefix for the application namespace's URIs
       * @param prefix
       *            the prefix for the application namespace's prefix
       */
      public XmlSchema(final String uriBase, final String prefix) {
          this.causewayMeta = new CausewaySchema();
          this.xsMeta = new XsMetaModel();
          this.helper = new Helper();
  
          final String base = new Helper().trailingSlash(uriBase);
          if (XsMetaModel.W3_ORG_XMLNS_URI.equals(base)) {
              throw new IllegalArgumentException("Namespace URI reserved for w3.org XMLNS namespace");
          }
          if (XsMetaModel.W3_ORG_XMLNS_PREFIX.equals(prefix)) {
              throw new IllegalArgumentException("Namespace prefix reserved for w3.org XMLNS namespace.");
          }
          if (XsMetaModel.W3_ORG_XS_URI.equals(base)) {
              throw new IllegalArgumentException("Namespace URI reserved for w3.org XML schema namespace.");
          }
          if (XsMetaModel.W3_ORG_XS_PREFIX.equals(prefix)) {
              throw new IllegalArgumentException("Namespace prefix reserved for w3.org XML schema namespace.");
          }
          if (XsMetaModel.W3_ORG_XSI_URI.equals(base)) {
              throw new IllegalArgumentException("Namespace URI reserved for w3.org XML schema-instance namespace.");
          }
          if (XsMetaModel.W3_ORG_XSI_PREFIX.equals(prefix)) {
              throw new IllegalArgumentException("Namespace prefix reserved for w3.org XML schema-instance namespace.");
          }
          if (CausewaySchema.NS_URI.equals(base)) {
              throw new IllegalArgumentException("Namespace URI reserved for Apache Causeway metamodel namespace.");
          }
          if (CausewaySchema.NS_PREFIX.equals(prefix)) {
              throw new IllegalArgumentException("Namespace prefix reserved for Apache Causeway metamodel namespace.");
          }
          this.uriBase = base;
          this.prefix = prefix;
      }
  
      /**
       * The base of the URI in use. All namespaces are concatenated with this.
       *
       * The namespace string will be the concatenation of the plus the package
       * name of the class of the object being referenced.
       *
       * If not specified in the constructor, then
       * {@link CausewaySchema#DEFAULT_URI_BASE}
       * is used.
       */
      public String getUriBase() {
          return uriBase;
      }
  
      /**
       * Returns the namespace URI for the class.
       */
      void setUri(final String fullyQualifiedClassName) {
          if (uri != null) {
              throw new IllegalStateException("URI has already been specified.");
          }
          this.uri = getUriBase() + helper.packageNameFor(fullyQualifiedClassName) + "/" + helper.classNameFor(fullyQualifiedClassName);
      }
  
      /**
       * The URI of the application namespace.
       *
       * The value returned will be <code>null</code> until a {@link Snapshot} is
       * created.
       */
      public String getUri() {
          if (uri == null) {
              throw new IllegalStateException("URI has not been specified.");
          }
          return uri;
      }
  
      /**
       * The prefix to the namespace for the application.
       */
      public String getPrefix() {
          return this.prefix;
      }
  
      /**
       * Creates an element with the specified localName, in the appropriate
       * namespace for the NOS.
       *
       * If necessary the namespace definition is added to the root element of the
       * doc used to create the element. The element is not parented but to avoid
       * an error can only be added as a child of another element in the same doc.
       */
      Element createElement(final Document doc, final String localName, final String fullyQualifiedClassName, final String singularName) {
          final Element element = doc.createElementNS(getUri(), getPrefix() + ":" + localName);
          element.setAttributeNS(CausewaySchema.NS_URI, CausewaySchema.NS_PREFIX + ":fqn", fullyQualifiedClassName);
          element.setAttributeNS(CausewaySchema.NS_URI, CausewaySchema.NS_PREFIX + ":singular", singularName);
          element.setAttributeNS(CausewaySchema.NS_URI, CausewaySchema.NS_PREFIX + ":plural", "");
          causewayMeta.addNamespace(element); // good a place as any
  
          addNamespace(element, getPrefix(), getUri());
          return element;
      }
  
      /**
       * Sets the target namespace for the XSD document to a URI derived from the
       * fully qualified class name of the supplied object
       */
      void setTargetNamespace(final Document xsdDoc, final String fullyQualifiedClassName) {
  
          final Element xsSchemaElement = xsdDoc.getDocumentElement();
          if (xsSchemaElement == null) {
              throw new IllegalArgumentException("XSD Document must have <xs:schema> element attached");
          }
  
          // targetNamespace="https://causeway.apache.org/ns/app/<fully qualified class
          // name>
          xsSchemaElement.setAttribute("targetNamespace", getUri());
  
          addNamespace(xsSchemaElement, getPrefix(), getUri());
      }
  
      /**
       * Creates an &lt;xs:element&gt; element defining the presence of the named
       * element representing a class
       */
      <T> Element createXsElementForNofClass(
              final Document xsdDoc,
              final Element element,
              final boolean addCardinality,
              final ExtensionData<T> extensions) {
  
          // gather details from XML element
          final String localName = element.getLocalName();
  
          // <xs:element name="AO11ConfirmAnimalRegistration">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element ref="causeway:title"/>
          // <!-- placeholder -->
          // </xs:sequence>
          // <xs:attribute ref="causeway:feature"
          // default="class"/>
          // <xs:attribute ref="causeway:oid"/>
          // <xs:attribute ref="causeway:annotation"/>
          // <xs:attribute ref="causeway:fqn"/>
          // </xs:complexType>
          // </xs:element>
  
          // xs:element/@name="class name"
          // add to XML schema as a global attribute
          final Element xsElementForNofClassElement = xsMeta.createXsElementElement(xsdDoc, localName, addCardinality);
  
          // xs:element/xs:complexType
          // xs:element/xs:complexType/xs:sequence
          final Element xsComplexTypeElement = xsMeta.complexTypeFor(xsElementForNofClassElement);
          final Element xsSequenceElement = xsMeta.sequenceFor(xsComplexTypeElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element ref="causeway:title"
          final Element xsTitleElement = xsMeta.createXsElement(helper.docFor(xsSequenceElement), "element");
          xsTitleElement.setAttribute("ref", CausewaySchema.NS_PREFIX + ":" + "title");
          xsSequenceElement.appendChild(xsTitleElement);
          xsMeta.setXsCardinality(xsTitleElement, 0, 1);
  
          // xs:element/xs:complexType/xs:sequence/xs:element ref="extensions"
          addXsElementForAppExtensions(xsSequenceElement, extensions);
  
          // xs:element/xs:complexType/xs:attribute ...
          xsMeta.addXsCausewayFeatureAttributeElements(xsComplexTypeElement, "class");
          xsMeta.addXsCausewayAttribute(xsComplexTypeElement, "oid");
          xsMeta.addXsCausewayAttribute(xsComplexTypeElement, "fqn");
          xsMeta.addXsCausewayAttribute(xsComplexTypeElement, "singular");
          xsMeta.addXsCausewayAttribute(xsComplexTypeElement, "plural");
          xsMeta.addXsCausewayAttribute(xsComplexTypeElement, "annotation");
  
          Place.setXsdElement(element, xsElementForNofClassElement);
  
          return xsElementForNofClassElement;
      }
  
      /**
       * Creates an <code>xs:element</code> element to represent a collection of
       * application-defined extensions
       *
       * The returned element should be appended to <code>xs:sequence</code>
       * element of the xs:element representing the type of the owning object.
       */
      <T> void addXsElementForAppExtensions(final Element parentXsElementElement, final ExtensionData<T> extensions) {
  
          if (extensions.size() == 0) {
              return;
          }
  
          // <xs:element name="extensions">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element name="app:%extension class short name%" minOccurs="0"
          // maxOccurs="1" default="%value%"/>
          // <xs:element name="app:%extension class short name%" minOccurs="0"
          // maxOccurs="1" default="%value%"/>
          // ...
          // <xs:element name="app:%extension class short name%" minOccurs="0"
          // maxOccurs="1" default="%value%"/>
          // </xs:sequence>
          // </xs:complexType>
          // </xs:element>
  
          // xs:element name="causeway-extensions"
          // xs:element/xs:complexType/xs:sequence
          final Element xsExtensionsSequenceElement = addExtensionsElement(parentXsElementElement);
  
          addExtensionElements(xsExtensionsSequenceElement, extensions);
  
          return;
      }
  
      /**
       * Adds an causeway-extensions element and a complexType and sequence elements
       * underneath.
       *
       * <p>
       * Returns the sequence element so that it can be appended to.
       */
      private Element addExtensionsElement(final Element parentXsElement) {
          final Element xsExtensionsElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsElement), "causeway-extensions");
          parentXsElement.appendChild(xsExtensionsElementElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
          final Element xsExtensionsComplexTypeElement = xsMeta.complexTypeFor(xsExtensionsElementElement);
          final Element xsExtensionsSequenceElement = xsMeta.sequenceFor(xsExtensionsComplexTypeElement);
  
          return xsExtensionsSequenceElement;
      }
  
      private String shortName(final String className) {
          final int lastPeriodIdx = className.lastIndexOf('.');
          if (lastPeriodIdx < 0) {
              return className;
          }
          return className.substring(lastPeriodIdx + 1);
      }
  
      /**
       * Creates an <code>xs:element</code> element to represent a value field in
       * a class.
       *
       * The returned element should be appended to <code>xs:sequence</code>
       * element of the xs:element representing the type of the owning object.
       */
      <T> Element createXsElementForNofValue(
              final Element parentXsElementElement,
              final Element xmlValueElement,
              final ExtensionData<T> extensions) {
  
          // gather details from XML element
          final String datatype = xmlValueElement.getAttributeNS(CausewaySchema.NS_URI, "datatype");
          final String fieldName = xmlValueElement.getLocalName();
  
          // <xs:element name="%owning object%">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element name="%%field object%%">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element name="causeway-extensions">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element name="%extensionClassShortName%"
          // default="%extensionObjString" minOccurs="0"/>
          // <xs:element name="%extensionClassShortName%"
          // default="%extensionObjString" minOccurs="0"/>
          // ...
          // <xs:element name="%extensionClassShortName%"
          // default="%extensionObjString" minOccurs="0"/>
          // </xs:sequence>
          // </xs:complexType>
          // </xs:element>
          // </xs:sequence>
          // <xs:attribute ref="causeway:feature" fixed="value"/>
          // <xs:attribute ref="causeway:datatype" fixed="causeway:%datatype%"/>
          // <xs:attribute ref="causeway:isEmpty"/>
          // <xs:attribute ref="causeway:annotation"/>
          // </xs:complexType>
          // </xs:element>
          // </xs:sequence>
          // </xs:complexType>
          // </xs:element>
  
          // xs:element/xs:complexType/xs:sequence
          final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement);
          final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element
          // name="%%field object%"
          final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName);
          parentXsSequenceElement.appendChild(xsFieldElementElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType
          final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement);
  
          // NEW CODE TO SUPPORT EXTENSIONS;
          // uses a complexType/sequence
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
          final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
          // name="causeway-extensions"
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
          addXsElementForAppExtensions(xsFieldSequenceElement, extensions);
  
          xsMeta.addXsCausewayFeatureAttributeElements(xsFieldComplexTypeElement, "value");
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "datatype", datatype);
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "isEmpty");
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "annotation");
  
          return xsFieldElementElement;
      }
  
      private <T> void addExtensionElements(final Element parentElement, final ExtensionData<T> extensions) {
  
          extensions.visit((final Class<T> extensionClass, final T extensionObject)->{
  
              // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
              // name="%extensionClassShortName%"
              final Element xsExtensionElementElement = xsMeta.createXsElementElement(
                      helper.docFor(parentElement), "x-" + shortName(extensionClass.getName()));
              xsExtensionElementElement.setAttribute("default", extensionObject.toString()); // the
              // value
              xsExtensionElementElement.setAttribute("minOccurs", "0"); // doesn't
              // need to
              // appear
              // in XML
              // (and
              // indeed won't)
              parentElement.appendChild(xsExtensionElementElement);
          });
  
      }
  
      /**
       * Creates an &lt;xs:element&gt; element defining the presence of the named
       * element representing a reference to a class; appended to xs:sequence
       * element
       */
      <T> Element createXsElementForNofReference(
              final Element parentXsElementElement,
              final Element xmlReferenceElement,
              final String referencedClassName,
              final ExtensionData<T> extensions) {
  
          // gather details from XML element
          final String fieldName = xmlReferenceElement.getLocalName();
  
          // <xs:element name="%owning object%">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element name="%%field object%%">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element ref="causeway:title" minOccurs="0"/>
          // <xs:element name="causeway-extensions">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element name="app:%extension class short name%" minOccurs="0"
          // maxOccurs="1" default="%value%"/>
          // <xs:element name="app:%extension class short name%" minOccurs="0"
          // maxOccurs="1" default="%value%"/>
          // ...
          // <xs:element name="app:%extension class short name%" minOccurs="0"
          // maxOccurs="1" default="%value%"/>
          // </xs:sequence>
          // </xs:complexType>
          // </xs:element>
          // <xs:sequence minOccurs="0" maxOccurs="1"/>
          // </xs:sequence>
          // <xs:attribute ref="causeway:feature" fixed="reference"/>
          // <xs:attribute ref="causeway:type" default="%%appX%%:%%type%%"/>
          // <xs:attribute ref="causeway:isEmpty"/>
          // <xs:attribute ref="causeway:annotation"/>
          // </xs:complexType>
          // </xs:element>
          // </xs:sequence>
          // </xs:complexType>
          // </xs:element>
  
          // xs:element/xs:complexType/xs:sequence
          final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement);
          final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element
          // name="%%field object%"
          final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName);
          parentXsSequenceElement.appendChild(xsFieldElementElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
          final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement);
          final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
          // ref="causeway:title"
          final Element xsFieldTitleElement = xsMeta.createXsElement(helper.docFor(xsFieldSequenceElement), "element");
          xsFieldTitleElement.setAttribute("ref", CausewaySchema.NS_PREFIX + ":" + "title");
          xsFieldSequenceElement.appendChild(xsFieldTitleElement);
          xsMeta.setXsCardinality(xsFieldTitleElement, 0, 1);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
          // name="causeway-extensions"
          addXsElementForAppExtensions(xsFieldSequenceElement, extensions);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:sequence
          // //
          // placeholder
          final Element xsReferencedElementSequenceElement = xsMeta.sequenceFor(xsFieldSequenceElement);
          xsMeta.setXsCardinality(xsReferencedElementSequenceElement, 0, 1);
  
          xsMeta.addXsCausewayFeatureAttributeElements(xsFieldComplexTypeElement, "reference");
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "type", "app:" + referencedClassName, false);
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "isEmpty");
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "annotation");
  
          return xsFieldElementElement;
      }
  
      /**
       * Creates an &lt;xs:element&gt; element defining the presence of the named
       * element representing a collection in a class; appended to xs:sequence
       * element
       */
      <T> Element createXsElementForNofCollection(
              final Element parentXsElementElement,
              final Element xmlCollectionElement,
              final String referencedClassName,
              final ExtensionData<T> extensions) {
  
          // gather details from XML element
          final String fieldName = xmlCollectionElement.getLocalName();
  
          // <xs:element name="%owning object%">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element name="%%field object%%">
          // <xs:complexType>
          // <xs:sequence>
          // <xs:element ref="causeway:oids" minOccurs="0" maxOccurs="1"/>
          // <!-- nested element definitions go here -->
          // </xs:sequence>
          // <xs:attribute ref="causeway:feature" fixed="collection"/>
          // <xs:attribute ref="causeway:type" fixed="%%appX%%:%%type%%"/>
          // <xs:attribute ref="causeway:size"/>
          // <xs:attribute ref="causeway:annotation"/>
          // </xs:complexType>
          // </xs:element>
          // </xs:sequence>
          // </xs:complexType>
          // </xs:element>
  
          // xs:element/xs:complexType/xs:sequence
          final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement);
          final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element
          // name="%field object%%"
          final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName);
          parentXsSequenceElement.appendChild(xsFieldElementElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType
          final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement);
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
          final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement);
  
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
          // ref="causeway:oids"
          final Element xsFieldOidsElement = xsMeta.createXsElement(helper.docFor(xsFieldSequenceElement), "element");
          xsFieldOidsElement.setAttribute("ref", CausewaySchema.NS_PREFIX + ":" + "oids");
          xsFieldSequenceElement.appendChild(xsFieldOidsElement);
          xsMeta.setXsCardinality(xsFieldOidsElement, 0, 1);
  
          // extensions
          addXsElementForAppExtensions(xsFieldSequenceElement, extensions);
  
          // //
          // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:choice
          // Element xsFieldChoiceElement =
          // xsMeta.choiceFor(xsFieldComplexTypeElement); // placeholder
          // xsMeta.setXsCardinality(xsFieldChoiceElement, 0, Integer.MAX_VALUE);
  
          // Element xsFieldTitleElement =
          // addXsNofRefElementElement(xsFieldSequenceElement, "title");
  
          // Element xsReferencedElementSequenceElement =
          // sequenceFor(xsFieldSequenceElement);
          // setXsCardinality(xsReferencedElementSequenceElement, 0, 1);
  
          xsMeta.addXsCausewayFeatureAttributeElements(xsFieldComplexTypeElement, "collection");
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "type", "app:" + referencedClassName, false);
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "size");
          xsMeta.addXsCausewayAttribute(xsFieldComplexTypeElement, "annotation");
  
          return xsFieldElementElement;
      }
  
      /**
       *
       * <pre>
       *     xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       *     xsi:schemaLocation=&quot;https://causeway.apache.org/ns/app/sdm.common.fixture.schemes.ao.communications ddd.xsd&quot;
       * </pre>
       *
       * Assumes that the URI has been specified.
       *
       * @param xmlDoc
       * @param fullyQualifiedClassName
       * @param schemaLocationFileName
       */
      void assignSchema(final Document xmlDoc, final String fullyQualifiedClassName, final String schemaLocationFileName) {
  
          final String xsiSchemaLocationAttrValue = getUri() + " " + schemaLocationFileName;
  
          final Element rootElement = xmlDoc.getDocumentElement();
  
          // xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          addNamespace(rootElement, XsMetaModel.W3_ORG_XSI_PREFIX, XsMetaModel.W3_ORG_XSI_URI);
  
          // xsi:schemaLocation="https://causeway.apache.org/ns/app/<fully qualified
          // class name>
          // sdm.common.fixture.schemes.ao.communications
          // sdm.common.fixture.schemes.ao.communications.AO11ConfirmAnimalRegistration.xsd"
          rootElement.setAttributeNS(XsMetaModel.W3_ORG_XSI_URI, "xsi:schemaLocation", xsiSchemaLocationAttrValue);
      }
  
      /**
       * Adds a previously created &lt;xs:element&gt; element (representing a
       * field of an object) to the supplied element (presumed to be a
       * <code>complexType/sequence</code>).
       */
      void addFieldXsElement(final Element xsElement, final Element xsFieldElement) {
          if (xsFieldElement == null) {
              return;
          }
          final Element sequenceElement = xsMeta.sequenceForComplexTypeFor(xsElement);
          sequenceElement.appendChild(xsFieldElement);
      }
  
      /**
       * Adds a namespace using the supplied prefix and the supplied URI to the
       * root element of the document that is the parent of the supplied element.
       *
       * If the namespace declaration already exists but has a different URI
       * (shouldn't normally happen) overwrites with supplied URI.
       */
      private void addNamespace(final Element element, final String prefix, final String nsUri) {
          final Element rootElement = helper.rootElementFor(element);
          // see if we have the NS prefix there already
          final String existingNsUri = rootElement.getAttributeNS(XsMetaModel.W3_ORG_XMLNS_URI, prefix);
          // if there is none (or it is different from what we want), then set the
          // attribute
          if (existingNsUri == null || !existingNsUri.equals(nsUri)) {
              helper.rootElementFor(element).setAttributeNS(XsMetaModel.W3_ORG_XMLNS_URI, XsMetaModel.W3_ORG_XMLNS_PREFIX + ":" + prefix, nsUri);
          }
      }
  
      Element addXsElementIfNotPresent(final Element parentXsElement, final Element childXsElement) {
  
          final Element parentChoiceOrSequenceElement = xsMeta.choiceOrSequenceFor(xsMeta.complexTypeFor(parentXsElement));
  
          if (parentChoiceOrSequenceElement == null) {
              throw new IllegalArgumentException("Unable to locate complexType/sequence or complexType/choice under supplied parent XSD element");
          }
  
          final NamedNodeMap childXsElementAttributeMap = childXsElement.getAttributes();
          final Attr childXsElementAttr = (Attr) childXsElementAttributeMap.getNamedItem("name");
          final String localName = childXsElementAttr.getValue();
  
          final NodeList existingElements = parentChoiceOrSequenceElement.getElementsByTagNameNS("*", childXsElement.getLocalName());
          for (int i = 0; i < existingElements.getLength(); i++) {
              final Element xsElement = (Element) existingElements.item(i);
              final NamedNodeMap xsElementAttributeMap = xsElement.getAttributes();
              final Attr attr = (Attr) xsElementAttributeMap.getNamedItem("name");
              if (attr != null && attr.getValue().equals(localName)) {
                  return xsElement;
              }
          }
  
          parentChoiceOrSequenceElement.appendChild(childXsElement);
          return childXsElement;
      }
  
  }
  