1

Need to create <entry><morerows/></entry>

  1. if <entry>/@morerows has following sibling <row> element
  2. Before need to check the position of <entry>/@morerows, then create the <entry><morerows/></entry> in the following sibling <row>.
  3. if you see in the input has less <entry> element in second element because in the first element using @morerows attribute(@morerows attribute means <rowspan> element concept)

Example: Suppose <entry>/@morerows position is 6th then need to create in the following sibling <entry><morerows/></entry> at position of 6th place in the following-sibling 6th place entry Please help me on above challenging task and thanks in advance

Input xml:

            <table>
                <tgroup>
                    <thead>
                        <row>
                            <entry><p></p></entry>
                            <entry namest="col2" nameend="col3"><p>Class A Common</p></entry>
                            <entry namest="col4" nameend="col5"><p>Class B Common</p></entry>
                            <entry morerows="1"><p>Additional</p></entry>
                            <entry morerows="1"><p>Retained</p></entry>
                            <entry morerows="1"><p>Accumulated</p></entry>
                            <entry namest="col9" nameend="col10"><p>Class A 1</p></entry>
                            <entry morerows="1"><p>Noncontrolling</p></entry>
                            <entry morerows="1"><p>Total</p></entry>
                        </row>
                        <row>
                            <entry><p>content here</p></entry>
                            <entry><p>content 1</p></entry>
                            <entry><p>content 2</p></entry>
                            <entry><p>content 1</p></entry>
                            <entry><p>content 2</p></entry>
                            
                            <entry><p>content 1</p></entry>
                            <entry><p>content 2</p></entry>
                            
                        </row>
                    </thead>
                </tgroup>
            </table>

Expected output:

<table>
    <tgroup>
        <thead>
            <row>
                <entry><p></p></entry>
                <entry namest="col2" nameend="col3"><p>Class A Common</p></entry>
                <entry namest="col4" nameend="col5"><p>Class B Common</p></entry>
                <entry morerows="1"><p>Additional</p></entry>
                <entry morerows="1"><p>Retained</p></entry>
                <entry morerows="1"><p>Accumulated</p></entry>
                <entry namest="col9" nameend="col10"><p>Class A 1</p></entry>
                <entry morerows="1"><p>Noncontrolling</p></entry>
                <entry morerows="1"><p>Total</p></entry>
            </row>
            <row>
                <entry><p>content here</p></entry>
                <entry><p>content 1</p></entry>
                <entry><p>content 2</p></entry>
                <entry><p>content 1</p></entry>
                <entry><p>content 2</p></entry>
                
                <entry><morerows/></entry>
                <entry><morerows/></entry>
                <entry><morerows/></entry>
                
                <entry><p>content 1</p></entry>
                <entry><p>content 2</p></entry>
                
                <entry><morerows/></entry>
                <entry><morerows/></entry>
            </row>
        </thead>
    </tgroup>
</table>
Kita Ansari
  • 121
  • 7
  • 1
    Will you please edit your sample and remove all the `` elements? they're distracting from the entry and morerows elements... too much scrolling :) Thanks. – Zach Young Dec 24 '21 at 08:41
  • 1
    That single example doesn't make a good spec of the requirements but look into existing table normalization examples. – Martin Honnen Dec 24 '21 at 10:11
  • Hi @ZachYoung I remove element, can you resolve this issue, this very challenging task. – Kita Ansari Dec 24 '21 at 11:16
  • 1
    Thank you. I agree with Martin... I'm having a hard time understanding what should happen. In your example, the final position of `` does not seem tied to the position of ``... Your examples show visually where they should end up, but only because of whitespace. – Zach Young Dec 24 '21 at 11:22
  • 1
    https://andrewjwelch.com/code/xslt/table/table-normalization.html and/or https://dpxslfaq.xml.com/xsl/sect2/N7450.html#d64662e622 might help. – Martin Honnen Dec 24 '21 at 11:33
  • Thanks Zach and Martin, I will send screen shot of visually table. – Kita Ansari Dec 24 '21 at 20:37

1 Answers1

1

