Я преобразовываю мои XSLT-стили в документацию, и я хочу получить богатый опыт в узлах комментариев для каждого блока кода, поэтому я хочу преобразовать следующий комментарий и вывод как xhtml:
Строка:
# This is a title with __bold__ text and *italic* # This is just a normal line - list point with some __bold__ - list point with a "link"[http://www.stackoverflow.com]
Желаемый вывод:
<h1> This is a title with <strong>bold</strong> and <span>italic</span> </h1>
<p>This is a normal line</p>
<ul>
<li>list point with some <strong>bold</strong></li>
<li>list point with a <a href="http://www.stackoverflow.com">link</a></li>
</ul>
Я пытался с рекурсивной функцией, которая использует xsl:analyze-string рекурсивно из набора правил, но не могу найти решение, которое действительно хорошо работает.
Кто-нибудь занимался этим в последнее время, или есть какой-нибудь фреймворк, в котором есть функции для этого?
спасибо заранее! :)
Edit: Added one dirty example:
<!-- Output comments -->
<xsl:template match="comment()" mode="COMMENT">
<xsl:copy-of select="ips:groupReplace(normalize-space(.),
'
(.*)(\n|\r)(.*),
(.*)\*(.*)\*(.*),
(.*)\*\*(.*)\*\*(.*),
(.*)__(.*)__(.*),
(.*)#(.*)#(.*),
(.*)-(.*)
',
'
br,
span.italic,
span.bold,
strong,
h1,
li
')" />
</xsl:template>
<!-- Initializing the iterateRegex function -->
<xsl:function name="ips:groupReplace">
<xsl:param name="string" as="xs:string" />
<xsl:param name="search" />
<xsl:param name="replace" />
<xsl:variable name="regex" select="tokenize($search, ',')" />
<xsl:variable name="replacements" select="tokenize($replace, ',')" />
<xsl:copy-of select="ips:iterateRegex(count($replacements), $string, $regex, $replacements)" />
</xsl:function>
<!-- Iterate each regex -->
<xsl:function name="ips:iterateRegex">
<xsl:param name="counter" />
<xsl:param name="string" />
<xsl:param name="list_regex" />
<xsl:param name="list_replace" />
<xsl:variable name="newStr">
<xsl:analyze-string select="$string" regex="{normalize-space($list_regex[$counter])}" flags="xm">
<xsl:matching-substring>
<xsl:variable name="cc" select="contains($list_replace[$counter], '.')" />
<xsl:variable name="tag" select="normalize-space(if ($cc) then (substring-before($list_replace[$counter], '.')) else ($list_replace[$counter]))" />
<xsl:copy-of select="regex-group(1)" />
<xsl:choose>
<xsl:when test="normalize-space(regex-group(2)) = ''">
<xsl:element name="{$tag}" />
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$tag}" >
<xsl:if test="$cc">
<xsl:attribute name="class" select="substring-after($list_replace[$counter],'.')" />
</xsl:if>
<xsl:copy-of select="regex-group(2)" />
</xsl:element>
</xsl:otherwise>
</xsl:choose>
<xsl:copy-of select="regex-group(3)" />
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:copy-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:variable name="count" select="number($counter) - 1" />
<xsl:choose>
<xsl:when test="$count > 0">
<xsl:copy-of select="ips:iterateRegex($count, $newStr, $list_regex, $list_replace)" />
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$newStr" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
Это преобразование (111 строк):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my"
exclude-result-prefixes="xml xsl xs my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vLines" select="tokenize(., '\n')"/>
<xsl:sequence select="my:parse-lines($vLines)"/>
</xsl:template>
<xsl:function name="my:parse-lines" as="element()*">
<xsl:param name="pLines" as="xs:string*"/>
<xsl:sequence select=
"my:parse-line($pLines, 1, count($pLines))"/>
</xsl:function>
<xsl:function name="my:parse-line" as="element()*">
<xsl:param name="pLines" as="xs:string*"/>
<xsl:param name="pLineNum" as="xs:integer"/>
<xsl:param name="pTotalLines" as="xs:integer"/>
<xsl:if test="not($pLineNum gt $pTotalLines)">
<xsl:variable name="vLine" select="$pLines[$pLineNum]"/>
<xsl:variable name="vLineLength"
select="string-length($vLine)"/>
<xsl:choose>
<xsl:when test=
"starts-with($vLine, '#')
and
ends-with($vLine, '#')
">
<xsl:variable name="vInnerString"
select="substring($vLine, 2, $vLineLength -2)"/>
<h1>
<xsl:sequence select="my:parse-string($vInnerString)"/>
</h1>
<xsl:sequence select=
"my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
</xsl:when>
<xsl:when test=
"starts-with($vLine, '- ')
and
not(starts-with($pLines[$pLineNum -1], '- '))
">
<ul>
<li>
<xsl:sequence select="my:parse-string(substring($vLine, 2))"/>
</li>
<xsl:sequence select=
"my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
</ul>
</xsl:when>
<xsl:when test="starts-with($vLine, '- ')">
<li>
<xsl:sequence select="my:parse-string(substring($vLine, 2))"/>
</li>
<xsl:sequence select=
"my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
</xsl:when>
<xsl:otherwise>
<p>
<xsl:sequence select="my:parse-string($vLine)"/>
</p>
<xsl:sequence select=
"my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:function>
<xsl:function name="my:parse-string" as="node()*">
<xsl:param name="pS" as="xs:string"/>
<xsl:analyze-string select="$pS" flags="x" regex=
'(__(.*?)__)
|
(\*(.*?)\*)
|
("(.*?)"\[(.*?)\])
'>
<xsl:matching-substring>
<xsl:choose>
<xsl:when test="regex-group(1)">
<strong>
<xsl:sequence select="my:parse-string(regex-group(2))"/>
</strong>
</xsl:when>
<xsl:when test="regex-group(3)">
<span>
<xsl:sequence select="my:parse-string(regex-group(4))"/>
</span>
</xsl:when>
<xsl:when test="regex-group(5)">
<a href="{regex-group(7)}">
<xsl:sequence select="regex-group(6)"/>
</a>
</xsl:when>
</xsl:choose>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:function>
</xsl:stylesheet>
при применении к этому XML-документу (предоставленный текст, усложненный вложенными конструкциями и заключенный в элемент):
<t># This is a title with __bold__ text and *italic* #
This is just a normal line
- list point with some __bold__
- list point with a __*"link"[http://www.stackoverflow.com]*__</t>
выдает желаемый правильный вывод:
<h1> This is a title with <strong>bold</strong> text and <span>italic</span>
</h1>
<p>This is just a normal line</p>
<p/>
<ul>
<li> list point with some <strong>bold</strong>
</li>
<li> list point with a <strong>
<span>
<a href="http://www.stackoverflow.com">link</a>
</span>
</strong>
</li>
</ul>
Обратите внимание: Механизм RegEx XPath 2.0 и XSLT 2.0 подходит для решения этой проблемы.
Думаю, вам понадобится парсер. Итак, эта таблица стилей реализует подробный:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="text" name="block">
<xsl:param name="pString" select="."/>
<xsl:if test="$pString != ''">
<xsl:choose>
<xsl:when test="starts-with($pString,'#')">
<xsl:call-template name="header">
<xsl:with-param name="pString"
select="substring($pString,2)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="starts-with($pString,'
')">
<xsl:call-template name="list">
<xsl:with-param name="pString"
select="substring($pString,2)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="paragraph">
<xsl:with-param name="pString"
select="$pString"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template name="header">
<xsl:param name="pString"/>
<xsl:variable name="vInside"
select="substring-before($pString,'#
')"/>
<xsl:choose>
<xsl:when test="$vInside != ''">
<h1>
<xsl:call-template name="inline">
<xsl:with-param name="pString" select="$vInside"/>
</xsl:call-template>
</h1>
<xsl:call-template name="block">
<xsl:with-param name="pString"
select="substring-after($pString,'#
')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="paragraph">
<xsl:with-param name="pString"
select="concat('#',$pString)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="list">
<xsl:param name="pString"/>
<xsl:variable name="vCheckList" select="starts-with($pString,'- ')"/>
<xsl:choose>
<xsl:when test="$vCheckList">
<ul>
<xsl:call-template name="listItem">
<xsl:with-param name="pString" select="$pString"/>
</xsl:call-template>
</ul>
<xsl:call-template name="block">
<xsl:with-param name="pString">
<xsl:call-template name="afterlist">
<xsl:with-param name="pString" select="$pString"/>
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="block">
<xsl:with-param name="pString" select="$pString"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="paragraph">
<xsl:param name="pString"/>
<xsl:choose>
<xsl:when test="contains($pString,'
')">
<p>
<xsl:value-of select="substring-before($pString,'
')"/>
</p>
</xsl:when>
<xsl:otherwise>
<p>
<xsl:value-of select="$pString"/>
</p>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="block">
<xsl:with-param name="pString"
select="substring-after($pString,'
')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="afterlist">
<xsl:param name="pString"/>
<xsl:choose>
<xsl:when test="starts-with($pString,'- ')">
<xsl:call-template name="afterlist">
<xsl:with-param name="pString"
select="substring-after($pString,'
')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="listItem">
<xsl:param name="pString"/>
<xsl:if test="starts-with($pString,'- ')">
<li>
<xsl:call-template name="inline">
<xsl:with-param name="pString"
select="substring-before(substring($pString,3),'
')"/>
</xsl:call-template>
</li>
<xsl:call-template name="listItem">
<xsl:with-param name="pString"
select="substring-after($pString,'
')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="inline">
<xsl:param name="pString" select="."/>
<xsl:if test="$pString != ''">
<xsl:choose>
<xsl:when test="starts-with($pString,'__')">
<xsl:call-template name="strong">
<xsl:with-param name="pString"
select="substring($pString,3)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="starts-with($pString,'*')">
<xsl:call-template name="span">
<xsl:with-param name="pString"
select="substring($pString,2)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="starts-with($pString,'"')">
<xsl:call-template name="link">
<xsl:with-param name="pString"
select="substring($pString,2)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($pString,1,1)"/>
<xsl:call-template name="inline">
<xsl:with-param name="pString"
select="substring($pString,2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template name="strong">
<xsl:param name="pString"/>
<xsl:variable name="vInside" select="substring-before($pString,'__')"/>
<xsl:choose>
<xsl:when test="$vInside != ''">
<strong>
<xsl:value-of select="$vInside"/>
</strong>
<xsl:call-template name="inline">
<xsl:with-param name="pString"
select="substring-after($pString,'__')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'__'"/>
<xsl:call-template name="inline">
<xsl:with-param name="pString" select="$pString"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="span">
<xsl:param name="pString"/>
<xsl:variable name="vInside" select="substring-before($pString,'*')"/>
<xsl:choose>
<xsl:when test="$vInside != ''">
<span>
<xsl:value-of select="$vInside"/>
</span>
<xsl:call-template name="inline">
<xsl:with-param name="pString"
select="substring-after($pString,'*')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'*'"/>
<xsl:call-template name="inline">
<xsl:with-param name="pString" select="$pString"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="link">
<xsl:param name="pString"/>
<xsl:variable name="vInside"
select="substring-before($pString,'"')"/>
<xsl:choose>
<xsl:when test="$vInside != ''">
<xsl:call-template name="href">
<xsl:with-param name="pString"
select="substring-after($pString,'"')"/>
<xsl:with-param name="pInside" select="$vInside"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'"'"/>
<xsl:call-template name="inline">
<xsl:with-param name="pString" select="$pString"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="href">
<xsl:param name="pString"/>
<xsl:param name="pInside"/>
<xsl:variable name="vHref"
select="substring-before(substring($pString,2),']')"/>
<xsl:choose>
<xsl:when test="starts-with($pString,'[') and $vHref != ''">
<a href="{$vHref}">
<xsl:value-of select="$pInside"/>
</a>
<xsl:call-template name="inline">
<xsl:with-param name="pString"
select="substring-after($pString,']')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('"',$pInside,'"')"/>
<xsl:call-template name="inline">
<xsl:with-param name="pString" select="$pString"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
С этим вводом:
<text>
# This is a title with __bold__ text and *italic* #
This is just a normal line
- list point with some __bold__
- list point with a "link"[http://www.stackoverflow.com]
</text>
Вывод:
<h1> This is a title with
<strong>bold</strong> text and
<span>italic</span>
</h1>
<p>This is just a normal line</p>
<ul>
<li>list point with some
<strong>bold</strong>
</li>
<li>list point with a
<a href="http://www.stackoverflow.com">link</a>
</li>
</ul>
Примечание : Посмотрите, сколько шаблонов похожи (они следуют шаблону), чтобы их можно было параметризовать. Я не делал этого в данном случае, потому что, похоже, есть еще вопросы, требующие какого-то парсера, поэтому к концу недели я повторно отправлю ответ, реализующий функциональный синтаксический анализатор и шаблон комбинаторов синтаксического анализатора, которые позволяют очень легко писать синтаксические анализаторы ( просто пишу свои грамматические правила).
Правка : решение XSLT 2.0. Эта таблица стилей:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="text">
<xsl:param name="pString" select="."/>
<xsl:analyze-string select="$pString"
regex="(#(.*)#
)|((- (.*)
)+)">
<xsl:matching-substring>
<xsl:choose>
<xsl:when test="regex-group(1)">
<h1>
<xsl:call-template name="inline">
<xsl:with-param name="pString"
select="regex-group(2)"/>
</xsl:call-template>
</h1>
</xsl:when>
<xsl:when test="regex-group(3)">
<ul>
<xsl:call-template name="list">
<xsl:with-param name="pString"
select="regex-group(3)"/>
</xsl:call-template>
</ul>
</xsl:when>
</xsl:choose>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:if test=".!='
'">
<p>
<xsl:call-template name="inline">
<xsl:with-param name="pString"
select="normalize-space(.)"/>
</xsl:call-template>
</p>
</xsl:if>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template name="list">
<xsl:param name="pString"/>
<xsl:analyze-string select="$pString" regex="- (.*)
">
<xsl:matching-substring>
<li>
<xsl:call-template name="inline">
<xsl:with-param name="pString"
select="regex-group(1)"/>
</xsl:call-template>
</li>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template name="inline">
<xsl:param name="pString" select="."/>
<xsl:analyze-string select="$pString"
regex="(__(.*)__)|(\*(.*)\*)|("(.*)"\[(.*)\])">
<xsl:matching-substring>
<xsl:choose>
<xsl:when test="regex-group(1)">
<strong>
<xsl:value-of select="regex-group(2)"/>
</strong>
</xsl:when>
<xsl:when test="regex-group(3)">
<span>
<xsl:value-of select="regex-group(4)"/>
</span>
</xsl:when>
<xsl:when test="regex-group(5)">
<a href="{regex-group(7)}">
<xsl:value-of select="regex-group(6)"/>
</a>
</xsl:when>
</xsl:choose>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
Вывод:
<h1> This is a title with
<strong>bold</strong> text and
<span>italic</span>
</h1>
<p>This is just a normal line</p>
<ul>
<li>list point with some
<strong>bold</strong>
</li>
<li>list point with a
<a href="http://www.stackoverflow.com">link</a>
</li>
</ul>