XSLTで論理和された値から構成要素の一覧を出力する

例えば以下のような項目の定義があって

 1:食品
 2:衣類
 4:雑貨
 8:本

ある変数がその論理和により算出されている時に

 1 --> 食品
 2 --> 衣類
 3 --> 食品,衣類
 4 --> 雑貨
 5 --> 食品,雑貨
 6 --> 衣類,雑貨
 7 --> 食品,衣類,雑貨
 8 --> 本

論理和を分解して表示したい。

出力結果

no 店舗名 店舗形態 取り扱い
1 ○○ストア スーパー 食品
2 △△百貨店 百貨店 食品 衣類 雑貨 本
3 ××デパート デパート 食品 衣類 雑貨

shop.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="shop.xsl" type="text/xsl" ?>
<shopList>
  <shop>
    <name>○○ストア</name>
    <shopType>1</shopType>
    <handling>1</handling>
  </shop>
  <shop>
    <name>△△百貨店</name>
    <shopType>3</shopType>
    <handling>15</handling>
  </shop>
  <shop>
    <name>××デパート</name>
    <shopType>2</shopType>
    <handling>7</handling>
  </shop>
</shopList>

type.xml

<?xml version="1.0" encoding="UTF-8"?>
<typeList>
  <type name="shopType" label="店舗形式">
    <element name="1">スーパー</element>
    <element name="2">デパート</element>
    <element name="3">百貨店</element>
  </type>
  <type name="handling" label="取り扱い" or="true">
    <element name="1">食品</element>
    <element name="2">衣類</element>
    <element name="4">雑貨</element>
    <element name="8">本</element>
  </type>
</typeList>

shop.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" encoding="shift_jis" indent="yes"/>
  <xsl:strip-space elements="*" />

  <!-- タイプのリストを読み込みます -->
  <xsl:variable name="TypeList" select="document('type.xml')/typeList"/>

  <xsl:template match="/shopList">
    <html>
      <body>
        <table border="1">
          <tr><th>no</th><th>店舗名</th><th>店舗形態</th><th>取り扱い</th></tr>
          <xsl:for-each select="shop">
            <tr>
              <th><xsl:value-of select="position()"/></th>
              <td><xsl:value-of select="name"/></td>
              <td><xsl:apply-templates select="shopType" mode="タイプ解決"/></td>
              <td><xsl:apply-templates select="handling" mode="タイプ解決"/></td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="*" mode="タイプ解決">
    <xsl:variable name="typeName" select="name(.)"/>
    <xsl:variable name="name" select="text()"/>
    <xsl:variable name="typeElements" select="$TypeList/type[@name=$typeName]"/>
    <xsl:choose>
      <!-- 論理和の場合 -->
      <xsl:when test="$typeElements[@or='true']">
        <xsl:call-template name="orResolved">
          <xsl:with-param name="name" select="$name"/>
          <xsl:with-param name="typeElements" select="$typeElements"/>
        </xsl:call-template>
      </xsl:when>
      <!-- 論理和以外の場合でタイプ要素あり -->
      <xsl:when test="$typeElements">
        <xsl:call-template name="nameResolved">
          <xsl:with-param name="name" select="$name"/>
          <xsl:with-param name="typeElements" select="$typeElements"/>
        </xsl:call-template>
        <br />
      </xsl:when>
      <!-- タイプ要素無し -->
      <xsl:otherwise><xsl:value-of select="$name"/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- 名前によるタイプ解決 -->
  <xsl:template name="nameResolved">
    <xsl:param name="typeElements"/>
    <xsl:param name="name"/>
    <xsl:choose>
      <xsl:when test="$typeElements">
        <xsl:value-of select="$typeElements/element[@name=$name]/text()"/>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="$name"/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- 論理和によるタイプ解決 -->
  <xsl:template name="orResolved">
    <xsl:param name="typeElements"/>
    <xsl:param name="name"/>
    <xsl:choose>
      <!-- カテゴリに 0 が存在する場合は論理和で判定できないので先に判断しておきます-->
      <xsl:when test="$name = 0">
        <xsl:value-of select="$typeElements/element[@name='0']/text()"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:for-each select="$typeElements/element">
          <xsl:variable name="currName" select="@name"/>
          <xsl:if test="(($name mod ($currName * 2)) div $currName) &gt;= 1">
            <xsl:value-of select="text()"/>&#160;
          </xsl:if>
        </xsl:for-each>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>


</xsl:stylesheet>