2009-11-16 7 views
2

Я подписываю строку с XSLT 1.0 и стараюсь, чтобы пустые строки не распознавались как токены. Вот целая функция, основанная на XSLT Cookbook:Когда тест висит в бесконечном цикле

<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> 

Соотношение string, что я передаю в это:

Европа; # 6; #global; # 3, #Middle Восток, Африка и Кавказа; 2; #Europe; # 6; #global; # 3; #Middle Востоке, в Африке и Кавказа

(The £ и $ показатели как раз там, поэтому я вижу, что пустые строки не выводятся. Это находится в SharePoint, поэтому его трудно отлаживать.)

Этот код зависает при обработке XSLT. Линией, вызывающей проблему, является <xsl:when test="not($delimiter) and $string != ''">. Как только я удаляю второй тест and, он работает снова. Я также пробовал and string($string) без успеха.

Кто-нибудь знает, почему это происходит и как его решить?

ответ

4

Я считаю, что мое подозрение было правильно: вы падаете на ваш пункт <xsl:otherwise> когда $string имеет значение, но $delimiter делает не, вызывая бесконечный цикл, как вы говорите.

Добавьте следующий новый пункт <xsl:when> после первого:

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

Это предотвратит выполнение от входа в <xsl:otherwise> блок, когда он не должен.


Более подробное объяснение того, что происходит и почему это зацикливание:

Есть три ветви в <xsl:choose> блоке.

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

Так что, когда ни $string, ни $delimiter содержат значения, то первое условие не выполняется (потому что $string != '' ложно). Второе условие проходит (потому что contains(nil,nil) всегда возвращает true (подтверждено в Visual Studio)), который снова вызывает шаблон с теми же параметрами (потому что substring-before возвращает пустую строку, поскольку она не содержит пустой разделитель). Эрго, бесконечный цикл.

Исправление для добавления нового пустого условия:

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

EDIT: Я пошарил, и я не могу найти ссылку на определенном поведении contains, когда второй параметр пусто или равно нулю. Тесты показали, что механизм XSLT Microsoft Visual Studio возвращает true, когда второй параметр либо пуст, либо равен нулю. Я не уверен, что это определенное поведение или если разработчик решит это. Кто-нибудь имеет окончательный ответ на это? Томалак, я смотрю на тебя.

+0

Спасибо, я вижу ошибку здесь. –

+0

@Alex Angas: проблем нет. Просто убедитесь, что никогда не сосредотачивайтесь только на одном разделе блока if if else. (Или его эквивалент на любом языке, который вы используете). Весь блок необходимо учитывать при отладке одной его части. – Welbog

+0

+1 Для отличного пояснения/объяснения. Да, я должен знать лучше. –

0

Не string зарезервированное слово? Можете ли вы попытаться заменить это имя на что-нибудь еще?

EDIT: Поставляется код побежал без проблем здесь: XSLT Tryit Editor v1.0 с помощью:

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

Это прямо из Поваренной книги XSLT и работает до сих пор ... Кажется странным, что они будут использовать это как имя переменной. –

+0

Можете ли вы разместить больше кода или просто проверить? Я не знаю, что поваренная книга –

+0

Обновлен вопрос с полным кодом и примером. –