How to implemented nested list in XSLT? - xslt-2.0

I wanted to add <p> tag for every <li> elements, but there is a special situation when it is a nested list. If there is descendant <ol> tag, this adding <p> must be closed before this <ol> tag. Although it is happened, I could not be able to remove the <s>test tag</s><br/>part repetition.
Please help!
Input:
<a>
<ol>
<li>
<s>test tag</s><br/>
<ol>
<li>list item1</li>
<li>list item2</li>
<li>list item3</li>
</ol>
</li>
</ol>
</a>
Expected output:
<a>
<ol>
<li>
<p><s>test tag</s><br/></p>
<ol>
<p><li>list item1</li></p>
<p><li>list item1</li></p>
<p><li>list item1</li></p>
</ol>
</li>
</ol>
</a>
XSLT code:
<xsl:template match="ol">
<ol>
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="li[not(descendant::ol)]">
<li>
<p>
<xsl:apply-templates/>
</p>
</li>
</xsl:template>
<xsl:template match="li[descendant::ol]">
<li>
<p>
<xsl:apply-templates select="node()[parent::li][following-sibling::ol]"/>
</p>
</li>
<xsl:apply-templates select=""/>
</xsl:template>
<xsl:template match="node()[parent::li][following-sibling::ol]"/>

In the case where there is no descendant ol, your desired output has a p element outside each li, whereas your stylesheet is putting a p inside each ol. I don't know which you actually want, but I assume you know how to fix it.
In the case where there is a descendant ol, you have only given one possible example of what you want, and one example does not constitute a specification. However, for that example, the following would work:
<xsl:template match="li[ol]">
<li>
<p>
<xsl:apply-templates select="node()[not(self::ol)]"/>
</p>
<xsl:apply-templates select="ol"/>
</li>
</xsl:template>
If this doesn't solve the problem in the general case, that's because you haven't said what's required in the general case.

Related

How do I get text that isn't wrapped in <p> tags to copy using XSLT?

I'm running an XSLT script to update questions to a newer format. The authors did not wrap the explanations in <p> tags. When I run the script those explanations drop out. If I wrap them in <p> tags they copy over just fine. I have 1200 questions so I'm really hoping not to have to wrap all the explanations in <p> tags one-by-one.
<!-- attributes, commments, processing instructions, text: copy as is -->
<xsl:template match="#*|comment()|processing-instruction()|/text()">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:if test="*[not(#property)]">
<section property="ktp:explanation-section" typeof="ktp:feedback" data-title="Feedback">
<xsl:attribute name="class">
<xsl:text>ktp-explanation-section</xsl:text>
<!-- append exclusion class, if any -->
<xsl:if test="string-length($exclusions)!=0">
<xsl:value-of select="$exclusions"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="data-uuid">
<xsl:call-template name="assignID"/>
</xsl:attribute>
<xsl:apply-templates select="*[not(#property)]|text()"/>
</section>

Best way to replace c:forEach with JSF tag

