• Get the Flash Player to see the slideshow.
  • Categories

  • Authors

  • Great Quotes

    Once we accept our limits, we go beyond them. — Albert Einstein

Get in touch...

To have a chat about
your CMS needs...

Call us 0207 193 2014
or
Email us on

Author Archive

Read a Single XML File in Java

Monday, March 2nd, 2009

The following should complement the supplied article for reading XML files (DCRs) based on a directory.  This will read the contents of a singe file

package com.littleforest.examples;
 
import com.interwoven.livesite.dom4j.Dom4jUtils;
import com.interwoven.livesite.external.ParameterHash;
import com.interwoven.livesite.runtime.RequestContext;
import com.interwoven.livesite.external.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import org.dom4j.*;
import java.util.Properties; /* Added for proxy use */
import java.io.IOException;
import com.interwoven.livesite.file.FileDALIfc; /* Added to avoid use of CDATA write */
 
public class xmlDcrUtilities{
 
public Document readSingle(RequestContext context)
{
//look in any single directory only
Document doc = Dom4jUtils.newDocument();
Element fileElem = doc.addElement("Records");
 
FileDALIfc fileDAL = context.getFileDal();
 
String absoluteFilePath = context.getParameterString("DCRPath");
 
Document fileDCR = Dom4jUtils.newDocument(fileDAL.read(absoluteFilePath))  ;
fileElem.add(fileDCR.getRootElement().createCopy())  ;
 
return doc;
}
 
}

Read List of XML Files from Directory Tree in Java

Monday, March 2nd, 2009

This is a TeamSite 6.7.2/LiveSite 3.1 compliant mechanism that can be used to read all DCR files into a single XML files for further processing:

 
package com.littleforest.examples;
 
import com.interwoven.livesite.dom4j.Dom4jUtils;
import com.interwoven.livesite.external.ParameterHash;
import com.interwoven.livesite.runtime.RequestContext;
import com.interwoven.livesite.file.FileDALIfc;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import org.dom4j.*;
import java.util.Properties; /* Added for proxy use */
import com.interwoven.livesite.external.*;
import java.io.IOException;
 
public class xmlFileListContents {
 
public Document fetchTree(RequestContext context)
{
//look in ALL directories
Document doc = Dom4jUtils.newDocument();
Element fileElem = doc.addElement("Records");
 
FileDALIfc fileDAL = context.getFileDal();
 
String areaRelativeBasePath = context.getParameterString("BaseDirectory");
 
String absoluteBasePath = fileDAL.getRoot() + fileDAL.getSeparator() + areaRelativeBasePath;
 
//recurse directories
String[] directories = fileDAL.getChildDirectories(absoluteBasePath);
int dirCount = directories.length;
 
for(int k=0;k<dirCount;k++)
{
String absoluteDirPath = absoluteBasePath + fileDAL.getSeparator() + directories[k];
 
//recurse files in directories
String[] files = fileDAL.getChildFiles(absoluteDirPath);
int count = files.length;
 
for(int j=0;j<count;j++)
{
String absoluteFilePath = absoluteDirPath + fileDAL.getSeparator() + files[j];
Document fileDCR = Dom4jUtils.newDocument(fileDAL.read(absoluteFilePath))  ;
 
//add path as an attribute
fileDCR.getRootElement().addAttribute("path",absoluteFilePath);
// add the dcr to the result doc;
fileElem.add(fileDCR.getRootElement().createCopy())  ;
//optionally, may prefer path as an element
//fileElem.addElement("../path").addText(absoluteFilePath);
}
 
}
 
//ensure we have those in the Base too
String[] baseFiles = fileDAL.getChildFiles(absoluteBasePath);
int fCount = baseFiles.length;
 
for(int i=0;i<fCount;i++)
{
String absoluteFilePath = absoluteBasePath + fileDAL.getSeparator() + baseFiles[i];
Document fileDCR = Dom4jUtils.newDocument(fileDAL.read(absoluteFilePath))  ;
 
//add path as an attribute
fileDCR.getRootElement().addAttribute("path",absoluteFilePath);
// add the dcr to the result doc;
fileElem.add(fileDCR.getRootElement().createCopy())  ;
//optionally, may prefer path as an element
//fileElem.addElement("../path").addText(absoluteFilePath);
}
 
return doc;
}
 
}

XSL:Sort by Date, Organise by Type

Monday, March 2nd, 2009

In this example, we have a collection of material which we want to make available to users: whitepapers, articles, tips etc.  We want to group these by material and show the relevant subheading when this changes, but avoid redisplaying the heading if it remains the same, say:

Whitepaper

Article

As per other examples, the key to doing this is to ensure we have the collection sorted correctly before we consider displaying them:

<xsl:apply-templates select="[list of items being processed]">
<xsl:sort select="./type"/>
<xsl:sort order="descending" select="substring(./created,7,4)"/>
<xsl:sort order="descending" select="substring(./created,4,2)"/>
<xsl:sort order="descending" select="substring(./created,1,2)"/>
</xsl:apply-templates>

At this point we have a set of items in ascending alphabetical types, reverse ordered by date.

We then call the familiar block to begin processing.

There is now some logic required here to say, if repeat type do not display, if a new type display.

In a scripting language, the obvious solution is to store the type in a variable, and do something if the new type differs from the last stored type.

Restrictions on <xsl:variable> mean we cannot do this.

