Пользовательский формат для - PullRequest
4 голосов
/ 06 июля 2011

Можно ли определить пользовательский формат для <xsl:number>?

У меня есть случай, когда требуется стандартный формат на основе альфы, но некоторые символы в алфавите запрещены (странное требование, но этоэто то, что требует клиент).Например, букву i нельзя использовать, поэтому при использовании <xsl:number> я должен получить последовательность: a, b, c, d, e, f, g, h, j, k, ..., aa,ab, ..., ah, aj, ...

В проекте используются XSLT 2.0 и Saxon, поэтому, если существует решение, специфичное для Saxon, это нормально.

XSLT 2.0 предоставляет возможность определять пользовательскую последовательность форматов?Предоставляет ли Saxon возможность зарегистрировать пользовательскую последовательность для использования с <xsl:number>?

Ответы [ 4 ]

3 голосов
/ 06 июля 2011

XSLT 2.0 предоставляет атрибут format для xsl:number, с помощью которого вы можете использовать токен формата aa, например.Вычисленное число зависит от выражения, оцененного внутри атрибута value, и будет отформатировано в соответствии с format.

. Учитывая это, вы можете подумать о том, что сначала оценит правильную последовательность чисел, исключаяте, которые будут соответствовать определенной букве.

Например, будет напечатана следующая инструкция:

  <xsl:number value="$sequence" format="aa"/>

(уведомление i исключено):

 a.b.c.d.e.f.g.h.j.k.l.m

если $sequence оценивается как (уведомление 9 пропущено):

1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13

Обратите внимание, что если у вас есть 12 элементов, ваше выражение сможет пропустить нежелательное число (9 для i) и увеличитьследование одному.Последний элемент, имеющий позицию 12, должен иметь соответствующее число 13.

Так что вам нужно просто алгоритм , который вычисляет требуемую последовательность ;что определенно зависит от вашего входного документа.

Ссылки: XSLT 2.0 Rec.

1 голос
/ 14 июля 2011

Я предложил следующее, более обобщенное решение, после того, как опубликовал свое первоначальное решение проблемы.Решение чисто XSLT и в основе все еще используется <xsl:number>, поэтому должно быть применимо к любому типу формата.

<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:ewh="http://www.earlhood.com/XSL/Transform"
                exclude-result-prefixes="#all">

<!-- Description: XSLT to generate a alpha formatted sequence
     label (via <xsl:number>), but disallowing specific characters
     from being used.
  -->

<!-- Algorithm: Given the index value of the item to generate
     a label for via <xsl:number>, we adjust the value so the resulting
     label avoids the use of the forbidden characters.

     This is achieved by converting the index value into a baseX
     number, with X the number of allowed characters.

     The baseX number will be converted into a reverse sequence
     of numbers for each ^E place.  For example, the number 12167
     converted to base23 will generate the following reverse sequence:

       Place:    (23^0, 23^1, 23^2, 23^3)
       Sequence: (   0,    0,    0,    1)   // 1000 in base23

     Having it in right-to-left order makes processing easier.

     Each item in the sequence will be a number from 0 to baseX-1.

     With the sequence, we can then just call <xsl:number> on
     each item and reverse concatenate the result.

     NOTE: Since <xsl:number> does not like 0 as a given value,
     the sequence must be processed so each item is within the
     range of 1-to-baseX.  For example, the above base23 example
     will be translated to the following:

       (23, 22, 22)
  -->

<xsl:output method="xml" indent="yes"/>

<!-- Number of allowed characters: This should be total number of chars of
     format-type desired minus the chars that should be skipped. -->
<xsl:variable name="lbase" select="23"/>
<!-- Sequence of character positions not allowed, with 1=>a to 26=>z -->
<xsl:variable name="lexcs" select="(9,12,15)"/> <!-- i,l,o -->

<!-- Helper Function:
     Convert integer to sequence of number of given base.
     The sequence of numbers is in reverse order: ^0,^1,^2,...^N.
  -->
<xsl:function name="ewh:get_base_digits" as="item()*">
  <xsl:param name="number" as="xs:integer"/>
  <xsl:param name="to"     as="xs:integer"/>
  <xsl:variable name="Q" select="$number idiv $to"/>
  <xsl:variable name="R" select="$number mod $to"/>
  <xsl:sequence select="$R"/>
  <xsl:if test="$Q gt 0">
    <xsl:sequence select="ewh:get_base_digits($Q,$to)"/>
  </xsl:if>
