This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

CONTEST: How to implement "templates"? ;-)


Hello,

Here is a contest for those with too much time (you can
only win the honor to be the one who came up with the
best solution ;-):

How can one implement "templates" with XSLT?

Explanation:

I have a XML file data.xml which contains lots of useful info:

    <root>
	<info id="info1">
	    <title>This is the title of info1</title>
	    ...
	</info>
	<info id="info2">
	    ...
	</info>
	...
    </root>

Then I have a file layout.xml which descibes the layout of the
result:

    <layout>
    <html>
    <head>
	<title><put-title-here/></title>
    </head>
    <body>
	...
    </body>
    </html>
    </layout>

And now the task: Write an XSLT script which creates one
HTML page for every /root/info element in data.xml and
uses the layout from layout.xml. Or to put it a different
way: How can I control how the XSLT processor processes one file
from another XML file?

And the advanced task: Write an XSLT script which can merge
different layout files. Example layout2.xml:

    <layout base="layout.xml">
    <html>
    <body bgcolor="black" text="white">
    </body>
    </html>

This should behave as if all attributes in layout2.xml
overwrite the attributes in layout.xml and should also work
with more complex element trees.

And if that's not enough for you: Extend this with a way to
define xml trees in the layout and/or the source file which
get expanded while the output is written. Example:

    <define name="h1">
	<table bgcolor="blue">
	    <tr><td><contents/></td><tr>
	</table>
    </define>

    ...

	<use name="h1">This is put where the contents element is</use>
    ...

Note that it should be possible to use use-elements inside of define
and use elements.

The winner is the person who comes up with the *fastest*
*and* the *most simple* way to do this.

*Fastest* means that if you need to run the XSTL processor
five times to achieve this, you can't win. Ideally, everything
should happen in one go or maybe two.

*Most obvious* means that the XSLT stylesheet should be simple
to understand (so it doesn't break that easily, it can be used
as a good example in the FAQ and people don't have to study if
for three days before admitting that they can't figure out what
it does). This also means that the script should be generous
with complicated layout files.

Extension elements as described by www.exslt.org can be used.

Anyone?

Here are some ideas for a solution:

    <!-- the info elements drive this -->
    <xsl:template match="info">
	<!-- Read and process the layout. Use a mode so we can define a generale
	     rule for all nodes to preserve the context of the info element. -->
	<xsl:variable name="template">
	    <xsl:apply-templates select="document('layout.xml')/layout/node()" mode="tpl">
		<xsl:with-param name="info" select="$info"/>
	    </xsl:apply-templates>
	</xsl:variable>

	<xsl:message>Writing info <xsl:value-of select="$path"/>...</xsl:message>
	<exslt:document href="{$path}">
	    <xsl:apply-templates select="exslt:node-set($template)"/>
	</exslt:document>
    </xsl:template>

This reads the layout and it allows to access the information in the info
element which started the whole thing. But how to merge different layouts?

Here is a solution for the define/use thingy:

    <!-- to make my life a little bit more simple, I've put define
         and use in the x namespace... -->
    <xsl:template match="x:define">
	<!-- do nothin. This skips the define nodes because they
             should only appear in the output when they are "used" -->
    </xsl:template>

    <xsl:template match="x:use" mode="tpl">
	<xsl:param name="info"/>
	
	<!-- Use what? -->
	<xsl:variable name="name" select="@name"/>
	<!-- Look up the define. How can I do that in several contexts at once? -->
	<xsl:variable name="template" select="//x:define[@name = $name]/node()"/>
	<!-- This is what should be put in place of the content element -->
	<xsl:variable name="content" select="node()"/>

	<!-- Same trick as above but we must pass the content along, too, now.
	     Is there a better way to do this? -->
	<xsl:if test="$template">
	    <xsl:apply-templates select="$template" mode="x-tpl">
		<xsl:with-param name="info" select="$info"/>
		<xsl:with-param name="content" select="$content"/>
	    </xsl:apply-templates>
	</xsl:if>
    </xsl:template>

    <!-- Copy everything and preserve the parameters. This breaks
         when a pattern with mode="tpl" matches. How can this be
	 prevented? -->
    <xsl:template match="@* | node()" mode="x-tpl">
	<xsl:param name="info"/>
	<xsl:param name="content"/>

	<xsl:copy>
	    <xsl:apply-templates select="@* | node()" mode="x-tpl">
		<xsl:with-param name="info" select="$info"/>
		<xsl:with-param name="content" select="$content"/>
	    </xsl:apply-templates>
	</xsl:copy>
    </xsl:template>

    <!-- Put the contents in here -->
    <xsl:template match="x:contents" mode="x-tpl">
	<xsl:param name="info"/>
	<xsl:param name="content"/>

	<xsl:apply-templates select="$content" mode="x-tpl">
	    <xsl:with-param name="info" select="$info"/>
	    <xsl:with-param name="content" select="$content"/>
	</xsl:apply-templates>
    </xsl:template>

Is there a better way to do this? Is there a way to nest these "macros"?
How can one lookup macros in different contexts/document? How do create
a reliable shadowing-order which won't surprise the user?

And lastly how can one nest layouts as described above?

Or isn't that possible?

-- 
==============================================
Sowatec AG,       CH-8330 Pfäffikon (ZH)
Witzbergstr. 7,   http://www.sowatec.com
Tel: +41-(0)1-952 55 55
Fax: +41-(0)1-952 55 66
----------------------------------------------
Aaron "Optimizer" Digulla, digulla@sowatec.com
==============================================

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]