Subject: Re: Formatting elements From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx> Date: Tue, 03 Oct 2000 20:48:48 +0100 |
Stuart, >I have managed to generate to new tags from the name attribute but cannot >get the <bulletlist> element generated. Can anyone help? It would have been helpful if you'd included the XSLT that wasn't working so we could see where you were having problems. I'm guessing that your input is a bit more complicated than just a title and a number of bullets, and that you have other things mixed up that makes working out where the bulletlist starts and stops quite difficult - something like: <document> <item name='title'><text>Title field</text></item> <item name='bullet1'><text>some text for item 1</text></item> <item name='bullet2'><text>some text for item 2</text></item> <item name='bullet3'><text/></item> <item name='bullet4'><text/></item> <item name='bullet5'><text/></item> <item name='bullet6'><text/></item> <item name='para'><text>paragraph text</text></item> <item name='bullet1'><text>some text for item 1</text></item> <item name='bullet2'><text>some text for item 2</text></item> <item name='bullet3'><text/></item> <item name='bullet4'><text/></item> </document> which you want to be: <document> <title>Title field</title> <bulletlist> <bullet>some text for item 1</bullet> <bullet>some text for item 2</bullet> <bullet></bullet> <bullet></bullet> <bullet></bullet> <bullet></bullet> </bulletlist> <para>paragraph text</para> <bulletlist> <bullet>some text for item 1</bullet> <bullet>some text for item 2</bullet> <bullet></bullet> <bullet></bullet> </bulletlist> </document> This generalisation turns the problem into a grouping problem - you need to identify which items make up the bulletlist. This is usually best approached using the Muenchian method (i.e. keys) to group the relevant nodes. This problem is really interesting, though, because the construction of the @names means that the Muenchian method is really hard to work with (because you don't know how long the number at the end is going to be). I'm sure a Muenchian solution to this is possible, but just thinking about constructing the key value made my head spin - David Carlisle will doubtless be able to help :) So I've used a different approach to the problem. It's relatively easy to work out whether an item is part of a list: if the last character of its name is a number, then it's part of a list: boolean(number(substring(@name, string-length(@name), 1))) Working out what number it is in the list is fairly straight-forward as well: translate all the letters in the name to spaces and turn the result into a number: <xsl:variable name="number" select="number(translate(@name, 'abcdefghijklmnopqrstuvwxyz', ' '))" /> If the $number is 1, then we've found the first item in a list; at this point we can create the wrapping '*list' element: <xsl:if test="$number = 1"> <xsl:variable name="name" select="substring(@name, 1, string-length(@name) - 1)" /> <xsl:element name="{$name}list"> ... </xsl:element> </xsl:if> Now the bit of logic that is a break from the normal method of grouping. Usually this would involve collecting together all the members of the group - all the items in the list - by finding all the items that have the same following-sibling:: that marks the end of the list. In this case, it's very hard to do because we the @names change from item to item (albeit retaining the same prefix). Instead, we're going to step through them one by one, passing the name of the new element (which is relatively easy to work out for that first item in the list we can guarantee the number only has one character in it) as a parameter. The first item in the list is the first to have templates applied to it. I'm using a mode here to distinguish between the normal processing of items and the processing of items when they belong to a list. <xsl:apply-templates select="." mode="item"> <xsl:with-param name="name" select="$name" /> </xsl:apply-templates> The template that this matches with checks whether the item starts with the $name we're interested in, and if so creates an element with that name. Then it applies templates in the same mode to *its* following sibling. Again, the same template is applied, and if it's another member of the list, it continues to process the next item and so on, effectively stepping through the items one at a time. To counter the situation where two lists are placed immediately one after another, a second parameter ($first) is declared so that it is only true() if the template is being applied to the first of a list. If $first is not true() and the @name of the item is the same as $name + '1', then the item is ignored - in effect it is part of the next list, not this one. The complete templates are: <xsl:template match="item" mode="list"> <xsl:param name="name" /> <xsl:param name="first" select="true()" /> <xsl:if test="starts-with(@name, $name) and ($first or @name != concat($name, '1'))"> <xsl:element name="{$name}"><xsl:value-of select="text" /></xsl:element> <xsl:apply-templates select="following-sibling::*[1]" mode="list"> <xsl:with-param name="name" select="$name" /> <xsl:with-param name="first" select="false()" /> </xsl:apply-templates> </xsl:if> </xsl:template> <xsl:template match="item"> <xsl:choose> <xsl:when test="boolean(number(substring(@name, string-length(@name), 1)))"> <xsl:variable name="number" select="number(translate(@name, 'abcdefghijklmnopqrstuvwxyz', ' '))" /> <xsl:if test="$number = 1"> <xsl:variable name="name" select="substring(@name, 1, string-length(@name) - 1)" /> <xsl:element name="{$name}list"> <xsl:apply-templates select="." mode="list"> <xsl:with-param name="name" select="$name" /> </xsl:apply-templates> </xsl:element> </xsl:if> </xsl:when> <xsl:otherwise> <xsl:element name="{@name}"><xsl:value-of select="text" /></xsl:element> </xsl:otherwise> </xsl:choose> </xsl:template> I hope that this helps. It's certainly proved an interesting challenge. Cheers, Jeni Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: Formatting elements, Gary L Peskin | Thread | Re: Formatting elements, Paul Tchistopolskii |
Re[2]: Adding a chekbox with XSL, Dylan Parker | Date | Selecting all descendants with no c, Taras Tielkes |
Month |