﻿<?xml version="1.0" encoding="utf-8"?>
<!--
  (c) 2010 Nesterovsky Bros. 

  A tuple interface:
    tuple:ref($items as item) as item() to wrap items into a tuple.
    tuple:deref($tuple as item()?) as item()* to unwrap items from a tuple.
    tuple:is-same($first as item(), $second as item()) as xs:boolean to test 
      whether two tuples are the same.
    
  This is a primitive implementation that uses element as a tuple.
  It supports items of type: 
    xs:string, xs:integer, xs:decimal, xs:boolean, element()
-->

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:tuple="http://www.nesterovsky-bros.com/xslt/tuple"
  xmlns:p="http://www.nesterovsky-bros.com/xslt/private/tuple"
  exclude-result-prefixes="xs tuple p">

  <!--
    Constructs a tuple from a sequence of items.
      $items - a sequence of items to wrap into a tuple.
      Returns a tuple item.
  -->
  <xsl:function name="tuple:ref" as="item()">
    <xsl:param name="items" as="item()*"/>

    <p:tuple>
      <xsl:for-each select="$items">
        <xsl:choose>
          <xsl:when test=". instance of xs:string">
            <p:string value="{.}"/>
          </xsl:when>
          <xsl:when test=". instance of xs:integer">
            <p:integer value="{.}"/>
          </xsl:when>
          <xsl:when test=". instance of xs:decimal">
            <p:decimal value="{.}"/>
          </xsl:when>
          <xsl:when test=". instance of xs:boolean">
            <p:boolean value="{.}"/>
          </xsl:when>
          <xsl:when test=". instance of element()">
            <xsl:sequence select="."/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:sequence select="error(xs:QName('unsupported-tuple-type'))"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </p:tuple>
  </xsl:function>

  <!--
    Dereferences a tuple.
      $tuple - an optional tuple item.
      Returns items wrapped into the tuple.
  -->
  <xsl:function name="tuple:deref" as="item()*">
    <xsl:param name="tuple" as="item()?"/>

    <xsl:if test="exists($tuple)">
      <xsl:variable name="element" as="element()" 
        select="$tuple/self::p:tuple"/>

      <xsl:for-each select="$element/*">
        <xsl:variable name="item" as="item()">
          <xsl:choose>
            <xsl:when test="self::p:string">
              <xsl:sequence select="xs:string(@value)"/>
            </xsl:when>
            <xsl:when test="self::p:integer">
              <xsl:sequence select="xs:integer(@value)"/>
            </xsl:when>
            <xsl:when test="self::p:decimal">
              <xsl:sequence select="xs:decimal(@value)"/>
            </xsl:when>
            <xsl:when test="self::p:boolean">
              <xsl:sequence select="xs:boolean(@value)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:sequence select="."/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>

        <xsl:sequence select="$item"/>
      </xsl:for-each>
    </xsl:if>
  </xsl:function>

  <!--
    Tests whether two tuples are equal. 
    Empty sequence is considered the same as other empty sequence.
      $first - a first tuple.
      $second - a second tuple.
  -->
  <xsl:function name="tuple:is-same" as="xs:boolean">
    <xsl:param name="first" as="item()?"/>
    <xsl:param name="second" as="item()?"/>

    <xsl:sequence select="
      if (exists($first)) then
        boolean($first is $second)
      else
        empty($second)"/>
  </xsl:function>
  
</xsl:stylesheet>