</xsl:function>

<!-- Helper Function:
     Compute carry-overs in reverse-base digit sequence.  XSLT starts
     numbering at 1, so we cannot have any 0s.
  -->
<xsl:function name="ewh:compute_carry_overs" as="item()*">
  <xsl:param name="digits" as="item()*"/>
  <xsl:variable name="d" select="subsequence($digits, 1, 1)"/>
  <xsl:choose>
    <xsl:when test="($d le 0) and (count($digits) = 1)">
      <!-- 0 at end of list, nothing to do -->
    </xsl:when>
    <xsl:when test="$d le 0">
      <!-- If digit <=0, need to perform carry-over operation -->
      <xsl:variable name="next" select="subsequence($digits, 2, 1)"/>
      <xsl:choose>
        <xsl:when test="count($digits) le 2">
          <xsl:sequence select="$lbase + $d"/>
          <xsl:sequence select="ewh:compute_carry_overs($next - 1)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$lbase + $d"/>
          <xsl:sequence select="ewh:compute_carry_overs(($next - 1,
              subsequence($digits, 3)))"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:when test="count($digits) le 1">
      <xsl:sequence select="$d"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$d"/>
      <xsl:sequence select="ewh:compute_carry_overs(subsequence($digits, 2))"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<!-- Helper Function:
     Given a number in the base range, determine number for
     purposes of <xsl:number>.  We loop thru the exclusion
     list and add 1 for each exclusion letter that has
     been passed.  The $digit parameter should be a number
     in the range [1..$lbase].
  -->
<xsl:function name="ewh:compute_digit_offset" as="xs:integer">
  <xsl:param name="digit"      as="xs:integer"/>
  <xsl:param name="excludes"   as="item()*"/>
  <xsl:variable name="l" select="subsequence($excludes, 1, 1)"/>
  <xsl:variable name="result">
    <xsl:choose>
      <xsl:when test="$digit lt $l">
        <xsl:value-of select="0"/>
      </xsl:when>
      <xsl:when test="count($excludes) = 1">
        <xsl:value-of select="1"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:variable name="rest">
          <xsl:value-of select="ewh:compute_digit_offset($digit+1,
              subsequence($excludes,2))"/>
        </xsl:variable>
        <xsl:value-of select="1 + $rest"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:value-of select="$result"/>
</xsl:function>

<!-- Retrieve alpha sequence label.
     This is the main function to call.
  -->
<xsl:function name="ewh:get-alpha-label" as="xs:string">
  <xsl:param name="number" as="xs:integer"/>
  <xsl:variable name="basedigits"
                select="ewh:get_base_digits($number,$lbase)"/>
  <xsl:variable name="digits"
                select="ewh:compute_carry_overs($basedigits)"/>
  <xsl:variable name="result" as="item()*">
    <xsl:for-each select="$digits">
      <xsl:variable name="digit" select="."/>
      <!-- Should not have any 0 values.  If some reason we do,
           we ignore assuming they are trailing items. -->
      <xsl:if test="$digit != 0">
        <xsl:variable name="value">
          <xsl:value-of select="$digit +
              ewh:compute_digit_offset($digit,$lexcs)"/>
        </xsl:variable>
        <xsl:variable name="number">
          <xsl:number value="$value" format="a"/>
        </xsl:variable>
        <xsl:sequence select="$number"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>
  <xsl:value-of select="string-join(reverse($result),'')"/>
</xsl:function>

<!-- For testing -->
<xsl:template match="/">
  <result>
    <xsl:for-each select="(1 to 1000,12166,12167,12168,279840,279841,279842)">
      <value n="{.}"><xsl:value-of select="ewh:get-alpha-label(.)"/></value>
    </xsl:for-each>
  </result>
</xsl:template>

</xsl:stylesheet>
1 голос
/ 06 июля 2011

Вы можете настроить вывод xsl: number в Saxon, написав реализацию интерфейса net.sf.saxon.lib.Numberer: возможно, вы захотите сделать его подклассом net.sf.saxon.expr.number..Numberer_en.Вам нужно будет изучить исходный код и выяснить, что нужно переопределить.

