Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old July 9th, 2003, 09:46 AM
Registered User
 
Join Date: Jun 2003
Posts: 4
Thanks: 0
Thanked 0 Times in 0 Posts
Default Hyperlink and Pargraph formatting

I need the text from an element to have hyperlinks and paragraphs formatted correctly.

I have 2 templates which do the job fine separately, but I am unable to get them to work together. My .xslt is below.

========= XSL ============
Code:
<xsl:stylesheet version = '1.0'

xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>

<xsl:output method="html" version="4.0" encoding="iso-8859-1" indent="yes"/>

<xsl:template match="/">

<xsl:apply-templates />

</xsl:template>



<xsl:template match="Sample">

<xsl:call-template name="text2hyper" />

<xsl:call-template name="NewLine2Break" />

</xsl:template>

<xsl:template name="text2hyper">

<xsl:param name="text" select="." />

<xsl:choose>

<xsl:when test="contains($text, 'http:')">

<xsl:variable name="textstart" select="substring-before($text,'http:')"
/>

<xsl:variable name="rest" select="concat(substring-after($text,'http:'),'
' )" />


<xsl:variable name="link" select="concat('http:',substring-before($rest,'
'))" />

<xsl:text>#160;</xsl:text>

<xsl:variable name="textend" select="substring-after($rest,' ')" />

<xsl:value-of select="$textstart" />

<a>

<xsl:attribute name="href"><xsl:value-of select="$link" /></xsl:attribute>

<xsl:value-of select="$link" />

</a>

<xsl:text>#160;</xsl:text>

<xsl:call-template name="text2hyper">

<xsl:with-param name="text" select="normalize-space($textend)" />


</xsl:call-template>


</xsl:when>

<xsl:otherwise>

<xsl:value-of select="$text" />

</xsl:otherwise>

</xsl:choose>


<br />

</xsl:template>



<xsl:template name="NewLine2Break">

<xsl:param name="string" select="text()"/>

<xsl:param name="from" select="'#xA;'" />

<xsl:param name="to">

<br /><br />

</xsl:param>

<xsl:choose>

<xsl:when test="contains($string, $from)">

<xsl:value-of select="substring-before($string, $from)" />

<xsl:copy-of select="$to" />

<xsl:call-template name="NewLine2Break">

<xsl:with-param name="string"

select="substring-after($string, $from)" />

<xsl:with-param name="from" select="$from" />

<xsl:with-param name="to" select="$to" />

</xsl:call-template>

</xsl:when>

<xsl:otherwise>

<xsl:value-of select="$string" />

</xsl:otherwise>

</xsl:choose>

</xsl:template>

</xsl:stylesheet>
 
Old July 11th, 2003, 04:57 AM
Friend of Wrox
 
Join Date: Jun 2003
Posts: 147
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via Yahoo to armmarti
Default

You have to call one template(replacing "breaks") and apply another template(putting "hrefs") on the result of the first call.
I changed some points in your code and added one main template, called "convert". Since both jobs(replacing line-breaks and putting hrefs) have many things in common, I wrote the main template, which accepts a parameter "handler-name" and selects appropriate "job" to perform.
I generalized the problem: now the source XML doc can contain any content, not just "plain text". It is more interesting, of course :)
Even in the case of "plain text", we have to use "RTF to nodeset" conversion; explicitly or implicitly.
Why? Because when you want to replace line-breaks and then put hrefs, the input of the second "operation" will contain "br" elements; when you want to put hrefs and then replace line-breaks, you'll have an "a" elements in the input to the second operation. So, we have to use "RTF to nodeset" conversion even in the simplest case of "plain-text".
I used Saxon 6.5.2. You just have to specify "version='1.1'" in the XML declaration, or explicitly use exslt:node-set() function. I have chosen the first approach.
Here is the code:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">

        <xsl:call-template name="convert">
            <xsl:with-param name="tree-fragment">
                <xsl:call-template name="convert">
                    <xsl:with-param name="tree-fragment" select="."/>
                    <xsl:with-param name="handler-name" select="'breaks'"/>
                </xsl:call-template>
            </xsl:with-param>
            <xsl:with-param name="handler-name" select="'hrefs'"/>
        </xsl:call-template>    
    </xsl:template>



    <xsl:template name="convert">
        <xsl:param name="tree-fragment"/>
        <xsl:param name="handler-name"/>

        <xsl:for-each select="$tree-fragment/child::node()">
            <xsl:choose>
                <xsl:when test="current()[self::processing-instruction() or self::comment()]">
                    <xsl:copy-of select="current()"/>
                </xsl:when>
                <xsl:when test="current()[self::text()]">

                    <xsl:choose>
                        <xsl:when test="$handler-name = 'hrefs'">
                            <xsl:call-template name="put-hrefs">
                                <xsl:with-param name="text" select="current()"/>
                            </xsl:call-template>    
                        </xsl:when>
                        <xsl:when test="$handler-name = 'breaks'">
                            <xsl:call-template name="put-breaks">
                                <xsl:with-param name="text" select="current()"/>
                            </xsl:call-template>    
                        </xsl:when>
                    </xsl:choose>
                </xsl:when>
                <xsl:when test="current()[self::*]">


                    <xsl:copy>
                        <xsl:for-each select="current()/@*">
                            <xsl:attribute name="{name(current())}">
                                <xsl:value-of select="current()"/>
                            </xsl:attribute>
                        </xsl:for-each>
                        <xsl:call-template name="convert">
                            <xsl:with-param name="tree-fragment" select="current()"/>
                            <xsl:with-param name="handler-name" select="$handler-name"/>
                        </xsl:call-template>
                    </xsl:copy>                
                </xsl:when>
            </xsl:choose>
        </xsl:for-each>        
    </xsl:template>

    <xsl:template name="put-hrefs">
        <xsl:param name="text"/>

        <xsl:choose>
            <xsl:when test="contains($text, 'http:')">
                <xsl:variable name="textstart" select="substring-before($text,'http:')"/>
                <xsl:variable name="rest" select="concat(substring-after($text,'http:'),'')" />
                <xsl:variable name="link" select="concat('http:',substring-before($rest,' '))" />
                <xsl:text>#160;</xsl:text>
                <xsl:variable name="textend" select="substring-after($rest,' ')" />
                <xsl:value-of select="$textstart" /><a><xsl:attribute name="href"><xsl:value-of select="$link" /></xsl:attribute><xsl:value-of select="$link" /></a>#160;
