0

My objective is analyze the text() node of a given XML document and identify the upper case words and if that word length > 3 add '*' character in between 3,4 characters (6,7 and 9,10.....)

example,

input XML :

<chap>
    <para>The BEGINNING of this COLUMN shows the INPUT and output</para>
</chap>

desired output:

<chap>
    <para>The BEG*INN*ING of this COL*UMN shows the INP*UT and output</para>
</chap>

I've written following xsl to do this task,

<xsl:template match="para">
        <xsl:analyze-string select="." regex="[(A-Z)]">
            <xsl:matching-substring>
                <xsl:variable name="reg" select="string(regex-group(0))"/>
                    <xsl:call-template name="add-star">
                        <xsl:with-param name="str" select="$reg"/>
                    </xsl:call-template>
            </xsl:matching-substring>

            <xsl:non-matching-substring>
                <xsl:value-of select="."/>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>

<xsl:template name="add-star">
        <xsl:param name="str" as="xs:string"/>
        <xsl:if test="string-length($str)>3">
            <xsl:call-template name="add-star">
                <xsl:with-param name="str" select="substring($str,4,200)"/>
            </xsl:call-template>
        </xsl:if>
        <xsl:sequence select="string-join(substring($str,1,3),'*')"/>
    </xsl:template>

but it does not work as expected.since there is no variables like oop languages I'm struggling to to this task in XSLT, could anyone suggest me how can I modify my code to do this task?

2 Answers 2

1

How about:

<xsl:template match="para">
    <xsl:copy>
        <xsl:analyze-string select="." regex="([A-Z]+)(\s|$)">
            <xsl:matching-substring>
                <xsl:variable name="sub" as="xs:string*">
                    <xsl:for-each select="0 to (string-length(regex-group(1)) - 1) idiv 3">
                        <xsl:sequence select="substring(regex-group(1), (. ) * 3 + 1, 3)" />
                    </xsl:for-each>
                </xsl:variable>
                <xsl:value-of select="string-join($sub, '*')"/>
                <xsl:value-of select="regex-group(2)"/>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:value-of select="."/>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:copy>
</xsl:template>

Test: http://xsltransform.net/jyRYYiN

Sign up to request clarification or add additional context in comments.

Comments

1

And here is another exslt-enabled xslt-1.0 solution:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fn="http://www.w3.org/2005/xpath-functions"
                xmlns:str="http://exslt.org/strings"
                xmlns:func="http://exslt.org/functions"
                xmlns:my="http://my.org/xsl"
                extension-element-prefixes="fn str func">

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

    <func:function name="my:to-upper">
        <xsl:param name="str" select="''"/>

        <func:result select="translate($str, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
    </func:function>

    <func:function name="my:is-upper">
        <xsl:param name="str" select="''"/>

        <func:result select="my:to-upper($str) = $str"/>
    </func:function>

    <xsl:template match="/">
        <xsl:apply-templates select="/node()"/>
    </xsl:template>

    <xsl:template match="*">
        <xsl:element name="{name()}">
            <xsl:copy-of select="./@*"/>
            <xsl:apply-templates select="./node()"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="text()">
        <xsl:if test="string-length(normalize-space(.)) > 0">
            <xsl:for-each select="str:tokenize(., ' ')">
                <xsl:choose>
                    <xsl:when test="my:is-upper(.)">
                        <xsl:choose>
                            <xsl:when test="string-length(.) > 3">
                                <xsl:for-each select="str:tokenize(., '')">
                                    <xsl:value-of select="."/>

                                    <xsl:if test="0 = position() mod 3 and not(position() = last())">
                                        <xsl:text>*</xsl:text>
                                    </xsl:if>
                                </xsl:for-each>
                            </xsl:when>

                            <xsl:otherwise>
                                <xsl:value-of select="."/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:when>

                    <xsl:otherwise>
                        <xsl:value-of select="."/>
                    </xsl:otherwise>
                </xsl:choose>

                <xsl:if test="not(position() = last())">
                    <xsl:text> </xsl:text>
                </xsl:if>
            </xsl:for-each>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Online Demo

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.