title: Grouping elements based on criteria found in attributes (XSLT)
body: <p>This question has been giving me a headache for quite a while now. Let's get to it.</p>
  
  <p>The document I'm processing:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;Message&gt;
      &lt;Specification&gt;
          &lt;!--1.--&gt;
          &lt;Fruit&gt;
              &lt;!--1.1--&gt;
              &lt;Apple/&gt;
              &lt;!--1.2--&gt;
              &lt;Separator/&gt;
              &lt;!--1.3--&gt;
              &lt;Orange/&gt;
              &lt;!--1.4--&gt;
              &lt;SeperatorCLRF/&gt;
          &lt;/Fruit&gt;
          &lt;!--2.--&gt;
          &lt;Cookie&gt;
              &lt;!--2.1--&gt;
              &lt;Dough/&gt;
              &lt;!--2.2--&gt;
              &lt;Separator/&gt;
              &lt;!--2.3--&gt;
              &lt;Chocolate&gt;
                  &lt;!--2.3.1--&gt;
                  &lt;Crisp/&gt;
                  &lt;!--2.3.2--&gt;
                  &lt;Chunk/&gt;
              &lt;/Chocolate&gt;
              &lt;!--2.4--&gt;
              &lt;SeparatorCLRF/&gt;
          &lt;/Cookie&gt;
      &lt;/Specification&gt;
      &lt;Repetitions&gt;
          &lt;Repeat_1 begin="2.2 " end="2.3.2" amount="5"/&gt;
          &lt;Repeat_2 begin="2.2 " end="2.4"/&gt;
      &lt;/Repetitions&gt;
  &lt;/Message&gt;
  </code></pre>
  
  <p>The idea is to transform this XML into an appropriate XSD. Child-elements (such as 1.x and 1.x.x) will be given their own complextype and referred to accordingly. What's important, however, is that the references that are found under "Repetitions" are located and <strong>also given their own complextype</strong> so that I'm able to tell: "These elements can be repeated an infinite amount of times." or "These elements can be repeated <strong>this</strong> amount of times."</p>
  
  <p>Since I've only just began to write XSL files the following snippet might be a bit too long, my apologies. </p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0"&gt;
      &lt;xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/&gt;
      &lt;xsl:strip-space elements="*"/&gt;
  
      &lt;xsl:template match="/"&gt;
          &lt;xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
              &lt;xs:element name="Message" type="Specification"/&gt;
              &lt;xsl:apply-templates/&gt;
          &lt;/xs:schema&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template match="Specification"&gt;
          &lt;xs:complexType name="Specification"&gt;
              &lt;xs:sequence&gt;
                  &lt;xsl:apply-templates select="//Specification/*" mode="references"/&gt;
              &lt;/xs:sequence&gt;
          &lt;/xs:complexType&gt;
          &lt;xsl:apply-templates select="//Specification/*" mode="data"/&gt;
          &lt;xsl:apply-templates select="//Specification/*" mode="subdata"/&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template match="//Specification/*" mode="references"&gt;
          &lt;xs:element&gt;
              &lt;xsl:attribute name="name" select="local-name()"/&gt;
              &lt;xsl:attribute name="type" select="local-name()"/&gt;
          &lt;/xs:element&gt;
      &lt;/xsl:template&gt;
  
      &lt;xsl:template match="//Specification/*" mode="data"&gt;
          &lt;xs:complexType name="{local-name()}"&gt;
              &lt;xs:sequence&gt;
                  &lt;xsl:for-each select="*"&gt;
                      &lt;xs:element&gt;
                          &lt;xsl:attribute name="name" select="local-name()"/&gt;
                          &lt;xsl:choose&gt;
                              &lt;xsl:when test="*"&gt;
                                  &lt;xsl:attribute name="type"
                                      select="concat(name(..),'_', local-name())"/&gt;
                              &lt;/xsl:when&gt;
                              &lt;xsl:otherwise&gt;
                                  &lt;xsl:attribute name="type"&gt;xs:string&lt;/xsl:attribute&gt;
                              &lt;/xsl:otherwise&gt;
                          &lt;/xsl:choose&gt;
                      &lt;/xs:element&gt;
                  &lt;/xsl:for-each&gt;
              &lt;/xs:sequence&gt;
          &lt;/xs:complexType&gt;
  
      &lt;/xsl:template&gt;
  
      &lt;xsl:template match="//Specification/*" mode="subdata"&gt;
          &lt;xsl:variable name="current_name" select="local-name()"/&gt;
          &lt;xsl:for-each select="*"&gt;
              &lt;xsl:if test="*"&gt;
                  &lt;xs:complexType&gt;
                      &lt;xsl:attribute name="name" select="concat($current_name,'_', local-name())"/&gt;
                      &lt;xs:sequence&gt;
                          &lt;xsl:for-each select="*"&gt;
                              &lt;xs:element&gt;
                                  &lt;xsl:attribute name="name" select="local-name()"/&gt;
                                  &lt;xsl:attribute name="type"&gt;xs:string&lt;/xsl:attribute&gt;
                              &lt;/xs:element&gt;
                          &lt;/xsl:for-each&gt;
                      &lt;/xs:sequence&gt;
                  &lt;/xs:complexType&gt;
              &lt;/xsl:if&gt;
          &lt;/xsl:for-each&gt;
  
      &lt;/xsl:template&gt;
  &lt;/xsl:stylesheet&gt;
  </code></pre>
  
  <p>Which results into the following XSD:</p>
  
  <pre><code>&lt;xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
      &lt;xs:element name="Message" type="Specification"/&gt;
      &lt;xs:complexType name="Specification"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Fruit" type="Fruit"/&gt;
              &lt;xs:element name="Cookie" type="Cookie"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
      &lt;xs:complexType name="Fruit"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Apple" type="xs:string"/&gt;
              &lt;xs:element name="Separator" type="xs:string"/&gt;
              &lt;xs:element name="Orange" type="xs:string"/&gt;
              &lt;xs:element name="SeperatorCLRF" type="xs:string"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
      &lt;xs:complexType name="Cookie"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Dough" type="xs:string"/&gt;
              &lt;xs:element name="Separator" type="xs:string"/&gt;
              &lt;xs:element name="Chocolate" type="Cookie_Chocolate"/&gt;
              &lt;xs:element name="SeparatorCLRF" type="xs:string"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
      &lt;xs:complexType name="Cookie_Chocolate"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Crisp" type="xs:string"/&gt;
              &lt;xs:element name="Chunk" type="xs:string"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
  &lt;/xs:schema&gt;
  </code></pre>
  
  <p>A couple of notes:</p>
  
  <ol>
  <li>The descendants of "Specification" are always different. Hence the usage of '/*'. </li>
  <li>The 1.x.x is the maximum depth. </li>
  <li>The comments will always be present (not sure if it matters) and can hopefully be used to solve the problem.</li>
  <li>Repetitions have an undefined amount of children (repeats), but they would all follow the same format: begin, end and <strong>optionally</strong> amount. Both begin and end could be: x. <strong>or</strong> x.x <strong>or</strong> x.x.x <strong>where</strong> x represents a number. </li>
  <li>The aforementioned <code>begin</code> and <code>end</code> will always be properly ordered. In other words, the reference in <code>end</code> will always come after the reference in <code>begin</code>. </li>
  </ol>
  
  <p>As you can see, the XSD does not include the repetitions yet. The aim is to have this: </p>
  
  <pre><code>&lt;xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
      &lt;xs:element name="Message" type="Specification"/&gt;
      &lt;xs:complexType name="Specification"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Fruit" type="Fruit"/&gt;
              &lt;xs:element name="Cookie" type="Cookie"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
      &lt;xs:complexType name="Fruit"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Apple" type="xs:string"/&gt;
              &lt;xs:element name="Separator" type="xs:string"/&gt;
              &lt;xs:element name="Orange" type="xs:string"/&gt;
              &lt;xs:element name="SeperatorCLRF" type="xs:string"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
      &lt;xs:complexType name="Cookie"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Dough" type="xs:string"/&gt;
              &lt;xs:element name="Repeat_2" type="Repeat_2" maxOccurs="unbounded"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
      &lt;xs:complexType name="Cookie_Chocolate"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Crisp" type="xs:string"/&gt;
              &lt;xs:element name="Chunk" type="xs:string"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
      &lt;xs:complexType name="Repeat_2"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Repeat_1" type="Repeat_1" maxOccurs="5"/&gt;
              &lt;xs:element name="SeparatorCLRF" type="xs:string"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
      &lt;xs:complexType name="Repeat_1"&gt;
          &lt;xs:sequence&gt;
              &lt;xs:element name="Separator" type="xs:string"/&gt;
              &lt;xs:element name="Chocolate" type="Cookie_Chocolate"/&gt;
          &lt;/xs:sequence&gt;
      &lt;/xs:complexType&gt;
  &lt;/xs:schema&gt;
  </code></pre>
  
  <p>The idea I had to solve the problem is as follows: while stepping through the nodes, constantly check if the current node can be found in the repetitions. Should a <code>begin</code> attribute be found, start wrapping the elements by having a starting tag. Then upon finding an <code>end</code> attribute, close the tag.</p>
  
  <p>Something like:</p>
  
  <pre><code>&lt;xsl:variable name="currentNode" select="preceding-sibling::comment()[1]"/&gt;
  
  &lt;xsl:for-each select="//Repetitions/Repeat"&gt;
      &lt;xsl:choose&gt;
              &lt;xsl:if test="$currentNode = @begin"&gt;
                  &lt;complexType&gt;
              &lt;/xsl:if&gt;
              &lt;xsl:if test="$currentNode = @end"&gt;
                  &lt;/complexType&gt;
              &lt;/xsl:if&gt;  
      &lt;/xsl:choose&gt;
  &lt;/xsl:for-each&gt;
  </code></pre>
  
  <p>The example above should illustrate what I'm trying to do with the starting- and ending tags. Obviously this cannot work since you must terminate tags within the if-statement. </p>
  
  <p>And the biggest problem, I suppose, would be that the wrapping of the "repetition elements" sort of destroys the rest of the XSLT. Ugh!</p>
  
  <p>I'm completely stuck at this point, because I keep stumbling on the problem of wanting to start a tag and then later on ending it. It's the only thing that seems logical to me, but it's impossible to do. Perhaps this cannot be solved purely by using XSLT. </p>
  
  <p>Hopefully someone can send me in the right direction! Thank you for reading anyway.</p>
  
  <p>EDIT1: It should be possible for me to add some extra data to the XML file. Something that might make it easier to get the result I want. If anyone reading this might know what would be the best to add, please leave a comment.</p>
  
  <p>EDIT2: I'm starting to think this problem is impossible to fix with the current structure. I've tried adding elements to the initial XML structure, but I keep finding myself stuck on one problem: actually wrapping the repeating blocks. If I create all the complexTypes (Like I do in the XSLT I gave) <strong>before</strong> determining the repeating blocks it becomes impossible to wrap properly, because a part of the repeating block might have to end in the complexType that's already made. I end up with the same problem even if I decide to determine the repeating blocks first. </p>
  
  <p>Really hope to get some pointers or idea's from people.</p>