We are upgrading from jsf 1.2 to jsf 2.
We are using apache myfaces 2.1 and rich faces 4.3.
Below is the xhtml code :
<h:form>
<h:panelGroup id="myMenu_type">
<div id="myMenu">
<ul>
<c:forEach items="#{bean.data}" var="val">
<li>
<h:commandLink value="#{val.label}" action="#{val.performAction}"/>
</li>
</c:forEach>
</ul>
</div>
</h:panelGroup>
</h:form>
I am getting duplicate id error for above xhtml code .
Generated anchor link tag <a> gets duplicate id for above code.
My question is , I want to get rid of c:forEach since I suspect that it is giving this duplicate id error as I am
mixing JSTL and JSF.
What is the best JSF way to replace above construct ? I am trying it out with rich:dataTable.
Is there any better alternative available ?
Please advice.
Strange part is that with same code , above issue is not observed for JSF 1.2.
EDIT 1 :
I have tried using <rich:list> as well as <a4j:repeat>
Below is the xhtml code
<div id="myMenu">
<rich:list value="#{bean.data}" var="val">
#{val}
</rich:list>
</div>
The strange thing is that when i print #{val} , it gives me value as javax.faces.component.UIViewRoot#28d0bbac whereas val is a normal
custom object.
Because of above issue , i cannot call any method on val , it gives Property 'xx' not found on type javax.faces.component.UIViewRoot error.
Above error is not observed when <c:forEach> is used.
EDIT 2 :
I am attaching the generated html for the above xhtml code which gives a clear idea of where duplicate id error is coming.
<tr><td>
<span id="form:myMenu_type">
One
Two
Three
Four
Five
Six
<div id="myMenu">
<ul>
<li >One
</li>
<li >Two
</li>
<li >Three
</li>
<li >Four
</li>
<li >Five
</li>
<li >Six
</li>
<li >Seven
</li>
<li >Eight
</li>
</ul>
</div>
</span>
</td></tr>
So as shown in above code , the anchor link tag <a> generated right below form:myMenu_type is the real issue. Its id is exactly the sameas one generated inside <li> - which is the correct one since i am using <li> explicitely. Why are anchor tags are getting generated is the real issue. Even if I dynamically assign ids inside <c:forEach> , same gets assigned to <a> links above giving same duplicate id error. I have used dummy ids to just explain the issue. Actual ids are dynamic ids generated by myFaces
Use ui:repeat of JSF
example:
<ui:repeat value="#{productMB.products}" var="product">
<table class="cart">
<tr>
<td class="image">
<div id="image_container">
<img title="#{product.type} #{product.mark} #{product.family} #{product.subfamily} #{product.model} #{product.partNumber}" src="#{product.imageURL}"/>
</div>
</td>
</tr>
</table>
</ui:repeat>

jQuery Mobile Split Button List Question

I am trying to get an image on the right hand side but it always appear as an arrow.
<ul data-role="listview" data-theme="g">
<li>
<a href="#">
<img src="../Images/play_button.gif" width="16" height="16" class="ui-li-icon" />
<span>Item 1</span>
<span class="ui-li-count">12</span>
</a>
<a href="#">
// I want an image to appear here
</a>
</li>
<li>
<img src="../Images/play_button.gif" width="16" height="16" class="ui-li-icon" />
<span>Item 2</span>
<span class="ui-li-count">9</span>
</li>
</ul>
I'm not sure exactly what is wrong with the layout you're using for the list view,
I would study the documentation and compare it to your code
http://jquerymobile.com/demos/1.0a1/#docs/lists/index.html
If you wanna replace those jqm icons with your icons, read this:
http://jquerymobile.com/demos/1.0b2/#/demos/1.0b2/docs/buttons/buttons-icons.html
See if the "custom icons" section would help you.
I found this as an overview of the list: http://www.youtube.com/watch?v=YaZhS_8vaYo It shows the arrows because the entire contents is wrapped in an <a>, I think if you put the link inside an H3, it will become more contained and not show the error. The only thing I haven't figured out is a custom icon; you may be able to just float it.
I viewed the page source and found it very helpful...
lists-split.html
looks like this.
<ul data-role="listview" data-split-icon="gear" data-split-theme="d">
<li><a href="index.html">
<img src="images/album-bb.jpg" />
<h3>Broken Bells</h3>
<p>Broken Bells</p>
</a>Purchase album
</li>
<li><a href="index.html">
<img src="images/album-hc.jpg" />
<h3>Warning</h3>
<p>Hot Chip</p>
</a>Purchase album
</li>
</ul>

Grouping in XSLT 2.0 similar to br to p problems

