2009-11-16 6 views
2

Je suis en train de scinder une chaîne avec XSLT 1.0 et d'essayer d'empêcher les chaînes vides d'être reconnues comme des marques. Voici l'ensemble de la fonction, sur la base XSLT Cookbook:Lorsque le test est suspendu dans une boucle infinie

<xsl:template name="tokenize"> 
    <xsl:param name="string" select="''" /> 
    <xsl:param name="delimiters" select="';#'" /> 
    <xsl:param name="tokensplitter" select="','" /> 
    <xsl:choose> 
     <!-- Nothing to do if empty string --> 
     <xsl:when test="not($string)" /> 

     <!-- No delimiters signals character level tokenization --> 
     <xsl:when test="not($delimiters)"> 
      <xsl:call-template name="_tokenize-characters"> 
       <xsl:with-param name="string" select="$string" /> 
       <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
      </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:call-template name="_tokenize-delimiters"> 
       <xsl:with-param name="string" select="$string" /> 
       <xsl:with-param name="delimiters" select="$delimiters" /> 
       <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
      </xsl:call-template> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

<xsl:template name="_tokenize-characters"> 
    <xsl:param name="string" /> 
    <xsl:param name="tokensplitter" /> 
    <xsl:if test="$string"> 
     <token><xsl:value-of select="substring($string, 1, 1)"/></token> 
     <xsl:call-template name="_tokenize-characters"> 
      <xsl:with-param name="string" select="substring($string, 2)" /> 
     </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

<xsl:template name="_tokenize-delimiters"> 
    <xsl:param name="string" /> 
    <xsl:param name="delimiters" /> 
    <xsl:param name="tokensplitter" /> 

    <!-- Extract a delimiter --> 
    <xsl:variable name="delimiter" select="substring($delimiters, 1, 1)"/> 
    <xsl:choose> 
     <!-- If the delimiter is empty we have a token --> 
     <xsl:when test="not($delimiter) and $string != ''"> 
      <xsl:text>£</xsl:text> 
      <token><xsl:value-of select="$string"/></token> 
      <xsl:text>$</xsl:text> 
      <xsl:value-of select="$tokensplitter"/> 
     </xsl:when> 
     <!-- If the string contains at least one delimiter we must split it --> 
     <xsl:when test="contains($string, $delimiter)"> 
      <!-- If it starts with the delimiter we don't need to handle the before part --> 
      <xsl:if test="not(starts-with($string, $delimiter))"> 
       <!-- Handle the part that comes before the current delimiter with the next delimiter. --> 
       <!-- If there is no next the first test in this template will detect the token. --> 
       <xsl:call-template name="_tokenize-delimiters"> 
        <xsl:with-param name="string" select="substring-before($string, $delimiter)" /> 
        <xsl:with-param name="delimiters" select="substring($delimiters, 2)" /> 
        <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
       </xsl:call-template> 
      </xsl:if> 
      <!-- Handle the part that comes after the delimiter using the current delimiter --> 
      <xsl:call-template name="_tokenize-delimiters"> 
       <xsl:with-param name="string" select="substring-after($string, $delimiter)" /> 
       <xsl:with-param name="delimiters" select="$delimiters" /> 
       <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
      </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
      <!-- No occurrences of current delimiter so move on to next --> 
      <xsl:call-template name="_tokenize-delimiters"> 
       <xsl:with-param name="string" select="$string" /> 
       <xsl:with-param name="delimiters" select="substring($delimiters, 2)" /> 
       <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
      </xsl:call-template> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

Rapport qualité string que je passe en est:

Europe; # 6; #Global; # 3; #Middle Est, Afrique et Caucase; 2; #Europe; # 6; #Global; # 3; #Middle Est, l'Afrique et du Caucase

(Les £ et $ indicateurs sont juste là pour que je puisse voir aucune chaîne vide n'est sortie. Ceci est dans SharePoint, il est donc difficile de déboguer.)

Ce code bloque le traitement du fichier XSLT. La ligne à l'origine du problème est <xsl:when test="not($delimiter) and $string != ''">. Dès que je retire le deuxième test and, il fonctionne à nouveau. J'ai également essayé and string($string) sans succès. Quelqu'un sait-il pourquoi cela se produit et comment le résoudre?

Répondre

4

Je crois que mon soupçon était correcte: vous tomber à travers votre <xsl:otherwise> clause lorsque $string a une valeur, mais $delimiter-t pas, provoquant une boucle infinie, comme vous le dites.

Ajouter la nouvelle clause <xsl:when> après la première qui suit:

<xsl:when test="not($delimiter) and $string = ''" /> 

qui empêchera l'exécution d'entrer dans le bloc <xsl:otherwise> quand il ne devrait pas.


Une explication plus élaborée de ce qui se passe et pourquoi il tourne en boucle:

Il y a trois branches dans le bloc <xsl:choose>.

<xsl:when test="not($delimiter) and $string != ''"> 
    <xsl:when test="contains($string, $delimiter)"> 
    <xsl:otherwise> 

Ainsi, lorsque ni $string ni $delimiter contiennent des valeurs, la première condition échoue (parce $string != '' est faux). La deuxième condition passe (car contains(nil,nil) renvoie toujours true (confirmé dans Visual Studio)), qui appelle à nouveau le modèle avec les mêmes paramètres (car le substring-before renvoie la chaîne vide car il ne contient pas le délimiteur vide). Ergo, une boucle infinie.

Le correctif est d'ajouter une nouvelle, l'état vide:

<xsl:when test="not($delimiter) and $string != ''"> 
    <xsl:when test="not($delimiter) and $string = ''" /> 
    <xsl:when test="contains($string, $delimiter)"> 
    <xsl:otherwise> 

EDIT: Je l'ai poussé autour et je ne peux pas trouver une référence au comportement défini de contains lorsque le second paramètre est vide ou nul. Des tests ont montré que le moteur XSLT de Microsoft Visual Studio renvoie true lorsque le second paramètre est vide ou nul. Je ne suis pas sûr si c'est le comportement défini ou si c'est à l'implémenteur de décider. Quelqu'un at-il une réponse concluante à cela? Tomalak, je te regarde.

+0

Merci, je peux voir l'erreur ici. –

+0

@Alex Angas: Pas de problème. Assurez-vous de ne jamais vous concentrer sur une seule section d'un bloc 'if/elseif' (ou son équivalent dans la langue que vous utilisez). Le bloc entier doit être pris en compte lors du débogage d'une partie de celui-ci. – Welbog

+0

+1 Pour une excellente clarification/explication. Oui, je devrais savoir mieux. –

0

Le mot réservé n'est-il pas string? Pouvez-vous essayer de remplacer ce nom pour autre chose?

EDIT: code fourni sans problème couru ici: XSLT Tryit Editor v1.0 en utilisant:

<xsl:call-template name="tokenize"> 
    <xsl:with-param name="string">Europe;#6;#Global...</xsl:with-param> 
</xsl:call-template> 
+0

Cela vient directement du livre de recettes XSLT et a fonctionné jusqu'à maintenant ... Cela semble étrange qu'ils l'utilisent comme nom de variable. –

+0

Pouvez-vous s'il vous plaît poster plus de code, ou un cas de test simple? Je ne connais pas ce livre de cuisine –

+0

Mise à jour de la question avec le code complet et l'exemple. –