<xsl:call-template name="put-hrefs">
                    <xsl:with-param name="text" select="normalize-space($textend)" />
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$text" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="put-breaks">
        <xsl:param name="text"/>
        <xsl:param name="from" select="'#x0A;'" />
        <xsl:param name="to"><br /><br /></xsl:param>
        <xsl:choose>
                    <xsl:when test="contains($text, $from)">
                        <xsl:value-of select="substring-before($text, $from)" />
                        <xsl:copy-of select="$to" />
                        <xsl:call-template name="put-breaks">
                            <xsl:with-param name="text" select="substring-after($text, $from)" />
                            <xsl:with-param name="from" select="$from" />
                            <xsl:with-param name="to" select="$to" />
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$text" />
                    </xsl:otherwise>
        </xsl:choose>
    </xsl:template>


</xsl:stylesheet>

If you'll see some #xc2 symbols(I guess the processor outputs them) in IE, change the encoding to UTF8.
 
Old July 11th, 2003, 10:20 AM
Registered User
 
Join Date: Jun 2003
Posts: 4
Thanks: 0
Thanked 0 Times in 0 Posts
Default

First, let me thank you for your efforts. This is very instructive!!
As I'm just beginning to implement XSLT please bear with me.

When I implement the changes you suggest I get an error:

"Keyword xsl:stylesheet may not contain PCDATA nodes"

Upon close inspectioin I can't find anything ( to my eye ) that qualifies as PCDATA.

Do you have a suggestion about how to proceed?

Thanks,

Pete
 
Old July 12th, 2003, 12:51 AM
Friend of Wrox
 
Join Date: Jun 2003
Posts: 147
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via Yahoo to armmarti
Default

I'm glad to help!

Which XSLT processor you are using?

Try Saxon 6.5.2, it's open source(Java) and also .exe is available; it's excellent.
In the command line:
Code:
saxon XML_SOURCE XSL_STYLESHEET > output.html
I tried and it works.

By the way, "&" disappers when a message is posted(in entity/character references). It's strange...

Cheers,
Armen
 
Old July 12th, 2003, 07:44 AM
Friend of Wrox
 
Join Date: Jun 2003
Posts: 147
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via Yahoo to armmarti
Default

I'm looking at the code and I have a feeling that the stylesheet is very unefficient.
It traverses the source tree twice in principle, although the code is readable and not complicated. It will be better
to force both "put-breaks" and "put-hrefs" templates to process temporary trees(better name for "tree fragment") instead of text contents...
In this approach we will "sink" deeper in the XML, will find textual pieces to process, and will apply one operation upon other's output.
Though in optimized version we'll traverse the source tree only once and will combine the 2 operations in deeper levels, as I imagine, the code will become complex and hard to read. On the other hand, it seems to me that for really big and "deep" XML sources new version will be really much more effective(it's intuitively stated: I have no real results to prove).

In any case I really enjoyed writing the stylesheet! :)

Cheers,
Armen





Similar Threads
Thread Thread Starter Forum Replies Last Post
Hyperlink carlos1972 Access VBA 2 November 7th, 2007 10:17 AM
How we can use hyperlink nagham Crystal Reports 0 February 9th, 2007 03:51 AM
Export to Excel Formatting/Hyperlink HockeyMom SQL Server DTS 0 June 5th, 2006 04:04 PM
Hyperlink viktor26 Classic ASP Basics 0 October 19th, 2003 09:48 AM
Hyperlink CW Classic ASP Databases 5 September 17th, 2003 06:45 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.