commit df659215b05dcb9c6bbfc124ed8295e9f76a5744 parent 438ba4c348fa7c7768e69ad312ae0d8b4b3462dc Author: Dan Callaghan <djc@djc.id.au> Date: Sat, 7 Aug 2010 16:33:59 +1000 using sphinx instead of mvn site Diffstat:
A | src/doc/sphinx/_static/default.css | | | 88 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/doc/sphinx/_templates/layout.html | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/doc/sphinx/_templates/localtoc.html | | | 6 | ++++++ |
A | src/doc/sphinx/_templates/searchbox.html | | | 12 | ++++++++++++ |
A | src/doc/sphinx/conf.py | | | 175 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/doc/sphinx/example-graph.rst.inc | | | 20 | ++++++++++++++++++++ |
A | src/doc/sphinx/index.rst | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/doc/sphinx/selector.rst | | | 296 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/doc/sphinx/spring.rst | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
A | src/doc/sphinx/xml-template.rst | | | 269 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/site/site.xml | | | 8 | -------- |
11 files changed, 1036 insertions(+), 8 deletions(-) diff --git a/src/doc/sphinx/_static/default.css b/src/doc/sphinx/_static/default.css @@ -0,0 +1,88 @@ +@import url("http://fonts.googleapis.com/css?family=Crimson+Text"); +@import url("http://fonts.googleapis.com/css?family=Inconsolata"); +@import url("http://fonts.googleapis.com/css?family=Nobile"); + +body { + font-family: sans-serif; + font-size: 0.9em; + line-height: 2.75ex; +} +img { + border: none; +} + +div.document { + margin: 0 auto; + max-width: 95ex; +} +div.body { + margin-right: 27ex; +} +div.sidebar { + font-size: 0.9em; + width: 27ex; + margin-left: 80ex; + position: fixed; +} + +dt { + margin-top: 1.5em; +} +dd { + margin-left: 1.5em; +} + +pre { + background-color: #ffffd0; + padding: 0.5em 1em; + overflow: auto; +} + +tt.xref { + font-weight: bold; +} +tt.descname { + font-weight: bold; + font-size: 1.2em; +} +dt:target, .highlighted { + background-color: #fbe54e; +} + +div.sidebar h3 { + margin: 1em 0 0 0; +} +div.sidebar div.toc ul { + margin: 0; + padding: 0; +} +div.sidebar div.toc ul li { + margin: 0 0 0 1.25em; + padding: 0.5em 0 0 0; + line-height: 1.2em; +} +div.sidebar input[type=text] { + width: 17ex; +} + +a.headerlink { + color: #aa0000; + text-decoration: none; + visibility: hidden; + padding-left: 0.35em; +} +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + +div.admonition { +} +p.admonition-title { + font-weight: bold; +} diff --git a/src/doc/sphinx/_templates/layout.html b/src/doc/sphinx/_templates/layout.html @@ -0,0 +1,70 @@ +{%- block doctype -%} +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +{%- endblock %} +{%- set url_root = pathto('', 1) %} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} + +{%- macro sidebar() %} + <div class="sidebar"> + {%- if sidebars != None %} + {#- new style sidebar: explicitly include/exclude templates #} + {%- for sidebartemplate in sidebars %} + {%- include sidebartemplate %} + {%- endfor %} + {%- endif %} + </div> +{%- endmacro %} + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" /> + {{ metatags }} + {%- block htmltitle %} + <title>{{ title|striptags|e }}</title> + {%- endblock %} + <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" /> + <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" /> + {%- for cssfile in css_files %} + <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" /> + {%- endfor %} + <script type="text/javascript"> + var DOCUMENTATION_OPTIONS = { + URL_ROOT: '{{ url_root }}', + VERSION: '{{ release|e }}', + COLLAPSE_INDEX: false, + FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}', + HAS_SOURCE: {{ has_source|lower }} + }; + </script> + {%- for scriptfile in script_files %} + <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script> + {%- endfor %} +{%- block linktags %} + <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" /> + <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" /> +{%- endblock %} +{%- block extrahead %} {% endblock %} + </head> + <body> +{%- block header %}{% endblock %} + +{%- block content %} + + <div class="document"> + {{ sidebar() }} + + {%- block document %} + <div class="body"> + {%- if pagename != 'index' %} + <a href="./">{{ shorttitle|e }}</a> » + {%- endif %} + + {% block body %} {% endblock %} + </div> + {%- endblock %} + + </div> +{%- endblock %} + + </body> +</html> diff --git a/src/doc/sphinx/_templates/localtoc.html b/src/doc/sphinx/_templates/localtoc.html @@ -0,0 +1,6 @@ +{%- if display_toc %} + <div class="toc"> + <h3>Contents</h3> + {{ toc }} + </div> +{%- endif %} diff --git a/src/doc/sphinx/_templates/searchbox.html b/src/doc/sphinx/_templates/searchbox.html @@ -0,0 +1,12 @@ +{%- if pagename != "search" %} +<div id="searchbox" style="display: none"> + <h3>{{ _('Quick search') }}</h3> + <form class="search" action="{{ pathto('search') }}" method="get"> + <input type="text" name="q" size="18" /> + <input type="submit" value="{{ _('Go') }}" /> + <input type="hidden" name="check_keywords" value="yes" /> + <input type="hidden" name="area" value="default" /> + </form> +</div> +<script type="text/javascript">$('#searchbox').show(0);</script> +{%- endif %} diff --git a/src/doc/sphinx/conf.py b/src/doc/sphinx/conf.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +# +# rdftemplate documentation build configuration file, created by +# sphinx-quickstart on Wed Jul 28 08:11:55 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, '/home/dan/miskinhill/sphinx-contrib/javadomain') +sys.path.insert(0, '/home/dan/miskinhill/sphinx-contrib/javadocref') + +import lxml.etree +pom = lxml.etree.parse(open(os.path.join('..', '..', '..', 'pom.xml'))) +def pom_xpath(expr): + return lxml.etree.XPath(expr, namespaces={'pom': 'http://maven.apache.org/POM/4.0.0'})(pom) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.todo', 'sphinx.ext.viewcode', 'sphinx.ext.graphviz', + 'sphinxcontrib.javadomain', 'sphinxcontrib.javadocref'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = pom_xpath('//pom:project/pom:name/text()')[0] +copyright = u'2010, Dan Callaghan' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = pom_xpath('//pom:project/pom:version/text()')[0] +# The full version, including alpha/beta/rc tags. +release = pom_xpath('//pom:project/pom:version/text()')[0] + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +highlight_language = 'java' + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'basic' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +html_style = 'default.css' + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +html_title = project + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +html_use_smartypants = False + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + 'index': ['searchbox.html'], + 'selector': ['localtoc.html', 'searchbox.html'], + 'xml-template': ['localtoc.html', 'searchbox.html'], + 'spring': ['localtoc.html', 'searchbox.html'], +} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +html_domain_indices = True + +# If false, no index is generated. +html_use_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +todo_include_todos = True +nitpicky = True +javadocref_locations = ['http://code.djc.id.au/rdftemplate/javadoc/latest/', + 'http://jena.sourceforge.net/javadoc/', + 'http://download-llnw.oracle.com/javase/6/docs/api/'] diff --git a/src/doc/sphinx/example-graph.rst.inc b/src/doc/sphinx/example-graph.rst.inc @@ -0,0 +1,20 @@ + +.. digraph:: example + + node [fontname = "Liberation Sans", fontsize = 12]; + edge [fontname = "Liberation Sans", fontsize = 11]; + + bob [shape = ellipse, style = filled, color = palegreen, label = "<bob>"]; + alice [shape = ellipse, style = filled, color = palegreen, label = "<alice>"]; + carol [shape = ellipse, style = filled, color = palegreen, label = "<carol>"]; + + bob_name [shape = box, style = filled, color = peachpuff, label = "\"Bob\"" ]; + alice_name [shape = box, style = filled, color = peachpuff, label = "\"Alice\"" ]; + carol_name [shape = box, style = filled, color = peachpuff, label = "\"Carol\"" ]; + + bob -> bob_name [label = "foaf:name"]; + alice -> alice_name [label = "foaf:name"]; + carol -> carol_name [label = "foaf:name"]; + + bob -> alice [label = "foaf:knows"] ; + bob -> carol [label = "foaf:knows"] ; diff --git a/src/doc/sphinx/index.rst b/src/doc/sphinx/index.rst @@ -0,0 +1,57 @@ +rdftemplate +=========== + +Rdftemplate is a library for generating XML documents from RDF data using +templates. + +The library supports evaluation of “:doc:`selector expressions <selector>`”, which use an +XPath-inspired syntax for selecting an ordered set of nodes from an arbitrary +starting context node. + +The library also supports rendering XML from :doc:`templates <xml-template>`. The template +interpolator recognises a number of Genshi-inspired template directives, which +are used to insert the result of a selector expression into the generated +XML. + +The library uses the Jena RDF model API. +It also includes optional support for :doc:`integrating with Spring +<spring>`, allowing templates to be used as Spring Web MVC views. + +Rdftemplate was developed for the `Miskin Hill`_ web site, where it is used to +generate output in various XML-based formats. You can view the `templates used +for Miskin Hill <http://code.miskinhill.com.au/hg/miskinhill-master/file/tip/web/src/main/resources/au/com/miskinhill/rdf/template/>`_ +to see some examples of how rdftemplate works. + +.. _Miskin Hill: http://miskinhill.com.au/ + +Development +----------- + +* `Javadoc <http://code.djc.id.au/rdftemplate/javadoc/latest/>`_ +* `Mercurial repository <http://code.djc.id.au/hg/rdftemplate/>`_ +* `Hudson build <http://hudson.miskinhill.com.au/job/rdftemplate/>`_ +* Send bugs and suggestions to `Dan C <mailto:djc@djc.id.au>`_ + +Quick start with Maven +---------------------- + +Add the following to your pom.xml: + +.. code-block:: xml + + <repositories> + <repository> + <id>code.djc.id.au</id> + <url>http://code.djc.id.au/maven2/</url> + </repository> + </repositories> + + <dependencies> + <dependency> + <groupId>au.id.djc</groupId> + <artifactId>rdftemplate</artifactId> + <version>1.1</version> + </dependency> + </dependencies> + +.. todo:: more examples, like calling TemplateInterpolator or using Spring views diff --git a/src/doc/sphinx/selector.rst b/src/doc/sphinx/selector.rst @@ -0,0 +1,296 @@ +Selector expressions +==================== + +Selector expressions are a concise way of selecting some set of nodes in an RDF +graph, given a particular starting node. For example, given the graph: + +.. include:: example-graph.rst.inc + +evaluating the selector expression ``foaf:knows/foaf:name`` with a starting node of +``<bob>`` would yield two literal nodes: ``"Alice"`` and ``"Carol"``. + +The syntax for selector expressions was inspired by `XPath`_ syntax, so if you +are familiar with XPath you will notice many similarities. + +.. todo:: make graphviz output prettier + +.. _XPath: http://www.w3.org/TR/xpath/ + +Syntax +------ + +Traversing +~~~~~~~~~~ + +At the heart of selector expressions is the notion of traversing a path through +the RDF graph along properties, in the same way XPath can traverse a document +tree. Selector expressions are always evaluated with respect to a context node +in the graph, which is the starting point for traversals. + +The simplest traversal consists of a single RDF property, such as +``foaf:knows``. This expression selects all nodes which are the object of +a foaf:knows predicate where the starting node is the subject. In other words, +it can be considered equivalent to the SPARQL query: + +.. code-block:: none + + SELECT ?o + WHERE { ?start foaf:knows ?o . } + +(Actually the *simplest* traversal is the empty string, which always evaluates +to the starting node. This is really only meaningful when used with other +syntax elements described below.) + +If we used ``<bob>`` in the example graph above as our starting node, the +expression ``foaf:knows`` would evaluate to two resource nodes: ``<alice>`` and +``<carol>``. In general a selector expression may yield zero or more results. +For example, if we used ``<alice>`` as a starting node, the result would be +empty. + +Multiple traversals may be chained together using ``/`` as a separator, as in +``foaf:knows/foaf:name``. + +Note that property URIs are always given in their prefixed form. In order to +keep the syntax simple, there is no way to specify a complete URI reference in +a selector expression. + +Inverse traversal +~~~~~~~~~~~~~~~~~ + +The direction of a property traversal can be inverted by prepending ``!`` to +the property name. For example, given some article as a starting node, the +expression ``dc:creator/!dc:creator/dc:title`` might be used to select the +title of all articles written by the authors of the starting node. + +.. _predicates: + +Predicates +~~~~~~~~~~ + +The set of nodes resulting from a traversal can be filtered with a predicate. +The predicate is given in square brackets (``[]``) following the property name. +Predicates may appear at any point in the chain of traversals. + +The following predicates are supported: + +``type`` + Includes only nodes of the given type. Use it like this: + ``!dc:creator[type=bibo:Article]``. + +``uri-prefix`` + Includes only resource nodes whose URI begins with the given string. Use it + like this: ``dc:identifier[uri-prefix='urn:issn:']``. + +Multiple predicates may be applied by joining them together with the ``and`` +keyword, as in ``!dc:creator[type=bibo:Article and uri-prefix='http://example.com/']``. + +Custom predicates may be defined at runtime by supplying a custom +:java:class:`PredicateResolver` implementation. + +.. _adaptations: + +Adapting the result +~~~~~~~~~~~~~~~~~~~ + +The result of evaluating a traversal is zero or more RDF nodes (in Java, +implementations of Jena’s :java:class:`RDFNode +<com.hp.hpl.jena.rdf.model.RDFNode>` interface). However, it is often necessary +to convert these RDF nodes into a more useful data type, or to perform some +post-processing on them. + +An adaptation is a function which takes an RDF node and “adapts” it in some +way. An adaptation can be specified at the end of a selector expression, +preceded by ``#`` and optionally followed by an argument list. For example, the +expression ``foaf:knows#uri`` would evaluate to the URIs of the people known to +the starting node. The distinction here is important: whereas ``foaf:knows`` +evaluates to zero or more :java:class:`RDFNodes +<com.hp.hpl.jena.rdf.model.RDFNode>`, ``foaf:knows#uri`` evaluates to zero or +more :java:class:`Strings <java.lang.String>` giving the URI of each node. + +The following adaptations are supported: + +``uri`` + Returns the URI of the RDF node as a :java:class:`String + <java.lang.String>`. Throws an exception if applied to a node which is not + a resource. + +``uri-slice`` + Returns a substring of the URI. This adaptation takes a single integer + argument specifying the number of characters to be removed. Use it like + this: ``dc:identifier[uri-prefix='urn:issn:']#uri-slice(9)``. + +``uri-anchor`` + Returns the anchor part of the URI, excluding the # character. Returns + empty string if there is no anchor part. + +``lv`` + Short for “literal value”. Returns the value of the literal RDF node, + converted to a Java object using Jena’s type conversion facilities (see + :java:method:`Literal#getValue() + <com.hp.hpl.jena.rdf.model.Literal#getValue()>`). Throws an exception if + applied to a node which is not a literal. + +``comparable-lv`` + Essentially the same as ``lv``, but with a runtime check to ensure the + literal value implements :java:class:`Comparable <java.lang.Comparable>`. + Only exists for type-safety reasons. + +``string-lv`` + Like ``lv``, but additionally calls toString() on the resulting object to + ensure it is always a String. This adaptation also strips all tags from XML + literals. + +``formatted-dt`` + Short for “formatted date-time”. This adaptation can only be applied to + literal nodes whose values are represented as Joda datetime types. It takes + a single string argument, specifying the date-time format to apply. Use it + like this: ``dc:created#formatted-dt('d MMMM yyyy')``. + + .. todo:: hacks for Joda are not in stock Jena + +Custom adaptations may be defined at runtime by supplying a custom +:java:class:`AdaptationFactory` implementation. + +Sorting the result +~~~~~~~~~~~~~~~~~~ + +RDF graphs by their nature do not define any ordering, so a selector expression +like ``foaf:knows`` will return its results in arbitrary order. When we expect +the result to contain more than one node, it is often useful to ensure +a predictable (repeatable) ordering of the resulting nodes. + +Sorting can be applied at any point in the chain of traversals, by giving +a sort expression enclosed in parentheses (``()``). The sort expression can be +a complete selector expression (including multiple traversals, nested sorts, +and any other selector features). The set of nodes in the traversal are then +sorted by evaluating the sort expression for each node, and sorting with these +values as keys. The sort expression may optionally be prepended with ``~`` to +indicate a reverse sort. + +For example, given an author as a starting node, +``!dc:creator(dc:title#comparable-lv)`` would evaluate to the works created by +that author, ordered by the title of each work. + +Note that the sort expression must always evaluate to a Java object which +implements :java:class:`Comparable <java.lang.Comparable>`, so it is typically +necessary to apply the ``comparable-lv`` adaptation in the sort expression. + +If one expression is not enough to uniquely sort each item in the result, +multiple sort expressions can be specified using ``,`` to separate them. + +Selecting from many results +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A sort expression may optionally be followed by a subscript ``[n]``, indicating +that only the *n*-th node in the result should be selected. For example, +``!dc:creator(~dc:date)[0]/dc:title`` might be used to select the title of an +author’s most recent work. + +Combining multiple expressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Selector expressions can be chained together using ``|``. The result of the +expression will be the result of each sub-expression chained together in +sequence. For example: ``!dc:creator | !bibo:translator``. + +Evaluating expressions +---------------------- + +The following classes in the au.id.djc.rdftemplate.selector package are +relevant for compiling and evaluating selector expressions: + +.. java:class:: au.id.djc.rdftemplate.selector.Selector<T> + + This interface represents the compiled version of a selector expression. It + is parametrised on the result type of the expression. + + .. java:method:: java.lang.Class<T> getResultType() + + Returns the result type of this selector expression. (This is the runtime + class of the type parameter T.) For a simple traversal this will be + :java:class:`RDFNode <com.hp.hpl.jena.rdf.model.RDFNode>`, or if an + adaptation is applied to the selector expression it will be the result + type of the adaptation (such as :java:class:`String <java.lang.String>` + or :java:class:`Object <java.lang.Object>`). + + .. java:method:: Selector<Other> withResultType(java.lang.Class<Other> otherType) + + A convenience method to cast the type parameter of this Selector. Always + returns this instance. Just a dumb hack to keep Java’s static type + checking happy. + + .. java:method:: java.util.List<T> result(com.hp.hpl.jena.rdf.model.RDFNode node) + + Evaluates this selector expression with respect to the given starting + node, and returns the result. + + .. java:method:: T singleResult(com.hp.hpl.jena.rdf.model.RDFNode node) + + Evaluates this selector expression with respect to the given starting + node, and returns the result. If the selector does not evaluate to + exactly one node, an exception is thrown. + +.. java:class:: au.id.djc.rdftemplate.selector.AntlrSelectorFactory + + Use this class to compile selector expressions into :java:class:`Selector` + instances. Instances of this class can safely be shared across threads (for + example, as singleton beans in Spring). + + .. java:method:: au.id.djc.rdftemplate.selector.Selector<?> get(java.lang.String expression) + + Compiles the given selector expression into a :java:class:`Selector` + instance. + + .. code-block:: java + + Selector<RDFNode> s1 = factory.get("foaf:knows").withResultType(RDFNode.class); + Selector<String> s2 = factory.get("foaf:knows/foaf:name#string-lv").withResultType(String.class); + + .. java:method:: void setAdaptationFactory(au.id.djc.rdftemplate.selector.AdaptationFactory adaptationFactory) + + Configures a custom :java:class:`AdaptationFactory` implementation for + selectors created by this factory. If this setter is not called, an + instance of :java:class:`DefaultAdaptationFactory + <au.id.djc.rdftemplate.selector.DefaultAdaptationFactory>` will be used. + + .. java:method:: void setPredicateResolver(au.id.djc.rdftemplate.selector.PredicateResolver predicateResolver) + + Configures a custom :java:class:`PredicateResolver` implementation for + selectors created by this factory. If this setter is not called, an + instance of :java:class:`DefaultPredicateResolver + <au.id.djc.rdftemplate.selector.DefaultPredicateResolver>` will be used. + + .. java:method:: void setNamespacePrefixMap(java.util.Map<String, String> namespacePrefixMap) + + Configure namespace prefix mappings for selectors created by this + factory. If this setter is not called, no namespace prefixes will be + defined. + +.. java:class:: au.id.djc.rdftemplate.selector.AdaptationFactory + + Implement this interface if you would like to use custom adaptations in your + selector expressions. + + Your implementation should fall back to + a :java:class:`DefaultAdaptationFactory + <au.id.djc.rdftemplate.selector.DefaultAdaptationFactory>` instance, so that + selector expressions have access to the builtin adaptations in addition to + your custom ones. + +.. java:class:: au.id.djc.rdftemplate.selector.PredicateResolver + + Implement this interface if you would like to use custom predicates in your + selector expressions. + + Your implementation should fall back to + a :java:class:`DefaultPredicateResolver + <au.id.djc.rdftemplate.selector.DefaultPredicateResolver>` instance, so that + selector expressions have access to the builtin predicates in addition to + your custom ones. + +.. java:class:: au.id.djc.rdftemplate.selector.EternallyCachingSelectorFactory + + Wrap an :java:class:`AntlrSelectorFactory` with this class if you want to + avoid compiling selectors anew every time. Do not use this class if the + number of different selector expressions is unbounded, as it will cause heap + exhaustion. diff --git a/src/doc/sphinx/spring.rst b/src/doc/sphinx/spring.rst @@ -0,0 +1,43 @@ +Integrating with Spring +======================= + +Evaluating selector expressions +------------------------------- + +You can define a :java:class:`SelectorFactory +<au.id.djc.rdftemplate.selector.SelectorFactory>` bean in your application +context: + +.. code-block:: xml + + <bean id="selectorFactory" class="au.id.djc.rdftemplate.selector.EternallyCachingSelectorFactory"> + <constructor-arg> + <bean class="au.id.djc.rdftemplate.selector.AntlrSelectorFactory"> + <property name="adaptationFactory"> + <bean class="com.example.MyAdaptationFactory" /> + </property> + <property name="predicateResolver"> + <bean class="com.example.MyPredicateResolver" /> + </property> + <property name="namespacePrefixMap"> + <bean class="com.example.MyNamespacePrefixMapper" /> + </property> + </bean> + </constructor-arg> + </bean> + +Rendering templates +------------------- + +Similarly, a :java:class:`TemplateInterpolator` bean can be defined: + +.. code-block:: xml + + <bean class="au.id.djc.rdftemplate.TemplateInterpolator"> + <constructor-arg ref="selectorFactory" /> + </bean> + +Views in Spring Web MVC +----------------------- + +.. todo:: RDFTemplateViewResolver diff --git a/src/doc/sphinx/xml-template.rst b/src/doc/sphinx/xml-template.rst @@ -0,0 +1,269 @@ +XML templates +============= + +Templates are pure XML, with special directives embedded in the document as +attributes and elements. Substitutions are also supported inside character data +and attribute values. + +The template syntax is inspired by the `Genshi`_ templating library for Python. + +.. _Genshi: http://genshi.edgewall.org/ + +XML namespace +------------- + +The XML elements and attributes handled by rdftemplate are defined in +a dedicated namespace: + +.. code-block:: none + + http://code.miskinhill.com.au/rdftemplate/ + +By convention this namespace is mapped to the prefix ``rdf``. For example: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml" + xmlns:rdf="http://code.miskinhill.com.au/rdftemplate/"> + ... + </html> + +All declarations of the rdftemplate namespace are stripped from the resulting +document when a template is rendered. + +Substitutions in character data and attribute values +---------------------------------------------------- + +Templates may contain substitutions embedded in character data or in attribute +values. Substitutions are delimited by ``${`` and ``}`` and contain +a :doc:`selector expression <selector>`, which is evaluated with respect to the +current context node (passed to the :java:class:`TemplateInterpolator` when +rendering the template). + +The selector expression must have exactly one result when evaluated. The type +of the result affects how substitution is performed: + +* If the selector expression evaluates to an + `XML literal node <http://www.w3.org/TR/rdf-concepts/#section-XMLLiteral>`_ + or an instance of :java:class:`XMLStream <au.id.djc.rdftemplate.XMLStream>`, + the entire XML tree is inserted into the resulting document. (This + substitution is only possible in character data; if it occurs in an attribute + value, an exception will be thrown.) + +* If the selector expression evaluates to any other type of literal node, it + will be converted to a Java type using Jena’s type conversion facilities, and + then toString() will be called on the converted object. + +* If any other Java object is encountered, toString() will be called on it. + This means that when a selector expression evaluates to a :java:class:`String + <java.lang.String>`, it will be inserted as-is. + +Consider the following example of a template for describing an article in HTML. +It uses subtitutions in character data and in attribute values: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml" + xmlns:rdf="http://code.miskinhill.com.au/rdftemplate/"> + <head> + <title>${dc:title#string-lv}</title> + <meta name="DC.creator" content="${dc:creator#string-lv}" /> + </head> + <body> + <p>Title: ${dc:title}</p> + </body> + </html> + +In this example, the object of ``dc:title`` might be an XML literal containing +an HTML ``<span>`` element. The ``#string-lv`` :ref:`adaptation <adaptations>` +is used to strip markup from XML literals in the ``<title>`` element and the +``content`` attribute, where markup is not permitted. On the other hand, the +title’s complete XML tree will be inserted into the ``<p>`` element with all +markup preserved. + +This example will also work correctly if the object of ``dc:title`` is a plain +literal rather than an XML literal, since ``#string-lv`` will pass the plain +literal through untouched. + +Substitutions with ``rdf:content`` +---------------------------------- + +The ``rdf:content`` attribute is used to replace an element’s content with the +result of evaluating a selector expression. The selector expression must have +exactly one result when evaluated. For example: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml" + xmlns:rdf="http://code.miskinhill.com.au/rdftemplate/"> + <body> + <h1 rdf:content="dc:title" /> + </body> + </html> + +The ``rdf:content`` attribute is stripped from the resulting document. The +substitution rules described above also apply for ``rdf:content`` (so XML +literals will be inserted as-is into the resulting document), with one minor +enhancement: if the selector expression evaluates to a literal node with +a language tag, that language will be set on the enclosing element in an +`xml:lang <http://www.w3.org/TR/REC-xml/#sec-lang-tag>`_ attribute (and also an +XHTML ``lang`` attribute, if the document uses the XHTML namespace). + +If the object of the ``dc:title`` property is the literal ``"Война и мир"@ru`` +in the example above, then the template would be rendered as: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1 xml:lang="ru" lang="ru">Война и мир</h1> + </body> + </html> + +Note that if the element has any content in the template, this will be +discarded when rendering. + +Repetition with ``rdf:for`` +--------------------------- + +The ``rdf:for`` attribute can be used to loop over zero or more results from +a selector expression. For each node in the results, the element and its +subtree will be rendered using that node as the context node. All template +constructs may be nested inside the subtree, including other ``rdf:for`` loops. + +For example, given the following RDF graph: + +.. include:: example-graph.rst.inc + +the following template could be used to produce an HTML listing of the people +who are known to a particular person: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml" + xmlns:rdf="http://code.miskinhill.com.au/rdftemplate/"> + <body> + <h1>${foaf:name}’s buddies</h1> + <ul> + <li rdf:for="foaf:knows(foaf:name)"> + <a href="#uri" rdf:content="foaf:name" /> + </li> + </ul> + </body> + </html> + +If ``<bob>`` is used as the context node, the ``foaf:knows(foaf:name)`` +expression will select two nodes: ``<alice>`` and ``<carol>``, in that order. +The ``<li>`` element will therefore be rendered twice, the first time with its +context node set to ``<alice>``, the second time set to ``<carol>``. The +resulting XML would be: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Bob’s buddies</h1> + <ul> + <li><a href="alice">Alice</a></li> + <li><a href="carol">Carol</a></li> + </ul> + </body> + </html> + +``rdf:for`` can also be given as an XML element, with its selector expression +in an attribute named ``each``. This will be rendered in the same way, except +that the entire ``rdf:for`` element is stripped out. In the example above, +``<li rdf:for="foaf:knows(foaf:name)">...</li>`` is equivalent to ``<rdf:for +each="foaf:knows(foaf:name)"><li>...</li></rdf:for>``. + +Because of the potential for ambiguity, it is illegal to combine ``rdf:for`` +with other directives on the same element. + +Concatenation with ``rdf:join`` +------------------------------- + +The ``rdf:join`` element behaves in the same way as the ``rdf:for`` element, +but it also accepts a ``separator`` attribute, which specifies a string to be +inserted between each repetition. + +For example, here is a briefer version of the template above: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml" + xmlns:rdf="http://code.miskinhill.com.au/rdftemplate/"> + <body> + <p>${foaf:name}’s buddies: + <rdf:join each="foaf:knows(foaf:name)" separator=", "> + <a href="#uri" rdf:content="foaf:name" /> + </rdf:join> + </p> + </body> + </html> + +Conditionals with ``rdf:if`` +---------------------------- + +The ``rdf:if`` attribute will cause an element and its subtree to be included +in the resulting document only if the selector expression evaluates to *one or +more* items. Use a :ref:`predicate <predicates>` in the selector expression to +express complex conditions. For example: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml" + xmlns:rdf="http://code.miskinhill.com.au/rdftemplate/"> + <body> + <p rdf:if="dc:identifier[uri-prefix='urn:issn:']"> + ISSN: ${dc:identifier#uri-slice(9)} + </p> + </body> + </html> + +When rendering this template, the ``<p>`` element will only be included if +there is some object of the ``dc:identifier`` property which satisfies the +``uri-prefix='urn:issn:'`` predicate. + +There is also an element form of ``rdf:if``. Use the ``test`` attribute to +specify the selector expression to test against, or the ``not`` attribute to +apply an inverse test. + +A pair of ``rdf:if`` elements can be used to make a choice between two +alternatives. For example: + +.. code-block:: xml + + <html xmlns="http://www.w3.org/1999/xhtml" + xmlns:rdf="http://code.miskinhill.com.au/rdftemplate/"> + <body> + <p> + <rdf:if test="ex:thumbnail"><img src="${ex:thumbnail#uri}" /></rdf:if> + <rdf:if not="ex:thumbnail">No thumbnail available :-(</rdf:if> + </p> + </body> + </html> + +Rendering templates +------------------- + +.. java:class:: au.id.djc.rdftemplate.TemplateInterpolator + + Use this class to render templates. Instances of this class can safely be + shared across threads (for example, as singleton beans in Spring). + + .. java:method:: TemplateInterpolator(au.id.djc.rdftemplate.selector.SelectorFactory selectorFactory) + + The given :java:class:`SelectorFactory + <au.id.djc.rdftemplate.selector.SelectorFactory>` will be used to + evaluate selector expressions when rendering templates. + + .. java:method:: java.lang.String interpolate(java.io.Reader reader, com.hp.hpl.jena.rdf.model.RDFNode node) + + Reads a template from the given :java:class:`Reader <java.io.Reader>`, + and renders it using the given node as context. The resulting XML + document is returned. + + A number of overrides of this method also exist for advanced use cases. + Refer to the `Javadoc for TemplateInterpolator + <http://code.djc.id.au/rdftemplate/javadoc/latest/au/id/djc/rdftemplate/TemplateInterpolator.html>`_ + for more details. diff --git a/src/site/site.xml b/src/site/site.xml @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/DECORATION/1.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 http://maven.apache.org/xsd/decoration-1.0.0.xsd"> - <body> - <menu ref="reports" /> - </body> -</project>