В Saxon PE / EE вы можете зарегистрировать Numberer для использования с данным языком в файле конфигурации Saxon.Для Saxon HE требуется немного больше работы: вам нужно реализовать интерфейс LocalizerFactory и зарегистрировать свой LocalizerFactory в конфигурации.

1 голос
/ 06 июля 2011

РЕДАКТИРОВАТЬ: существует альтернативное, более общее решение, которое публикуется как отдельный ответ. Я оставляю этот ответ, поскольку для некоторых он все еще может иметь значение.

Мне нравится мышление @ empo (я его модифицировал), но я думаю, что может быть трудно найти рабочее решение. Требуется умный алгоритм / уравнение, чтобы придумать правильный порядковый номер на основе необработанной последовательности, чтобы избежать получения метки, которая не содержит запрещенных символов. В настоящее время такой алгоритм ускользает от меня.

Один метод, который я придумал, - это создать собственную функцию, а не использовать <xsl:number>. По сути, мы имеем дело с базовым набором 23, буквы от a до z, но исключая символы i, l и o. Функция, которую я придумал, работает только до zz, но этого должно быть достаточно для того, что нужно (обеспечивает маркировку до 552 элементов).

<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:ewh="http://www.earlhood.com/XSL/Transform"
                exclude-result-prefixes="#all">

<xsl:output method="xml" indent="yes"/>

<xsl:variable name="letters" select="'abcdefghjkmnpqrstuvwxyz'"/>
<xsl:variable name="lbase" select="23"/>

<xsl:function name="ewh:get-alpha-label" as="xs:string">
  <xsl:param name="number" as="xs:integer"/>
  <xsl:variable name="quotient" select="$number idiv $lbase"/>
  <xsl:variable name="remainder" select="$number mod $lbase"/>
  <xsl:variable name="p1">
    <xsl:choose>
      <xsl:when test="($quotient gt 0) and ($remainder = 0)">
        <xsl:value-of select="substring($letters,($quotient - 1),1)"/>
      </xsl:when>
      <xsl:when test="($quotient gt 0) and ($remainder gt 0)">
        <xsl:value-of select="substring($letters,$quotient,1)"/>
      </xsl:when>
      <xsl:otherwise/>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="p0">
    <xsl:choose>
      <xsl:when test="$remainder = 0">
        <xsl:value-of select="substring($letters,$lbase,1)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="substring($letters,$remainder,1)"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:value-of select="concat($p1,$p0)"/>
</xsl:function>

<xsl:template match="/">
  <result>
    <value n="9"><xsl:value-of select="ewh:get-alpha-label(9)"/></value>
    <value n="12"><xsl:value-of select="ewh:get-alpha-label(12)"/></value>
    <value n="15"><xsl:value-of select="ewh:get-alpha-label(15)"/></value>
    <value n="23"><xsl:value-of select="ewh:get-alpha-label(23)"/></value>
    <value n="26"><xsl:value-of select="ewh:get-alpha-label(26)"/></value>
    <value n="33"><xsl:value-of select="ewh:get-alpha-label(33)"/></value>
    <value n="46"><xsl:value-of select="ewh:get-alpha-label(46)"/></value>
    <value n="69"><xsl:value-of select="ewh:get-alpha-label(69)"/></value>
    <value n="70"><xsl:value-of select="ewh:get-alpha-label(70)"/></value>
    <value n="200"><xsl:value-of select="ewh:get-alpha-label(200)"/></value>
    <value n="552"><xsl:value-of select="ewh:get-alpha-label(552)"/></value>
  </result>
</xsl:template>

</xsl:stylesheet>

Когда я выполняю вышеизложенное, я получаю следующий вывод:

<result>
   <value n="9">j</value>
   <value n="12">n</value>
   <value n="15">r</value>
   <value n="23">z</value>
   <value n="26">ac</value>
   <value n="33">ak</value>
   <value n="46">az</value>
   <value n="69">bz</value>
   <value n="70">ca</value>
   <value n="200">hs</value>
   <value n="552">zz</value>
</result>

Было бы неплохо, если бы XSLT предоставил возможность определять пользовательскую последовательность символов для использования с <xsl:number>. Похоже, что такая возможность будет обобщать <xsl:number> без использования пользовательских расширений, которые я не знаю, если какой-либо механизм XSLT обеспечивает <xsl:number>.

...