Loosely, in XSLT position() translates to count [forwards].  XLST has a function that is similar to ‘count backwards’ which is preceding-sibling [OK, it means invert the node set and count forwards, but you get the idea].

Therefore by saying preceding-sibling::*[1]/type XPath expression say “go to the first preceding sibling, then get its type element’s contents” and the expression provides a ready means of testing “is the current value the same as the last one”.

It is worth pointing out that this is a sophisticated function, there are similar ones such as following-sibling, preceding, ancestor, or ancestor-or-self which may be more appropriate to the use required.

It is also important to note the use of ‘*[1]/type’.  The * is a wildcard and this would not work if there were multiple child elements type within the given element.  The [1] is important too as this means go-back-one: or, more accurately, reverse the list and read the next … If the [1] was omited, the expression would say, look backwards anywhere in the list.

Finally, reading backwards would return a null value if the top of the list was reached, so a failsafe is required to make sure we are not at the top of the list.

<xsl:choose>
<xsl:when test="position() = 1">
<!-- display the type-->
<xsl:value-of select="./type"/>
</xsl:when>
<xsl:when test="position() &gt; 1">
<!-- only read backwards now we have read at least one record -->
<xsl:if test="./type != preceding-sibling::*[1]/type">
<!-- only if the type has changed-->
<xsl:value-of select="./type"/>
</xsl:if>
</xsl:when>
</xsl:choose>

XSL:Sort By Date, Limit Returned Results

Monday, March 2nd, 2009

In an accompanying article, a simple method to sort by date was introduced.

Normally it is desirable to limit the number of results shown.

There are various complex ways of doing this posted on other sites, the fundamental problem being that <xsl:variable> works as a constant in practice, i.e set only once.

Trying to implement a counter as per scripting languages becomes difficult – although not impossible – so rather than fight against the restrictions XSLT gives, better to use the inbuilt position() function, which will tell you how far it has progressed along the current record set.

Provided all the select criteria have been applied to the record set FIRST, the position() function will effectively map to counter.

To complete the process, set a global variable (probably based on a Datum) to fix the displayed items

<xsl:variable name="x-rows" select="//Datum[@Name='FixLimit']"/>
 
<xsl:template match="/">
 
<xsl:apply-templates select=”[some repeating datum]“&gt;
<xsl:sort order=”descending” select=”substring(//somedate,7,4)> <!-- year->
<xsl:sort order=”descending” select=”substring(//somedate,4,2)> <!-- month-->
<xsl:sort order=”descending” select=”substring(//somedate,1,2)> <-- day-->
 
</xsl:apply-templates>

Now in the add a ‘count’ test for each time the executes:

<xsl:template match=”[match repeating datum]>
   <xsl:if test="position() &lt;= $x-rows">
   ...
   </xsl:if>
</xsl:template>

to stop processing once the row limit has been reached.

XSL:Sort By Date

Monday, March 2nd, 2009

The following  should do the work

<xsl:apply-templates select="[some repeating datum]">
<xsl:sort order="descending" select="substring(//somedate,7,4)"> <!-- year-->
<xsl:sort order="descending" select="substring(//somedate,4,2)"> <!-- month-->
<xsl:sort order="descending" select="substring(//somedate,1,2)"> <!-- day-->
</xsl:apply-templates>

This assumes the date is in the form dd/mm/yyyy: the seperators could be different as they are ignored anyway based on the substring.

The important thing to realise here is that the <xsl:sort> functions do not do anything APART from sort, so there is nothing to see at this point if debugging

The work is done in a receiving

<xsl:template match="[match repeating datum]">
 
...
 
</xsl:template>

The ensures items are delivered to this block in the correct order, what is displayed is determined in the section

XSL Extract File Suffix, Convert to Uppercase, Display

Monday, March 2nd, 2009
<xsl:value-of
select="translate(substring-after(/Properties/Data/Datum[@Name='filename'],'.'),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>

LiveSite XSL Reference II

Friday, November 28th, 2008

Nested Quotations with XSL

It is not unusual to come across a situation where nested quotations fail (are exceeded) in an apperance XSL document.

Say I want to tie a Javascript function to an input control, but I want to pass the value of a datum-type as an argument to the funtion.

Therefore I want to describe an attribute value something of the order “JavaScript:handleJSFunction (’//Datum[@Name="someDatum"]‘);”

In practice this will fail as the nested quotes will not resolve (and CDATA will not work either, it will generate errors).

The solution is to take the theoretical expression:

<input type=”checkbox” name=”cb_boolean1″ value=”false” onClick=”JavaScript:handleJSFunction (’//Datum[@Name="someDatum"]‘);”/>

and break out the expression using <xsl:attribute/> tags, then using <xsl:text/> together with <xsl:value-of/> to concentatate the attribute string value with the required Datum value (s), thus avoiding interpolation errors::

            <input>
               <xsl:attribute name=”type”><xsl:text>checkbox</xsl:text></xsl:attribute>
               <xsl:attribute name=”name”><xsl:text>cb_boolean1</xsl:text></xsl:attribute>
               <xsl:attribute name=”value”><xsl:text>false</xsl:text></xsl:attribute>
               <xsl:attribute name=”onClick”>
                 <xsl:text>JavaScript:handleJSFunction(’</xsl:text>
                 <xsl:value-of select=”//Datum[@Name='someDatum']“/>
                 <xsl:text>’);</xsl:text>
               </xsl:attribute>

       </input>