In XSLT 1.0, a common question in forums was how to convert flat HTML into hierarchical XML, which many times boiled down to nesting text in between <br /> tags in <p> tags.
I have a similar problem, which I think I've partially solved using XSLT 2.0, but it's a new approach to me and I'd like to get a second opinion.
The XHTML source has <span class="pageStart"></span> scattered throughout. They can appear in several different parent nodes. I want to wrap all the nodes between one page start marker and the next in an <page> node. The solution I currently have is:
<xsl:template match="*[child::span[#class='pageStart']]">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:for-each-group select="node()"
group-starting-with="span[#class='pageStart']">
<page>
<xsl:apply-templates select="current-group()"/>
</page>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
There's at least one flaw with this -- the parent node of the marker gets a <page> as a child node when I don't want it. In other works, if there's a <div> that has a child page marker anywhere in it, an <page> node is created as an immediate child of <div> in addition to the locations I expect.
I had hoped that I could simply make the template rule be <xsl:template match="span[#class='pageStart']"> but current-group() seems to be empty no matter what I try. The common sense approach I tried was <xsl:for-each-group select="node()" group-starting-with="span[#class='pageStart']">.
Is there an easier way to solve this problem that I'm missing?
EDIT
Here's an example of the input:
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head></head>
<body>
<span class="pageStart"/>
<p>...</p>
<div>...</div>
<img />
<p></p>
<span class="pageStart"/>
<div>...</div>
<span class="pageStart"/>
<p>...</p>
<div>
<span class="pageStart"/>
<p>...</p>
<p>...</p>
<span class="pageStart"/>
<div>...</div>
<img/>
</div>
</body>
</html>
I assume the last two nested pages make this problem more difficult, so I'd be perfectly happy getting this as the output, or something close:
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head></head>
<body>
<page>
<span class="pageStart"/>
<p>...</p>
<div>...</div>
<img />
<p></p>
</page>
<page>
<span class="pageStart"/>
<div>...</div>
</page>
<page>
<span class="pageStart"/>
<p>...</p>
<div>
<page>
<span class="pageStart"/>
<p>...</p>
<p>...</p>
</page>
<page>
<span class="pageStart"/>
<div>...</div>
<img/>
</page>
</div>
</page>
</body>
</html>
This transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[span/#class='pageStart']">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each-group select="node()"
group-starting-with="span[#class='pageStart']">
<page>
<xsl:apply-templates select="current-group()"/>
</page>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<html>
<head></head>
<body>
<span class="pageStart"/>
<p>...</p>
<div>...</div>
<img />
<p></p>
<span class="pageStart"/>
<div>...</div>
<span class="pageStart"/>
<p>...</p>
<div>
<span class="pageStart"/>
<p>...</p>
<p>...</p>
<span class="pageStart"/>
<div>...</div>
<img/>
</div>
</body>
</html>
produces the wanted, correct result:
<html>
<head/>
<body>
<page>
<span class="pageStart"/>
<p>...</p>
<div>...</div>
<img/>
<p/>
</page>
<page>
<span class="pageStart"/>
<div>...</div>
</page>
<page>
<span class="pageStart"/>
<p>...</p>
<div>
<page>
<span class="pageStart"/>
<p>...</p>
<p>...</p>
</page>
<page>
<span class="pageStart"/>
<div>...</div>
<img/>
</page>
</div>
</page>
</body>
</html>

Grouping of similer nodes

I have follwing problem in grouping the similer nodes with xsl:
input:
<?xml version="1.0" encoding="UTF-8"?>
<concept id="ads">
<p>para1 content goes here</p>
<Bullet>1</Bullet>
<Bullet>2</Bullet>
<Bullet>3</Bullet>
<p>para2 content goes here</p>
<Bullet>4</Bullet>
<Bullet>5</Bullet>
<p>para2 content goes here</p>
</concept>
Output should be:
<?xml version="1.0" encoding="UTF-8"?>
<concept id="ads">
<p>para1 content goes here</p>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<p>para2 content goes here</p>
<ul>
<li>4</li>
<li>5</li>
</ul>
<p>para2 content goes here</p>
</concept>
All the "Bullet" element which does not have imideate preceding sibling as "Bullet" should wrap up in "UL" and "Li" elements.
I am trying something like this but not able to achive the result:
<xsl:for-each select="Bullet[not(preceding-sibling::Bullet)]">
<ul>
<xsl:copy-of select="."/>
<xsl:variable name="current-name" select="generate-id(.)"/>
<xsl:for-each select="following-sibling::*[Bullet][generate-id(preceding-sibling::*[Bullet]) = $current-name]">
<xsl:copy-of select="."/>
</xsl:for-each>
</ul>
</xsl:for-each>
Please help.
---Related Question ----
This works fine on given xml input, but
1. In my actual xml these <Bullet> tags can appear anywhere under concept node, so grouping with <p> element does not work in that case.
Currently I am processing all the nodes except "Bullet" nodes, so i need to match only <Bullet> nodes which is having immediate following sibling as <Bullet> and wrap the sequence in <ul> and each <Bullet> in <li>. My current context node is <concept>.
Current stylesheet as follows:
nodes which should render at the same place as in input.-->
Actual XML pattern:
<?xml version="1.0" encoding="UTF-8"?>
<concept id="ads">
<p>para1 content goes here</p>
<Body-text>Body1 content goes here</Body-text>
<Bullet>1</Bullet>
<Bullet>2</Bullet>
<Bullet>3</Bullet>
<Body-text>Body2 content goes here</Body-text>
<p>para2 content goes here</p>
<Bullet>4</Bullet>
<Bullet>5</Bullet>
<p>para3 content goes here</p>
<Bullet>6</Bullet>
<Bullet>7</Bullet>
<Body-text>Body2 content goes here</Body-text>
<Bullet>6</Bullet>
<Bullet>7</Bullet>
</concept>
<concept id="ads">
<p>para1 content goes here</p>
<Body-text>Body1 content goes here</Body-text>
<Bullet>1</Bullet>
<Bullet>2</Bullet>
<Bullet>3</Bullet>
<Body-text>Body2 content goes here</Body-text>
<p>para2 content goes here</p>
<Bullet>4</Bullet>
<Bullet>5</Bullet>
<p>para3 content goes here</p>
<Bullet>6</Bullet>
<Bullet>7</Bullet>
<Body-text>Body2 content goes here</Body-text>
<Bullet>6</Bullet>
<Bullet>7</Bullet>
</concept>
This stylesheet:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()[1]|#*"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="Bullet[preceding-sibling::node()[1]
[not(self::Bullet)]]">
<ul>
<xsl:call-template name="makeLi"/>
</ul>
<xsl:apply-templates select="following-sibling::node()
[not(self::Bullet)][1]"/>
</xsl:template>
<xsl:template match="Bullet" name="makeLi">
<li>
<xsl:value-of select="."/>
</li>
<xsl:apply-templates select="following-sibling::node()[1]
[self::Bullet]"/>
</xsl:template>
</xsl:stylesheet>
EDIT: Just changing the following applying "first Bullet" rule to first next not Bullet instead of p.
Output (wrapping your input with root element to be wellformed):
<concept id="ads">
<p>para1 content goes here</p>
<Body-text>Body1 content goes here</Body-text>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<Body-text>Body2 content goes here</Body-text>
<p>para2 content goes here</p>
<ul>
<li>4</li>
<li>5</li>
</ul>
<p>para3 content goes here</p>
<ul>
<li>6</li>
<li>7</li>
</ul>
<Body-text>Body2 content goes here</Body-text>
<ul>
<li>6</li>
<li>7</li>
</ul>
</concept>
<concept id="ads">
<p>para1 content goes here</p>
<Body-text>Body1 content goes here</Body-text>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<Body-text>Body2 content goes here</Body-text>
<p>para2 content goes here</p>
<ul>
<li>4</li>
<li>5</li>
</ul>
<p>para3 content goes here</p>
<ul>
<li>6</li>
<li>7</li>
</ul>
<Body-text>Body2 content goes here</Body-text>
<ul>
<li>6</li>
<li>7</li>
</ul>
</concept>
Note: Fine grained traversal.
With grouping, this stylesheet:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="concept">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each-group select="*"
group-adjacent="boolean(self::Bullet)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<ul>
<xsl:apply-templates select="current-group()"/>
</ul>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="Bullet">
<li>
<xsl:value-of select="."/>
</li>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
EDIT: Ussing group-adjacent.
This XSLT 2.0 transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each-group select="*" group-starting-with="p">
<xsl:copy-of select="current-group()[1]"/>
<xsl:if test="current-group()[2]">
<ul>
<xsl:apply-templates select="current-group()[position() gt 1]"/>
</ul>
</xsl:if>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="Bullet">
<li><xsl:value-of select="."/></li>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<concept id="ads">
<p>para1 content goes here</p>
<Bullet>1</Bullet>
<Bullet>2</Bullet>
<Bullet>3</Bullet>
<p>para2 content goes here</p>
<Bullet>4</Bullet>
<Bullet>5</Bullet>
<p>para2 content goes here</p>
</concept>
produces the wanted, correct result:
<concept id="ads">
<p>para1 content goes here</p>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<p>para2 content goes here</p>
<ul>
<li>4</li>
<li>5</li>
</ul>
<p>para2 content goes here</p>
</concept>
Do note: The use of <xsl:for-each-group> with the group-starting-with attribute and the current-group() function.

Resources