Home > Net >  How to simplify identical blocks of code?
How to simplify identical blocks of code?

Time:04-19

Input xml file:

<Config>
  <Fix title="title" description="description" React="200" Lock="E0" Hold="50">
    <Name type="type" value="value" Hold="50">
      <Image select="2" readonly="false" />
    </Name>
  </Fix>
</Config>

The "updates.xml" file from which updates for the input file are taken - Lock, React and Hold tags with const="?" attribute:

<Macro>
  <Item key="Element">
    <tag1 title="title1" description="description1" />
    <tag2 title="title2" description="description2" />
    <tag3 title="title3" description="description3" />
  </Item>
  <Item key="Attribute">
    <Lock title="title4" const="?" description="description4" />
    <React title="title5" const="?" description="description5" />
    <Hold title="title6" const="?" description="description6" />
  </Item>
</Macro>

If the input file contains tags from the "updates.xml" file, then the attributes from the "updates.xml" file are copied into it. Also, if a tag in the input file has the Lock, React, or Hold attributes, then after transformation, those attributes become child tags, not attributes, and the values of the Lock, React, or Hold attributes from the input file become the values of the const attribute of the Lock, React, or Hold tags of file after transformation, respectively. The file after XSLT1.0 transformation should look like this:

<Config>
  <Fix title="title" description="description">
    <Lock const="E0" title="title4" description="description4" />
    <React const="200" title="title5" description="description5" />
    <Hold const="50" title="title6" description="description6" />
    <Name type="type" value="value">
      <Hold const="50" title="title6" description="description6" />
      <Image select="2" readonly="false" />
    </Name>
  </Fix>
</Config>

The XSLT1.0 transformation file has three identical choose code blocks for the Lock, React, or Hold tags:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
  exclude-result-prefixes="exslt"
  version="1.0">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:strip-space  elements="*"/>

  <xsl:output method="xml" indent="yes" />
  <xsl:variable name="updates" select="document('updates.xml')"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*">
    <xsl:copy>

      <xsl:variable name="name" select="name()"/>
      <xsl:apply-templates select="$updates//*[name(.) = $name]/@*"/>
      <xsl:apply-templates select="@*"/>

      <xsl:choose>
        <xsl:when test="self::node()[@Lock]">
          <Lock const="{@Lock}">
            <xsl:copy-of select="$updates//Lock/@*[name(.) != 'const']"/>
          </Lock>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="($updates//*[name(.) = $name])[@Lock]">
            <Lock>
              <xsl:attribute name="const">
                <xsl:value-of select="$updates//*[name(.) = $name]/@Lock"/>
              </xsl:attribute>
              <xsl:copy-of select="$updates//Lock/@*[name(.) != 'const']"/>
            </Lock>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:choose>
        <xsl:when test="self::node()[@React]">
          <React const="{@React}">
            <xsl:copy-of select="$updates//React/@*[name(.) != 'const']"/>
          </React>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="($updates//*[name(.) = $name])[@React]">
            <React>
              <xsl:attribute name="const">
                <xsl:value-of select="$updates//*[name(.) = $name]/@React"/>
              </xsl:attribute>
              <xsl:copy-of select="$updates//React/@*[name(.) != 'const']"/>
            </React>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:choose>
        <xsl:when test="self::node()[@Hold]">
          <Hold const="{@Hold}">
            <xsl:copy-of select="$updates//Hold/@*[name(.) != 'const']"/>
          </Hold>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="($updates//*[name(.) = $name])[@Hold]">
            <Hold>
              <xsl:attribute name="const">
                <xsl:value-of select="$updates//*[name(.) = $name]/@Hold"/>
              </xsl:attribute>
              <xsl:copy-of select="$updates//Hold/@*[name(.) != 'const']"/>
            </Hold>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@Lock" />
  <xsl:template match="@React" />
  <xsl:template match="@Hold" />

</xsl:stylesheet>

How can I convert the three choose blocks for the Lock, React, or Hold tags into one?

CodePudding user response:

You can parametrize a template and call it or apply-templates (using a mode):

  <xsl:template match="*">
    <xsl:copy>

      <xsl:variable name="name" select="name()"/>
      <xsl:apply-templates select="$updates//*[name(.) = $name]/@*"/>
      <xsl:apply-templates select="@*"/>

      <xsl:apply-templates select="." mode="compare">
        <xsl:with-param name="att" select="'Lock'"/>
      </xsl:apply-templates>
      
      <xsl:apply-templates select="." mode="compare">
        <xsl:with-param name="att" select="'React'"/>
      </xsl:apply-templates>
      
      <xsl:apply-templates select="." mode="compare">
        <xsl:with-param name="att" select="'Hold'"/>
      </xsl:apply-templates>

      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="*" mode="compare">
    <xsl:param name="att"/>
    <xsl:variable name="name" select="name()"/>
    <xsl:choose>
      <xsl:when test="self::node()[@*[name() = $att]]">
      <xsl:element name="{$att}"> 
        <xsl:attribute name="const">
          <xsl:value-of select="@*[name() = $att]"/>
        </xsl:attribute>
        <xsl:copy-of select="$updates//*[name() = $att]/@*[name(.) != 'const']"/>
      </xsl:element>
      </xsl:when>
      <xsl:otherwise>
      <xsl:if test="($updates//*[name(.) = $name])[@*[name() = $att]]">
        <Lock>
          <xsl:attribute name="const">
            <xsl:value-of select="$updates//*[name(.) = $name]/@*[name() = $att]"/>
          </xsl:attribute>
          <xsl:copy-of select="$updates//*[name() = $att]/@*[name(.) != 'const']"/>
        </Lock>
      </xsl:if>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

It doesn't necessarily become more readable code that way.

  • Related