tags:

views:

120

answers:

3

I have a piece of XML data which I need to transform into WML.

It's something like this:

 <root>
  <category name="music"/>
  <subcategory name="classic"/>
  <subcategory name="rock"/>
  <subcategory name="Techno"/>
  <node type="music" subtype="classic" name="beethoven"/>
  <node type="music" subtype="classic" name="chopin"/>
  <record author="beethoven" name="moonlight sonata"/>
  …
 </root>

I cannot change the file structure.

Some Nokia mobile browsers cannot load lots of <card>'s into memory.

So, depending on the mobile browser, the WML page should be either a whole set of <card>'s, or some subset of <card>'s.

For instance, if I download a page with a normal browser, it should look like this:

<wml>
 <card id="TOC">
  <p><a href="#contents">Contents</a></p>
  <p><a href="#az">A-Z</a></p>
 </card>
 <card id="contents">
  <p><a href="#music">music</a></p>
  <p><a href="#video">video</a></p>
  <p><a href="#java">java</a></p>
 </card>
 <card id="az">
  <p><a href="#beethoven">beethoven</a></p>
  <p><a href="#chopin">chopin</a></p>
 </card>
 <card id="music">
  <p><a href="#classic">classic</a></p>
  <p><a href="#rock">rock</a></p>
  <p><a href="#Techno">Techno</a></p>
 </card>
 <card id="classic">
  <p><a href="#beethoven">beethoven</a></p>
  <p><a href="#chopin">chopin</a></p>
 </card>
 …
</wml>

, so that the user can browse without extra round-trips to the server.

However, when I use Nokia and visit the start page, the page should look like this:

http://example.com/

<wml>
 <card id="TOC">
  <p><a href="#contents">Contents</a></p>
  <p><a href="#az">A-Z</a></p>
 </card>
 <card id="contents">
  <p><a href="#music">music</a></p>
  <p><a href="#video">video</a></p>
  <p><a href="#java">java</a></p>
 </card>
 <card id="az">
  <p><a href="/beethoven">beethoven</a></p>
  <p><a href="/chopin">chopin</a></p>
 </card>
 <card id="music">
  <p><a href="/classic">classic</a></p>
  <p><a href="/rock">rock</a></p>
  <p><a href="/Techno">Techno</a></p>
 </card>
 <card id="video">
  <p><a href="/movies">Movies</a></p>
 </card>
 <card id="java">
  <p><a href="/games">Games</a></p>
 </card>
</wml>

, when I visit the href, it should show the inner contents:

http://example.com/classic

<wml>
 <card id="TOC">
  <p><a href="#contents">Contents</a></p>
  <p><a href="#az">A-Z</a></p>
 </card>
 <card id="contents">
  <p><a href="/music">music</a></p>
  <p><a href="/video">video</a></p>
  <p><a href="/java">java</a></p>
 </card>
 <card id="az">
  <p><a href="#beethoven">beethoven</a></p>
  <p><a href="#chopin">chopin</a></p>
 </card>
 <card id="classic">
  <p><a href="#beethoven">beethoven</a></p>
  <p><a href="#chopin">chopin</a></p>
 </card>
 …
</wml>

Basically, the XSLT should do the following things:

  • Accept some kind of a parameter of what is to be shown: the category, the subcategory etc.

  • Count the <card>'s that would be shown.

    1. If we load only types and TOC, we get 2 cards (we always show them)
    2. If we load only types and subtypes, we get 10 cards.
    3. If we load types, subtypes and titles, we show 100 cards.
    4. If we load everything, we show 300 cards.

    Nokia cannot handle more than 120 cards, so we just stop on level 3.

    If XML changes and level 3 requires 130 cards, we should stop on level 2.

  • Don't show a <card> if it is below certain level

  • Replace the # (inner links) with / (outer links) if the card is not to be shown.

Is it possible to do in a single XSL file?

+1  A: 

I'm not sure what the question really is. As phrased, the answer is "yes, it is possible" - xsl:if and xsl:choose should be quite sufficient to handle all your conditions. You'll have to communicate restrictions (such as the fact that the result will go to a Nokia) to the stylesheet via parameters - see xsl:param.

Pavel Minaev
A: 

I'd suggest creating variables which accumulatively count all the nodes of each "level" (type, subtype, title, …) and provide a parameter to your XSL indicating the maximum number of cards to generate. The XSL might look something like this:

<xsl:stylesheet …>
  <xsl:param name="max-cards" select="999999"/>

  <xsl:template match="/root">
    <!-- "2" here for the type/TOC cards -->
    <xsl:variable name="nSubs" select="2 + count(subcategory)"/>
    <xsl:variable name="nNodes" select="$nSubs + count(node)"/>
    <xsl:variable name="nRecs" select="$nNodes + count(record)"/>

    <!-- generate types & TOC here -->

    <xsl:if test="$nSubs < $max-cards">
        <!-- generate subtypes here -->
    </xsl:if>

    <xsl:if test="$nNodes < $max-cards">
        <!-- generate titles here -->
    </xsl:if>

    <xsl:if test="$nRecs < $max-cards">
        <!-- generate everything else here -->
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Stylesheet parameters could be similarly used to restrict the generation of upper levels, but an example for that would be excessively long for SO. ^.^

Ben Blank
A: 

My understanding is that XSL is turing complete so the answer to pretty much any 'is it possible?' question is going to be yes. The answer to 'are you going to like it?', maybe not so much :-)

A simple way to do it and keep it modular would to use <xsl:param> to pass in the control parameters, and <xsl:choose> to select the format you want to display, and then branch/delegate to specific templates or functions for each format.

Where your formats have things in common you can DRY it up by delegating those parts to their own templates or functions, reusing them in the higher level templates for the main formats. Divide and conquer basically.

edit: to be more specific about what I mean by delegating, I mean explicitly calling templates and parameterising them, e.g.:

   <xsl:call-template name="showcard">
     <xsl:with-param name="kind" select="nokia"/>
   </xsl:call-template>

With those templates in turn delegating to others, etc. You can also get a lot from value-of and apply-templates directing the flow to specific templates. Though your case is probably simpler, this may lead to more readable code.

frankodwyer
In fact, what I want is to have a reusable template, so if I want to change the card layout I do it in one place (for both `Nokia` and normal browsers). I know about params and chooses, I just can't use them properly, everything I get looks like a terrible mess.
Quassnoi
yes but this will still be just one xsl file, with a bunch of xsl:template components to descend the xml input. I think of xsl:template as a subroutine or procedure/function.
frankodwyer