RE: Grouping into a table (for vertical alignment)
Thanks Wendell... trying to figure it out now :p >Jeni works through one of these in entry 12 in the FAQ >page at http://www.dpawson.co.uk/xsl/sect2/N4486.html#d4726e727. Looking at it too, entry 12 actually allows me to handle my problem without a <xsl:key> ... I got a question on entry 17: > <xsl:apply-templates mode="inGroup" > select="following-sibling::*[position() < $vGroupLength]"/> "vGroupLength" is the number of elements in the group. How does the "position()" work when used in the above manner, is it just the "context position" within (1) the node list of only "following-sibling" or (2) a node list of all siblings? I guess the first, but I just want to confirm. I really appreciate all the help I am getting from this list. Thanks all. Regards, Daniel -----Original Message----- From: Wendell Piez [mailto:wapiez@xxxxxxxxxxxxxxxx] Sent: Thursday, 27 May, 2004 11:58 PM To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx Subject: RE: Grouping into a table (for vertical alignment) Hi Daniel, Thanks for the more detailed specification. It makes your problem clearer. At 02:13 AM 5/27/2004, you wrote: >Currently the input XML structure has a <form> tag which can have either: > > 1) "presentation tags" like <text>, <image>, etc which require to fill the >whole width (in the HTML, these are already placed in <div> to get its own >line WITHOUT any <table>) > > 2) "entry tags" like <input>, <password>, <checkbox>, etc which require to >be separated into a label and a value (in HTML, I want these to be placed in >a <table>, EITHER a new table if the previous tag was a "presentation tag" >OR a new row in the previous table if the previous tag was an "entry tag") > > >The special considerations for this are: > > A) there can be any sequence of "presentation tags" and "entry tags" > > B) if there are "presentation tags" in-between groups of "entry tags", then >each separated group of entry tags will have their own table. > > C) I do _not_ want to put "presentation tags" in a table in anyway (eg. use >a single column with "colspan=2") This appears to be a grouping problem. You want to group sequences of entry tags into tables, leaving presentation tags, which are arbitrarily mixed among them, outside the tables. (Be warned that since grouping problems are up-conversions, they're outside the domain of problems that are very easy with XSLT 1.0. Yet since they're so common we've worked out ways of dealing with them. Note that given appropriate wrapper elements in the input, this up-conversion would be unnecessary since the grouping would already be there.) There are a couple of different approaches to this, including using keys to achieve positional grouping, or using templates in a special mode to "walk" the input tree node by node testing whether the next element should be in the table. Your input, again: <form> <name>form</name> <action>submit.do</action> <method>post</method> <content> <text> <value>Please enter your Name and Password.</value> <class>instruction</class> </text> <input> <name>username</name> <label>Name: </label> <value></value> <class>mandatory</class> </input> <password> <name>password</name> <label>Password :</label> <value></value> <class>mandatory</class> </password> <text> <value>All attempts are logged.</value> <class>warning</class> </text> </content> </form> Let's look at the key-based solution. The trick here is that elements to be grouped need to be associated with particular elements -- one for each group -- as "handles" to allow them to be grouped. Assuming your "presentation tags" are <text> and your "input tags" are <password> and <input>, it's convenient in this case to define the handle as "any password or input not preceded by a text", namely those elements that will become the first in any particular table. We need to associate all passwords or inputs with these particular passwords or inputs (the first in their group). This can be done with a key like this: <xsl:key name="inputs-by-handle" match="password | input" use="generate-id((self::node() | preceding-sibling::password | preceding-sibling::input)[not(preceding-sibling::*[self::password|self::i nput])])[last()]"/> I know that's an abominable snowman so let's break down the XPath in the 'use' attribute: (self::node() | preceding-sibling::password | preceding-sibling::input) collects the context node (each password or input) in a group with all its preceding sibling passwords and inputs [not(preceding-sibling::*[self::password|self::input])] a predicate operates on that group, filtering those whose immediately preceding sibling element (preceding-sibling::*) is not a password or input. (Either it's a text, or it doesn't exist since the node is the first of its siblings.) [last()] finally, a predicate selects the last of these, in document order. Wrapping the XPath in generate-id() returns a unique identifier for this node. Consequently, whenever we process a password or input element, we can ask for all the passwords and inputs that "belong" with it. Consequently we can have a template to match passwords and inputs like this: <xsl:template match="password | input"> <xsl:if test="key('inputs-by-handle',generate-id()"> <!-- this test is true only if this password or input is a handle (that is, is the first in a run of them, even if a run of one only) since those that are not, will retrieve empty node sets when the key() is called with their unique IDs --> <table> <xsl:apply-templates select="key('inputs-by-handle',generate-id()" mode="make-row"/> <!-- match the passwords and inputs again in the 'make-row' mode to create our table rows --> </table> </xsl:if> </xsl:template> Note that if we have more element types than just text, password, input, our XPaths become more complex. This problem will presumably be much easier in XSLT 2.0, and presumably Jeni or Mike K or someone will soon show us how. :-> Also, if you think this is too baroque you could try the other technique, the "forward walk". Jeni works through one of these in entry 12 in the FAQ page at http://www.dpawson.co.uk/xsl/sect2/N4486.html#d4726e727. Or maybe a friendly XSLTer with some extra time on their hands will work it out for you. Finally, sometimes when grouping gets really complex (your element types proliferate) it's easiest to work in two passes, one of them to mark the elements to be grouped according to the grouping criteria, and the second to group according to the marks. (To do two passes in a client, however, you need a node-set() extension function not available in all processors.) I hope this helps, 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 ======================================================================
PURCHASE STYLUS STUDIO ONLINE TODAY!
Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!
Download The World's Best XML IDE!
Accelerate XML development with our award-winning XML IDE - Download a free trial today!
Subscribe in XML format