<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:user="urn:my-scripts"
    exclude-result-prefixes="msxsl user">

  <msxsl:script language="JScript" implements-prefix="user">
<![CDATA[

/*
EndsWith and StartsWith added for P14 Bug #: 616542 ('CHS: Filter doesn't support surrogate').
It seems that XSLT string manipulation methods like: substring, string-length, and starts-with
don't work with UTF-16 Chinese surrogate character pairs, so I had to make JScript-based methods
to do the same thing.
*/

function EndsWith(sTestString, sTestChar)
{
   var bRetVal = false;
   
   if (sTestString != null && sTestChar != null)
   {
      if (sTestString.substring(sTestString.length - 1) == sTestChar)
      {
         bRetVal = true;
      }
   }
   
   return bRetVal;
}

function StartsWith(sTestString, sTestChar)
{
   var bRetVal = false;
   
   if (sTestString != null && sTestChar != null)
   {
      if (sTestString.substring(0, 1) == sTestChar)
      {
         bRetVal = true;
      }
   }
   
   return bRetVal;
}

function StripLeadingAndTrailingPercentSigns(str)
{
   if (str == null)
   {
      return '';
   }
   
   if (str.substr(0, 1) == '%')
   {
      str = str.substr(1);
   }
   
   if (str.substr(str.length -1) == '%')
   {
      str = str.substr(0, str.length -1);
   }

   return str;
}

function ReplaceSpecialChars(str)
{
   if (str == '')
   {
      return '';
   }

   var re;
   
   re = /\[%\]/g;
   str = str.replace(re, '%');
   
   re = /\[_\]/g;
   str = str.replace(re, '_');

   re = /\[\^\]/g;
   str = str.replace(re, '^');

   re = /\[\[\]/g;
   str = str.replace(re, '[');

   re = /\[\]\]/g;
   str = str.replace(re, ']');

   return str;
}

]]>
   </msxsl:script>
      
   <xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
   
   <xsl:template match="/">
      <xsl:apply-templates select="Filter" />
   </xsl:template>
   
   <xsl:template match="Filter">   
      <Filter>
         <xsl:choose>
            <xsl:when test="Criteria/LogicalOperator/@logicalOperationType = 'Nop'">
               <FieldComparison>
                  <xsl:call-template name="BuildClause">
                     <xsl:with-param name="pContextNode" select="Criteria/LogicalOperator/FieldOperator" />
                  </xsl:call-template>
               </FieldComparison>
            </xsl:when>
            <xsl:otherwise>
               <xsl:apply-templates select="Criteria/LogicalOperator" />
            </xsl:otherwise>
         </xsl:choose>
      </Filter>
   </xsl:template>
   
   <xsl:template match="LogicalOperator">
      <LogicalOperator>
         <xsl:attribute name="Type">
            <xsl:value-of select="@logicalOperationType" />
         </xsl:attribute>
         <xsl:apply-templates select="LogicalOperator" />
         <xsl:apply-templates select="FieldOperator" />
      </LogicalOperator>
   </xsl:template>
   
   <xsl:template match="FieldOperator">
      <FieldComparison>
         <xsl:call-template name="BuildClause">
            <xsl:with-param name="pContextNode" select="." />
         </xsl:call-template>
      </FieldComparison>
   </xsl:template>
   
   <xsl:template name="BuildClause">
      <xsl:param name="pContextNode" />
      <Field>
         <xsl:value-of select="$pContextNode/Field/@fieldName" />
         <xsl:value-of select="$pContextNode/CustomField/@fieldName" />
      </Field>
      <Operator>
         <xsl:call-template name="TranslateOperator">
            <xsl:with-param name="pFieldOperationType" select="$pContextNode/@fieldOperationType" />
            <xsl:with-param name="pOperand" select="$pContextNode/Operand" />
         </xsl:call-template>
      </Operator>
      <xsl:call-template name="RenderOperand">
            <xsl:with-param name="pFieldOperationType" select="$pContextNode/@fieldOperationType" />
            <xsl:with-param name="pOperand" select="$pContextNode/Operand" />
      </xsl:call-template>
   </xsl:template>

   <!--
   Javascript Extension methods in XSLT are not cross browser.
   Have XSLT 1.0 string functions and templates made to mimic javascript functions behavior for non-IE.
   Branch code so that the IE behavior stays untouched, in case javascript extensions were helping against some limitations of the XSLT string manipulation (e.g. CHS characters maybe)
   -->
   <xsl:template name="TranslateOperator">
      <xsl:param name="pFieldOperationType" />
      <xsl:param name="pOperand" />
      <xsl:variable name="vLike">%</xsl:variable>
      
      <xsl:choose>

         <!-- String only -->
         <xsl:when test="$pFieldOperationType='Like'">
            <xsl:choose>
               <xsl:when test="function-available('user:EndsWith') and function-available('user:StartsWith')">
                  <!-- IE -->
                  <!-- test for: string ends-with "%" -->
                  <xsl:if test="user:EndsWith(string($pOperand), string($vLike))">
                     <xsl:choose>
                        <!-- test for: string starts-with "%" -->
                        <xsl:when test="user:StartsWith(string($pOperand), string($vLike))">
                           <xsl:text>Contains</xsl:text>
                        </xsl:when>
                        <xsl:otherwise>
                           <xsl:text>BeginsWith</xsl:text>
                        </xsl:otherwise>
                     </xsl:choose>
                  </xsl:if>
               </xsl:when>
               <xsl:otherwise>
                  <!-- non IE -->
                  <!-- test for: string ends-with "%" -->
                  <xsl:if test="substring($pOperand, string-length($pOperand) - string-length($vLike) + 1) = $vLike">
                     <xsl:choose>
                        <!-- test for: string starts-with "%" -->
                        <xsl:when test="starts-with($pOperand, $vLike)">
                           <xsl:text>Contains</xsl:text>
                        </xsl:when>
                        <xsl:otherwise>
                           <xsl:text>BeginsWith</xsl:text>
                        </xsl:otherwise>
                     </xsl:choose>
                  </xsl:if>
               </xsl:otherwise>
            </xsl:choose>
         </xsl:when>
         
         <!-- String only -->
         <xsl:when test="$pFieldOperationType='NotLike'">
            <xsl:text>Excludes</xsl:text>
         </xsl:when>
      
         <!-- NOTE: LikeContains and NotContain were never used in P12 -->
         
         <!-- String only -->
         <xsl:when test="$pFieldOperationType='LikeContains'">
            <xsl:text>Contains</xsl:text>
         </xsl:when>

         <!-- String only -->
         <xsl:when test="$pFieldOperationType='NotContain'">
            <xsl:text>Excludes</xsl:text>
         </xsl:when>

         <xsl:when test="$pFieldOperationType='Equal'">
            <xsl:text>Equals</xsl:text>
         </xsl:when>

         <xsl:when test="$pFieldOperationType='NotEqual'">
            <xsl:text>NotEqual</xsl:text> <!-- same as P12 -->
         </xsl:when>

         <!-- SV only -->
         <xsl:when test="$pFieldOperationType='LessThan'">
            <xsl:text>LessThan</xsl:text> <!-- same as P12 -->
         </xsl:when>

         <!-- SV only -->
         <xsl:when test="$pFieldOperationType='LessThanEqual'">
            <xsl:text>LessThanOrEqual</xsl:text>
         </xsl:when>

         <!-- SV only -->
         <xsl:when test="$pFieldOperationType='GreaterThan'">
            <xsl:text>GreaterThan</xsl:text> <!-- same as P12 -->
         </xsl:when>

         <!-- SV only -->
         <xsl:when test="$pFieldOperationType='GreaterThanEqual'">
            <xsl:text>GreaterThanOrEqual</xsl:text>
         </xsl:when>

         <!-- SV only; String only -->
         <xsl:when test="$pFieldOperationType='LikeBeginsWith'">
            <xsl:text>BeginsWith</xsl:text>
         </xsl:when>

         <!-- NEW FOR P14 : MV only -->
         <xsl:when test="$pFieldOperationType='Contain'">
            <xsl:text>ContainsValues</xsl:text> <!-- AKA IsSupersetOf -->
         </xsl:when>

         <!-- NEW FOR P14 : MV only -->
         <xsl:when test="$pFieldOperationType='NotContain_MULTI_VALUES'">
            <xsl:text>ExcludesValues</xsl:text> <!-- AKA IsNotSupersetOf -->
         </xsl:when>
      </xsl:choose>
   </xsl:template>

   <xsl:template name="RenderOperand">
      <xsl:param name="pFieldOperationType" />
      <xsl:param name="pOperand" />

      <xsl:choose>
         <!-- 
         For Like and NotLike, we need to clean up the operand (by stripping off leading & trailing '%' symbols) and replacing special characters
         -->
         <xsl:when test="$pFieldOperationType='Like' or $pFieldOperationType='NotLike'">
            <xsl:choose>
               <xsl:when test="function-available('user:StripLeadingAndTrailingPercentSigns') and function-available('user:ReplaceSpecialChars')">
                  <!-- IE -->
                  <xsl:variable name="v1" select="user:StripLeadingAndTrailingPercentSigns(string($pOperand))" />
                  <xsl:variable name="v2" select="user:ReplaceSpecialChars(string($v1))" />
                  <Operand>
                     <xsl:value-of select="$v2" />
                  </Operand>
               </xsl:when>
               <xsl:otherwise>
                  <!-- non-IE -->
                  <xsl:variable name="v1">
                     <xsl:call-template name="StripLeadingAndTrailingPercentSigns_NonIE">
                        <xsl:with-param name="pOperand" select="$pOperand"></xsl:with-param>
                     </xsl:call-template>
                  </xsl:variable>
                  <xsl:variable name="v2">
                     <xsl:call-template name="ReplaceSpecialChars_NonIE">
                        <xsl:with-param name="pOperand" select="$v1"></xsl:with-param>
                     </xsl:call-template>
                  </xsl:variable>
                  <Operand>
                     <xsl:value-of select="$v2" />
                  </Operand>
               </xsl:otherwise>
            </xsl:choose>
         </xsl:when>

         <xsl:otherwise>
            <xsl:choose>
               <!-- 
               If the LTUID attribute is not empty, it means that we're dealing with a custom field based on a lookup table.
               We have to handle the case where we are dealing with more than one GUID operand from a  multi-valued lookup table.
               -->
               <xsl:when test="$pOperand/@LTUID != ''">
                  <xsl:call-template name="FormatMultiValuedOperands">
                     <xsl:with-param name="pGuidList" select="$pOperand" />
                  </xsl:call-template>
               </xsl:when>
               <xsl:otherwise>
               <Operand>
                  <xsl:value-of select="$pOperand" />
               </Operand>
               </xsl:otherwise>
            </xsl:choose>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
   
   <!--   
   Recursive function for dealing with a comma-separated list of guids like:
   
   "e911874f-cec5-404b-93f1-661ef2c7de35,c795496a-1d2c-4415-aed4-33b8b0bb8cd3,b929e2f8-09ff-4ac8-b685-976fe06a8da7"
   
   This template strips off the first one, and calls itself with the remaining list.
   There is no XSLT equivalent for the Split() method, so we have to use a combination of substring-before & substring-after. 
   -->
   <xsl:template name="FormatMultiValuedOperands">
      <xsl:param name="pGuidList" />
      <xsl:variable name="vNewGuidList" select="concat(normalize-space($pGuidList), '')" />
      <xsl:variable name="vFirstGuid" select="substring-before($vNewGuidList, ',')" />
      <xsl:variable name="vRemainingGuids" select="substring-after($vNewGuidList, ',')" />

      <Operand>
         <!-- HACK for dealing with the final value in the list -->
         <xsl:choose>
            <!--
            If $vRemainingGuids is empty, it means that we didn't find a comma in the list of guids.
            Therefore, we must be looking at the last value.  If so, we'll use the list passed in as a parameter to this template.
            -->
            <xsl:when test="$vRemainingGuids = ''">
               <xsl:value-of select="$pGuidList" />
            </xsl:when>
            <xsl:otherwise>
               <xsl:value-of select="$vFirstGuid" />
            </xsl:otherwise>
         </xsl:choose>
      </Operand>
            
      <xsl:if test="$vRemainingGuids != ''">
         <xsl:call-template name="FormatMultiValuedOperands">
            <xsl:with-param name="pGuidList" select="$vRemainingGuids" />
         </xsl:call-template>
      </xsl:if>   
   </xsl:template>

   <!--
   Javascript Extension methods in XSLT are not cross browser.
   Have XSLT 1.0 string functions and templates made to mimic javascript functions behavior for non-IE.
   Branch code so that the IE behavior stays untouched, in case javascript extensions were helping against some limitations of the XSLT string manipulation (e.g. CHS characters maybe)
   -->
   <xsl:template name="StripLeadingAndTrailingPercentSigns_NonIE">
      <xsl:param name="pOperand" />
      <xsl:variable name="vLike">%</xsl:variable>

      <xsl:choose>
         <xsl:when test="starts-with($pOperand, $vLike)">
            <!-- skip first and last vLike letter(s) for now -->
            <xsl:value-of select="substring($pOperand, string-length($vLike) + 1, string-length($pOperand) - string-length($vLike) - string-length($vLike))" />
         </xsl:when>
         <xsl:otherwise>
            <!-- skip last letter for now -->
            <xsl:value-of select="substring($pOperand, 1, string-length($pOperand) - string-length($vLike))" />
         </xsl:otherwise>
      </xsl:choose>
      <xsl:if test="substring($pOperand, string-length($pOperand) - string-length($vLike) + 1) != $vLike">
         <!-- keep the last letter as it's not '%' -->
         <xsl:value-of select="$vLike" />
      </xsl:if>
   </xsl:template>

   <!--
   Simulate global regex replace thru recursive string manipulation.
   -->
   <xsl:template name="MyGlobalStringReplace">
      <xsl:param name="text"/>
      <xsl:param name="token"/>
      <xsl:param name="newToken"/>
      <xsl:choose>
         <xsl:when test="contains($text,$token)">
            <xsl:value-of select="concat(substring-before($text,$token),$newToken)"/>
            <xsl:call-template name="MyGlobalStringReplace">
               <xsl:with-param name="text" select="substring-after($text,$token)"/>
               <xsl:with-param name="token" select="$token"/>
               <xsl:with-param name="newToken" select="$newToken"/>
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <xsl:value-of select="$text"/>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>

   <!--
   Replace server escaped special chars with char only (e.g. [%] with %)
   -->
   <xsl:template name="ReplaceSpecialChars_NonIE">
      <xsl:param name="pOperand" />

      <xsl:variable name="vLikeEscaped">[%]</xsl:variable>
      <xsl:variable name="vLike">%</xsl:variable>

      <xsl:variable name="vUnderscoreEscaped">[_]</xsl:variable>
      <xsl:variable name="vUnderscore">_</xsl:variable>

      <xsl:variable name="vCaretEscaped">[^]</xsl:variable>
      <xsl:variable name="vCaret">^</xsl:variable>

      <xsl:variable name="vOpenBracketEscaped">[[]</xsl:variable>
      <xsl:variable name="vOpenBracket">[</xsl:variable>

      <xsl:variable name="vCloseBracketEscaped">[]]</xsl:variable>
      <xsl:variable name="vCloseBracket">]</xsl:variable>

      <xsl:variable name="v1">
         <xsl:call-template name="MyGlobalStringReplace">
            <xsl:with-param name="text" select="$pOperand"></xsl:with-param>
            <xsl:with-param name="token" select="$vLikeEscaped"></xsl:with-param>
            <xsl:with-param name="newToken" select="$vLike"></xsl:with-param>
         </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="v2">
         <xsl:call-template name="MyGlobalStringReplace">
            <xsl:with-param name="text" select="$v1"></xsl:with-param>
            <xsl:with-param name="token" select="$vUnderscoreEscaped"></xsl:with-param>
            <xsl:with-param name="newToken" select="$vUnderscore"></xsl:with-param>
         </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="v3">
         <xsl:call-template name="MyGlobalStringReplace">
            <xsl:with-param name="text" select="$v2"></xsl:with-param>
            <xsl:with-param name="token" select="$vCaretEscaped"></xsl:with-param>
            <xsl:with-param name="newToken" select="$vCaret"></xsl:with-param>
         </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="v4">
         <xsl:call-template name="MyGlobalStringReplace">
            <xsl:with-param name="text" select="$v3"></xsl:with-param>
            <xsl:with-param name="token" select="$vOpenBracketEscaped"></xsl:with-param>
            <xsl:with-param name="newToken" select="$vOpenBracket"></xsl:with-param>
         </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="v5">
         <xsl:call-template name="MyGlobalStringReplace">
            <xsl:with-param name="text" select="$v4"></xsl:with-param>
            <xsl:with-param name="token" select="$vCloseBracketEscaped"></xsl:with-param>
            <xsl:with-param name="newToken" select="$vCloseBracket"></xsl:with-param>
         </xsl:call-template>
      </xsl:variable>

      <xsl:value-of select="$v5" />
   </xsl:template>
</xsl:stylesheet>
