Алексей Валиков - Технология XSLT
Для форматирования номеров в последовательности 1.1.a, 1.1.б, 1.1.в, …, 1.2.а и так далее можно использовать объявление вида:
<xsl:number
format="1.&#х430;"
level="multiple"
count="chapter|section"
from="doc"/>
Представим теперь себе следующую ситуацию: нам нужно начать нумерацию с латинской буквы i для того, чтобы получить последовательность номеров вида i, j, k, l, m и так далее. Первое, что приходит в голову — это запись вида
<xsl:number format="i" ... />
Однако вместо требуемой последовательности мы получим последовательность строчных римских цифр: i, ii, iii и так далее. Иными словами, некоторые форматирующие токены определяют нумерующую последовательность двусмысленно: одним вариантом является алфавитная последовательность, начинающаяся этим токеном, другим — некая традиционная для данного языка (например, последовательность римских цифр для английского).Для того чтобы различать эти последовательности в двусмысленных ситуациях, в xsl:number существует атрибут letter-value. Если его значением является "alphabetic", нумерующая последовательность является алфавитной, значение "traditional" указывает на то, что следует использовать традиционный для данного языка способ. Если атрибут letter-value опущен, процессор может сам выбирать между алфавитным и традиционным способами нумерации.
При использовании цифровых форматов нумерации (иными словами, токенов вида 1, 01, 001 и так далее) цифры в номере можно разделить на группы, получив, например, такие номера как "2.00.00" из 20000 или "0-0-0-2" из 2. Для этой цели в xsl:number используется пара атрибутов grouping-separator и grouping-size.
Атрибут grouping-separator задает символ, который следует использовать для разбивки номера на группы цифр, в то время как grouping-size указывает размер группы. Эти атрибуты всегда должны быть вместе — если хотя бы один из них опущен, второй просто игнорируется.
ПримерЭлемент xsl:number вида
<xsl:number
format="[00000001]"
grouping-separator="."
grouping-size="2"/>
будет генерировать номера в следующей последовательности:
1 → '[00.00.00.01]'
2 → '[00.00.00.02]'
...
999 → '[00.00.09.99]'
1000 → '[00.00.10.00]'
Пожалуй, следует упомянуть, что в значениях атрибутов format, lang, letter-value, grouping-size и grouping-separator могут быть указаны шаблоны значений, иными словами могут использоваться выражения в фигурных скобках. Это может быть полезно, например, для того, чтобы сгенерировать форматирующие токены во время выполнения преобразования.
ПримерВ следующем шаблоне формат номера секции зависит от значения атрибута format ее родительского узла:
<xsl:template match="section">
<xsl:number
format="{../@format}-1 "
level="multiple"
count="chapter|section"/>
<xsl:value-of select="@title"/>
</xsl:template>
При обработке входящего документа
<doc>
<chapter format="I" title="First Chapter">
<section title="First Section"/>
<section title="Second Section"/>
<section title="Third Section"/>
</chapter>
</doc>
нумерация секций будет выглядеть как
I-1 First Section
I-2 Second Section
I-3 Third Section
Если же атрибут format элемента chapter будет иметь значение 1, секции будут пронумерованы в виде
1-1 First Section
1-2 Second Section
1-3 Third Section
Форматирование чисел
Мы уже познакомились с функцией языка XPath string, которая конвертирует свой аргумент в строку. Эта функция может преобразовать в строку и численное значение, но возможности ее при этом сильно ограничены.
К счастью, XSLT предоставляет мощные возможности для форматирования строкового представления чисел при помощи функции format-number и элемента xsl:decimal-format.
Функция format-number
Запись функции имеет следующий вид:
string format-number(number, string, string?)
Функция format-number принимает на вход три параметра. Первым параметром является число, которое необходимо преобразовать в строку, применив при этом форматирование. Вторым параметром является образец, в соответствии с которым будет форматироваться число. Третий параметр указывает название десятичного формата, который следует применять.
Образец форматирования в XSLT определяется точно так же, как в классе DecimalFormat языка Java. Для того чтобы читателю, не знакомому с Java, не пришлось изучать документацию этого языка, мы приведем полный синтаксис образцов форматирования. Продукции образца форматирования мы будем помечать номерами с префиксом NF, чтобы не путать их с другими продукциями.
Прежде всего, образец форматирования может состоять из двух частей: первая часть определяет форматирование положительного числа, вторая часть — отрицательного. Запишем это в виде EBNF-продукции:
[NF 1] NFPattern ::= NFSubpattern (NFSubpatternDelim NFSubpattern)?
Двум частям образца форматирования соответствуют нетерминалы NFSubpattern, которые разделены нетерминалом NFSubpatternDelim.
В случае если вторая часть образца форматирования опушена, отрицательные числа форматируются точно так же, как и положительные, но им предшествует префикс отрицательного числа (по умолчанию — знак "минус", "-").
Примерыformat-number(1234.567,'#.00;negative #.00') → '1234.57'
format-number(-1234.567,'#.00/negative #.00') → 'negative 1234.57'
format-number(-1234.567,'#.00') → '-1234.57'
Каждая из частей образца форматирования состоит из префикса (NFPrefix), целой части (NFInteger), необязательной дробной части (NFFractional) и суффикса (NFSuffix).
[NF 2] NFSubpattern ::= NFPrefix NFinteger NFFractional? NFSuffix
Префикс или суффикс образца форматирования могут содержать символ процента. Если суффикс содержит символ процента, число должно быть умножено на 100 и выведено со знаком процента. Наличие символа процента в префиксе на форматирование не влияет.
Примерformat-number(0.45,'0.00%') → '45.00%'
format-number(0.45,'0.##%') → '45.00%'
format-number(0.45678,'%0.00') → '%0.46'
format-number(0.45678,'0.####%') → '45.678%'
Префикс задает строку, которая будет предшествовать числу, это может быть последовательность любых неформатирующих символов (NFChar) плюс символ процента (NFPercent). Аналогично, суффикс будет следовать за числом, и он тоже не может содержать форматирующих символов (за исключением символа процента).
[NF 3] NFPrefix ::= (NFChar NFPercent?)*
[NF 4] NFSuffix ::= (NFChar NFPercent?)*
ПримерЕсли мы хотим заключить наше число, к примеру, в квадратные скобки, мы должны будем включить в его образец форматирования префикс "[" и суффикс "]":
format-number(123456, '[#]') → '[123456]'
Нетерминал NFinteger определяет, как будет выглядеть целая часть числа. Он начинается несколькими символами NFOptDigit (по умолчанию "#"), показывающими позиции, в которых цифры необязательны, и состоит из символов NFReqDigit (по умолчанию "0"), показывающих позиции обязательных цифр, а также символа NFGroupDelim (по умолчанию ","), показывающего позицию символа-разделителя групп цифр.
[NF 5] NFInteger ::= NFOptDigit*
(NFReqDigit* NFGroupDelim
| NFGroupDelim NFOptDigit*)?
NFReqDigit+
Примерыformat-number(1234.56,'#0000') → '1235'
format-number(1234.56,'00000') → '01235'
format-number(1234.56,'00,000') → '01,235'
format-number(1234.56,'000,00') → '0,12,35'
ЗамечаниеНекоторые процессоры позволяют указывать несколько символов-разделителей. Однако даже в этом случае они учитывают только последний из этих символов.
Примерformat-number(123456789.0123,'0000,000,00') → '1,23,45,67,89'
Дробная часть числа, представленная нетерминалом NFFraction, начинается символом-разделителем целой и дробной части NFFractionDelim (по умолчанию "."), продолжается последовательностью символов обязательных позиций цифр NFReqDigit и заканчивается последовательностью символов необязательных позиций NFOptDigit:
[NF 6] NFFraction ::= NFFractionDelim NFReqDigit* NFOptDigit*
Примерыformat-number(1234.567,'#.00') → '1234.57'