I wrote a pipeline of several steps (using several modes), the first transforms your input into the rowspan/colspan based transformation format the solution in https://stackoverflow.com/a/36106927/252228, based on the already mentioned https://andrewjwelch.com/code/xslt/table/table-normalization.html, uses, then basically the templates from that solution are used, with the sole adaption of matching/selecting row and entry instead of tr and th/td and ensuring that the final mode removes the helper rowspan attributes and fills in the <entry><morerows/></entry> elements:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:key name="col" match="table/tgroup/colspec" use="@colname"/>
  
  <xsl:mode name="html-row-col-span" on-no-match="shallow-copy"/>
  
  <xsl:template mode="html-row-col-span" match="entry/@morerows">
    <xsl:next-match/>
    <xsl:attribute name="rowspan" select=". + 1"/>
  </xsl:template>
  
  <xsl:template mode="html-row-col-span" match="entry[@namest and @nameend]/@namest">
    <xsl:next-match/>
    <xsl:attribute name="colspan" select="key('col', ../@nameend, ancestor::tgroup)/@colnum - key('col', ., ancestor::tgroup)/@colnum + 1"/>
  </xsl:template>

  <xsl:mode name="colspan" on-no-match="shallow-copy"/>
  
  <xsl:mode name="rowspan" on-no-match="shallow-copy"/>
  
  <xsl:mode name="final-cleanup" on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes" suppress-indentation="entry"/>
  
  <xsl:template match="table/tgroup">
    <xsl:variable name="row-and-colspan">
      <xsl:apply-templates select="." mode="html-row-col-span"/>
    </xsl:variable>
    <xsl:variable name="tgroup_with_no_colspans">
        <xsl:apply-templates select="$row-and-colspan" mode="colspan" />
    </xsl:variable>
    <xsl:variable name="tgroup_with_normalized_rowspans">
        <xsl:apply-templates select="$tgroup_with_no_colspans" mode="rowspan" />
    </xsl:variable>
    <xsl:apply-templates select="$tgroup_with_normalized_rowspans" mode="final-cleanup"/>
  </xsl:template>
  
  <xsl:template mode="colspan" match="entry[@colspan]">
    <xsl:next-match/>
    <xsl:for-each select="2 to @colspan">
      <entry><col-span-filler/></entry>
    </xsl:for-each>
  </xsl:template>
  
  <xsl:template mode="colspan" match="@colspan"/>

  <xsl:template match="thead | tbody" mode="rowspan">
      <xsl:copy>
          <xsl:copy-of select="row[1]"/>
          <xsl:apply-templates select="row[2]" mode="rowspan">
              <xsl:with-param name="previousRow" select="row[1]"/>
          </xsl:apply-templates>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="row" mode="rowspan">
    <xsl:param name="previousRow" as="element()" />

    <xsl:variable name="currentRow" select="." />

    <xsl:variable name="normalizedEntries">
        <xsl:for-each select="$previousRow/*">
            <xsl:choose>
                <xsl:when test="@rowspan &gt; 1">
                    <xsl:copy>
                        <xsl:attribute name="rowspan">
                            <xsl:value-of select="@rowspan - 1" />
                        </xsl:attribute>
                    </xsl:copy>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="$currentRow/entry[1 + count(current()/preceding-sibling::*[not(@rowspan) or (@rowspan = 1)])]" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="newRow" as="element(row)">
        <xsl:copy>
            <xsl:copy-of select="$currentRow/@*" />
            <xsl:copy-of select="$normalizedEntries" />
        </xsl:copy>
    </xsl:variable>

    <xsl:copy-of select="$newRow" />

    <xsl:apply-templates select="following-sibling::row[1]" mode="rowspan">
        <xsl:with-param name="previousRow" select="$newRow" />
    </xsl:apply-templates>
  </xsl:template> 
  
  <xsl:template mode="final-cleanup" match="entry[col-span-filler] | entry[@morerows]/@rowspan"/>
  
  <xsl:template mode="final-cleanup" match="entry[@rowspan and not(@morerows)]">
    <xsl:copy>
      <morerows/>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>

