Subject: Re: Newbie question on XSL and lists From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx> Date: Fri, 08 Sep 2000 12:13:05 +0100 |
At 10:50 AM 9/8/00 +0200, Michiel wrote: ... >The algoritm should be something like... This is an interesting exercise in rethinking procedural-style into XSL-declarative style. Basically your logic, as I see it, translates into: >If <par-f> contains a <listing> > process the <listing> >else > display the content of <par-f> >endif For all <par-f>, process the children For all <listing> in <par-f>, process the children For all children of <par-f> not <listing>, display contents >if the <listing> contains <item> > process the <item> >else > /* error */ >endif For all <item> inside <listing>, process the children For all children of <listing> not <item>, throw an error >if the <item> contains a <par-f> > process the <par-f> >else > /* error */ >endif For all <par-f> inside <item>, process the children For all children of <item> not <par-f>, throw an error Collecting and reordering the rules, we have: 1. For all <par-f>, process the children 2. For all <par-f> inside <item>, process the children 3. For all <listing> in <par-f>, process the children 4. For all children of <par-f> not <listing>, display contents 5. For all children of <item> not <par-f>, throw an error 6. For all children of <listing> not <item>, throw an error 7. For all <item> inside <listing>, process the children 2. is subsumed by 1., so both can be expressed <xsl:template match="par-f"> <xsl:apply-templates/> </xsl:template> (As is happens, this is identical to the default template, so you shouldn't need it -- but we'll come back to this.) Since the default rule for processing in XSL ends up descending the tree recursively until contents are displayed, 3. and 4. are also handled by it. 5. and 6. are the two "error-throwing" rules, which could look like: <xsl:template match="item/*[not(self::par-f)] | listing/*[not(self::item]"> <xsl:message>Out of place element not processed</xsl:message> </xsl:template> (It matches any element inside an item that is not a par-f, or any element inside a listing that is not an item.) But you know, this kind of validation is what DTDs are good at, so if your input is already valid to the structure you want, you shouldn't need this stuff. That leaves 7., which ought to be pretty straightforward: <xsl:template match="item"> <xsl:apply-templates/> </xsl:template> (Since I don't see you'd have any items not inside lists, this just catches all of them. Again, it's the default rule.) So -- this is great, but it's still not going to give us the output we want. We haven't looked at what we want to happen in presentation. There are two rules we can state: 8. Items should each get a new line, be preceded with a "*", and indented if they're deeper than one listing deep (to the number of listings) 9. The first par-f in an item should be on the same line as the "*" generated by the item. 10. Subsequent par-f elements inside items should be on new lines and be indented however many listings they are nested inside. So we have to edit our rules: For 8.: <xsl:template match="item"> <xsl:text>
</xsl:text> <!-- a trick to get a new line --> <!-- but what do we do for the indent? --> <xsl:text>* </xsl:text> <!-- here's our * --> <xsl:apply-templates/> <!-- this will process our children --> </xsl:template> For 9. and 10.: <xsl:template match="item/par-f"> <!-- only applies to par-f inside item --> <xsl:if test="position() > 1"> <!-- we only want new line and indent on non-first par-f --> <xsl:text>
</xsl:text> <!-- the new line --> <!-- same problem with indenting --> </xsl:if> <xsl:apply-templates/> </xsl:template> Notice the special rule for par-f inside item. You were right: we've disentangled rule 2. from 1. since it turns out they're different after all. Okay, almost done ... Now -- Chris is correct that beginners should stay away from xsl:for-each! It seems to do something it doesn't do, if you think it has anything to do with for-next loops. It's just a quick easy way to iterate over a node set and do something for each node in it. As it happens, however, it does provide us an elegant solution to our indenting/nesting problem. We can do something like <xsl:for-each select="ancestor::listing">...</xsl:for-each> to do something (such as create white space) for each ancestor <listing> ... that is, accounting for how deep an element is in a listing structure. Emending our two templates (first the par-f): <xsl:template match="item/par-f"> <xsl:if test="position() > 1"> <xsl:text>
</xsl:text> <!-- the new line --> <xsl:for-each select="ancestor::listing"> <xsl:text> </xsl:text> <!-- gives us two spaces --> </xsl:for-each> </xsl:if> <xsl:apply-templates/> </xsl:template> Now for that to work, you'll need: <xsl:strip-space elements="item"/> ...so loose whitespace nodes inside items in your source don't mess up your counting. For the item, it's a bit trickier, since we indent for ancestor listings only after the first one: <xsl:template match="item"> <xsl:text>
</xsl:text> <xsl:for-each select="ancestor::listing[position() > 1]"> <xsl:text> </xsl:text> </xsl:for-each> <xsl:text>* </xsl:text> <xsl:apply-templates/> </xsl:template> All this is assuming you are outputting to plain text. Use <xsl:output method="text"/>. If you are creating a tag structure (after all, it really seems you want HTML), you'll use <ul> elements or such like instead of all this fancy indenting magic. In fact -- the method provided won't work when you display in a browser, since it does whitespace munging and ruins all your hard work! So you can dump all the stuff to do the indenting, taking advantage of your list-wrapper (<listing>) elements to do it for you in your browser: <xsl:strip-space elements="item"/> <xsl:template match="listing"> <ul> <xsl:apply-templates/> </ul> </xsl:template> <xsl:template match="item"> <li> <xsl:apply-templates/> </li> </xsl:template> <xsl:template match="item/par-f"> <xsl:if test="position() > 1"> <br/> </xsl:if> <xsl:apply-templates/> </xsl:template> Simple, isn't it? Unless you want that error-catching, in which case you saw what you could do. Good luck, Wendell ====================================================================== Wendell Piez mailto:wapiez@xxxxxxxxxxxxxxxx Mulberry Technologies, Inc. http://www.mulberrytech.com 17 West Jefferson Street Direct Phone: 301/315-9635 Suite 207 Phone: 301/315-9631 Rockville, MD 20850 Fax: 301/315-8285 ---------------------------------------------------------------------- Mulberry Technologies: A Consultancy Specializing in SGML and XML ====================================================================== XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: Newbie question on XSL and list, Mike Brown | Thread | Newbie question on XSL and lists, Michiel Verhoef |
RE: Special characters turn out as , Kay Michael | Date | Re: Newbie question on XSL and list, Wendell Piez |
Month |