Subject: Re: Complex table of content generation From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx> Date: Wed, 28 Jun 2000 21:36:22 +0100 |
Jean-Claude, I am a little concerned that I'm misunderstanding what you mean by 'hyperlink' in your example. I will assume that you are producing HTML, and that the details of each component are a named anchor, and the links to them can be done through reference to the anchor, e.g.: <a name="C">Component = C</a> Parent = <a href="#C4">C4</a> Children = <a href="#C1">C1</a>, <a href="#C2">C2</a>, <a href="#C3">C3</a>, <a href="#C4">C4</a> In other words, the 'hyperlinks' are all defined in the HTML that you're producing - it's just a matter of producing HTML that contains the right references to the right components. >Here is an example of what I want: > >1. Table of content (may be by alphabetical order) The following template selects all the 'Component' elements and sorts them alphabetically, producing HTML links to the details of the component (assuming that the details are in the same document as the table of contents). It ignores all the SubComponent stuff, focussing on only the Components - note that this relies on all the components, whether they have subcomponents or nor, being defined within a 'Component' element. [Note the spacing on these templates has been laid out for logic in the stylesheet rather than spacing within the output - you probably have to litter them with xsl:text to get precisely what you want...] <xsl:template match="Components" mode="table-of-contents"> <div id="contents"> <xsl:for-each select="Component"> <xsl:sort select="@name" order="ascending" /> <a href="#{@name}"><xsl:value-of select="@name" /></a> </xsl:for-each> </div> </xsl:template> >2. List of all components (by logical order) This is a little tricky because you need to define what makes up a logical hierarchy under your scheme, and how that should be represented within your output. The first question is 'how do you identify the top component?'. There are several options: 1. the one without a parent 2. the first in the alphabetical list 3. by human choice In your example, you have chosen that component 'C' is the top of the hierarchy, even though it is a subcomponent of component 'C4'. So Option 1 cannot be what is being applied. I think you are probably using Option 2, but for the sake of extensibility, I implemented Option 3 - passing in the name of the top component through a parameter: <xsl:param name="top-component" select="'C'" /> The second question is 'how do you represent a logical hierarchy?'. Within your example, the general form is: top component, single subcomponent, all sub-subcomponents e.g. C, C3, C1.1 - C2 - C3 - C4 You have deliberately not gone any further than three levels down. This seems to be a fairly hard-wired decision, so I have similarly hard-wired it into the template for doing the logical hierarchy. But if you can express a more general rule, then we can use that instead. So, first a key. This is just to provide a shortcut between a component name and its subcomponents (in the case where you don't have the Component element): <xsl:key name="super-sub" match="SubComponent" use="../@name" /> When you're designing keys for referencing, think 'what do I know?', 'what do I want to know?' and 'how would I get *here* from *there*?'. In this case, we know the name of the component, we want to know its subcomponents, and the way you get here from there is by finding the parent's @name attribute. The following template gives the output that you desired, with HTML links. <xsl:template match="Components" mode="logical-order"> <div id="logical-order"> <!-- select the top component, as defined by the parameter --> <xsl:for-each select="Component[@name=$top-component]"> <!-- go through each subcomponent of the top component --> <xsl:for-each select="SubComponent"> <!-- each composition is numbered according to the position() of the subcomponent within the list - may be another rule to use here? --> Composition <xsl:value-of select="position()" /> = <!-- link to top component --> <a href="#{$top-component}"><xsl:value-of select="$top-component" /></a>, <!-- link to subcomponent --> <a href="#{.}"><xsl:value-of select="." /></a>, <!-- go through each of the sub-subcomponents, accessed through the 'super-sub' key defined above, indexed on the content of the 'SubComponent' element --> <xsl:for-each select="key('super-sub', .)"> <!-- link to sub-subcomponent --> <a href="#{.}"><xsl:value-of select="." /></a> <!-- append a dash unless it is last in the list --> <xsl:if test="position() != last()"> - </xsl:if> </xsl:for-each> </xsl:for-each> </xsl:for-each> </div> </xsl:template> >2.2 Description of each component (each name is a hyperlink, too) Since the descriptions include the parent of the particular component, you need a key where 'here' is the name of a subcomponent, 'there' is a Component element and 'here from there' is that the subcomponent name should be the value of a SubComponent child of the Component element: <xsl:key name="sub-super" match="Component" use="SubComponent" /> The following template gives the details that you described, with HTML links, and with 'None' as the default where there aren't any parents or children of the particular component: <xsl:template match="Components" mode="details"> <!-- goes through the components in the order they appear in the input - may want to sort this alphabetically --> <xsl:for-each select="Component"> <!-- define the HTML anchor for the component --> <a name="{@name}">Component = <xsl:value-of select="@name" /></a> <!-- define a variable to hold the list of supercomponents of the component using the 'sub-super' key --> <xsl:variable name="parent" select="key('sub-super', @name)" /> Parent = <xsl:choose> <!-- if there are any parents... --> <xsl:when test="$parent"> <!-- go through the parents one by one (document order) --> <xsl:for-each select="$parent"> <a href="#{@name}"><xsl:value-of select="@name" /></a> <xsl:if test="position() != last()">, </xsl:if> </xsl:for-each> </xsl:when> <!-- if there aren't, output 'None' --> <xsl:otherwise>None</xsl:otherwise> </xsl:choose> <!-- define a variable to hold the list of subcomponents of the component (SubComponent child nodes) --> <xsl:variable name="children" select="SubComponent" /> Children = <xsl:choose> <!-- if there are any children... --> <xsl:when test="$children"> <!-- go through the children one by one (document order) --> <xsl:for-each select="$children"> <a href="#{.}"><xsl:value-of select="." /></a> <xsl:if test="position() != last()">, </xsl:if> </xsl:for-each> </xsl:when> <!-- if there aren't, output 'None' --> <xsl:otherwise>None</xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:template> This produces the output that you specified in your email, but I am concerned that it is not general enough for you. For example, you mentioned branches with very different depths, which isn't covered here. I have used xsl:for-each in these examples both for brevity and because the definition of the output you wanted from the logical order seemed to be in terms of 'depth', which is easier to manage with xsl:for-each than xsl:apply-templates. However, if you can define the stopping condition (i.e. when do you stop going down a branch?) in terms of things that you know about a particular component (e.g. has it got a longer name than its parent?), then you could (should?) probably use xsl:apply-templates instead. Anyway, I hope this helps if not *solve* your problem, at least let you frame some other questions that can help us take you closer to a solution next time :) Cheers, Jeni Dr Jeni Tennison Epistemics Ltd * Strelley Hall * Nottingham * NG8 6PE tel: 0115 906 1301 * fax: 0115 906 1304 * email: jeni.tennison@xxxxxxxxxxxxxxxx XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
context position incorrect, Steve Brown | Thread | Re: Complex table of content genera, Jean-Claude Tarby |
Re: Selecting an complementary set , Ragnar Schierholz | Date | Re: How to select text only for ele, Nick Browne |
Month |