The code uses XSLT 3 with e.g. xsl:mode and but you could also write it as XSLT 2, like the solution of Andrew Welch and my adaption of a previous StackOverflow post. The sibling recursion used could probably also reimplemented more efficiently in XSLT 3 using xsl:iterate.

Here is a pure XSLT 3 solution also working with streaming if SaxonCS or Saxon EE is used that uses an accumulator instead of a key and two nested xsl:iterate:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:map="http://www.w3.org/2005/xpath-functions/map"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="xml" indent="yes" suppress-indentation="entry"/>
  
  <xsl:accumulator name="colnums" as="map(xs:string, xs:integer)" initial-value="map{}" streamable="yes">
    <xsl:accumulator-rule
      match="tgroup" select="map{}"/>
    <xsl:accumulator-rule
      match="colspec"
      select="map:put($value, xs:string(@colname), xs:integer(@colnum))"/>
  </xsl:accumulator>
  
  <xsl:mode name="fill-rowspan-colspan" on-no-match="shallow-copy" use-accumulators="colnums" streamable="yes"/>
  
  <xsl:template mode="fill-rowspan-colspan" match="entry">
    <xsl:next-match/>
    <xsl:iterate select=".[@namest] ! (1 to (accumulator-before('colnums')(@nameend)!xs:integer(.) - accumulator-before('colnums')(@namest)!xs:integer(.)))">
      <entry>
        <col-span-filler/>
      </entry>
    </xsl:iterate>
  </xsl:template>
  
  <xsl:template mode="fill-rowspan-colspan" match="entry/@morerows">
    <xsl:next-match/>
    <xsl:attribute name="rowspan" select=". + 1"/>
  </xsl:template>

  <xsl:mode on-no-match="shallow-copy" use-accumulators="colnums" streamable="yes"/>
  
  <xsl:template match="thead | tbody">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:iterate select="row">
        <xsl:param name="prev-row" as="element(row)?" select="()"/>
        <xsl:variable name="new-row" as="element(row)">
          <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="entry" mode="fill-rowspan-colspan"/>
          </xsl:copy>
        </xsl:variable>
        <xsl:variable name="new-row" as="element(row)">
          <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:choose>
              <xsl:when test="not($prev-row)">
                <xsl:copy-of select="$new-row/*"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:iterate select="$prev-row!entry">
                  <xsl:choose>
                      <xsl:when test="@rowspan > 1">
                        <xsl:copy>
                          <xsl:attribute name="rowspan" select="@rowspan - 1"/>
                        </xsl:copy>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:copy-of select="$new-row/entry[1 + count(current()/preceding-sibling::*[not(@rowspan) or @rowspan = 1])]"/>
                      </xsl:otherwise>                
                  </xsl:choose>
                </xsl:iterate>            
              </xsl:otherwise>
            </xsl:choose>
          </xsl:copy>
        </xsl:variable>
        <xsl:apply-templates select="$new-row" mode="clean-up"/>
        <xsl:next-iteration>
          <xsl:with-param name="prev-row" select="$new-row"/>
        </xsl:next-iteration>
      </xsl:iterate>
    </xsl:copy>
  </xsl:template>
  
  <xsl:mode name="clean-up" on-no-match="shallow-copy"/>
  
  <xsl:template mode="clean-up" match="entry[@rowspan = 1]">
    <xsl:copy>
      <morerows/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template mode="clean-up" match="entry[col-span-filler] | entry/@rowspan"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:next-match/>
    <xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
  </xsl:template>

</xsl:stylesheet>

Note: both presented stylesheets assumes the colspec sections from your first edit of the question are present in the input sample as otherwise it is not possible to compute the colspan form an entry with @namest and @nameend attributes.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Today I posted correct input, Please look on this and help me @martin – Kita Ansari Dec 28 '21 at 11:22
  • @KitaAnsari, what does that mean, "today I posted correct input"? Did you post some input sample previously that you now consider incorrect and expect us to adapt any answers to the new input sample, even without showing and explaing what your expected "correct(ed)" output is? If the only thing missing is the `col` index attribute on the newly created `entry` then change the template for them to ``. – Martin Honnen Dec 28 '21 at 11:26