xsl-list
[Top] [All Lists]

URIResolver howto

2002-10-01 07:43:44
Hi, [I sent this with the wrong email address, sorry if it comes through twice]

I recieved a few requests for a better example of how to use the URIResolver. I
hope the information below helps.

When you need control over xsl:include/import or the document(), you need to use
an URIResolver - you cannot do it in your stylesheet. This is very useful when
you have a 'primary' XSL[1]  and it includes, imports or uses document() to
bring in other XML/XSL files/streams from two or more different points of
control.

As a simple example lets say a user logs on to a server-side application. The
user can choose different navigation styles to get around a site (dynamic JS or
static HTML). You would need to somehow get or keep a user preference that tells
the app which nav option they have choosen.

We might have a primary XSLT like:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
<xsl:include href="head.xsl"/>
<xsl:include href="banner.xsl"/>
<xsl:include href="nav.xsl"/>
<xsl:include href="footer.xsl"/>
<xsl:template match="/">
<html>
  <xsl:call-template name="head"/>
  <body>
    <xsl:call-template name="banner"/>
    <div id="leftcol">
      <xsl:call-template name="nav"/>
    </div>
    <div id="centercol">
       call some templates
    </div>
    <div id="rightcol">
      <div class="floater">
        call some templates
      </div>
    </div>
    <br clear="all"/>
    <xsl:call-template name="footer"/>
  </body>
</html>
</xsl:template>
</xsl:stylesheet>

There are two different sets of the head and nav and to keep things modular. So
you might use static_head.xsl & static_nav.xsl or dynamic_head.xsl &
dynamic_nav.xsl. You need an URIResolver to handle which set to include.

You may have a method that starts transformations in the app:

void xform(ServletContext servlet_context, HttpServletRequest req,
HttpServletResponse res, long _start_time)
        throws TransformerException, java.io.IOException {

  System.setProperty(
    "javax.xml.transform.TransformerFactory",
    "com.icl.saxon.TransformerFactoryImpl");
  ServletOutputStream out = res.getOutputStream();
  HttpSession http_session = req.getSession();
  String nav_style = http_session.getAttribute("nav_style").toString();
  String source = http_session.getAttribute("source").toString();
  String style = http_session.getAttribute("style").toString();
  try {
      Templates pss = tryCache(servlet_context, style, nav_style);
      Transformer transformer = pss.newTransformer();
      Properties details = pss.getOutputProperties();
      String mime =
pss.getOutputProperties().getProperty(OutputKeys.MEDIA_TYPE);
      if (mime==null) {
        res.setContentType("text/html");
      } else {
        res.setContentType(mime);
      }
      transformer.setParameter("nav_style", nav_style);
      transformer.transform(
          new StreamSource(source)),
          new StreamResult(out));
      out.close();
      out.flush();
    } catch (Exception err) {
      System.out.println(err);
    }
}

Your tryCache method might look like:

private synchronized Templates tryCache(ServletContext serv_context, String
path, String nav_style)
    throws TransformerException, java.io.IOException {
  String full_path = serv_context.getRealPath(path);
  Templates x = (Templates)this.cache.get(path);
  if (x==null) {
      TransformerFactory factory = TransformerFactory.newInstance();
      factory.setURIResolver(new MyResolver(serv_context, nav_style));
      x = factory.newTemplates(new StreamSource(new File(full_path)));
      this.cache.put(path, x);
  }
  return x;
}

This primary XSLT needs to find files identified in the xsl:includes/imports and
document(). The following URIResolver simply checks the href argument
automatically sent in by the transformation process. The href argument is the
value of the href in the xsl:include/import or the first (or only) argument from
the document function. The base argument is either the location of the primary
XSLT or the second argument in the document(). If it sees head.xsl or nav.xsl it
prepends the filename with the nav style preference string:

class MyResolver implements URIResolver {
  String base_path;
  String nav_style;
  public MyResolver(ServletContext context, String style) {
    this.base_path = context.getRealPath("/WEB-INF/styling/");
    this.nav_style = style;
  }

  public Source resolve(String href,String base) {
    StringBuffer path = new StringBuffer(this.base_path);
    if (href.equals("head.xsl") |
         href.equals("nav.xsl")) {
      path.append(this.nav_style);
      path.append("_");
    }
    path.append(href);
    File file = new File(path.toString());
    if(file.exists()) return new StreamSource(file);
    return null;
  }
}


best,
Rob Koberg
livestoryboard.com

[1] the XSL used as the argument in the transformation against the main source
XML



 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



<Prev in Thread] Current Thread [Next in Thread>