This document introduces the key elements of the SPARQL Web Pages (SWP) framework.
SWP is an RDF-based language for describing user interfaces that render
Semantic Web data.
Dedicated properties such as ui:view
are used to link RDFS/OWL
resources with user interface descriptions.
User interface components are described with the help of HTML or XML snippets
that may contain SPARQL queries to dynamically insert data-driven content.
This document is part of the SPARQL Web Pages Specification.
{= ... }
let:
Assignments, Scoping and ui:group
{# ... }
and letrs:
ui:forEach
ui:if
and ui:else
ui:call
ui:setContext
xmlns:
declarations using ui:xmlns
(Advanced Topic)ui:try
, ui:catch
and ui:throw
(Advanced Topic)ui:task
and ui:subTask
(Advanced Topic)ui:prototype
ui:headIncludes
ui:errorPrototype
and ui:throw
ui:overrides
ui:insertionPoint
and ui:insert
ui:dynamicView
(Advanced Topic)?thisNode
and ?thisParent
(Advanced Topic)ui:graph
ui:createLink
ui:loadable
ui:update
, ui:transaction
and ui:add/remove/setPropertyValue
ui:handle
ui:tempGraph
ui:parse
ui:bind
and ui:stringify
ui:CDATA
ui:return
ui:setPrefix
ui:chunk
SPARQL.ask
SPARQL.expr
SPARQL.function
SPARQL.select
SWP.element
SWP.resultSetToArray
Semantic Web languages (RDF, OWL, SPARQL, etc) are well established for describing domain models and ontologies. Semantic Web resources have unique identifiers that makes it possible to link them together, and reuse information from distributed repositories.
A key idea of Semantic Web languages is that resources can be self-describing:
an instance may point to its class (via rdf:type
), and Semantic Web
agents can retrieve additional information about the class to learn about its
properties, its position in an inheritance hierarchy, and rules or constraints that
can be used to infer implicit information from what has been explicitly stated.
This ability to dynamically discover what to do with arbitrary RDF resources in
an open network is one of the key advantages of RDF over comparable frameworks.
While most existing RDF-based languages focus on describing models, it is usually necessary to apply different frameworks to put those models into user interfaces and executable applications. Frequently, developers use APIs for mainstream languages (such as Jena for Java) to bridge the RDF world with the code that is used for the rest of an application. Bridging mainstream objects with RDF resources is often quite complex, and many of the advantages of a dynamic, declarative and self-describing modeling language like RDF are lost in this process.
SWP takes the Semantic Web ideas one step further and applies them to user interface descriptions. In particular, SWP can be used to make RDF resources self-describing so that generic software agents can learn how to visualize them at execution time. Furthermore, SWP creates a very seamless transition from an application's model to its view (and control) components, reducing the need to switch between languages and paradigms. As a result of this, SWP improves the productivity of modern web developers by supporting a model-driven approach that exploits the strengths of the Semantic Web infrastructure.
Assuming you are familiar with HTML and languages like JSP or PHP, then the following SWP
snippet may serve as a good introduction to how SWP works.
The following (TopBraid Composer) screenshot shows the definition of an SWP view that is
associated with the class skos:Concept
.
The property ui:instanceView
is used to link a class with a SWP snippet.
SWP snippets may consist of any textual language, but in particular XML-based notations
and here in particular XHMTL.
The example above includes a short HTML snippet that defines a rendering of SKOS concepts so
that they display the name of the concept in a heading, and then a list of narrower concepts
(children). The SWP engine would render an example instance as shown below:
The HTML snippet contains a couple of elements and attributes that are interpreted as instructions
by the SWP engine. For example, when executing the construct {= ui:label(?this) }
,
the engine will invoke the SPARQL function ui:label
and insert the function's result
into the HTML document. Here, the variable ?this
points to the current instance
of the class that we are looking at. ui:label
is a built-in function that generates
a human-readable rendering of a given resource, typically using properties such as rdf:label
.
SWP also defines a collection of control elements.
In the example above, <ui:forEach>
is used to iterate through the results of
a SPARQL SELECT query (listing all child concepts of the concept specified by ?this
).
The control element <ui:resourceView>
will recursively insert the best suitable
SWP rendering for the specified resource. In the example above, this will insert a hyperlink to
the child concepts, but this is entirely model-driven so that the engine may insert other renderings
for resources that have different ui:instanceViews
associated with them.
SWP is primarily defined as a collection of RDF vocabularies that can be used to link resources
with user interface descriptions.
Two key properties for the linkage are ui:view
and ui:instanceView
.
These properties point to UI snippets that are either stored in external text files, or
by means of RDF data structures (usually blank nodes).
The difference between those two options will be clarified later, in section 6.
The SWP snippets can contain XML elements from a target language such as XHTML or SVG. The snippets can also just create text nodes that may be used to construct JSON and similar languages. The execution of SWP engines usually happens on a server, and that server can consult any number of ontologies to build responses, supporting multiple variations of the Model-View-Control architecture, ranging from simple document generators to complex Ajax use cases.
The key idea of SWP is to embed SPARQL queries and expressions into UI snippets.
These SPARQL queries often create new variable values (bindings) that can be used further down
the road. For example, if a SELECT query binds a result variable ?narrower
then
this variable can be used within all child nodes of the element that has created it.
It is then possible to write new SPARQL queries that reference the ?narrower
variable and have its value pre-bound. These fundemantal topics around variable scoping and
SPARQL embedding are covered by the following section 2.
SWP supports various mechanisms for attaching user interface snippets with an RDF model.
The starting point is often to have a resource that shall be displayed. Any individual resource
can have a link to a SWP snippet using the property ui:view
.
However, a more common use case is that all instances of a given class (and its subclasses)
share the same visualization, and for those the property ui:instanceView
can be
used. A dedicated control element <ui:resourceView>
can be used to insert
the appropriate snippet based on those properties. This is explained in detail in
section 3.
In addition to attaching (HTML) snippets to RDF classes and instances, SWP makes it easy to modularize UI code into user-defined classes. These classes can be instantiated as new XML tags, and arguments can be passed into the class to populate a snippet called the "prototype" of that class. This approach makes it possible to split long HTML documents into smaller building blocks that are much easier to manage and reuse. User-defined SWP classes will be covered by section 4.
While it is perfectly fine to use SWP only for comparably simple tasks such as generating a documentation page from an ontology, the framework has been designed to also cover more complex use cases, including complete web applications with multiple interlinked pages and Ajax callbacks. The corresponding control elements and design patterns supported by SWP for application development are introduced by section 5.
SWP documents (snippets) must be well-formed XML trees with a single root element. At rendering time, an SWP engine will walk this tree structure starting at the root and evaluate each node recursively. Nodes may have embedded SPARQL expressions, queries and other control elements as described in the following subsections.
{= ... }
SPARQL expressions are SPARQL structures that are typically used in FILTER or BIND assignments
and that only returns a single value. Examples include (bracketted) expressions, calls of built-ins,
or function calls.
Any such SPARQL expression can be embedded into a SWP document using the
{= ... }
syntax as shown in the following example:
The answer is {= 40 + 2 }
At execution time, the expression will be evaluated, and the result will be inserted
into the target document. The result of the mathematical operation above is an RDF
literal, and all literals will simply be inserted as their lexical form, e.g. 42
.
If the result of the expression is a URI resource, then the URI will be inserted,
such as in the following example (where ?link
is assumed to be bound to
a given resource).
<a href="{= ?link }">Click here</a>
If the expression returns a blank node or unbound, then nothing will be inserted into the target document.
Note that special characters in SWP documents must be escaped according to the usual XML syntax rules.
For example, inside of attributes, "
needs to be escaped with "
.
Another frequently needed character is <
for <
.
let:
Assignments, Scoping and ui:group
Like most programming languages, SWP allows developers to assign new variables.
Any element in a SWP document may contain attributes with the namespace prefix
let
. In the following example, the variable ?birthYear
is bound to 1971. This value can then be used in other SPARQL expressions or queries
contained within the children of the element holding the let:
assignment.
<div let:birthYear="{= xsd:integer(1971) }"> Your age is {= 2010 - ?birthYear } years. </div>
In general, variables bound in one element will be visible in all children (recursively).
Variables bound within an element are not visible in the sibling attributes of that same element.
The special attribute let:_
can be used to assign multiple variables.
The _
acts as a placeholder for all variables projected by the associated SELECT query.
<div let:_="{# SELECT ?one ?two WHERE { BIND (1 AS ?one) . BIND (1 + 1 AS ?two) } }"> <span>{= ?one } and {= ?two } are bound in the child elements of the div.</span> </div>
In some cases, it is desirable to create a variable binding without having to create
an XML element that would go into the target document. In those cases, the control
element ui:group
can be used as an empty element to hold those.
In the following example, only the text is created, and (unlike in the snippet above)
no extra div
:
<ui:group let:birthYear="{= xsd:integer(1971) }"> Your age is {= 2010 - ?birthYear } years. </ui:group>
Tip: The control element ui:group
is also useful in cases where the result
of the SWP rendering is not a well-formed XML document (with a single root etc),
but rather some text such as JSON. You basically create a single ui:group
as root and then only use text nodes and other control elements.
{# ... }
and letrs:
Taking the idea of embedded SPARQL expressions further, it is also possible to
embed SPARQL SELECT queries into SWP documents, surrounded by {# ... }
.
The result of such queries depends on the context though, and the result may either
be interpreted as a tabular SPARQL result set, or just a single value.
In the following example, the value of the href
attribute will be
the first result of the given SELECT query:
<a href="{# SELECT ?link WHERE { ?this ex:link ?link } }">Click here</a>
The example above assumes that there is only a single value for ex:link
at the resource specified by ?this
. The system will run the query,
use its first result value, and discard the rest. Since SPARQL does not give you
any guarantees about the order of bindings in a result set, this may actually lead
to unpredictable results unless you are using ORDER BY
.
Query modifiers (Advanced Topic):
SWP supports four system properties ui:queryLimit
, ui:queryOffset
,
ui:queryOrderBy
and ui:queryOrderByDir
that can be used to modify
the SELECT queries found within the same element.
They can be used to specify values for the SPARQL keywords LIMIT
, OFFSET
and ORDER BY
that must be fixed values in standard SPARQL syntax.
An example use case of these properties is callbacks for user interface components that
require paging through large result sets.
The following example will only produce 8 bindings for ?class
:
<ul let:max="{= 8 }"> <ui:forEach ui:queryLimit="{= ?max }" ui:resultSet="{# SELECT ?class WHERE { ?class a rdfs:Class } }"> <li>{= ui:label(?class) }</li> </ui:forEach> </ul>
Result Sets as variables:
In order to treat the results of a SELECT query as a proper result set, in
which multiple rows and columns can be traversed, the letrs:
namespace prefix can be used. In the following example, a SELECT query is
executed and stored in variable ?rs
. The variable points to
a spr:Table
following the SPIN
Result Sets format. This means that it is possible to run queries over
the result set itself, e.g. to count the number of rows and columns:
<ui:group letrs:rs="{# SELECT ?firstName ?lastName WHERE { ?person ex:firstName ?firstName . ?person ex:lastName ?lastName . } }"> This result set has {= spr:rowCount(?rs) } rows and {= spr:colCount(?rs) } columns. </ui:group>
The main use case of this will become apparent in the conjunction of
ui:forEach
, but using letrs:
makes it possible to
avoid duplicate computations of the same result sets, and to do meta-queries.
ui:forEach
The SWP element ui:forEach
can be used to traverse the
result set of a SPARQL SELECT query row by row.
In each iteration, the result variables of the SELECT query will be
bound in the children of the element.
The child nodes of the ui:forEach
element will be multiplied
for each iteration.
The following example produces an HTML list of subclasses of the given
resource ?this
.
<h1>Subclasses</h1> <ol> <ui:forEach ui:resultSet="{# SELECT ?label WHERE { ?subClass rdfs:subClassOf ?this . ?subClass rdfs:label ?label . } ORDER BY ?label }" > <li>{= ?label }</li> </ui:forEach> </ol>
Example output:
<h1>Subclasses</h1> <ol> <li>Customer</li> <li>Person</li> </ol>
As an optional argument of ui:forEach
, the name of an index variable
can be provided using ui:indexVar
.
The value of this variable will be the iteration counter, starting with 0.
Another optional argument of ui:forEach
is ui:separator
.
This can contain any string that shall be inserted between each row of the result set.
For example, <ui:forEach ui:resultSet="..." ui:separator=",">...
will insert a comma between each entry of the output.
Note that the ui:resultSet
of the ui:forEach
does
not need to be a SELECT query by itself. Instead, it is possible to pass in
a SPIN Result Set that may have been evaluated in a previous step, e.g. using
a letrs:
assignment or a SPIN template ui:call
, e.g.
<ui:forEach ui:resultSet="{= ?rs }">
if the variable ?rs
has been bound to a result set in a surrounding element.
The argument ui:bindVars
can be used to narrow down which variables of
such result sets shall be visible in the child elements, as described later.
The two optional arguments ui:offset
and
ui:limit
can be used to simulate "paging" through a result set
similar to the OFFSET and LIMIT keywords in SPARQL. Their values must be integers.
ui:if
and ui:else
SWP defines a control element called ui:if
that
can be used to branch the execution flow depending on a condition.
ui:if
evaluates an argument ui:condition
.
If the condition is (the RDF literal) true
, then
the children of the ui:if
will be inserted into the document.
Otherwise nothing will be created, unless there is an ui:else
branch immediately following the ui:if
element.
In the following example, the isIRI
function is evaluated
on the current binding for ?this
.
Depending on whether this is true or false, either of the two branches
will be inserted: An a
hyperlink or a span
.
<ui:if ui:condition="{= isIRI(?this) }"> <a href="{= ?this }" >{= ui:label(?this) }</a> </ui:if> <ui:else> <span class="blankNode">{= ui:label(?this) }</span> </ui:else>
Note that the type of RDF literals will be ignored to simplify the syntax,
i.e. "true"
is equal to "true"^^xsd:boolean
.
The control element ui:elseif
provides syntactic sugar
for a combination of ui:else
with an embedded ui:if
.
ui:elseif
must have a ui:condition
argument.
ui:call
In some cases, embedding complete SPARQL queries makes a SWP document
hard to maintain. It is often desirable to keep the queries more cleanly
separated from the actual rendering document.
SPIN Templates
are a powerful mechanism for making SPARQL queries reusable. The basic idea
is that queries get a URI and any number of arguments, and those arguments
can be filled in when the template is used.
The SWP control element ui:call
can be used to evaluate
SPIN Templates that wrap a SELECT query and to bind the result set to a
variable.
The following example assumes that there is a SPIN template
ex:GetSubClasses
which takes one argument arg:superClass
and returns a list of labels for the subclasses of that argument ?superClass
.
The body of that template may look like the following:
SELECT ?label WHERE { ?subClass rdfs:subClassOf ?superClass . ?subClass rdfs:label ?label . } ORDER BY ?label
A SWP document may now "call" this template with the following syntax:
<h1>Subclasses</h1> <ui:call ui:template="ex:GetSubClasses" arg:superClass="{= ?this }" ui:varName="rs"> <ol> <ui:forEach ui:bindVars="?label" ui:resultSet="{= ?rs }"> <li>{= ?label }</li> </ui:forEach> </ol> </ui:call>
By default, ui:call
will put the result set into the variable
?rs
, but other variable names can be specified using ui:varName
.
Also note that the embedded ui:forEach
uses ui:bindVars
to select which variables from the template call's result set shall be used.
The values of ui:bindVars
must be comma-separated lists of variable names
(spelling out the ? as well as space characters between the variables are OK).
Only variables that are both in the result set and the ui:bindVars
will be visible
in the children of the ui:forEach
element.
Compared to the example from sub-section 2.4, this snippet with ui:call
is
potentially easier to read and maintain for the page designer, and the query can be reused
in multiple places. The recommended pattern is to store the templates in separate (.spin.)
files that do not use any SWP elements, but only contain the "model" or "control"
parts of a Model-View-Control architecture.
The main disadvantage of using ui:call
is that the names of the result variables
are not known to the snippet, and in general the binding between the UI code and the queries
is a bit loose. It is often a matter of taste which approach works better.
ui:call
can also be used to execute arbitrary SPARQL queries represented
as strings, using ui:queryString
. This supports scenarios in which a SPARQL
query string is constructed at runtime, e.g. by concatenating other strings.
From TopBraid 6.0 onwards, ui:call
supports the arguments ui:offset
and ui:limit
to produce result sets that are smaller, i.e. have less rows than
the full result set delivered by the SELECT queries.
These options should only be used if the query produces a reliable ordering, e.g. using ORDER BY
.
ui:setContext
SPARQL has the notion of the default graph, which is the graph used by the WHERE
clause unless some other named graph has been specified (e.g., using GRAPH
).
In many cases, it is convenient to explicitly switch to a different default graph while
the SWP engine executes. In the following example, the control element ui:setContext
is used to switch to a different graph for the child nodes.
<h1>Links</h1> <ui:forEach ui:resultSet="{# SELECT ?link ?home WHERE { ?link a ex:Link . ?link ex:home ?home . } }" <ui:setContext ui:queryGraph="{= ?home }"> <h2>{= ui:label(?link) }</h2> ... </ui:setContext> </ui:forEach>
The block inside of the setContext
element will operate on a different default
graph, specified by the variable ?home
. This also means that SPARQL functions
such as ui:label
will use a different context, and may retrieve the
rdfs:label
from the home graph.
Common use cases of ui:setContext
are in conjunction with ui:createLink
which is handled further below.
ui:setContext
can also be used to set context variables
that can later be queried using the function ui:contextValue
.
A context variable is visible within all children of the ui:setContext
,
including the prototypes of any nested elements. In the following example, the
variable active
can be queried inside of the children and the ex:MyElement
element.
<div> <ui:setContext ui:varName="active" ui:varValue="{= true }"> <span>Active is now {= ui:contextValue('active') }</span> <ex:MyElement ... /> </ui:setContext> <-- The variable is no longer bound here --> </div>
Note that the values of ui:varValue
are turned into string literals by default.
If you want to have the variable point to an RDF literal of a different type or a resource node
then you need to use the {= ... }
notation as in the example above.
xmlns:
declarations using ui:xmlns
(Advanced Topic)
Any SWP element may carry the attribute ui:xmlns
. The value of this attribute
must be a URI resource. If present and if the resource is from a namespace with a valid prefix,
then the generated HTML or XML element will have an attribute xmlns:
for the
namespace of the resource. For example, assuming the prefix ex
point to
http://example.org/ex#
, then the following snippet
<div ui:xmlns="ex:MyResource" />
will produce
<div xmlns:ex="http://example.org/ex#" />
ui:try
, ui:catch
and ui:throw
(Advanced Topic)
SWP includes some control elements to raise exceptions that bubble up the call stack, and to catch exceptions.
ui:throw
can be used to throw an exception, together with a message.
ui:try
and ui:catch
must be used together, as shown in the following example.
Note that inside of the ui:catch
block, the variables ?message
, ?exception
,
?swpTrace
and ?stackTrace
will provide details about what happened.
<ui:group> <ui:try> <ui:throw ui:message="Test message"/> <div>This will not be reached</div> </ui:try> <ui:catch> <div>Error: {= ?message }</div> </ui:catch> </ui:group>
Note that ui:catch
will also catch any other (Java) Exceptions produced by its child elements.
ui:task
and ui:subTask
(Advanced Topic)
It is a good practice to provide feedback for long-running processes and allow users to cancel them.
The control elements ui:task
and ui:subTask
can be used for these purposes.
You need to place them around the sections that shall be monitored or are cancelable by the user.
A typical use case is illustrated by the following example.
<ui:task ui:taskName="Generating table..." ui:totalWork="{= spr:rowCount(?rs) }> <ui:forEach ui:resultSet="{= ?rs }" ui:indexVar="index"> <ui:subTask ui:subTaskName="Row {= ?index }" ui:work="1"> ... do the work on row ?index of ?rs </ui:subTask> </ui:forEach> </ui:task>
In the example above, the user can cancel before each iteration of the ui:forEach
.
There is no particular feedback that could be used to determine whether the user has canceled.
The display of an actual progress dialog is the responsibility of a library such as SWA. In the case of SWA, the typical workflow is for the client JS code to do the following:
var progressId = 'swa-progress-' + Math.random(); swa.openProgressMonitorDialog(progressId, "Title"); var params = { _viewClass : '...', _progressId : progressId, ... } ... make actual request, and call swa.closeProgressMonitorDialog() when done
The SWP vocabulary defines a couple of properties that can be used to associate types of visualizations with certain RDF resources. SWP engines can use these properties to select and propose a suitable screen rendering (or business intelligence report, etc) for a given resource.
ui:view
The property ui:view
links a specific RDF resource
with one or more UI elements.
The following example (in Turtle notation) instructs SWP engines to render the resource
ex:PaulPlatypus
with the ex:PlatypusImage
.
ex:PaulPlatypus a ex:Platypus ; rdfs:label "Paul Platypus"^^xsd:string ; ui:view ex:PlatypusImage .
Technically, views must be instances of the class ui:Element
or its subclasses.
Editing tools such as TopBraid Composer make it easy to edit those views as blank node
structures, with a custom HTML-like editor that directly appears on the form of the associated
resource. Thus the details of whether a view is an RDF blank node or not become irrelevant.
In the following screenshot, a ui:view
has been attached to the class
skos:Concept
:
The variable ?this
has a special meaning in such views: it is bound to the
surrounding resource. In the example above, ?this
will point to the class
skos:Concept
itself.
If a SWP engine has been asked to render a given resource, it will look for associated
value of the ui:view
property first. The more common case though is to use
ui:instanceView
, which is described in the following sub-section.
ui:instanceView
The property ui:instanceView
links an RDFS/OWL class with
a UI element to instruct an engine to represent all instances of that class
with the given visualization.
SWP engines should consider inheritance so that visualizations attached
to subclasses shall be preferred over those attached to superclasses.
We have already seen an example of ui:instanceView
in the
introduction.
Another example is shown below. In that example, three different views have
been attached to the class rdfs:Resource
:
In those views, the variable ?this
will be pre-bound to the instance
that is currently rendered.
The three views above define different ways of visualizing instances of that class.
The property ui:id
is used to distinguish them.
The control element ui:resourceView
(described below) can be used to
select which of the views shall be picked.
A similar mechanism is supported by the SWP server using a servlet argument to
specify the matching ids.
The general mechanism for selecting a view also considers a property ui:priority
which is also shown in the screenshot above. Each view can have a priority which is used
if the same class has multiple views associated with it. A typical use case is the SWA library
which defines "global" views that can be applied to all resources.
These global views are, as shown above, attached to the class rdfs:Resource
.
However, someone else may want to override those default views, and create their own
alternative views in some other file. In those cases, they should associate a ui:priority
higher than the one used by the SWA views. Since the default value is 0,
it is sufficient to just specify no priority in that particular case.
For a maximum of control over the view selection mechanism, it is possible to use SPIN expressions
as values of ui:priority
. For example, you can set the priority as
ui:priority="{= IF(ui:contextValue('myAppName'), 10, -10) }"
to return 10 if the
view is used in a given context, and -10 otherwise. This design pattern can be used to define
application-specific views: the surrounding code of the application would use
ui:setContext
to set variables that can be queries in the priority expression.
ui:resourceView
One of the strengths of SWP is the linkage between resources (or their classes)
and views. This makes it possible to define pages that contain other views as children,
and those children are dynamically selected while the page is being created.
For example, if you need to display a list of items, and each item may be of an unknown
type, then each list item may use a different rendering for the underlying resources -
a picture for a foaf:Person
, a summary of past purchases for a
ex:Customer
etc.
The control element ui:resourceView
instructs the engine to insert the
most suitable rendering of a given resource into the target document.
In the following example, ui:resourceView
is used to populate a table:
<table> <ui:forEach ui:resultSet="{# SELECT ?item WHERE { ?this ex:contains ?item . } }"> <tr> <td> <ui:resourceView ui:resource="{= ?item }" ui:matchIds="icon" /> <td> <td> <ui:resourceView ui:resource="{= ?item }" ui:matchIds="summary" /> <td> </tr> </ui:forEach> </table>
ui:resourceView
must contain a ui:resource
that points to the
resource to render.
The engine will select a suitable view by means of the following algorithm:
ui:view
of the resource.
ui:instanceView
associated with
the types of the resource, walking up the parent hierarchy if needed.
As shown in the example for ui:instanceView
above,
there are sometimes different kinds of views available for a given resource,
such as a full page or a shorter summary snippet. In order to distinguish
those, the property ui:id
can be attached to the views.
Suggested values of this property are
The argument ui:matchIds
can be set for ui:resourceView
control elements to specify which kinds of ids shall be accepted. The values
of this argument are comma-separated strings, such as "summary,label" to indicate
that a "summary" is preferred, but a "label" would also be accepted if no "summary" exists.
If left blank, then only views with no ui:id
or with "full" will
be selected. If you want to explicitly address view without ui:id
, then
use *
, e.g. "label,*" will first try to find a label and if this fails
it will fall back to those views that have no ui:id
.
In the example above, the system will create an HTML table with two columns, and one
row for each ?item
. For the first column, the system will attempt to
find a view with the id icon
, which may or may not be present.
For the second column, the system will insert a summary
which is usually
always present if the tui
ontology has been installed.
ui:classView
(Advanced Topic)
Very similar to ui:resourceView
,
the control element ui:classView
can be used to insert an SWP view
that is attached to a class using ui:instanceView
. The main difference
though is that ui:classView
does not operate on an instance, and the
variable ?this
is not bound when the view is inserted.
ui:classView
is rarely needed but there are use cases where applications want to reuse
the same views in different modes, e.g. once as an instance viewer and once as
a search form for instances.
The attributes of ui:classView
are ui:class
to point to the
class, and ui:matchIds
to select the ids to match. All other arguments
that are not from the ui:
namespace will be passed as pre-bound variables
into the instance view.
A key benefit of SWP compared to other template languages is
the ability to define and instantiate customized element types that can be used as nodes in XML documents.
This makes it possible to reuse recurring building blocks, leading to better structured code.
User-defined SWP elements are RDFS classes (instances of the metaclass ui:NodeClass
to be precise)
that have a so-called prototype attached to them.
Element classes should be subclass of ui:Element
.
The SWP namespace includes several suggested superclasses to use, e.g. subclass ui:ViewElements
in the Classes view of TopBraid Composer to get started with an element that produces an HTML view.
Classes may also have arguments, which are mapped to XML attributes when instantiated.
The prototype may reference those argument values to generate output.
These concepts are explained in detail in the following subsections.
ui:prototype
The property ui:prototype
links a user-defined class with
a template instance that serves as blueprint for any instance of the class.
At execution time, the SWP engine will create a deep copy of this prototype
as a starting point to populate the instance node.
The following example will illustrate this concept.
Let's assume we want to (repeatedly) create a HTML list of children
of the given person (?parent
).
We can define an element class ex:ChildrenList
as a subclass of
ui:Element
and of type ui:NodeClass
with
the following prototype:
<ul> <ui:forEach ui:resultSet="{# SELECT ?child WHERE { ?parent ex:child ?child . } ORDER BY ui:label(?child) }"> <li>{= ui:label(?child) }</li> </ui:forEach> </ul>
The element class must declare that it takes an argument with the local name
parent
, e.g. arg:parent
(more on arguments in the next subsection).
The new class can now be used as a named XML element anywhere, such as in
the following snippet:
<div> <h1>Children of {= ui:label(?this) }</h1> <ex:ChildrenList arg:parent="{= ?this }" /> </div>
The execution engine will replace any occurrence of the user-defined element class with its prototype. The prototype can contain any SWP structure, including control elements.
User-defined classes can be made configurable with arguments.
Arguments declare the (XML) attributes that the user can specify for instances
of the class. The property spin:constraint
is used to link
a user-defined class with its spl:Arguments
.
Let's assume we want to extend the example from the subsection above, so that
we can not only display subclasses, but values of any relationship, such
as skos:broader
or ex:hasChild
.
In order to do so, we want to be able to substitute the rdfs:subClassOf
predicate with any other given property. We declare an argument
with spl:predicate
pointing to the property arg:property
,
having the spl:valueType
of rdf:Property
.
We can now change the prototype of our class to the following:
<ul> <ui:forEach ui:resultSet="{# SELECT ?value WHERE { ?value ?property ?object . } ORDER BY ?value }"> <li>{= ui:label(?value) }</li> </ui:forEach> </ul>
And we can use this new class (now called ex:AnyPropertyList
):
<div> <h1>Subclasses of {= ui:label(?this) }</h1> <ex:AnyPropertyList arg:property="rdfs:subClassOf" arg:object="{= ?this }" /> </div>
This creates exactly the same output, but with a more reusable class.
Note that the variable ?property
will be pre-bound for all
nodes of the prototype. The rule is that the system will automatically
create variable bindings for variables that have the local name of the
predicate declared in the argument. Above, the predicate arg:property
will be mapped to the variable ?property
, and the property
arg:object
will be bound to ?object
.
Note that if you subclass another SWP class, then all "inherited" arguments will also be bound to variables. Due to this, inheritance hierarchies between SWP classes make most sense to share the definitions of arguments, no matter of whether the prototypes look similar or not.
In some cases, the list of arguments can become quite long, such as in
<ex:AnyPropertyList arg:property="{= ?property }" arg:object="{= ?object }" />
.
If the variables ?property
and ?object
are already bound in the surrounding
elements, then it is sufficient to write <ex:AnyPropertyList ui:args="*" />
.
The built-in ui:args="*"
will automatically match any existing variable bindings
from the outside with the declared arguments of the element.
Similar to ui:args="*"
it is possible to declare variables that shall be used
as default values if no other value is present. The following snippet illustrates the use of
the default:
namespace for that purpose:
<ui:group default:property="{= rdfs:label }"> <ex:AnyPropertyList arg:object="{= ?this }" > </ui:group>
default:
is similar to let:
assignments, but in addition to binding
a variable into the current scope, it also instructs the engine to pass its values into namesake
arguments of contained elements. In the example above, the argument arg:property
is
implicitly bound to rdfs:label
.
Note that default
variables will by default be string literals, so if you need
proper RDF resources or literals of other datatypes, then use the {= ... }
notation.
Classes that only declare arguments, or otherwise group together related classes,
can be marked as not instantiable using ui:abstract
.
Classes that are meant to be used only within the current file and not outside
of it should be marked as ui:private
.
The following TopBraid Composer screenshot shows a complete user-defined SWP class as an example.
This example class can be instantiated as shown below:
<tui:Link tui:node="owl:Thing" />
Note that the recommended namespace prefix for all arguments is arg:
, which is pre-defined with SPIN.
TopBraid Composer has a convenience feature Create spl:Argument... in the drop down menu behind
spin:constraints
to create arguments in the arg:
namespace.
ui:headIncludes
The property ui:headIncludes
can link a SWP node class with
one or more SWP snippets.
At execution time, the engine will insert those snippets into the (HTML) head section
of the result page (and not into the current context).
Typical use cases are JavaScript snippets, the HTML title tag or other global
definitions such as inline stylesheets.
In the following example, various components require a global include of a
certain JavaScript library. An abstract superclass has been introduced with
the following ui:headIncludes
:
<ui:group let:base="lib/extjs/"> <link href="{= fn:concat(?base, "resources/css/ext-all.css") }" rel="stylesheet" type="text/css"/> <script src="{= fn:concat(?base, "adapter/ext/ext-base.js") }" type="text/javascript"/> <script>Ext.BLANK_IMAGE_URL = '{= ?base }resources/images/default/s.gif';</script> <script src="{= fn:concat(?base, "ext-all.js") }" type="text/javascript"/> <script> Ext.onReady(function() { Ext.QuickTips.init(); }); </script> </ui:group>
Any component that requires the above snippet in the HTML page can simply be made a subclass of that abstract superclass, inheriting the head includes. Note that the system will prevent duplicate head includes if they originate from the same class.
ui:errorPrototype
and ui:throw
(Advanced Topic)
Similar to ui:prototype
, the property ui:errorPrototype
links a user-defined class with zero or more SWP snippets.
If such a class is instantiated, then the engine will first attempt to build
XML nodes for all error prototypes (including those from superclasses).
If any of those error prototypes creates a non-empty XML document, then this
document will be used instead of the ui:prototype
.
Error prototypes are often a convenient mechanism to implement user-friendly
error handling. For example, assume you have a couple of components that are
only visible if the current user has certain privileges.
In this case, you can create an abstract SWP class that acts as a superclass
of your components. In that superclass, define an ui:errorPrototype
that validates the permissions and creates a simple text such as shown below.
<ui:if ui:condition="{= !ex:currentUserHasReadAccess() }"> <span>You do not have sufficient privileges</span> </ui:if>
In the example above, if the condition is false, then the snippet will be
turned into an "empty" XML document, and the system will continue with the
default behavior of using the ui:prototype
instead.
In some cases, using ui:errorPrototypes
is too inefficient,
because lots of (duplicate) computations would be needed to check for error
conditions, in multiple (error) prototypes. For those cases, SWP provides
a built-in control element ui:throw
that simply interrupts the
execution of the engine and fails with a servlet error, providing a human-readable
error message.
ui:overrides
(Advanced Topic)It is a good practice to organize and distribute SWP class definitions in libraries so that they can be reused in different places. Such generic libraries can significantly reduce development costs. However, like with any reusable library, developers may want to make small adjustments here or there, for example to insert additional HTML elements. One (naive) approach for that is to make a deep copy of the existing class definitions and/or move them to another namespace and then modify it there. However, this quickly leads to unmanageable code because the new cloned system is out of synch and will no longer benefit from updates to the underlying components.
The property ui:overrides
can be used to declare that a given element class
should substitute the ui:prototype
and ui:errorPrototype
values
of a given class. In order to override a given element ex:BaseClass
, create
a subclass ex:SubClass
of it and attach the property ui:overrides
to it, pointing back to the original ex:BaseClass
.
Then define a new ui:prototype
at ex:SubClass
. Whenever the
element ex:BaseClass
is used, the system will from now on use the prototype
from the subclass.
ui:insertionPoint
and ui:insert
User-defined components may declare insertion points that serve as a plug-in mechanism for other components to use.
An example use case is the following. Assume you have a user interface widget with a tool bar and that may contain a variable number of buttons. Subclasses of your widget may want to add custom buttons that are specific to them. You may want to have many of such subclasses, without limiting what could potentially go into the components.
In this example, the base class ex:BaseComponent
may define
the following prototype:
<div> <table> <tr> <td>My Component</td> <td> <ui:insertionPoint ui:pointId="toolbar" /> </td> </tr> </table> <span>Some more stuff goes here...</span> </div>
A SWP class ex:SubComponent
may be declared as a subclass
of ex:BaseComponent
with the following prototype:
<ex:BaseComponent arg:... > <ui:insert ui:into="toolbar"> <a href="action1.html">Button1</a> <a href="action2.html">Button2</a> <ui:insert> </ex:BaseComponent>
When ex:SubComponent
is instantiated, the result will include
the nodes defined in the ui:insert
into the ui:insertionPoint
:
<div> <table> <tr> <td>My Component</td> <td> <a href="action1.html">Button1</a> <a href="action2.html">Button2</a> </td> </tr> </table> <span>Some more stuff goes here...</span> </div>
ui:insertionPoint
must have a single container element around
it - the td
in the example above.
This container element must produce a node in the resulting XML DOM tree.
Some built-in control elements such as ui:group
do not produce a DOM tree node,
and therefore cannot be used as parents of ui:insertionPoint
.
Note that it is not necessary to explicitly use ui:insert
.
Instead you can write the example above as
<ex:BaseComponent arg:... > <div> <a href="action1.html">Button1</a> <a href="action2.html">Button2</a> </div> </ex:BaseComponent>
In this shorter form each direct child of the component that declare the insertion point will be inserted into the next available insertion point. If there are more than one extension points, then it will traverse them alphabetically.
Advanced side bar: Note that there are other mechanisms of creating plug-in behavior.
Since SWP views are RDF resources which you can query with SPARQL
(using GRAPH ui:graph { ... }
), you can store extra metadata
in your SWP models to identify those components that shall be used as
"plug-ins".
A detailed discussion of these design patterns goes beyond the scope of this document.
ui:dynamicView
(Advanced Topic)
SWP is entirely RDF and SPARQL driven, which means that the structure of SWP declarations
itself can be queried at run-time to dynamically select which (HTML) elements to insert
into the target document. A typical use case in ontology-driven applications is to have
widgets that have different content depending on the type of object to render.
For example, a string literal should be displayed using a span
while
resources ending with .png
should be displayed using img
.
The control element ui:dynamicView
allows SWP developers to dynamically
select the type of element to insert when the page is being constructed.
In the following example, ui:dynamicView
is used to insert a suitable widget
for an object, while the function ex:selectWidget
gets the most suitable
user-defined element for the provided argument.
<ui:dynamicView ui:class="{= ex:selectWidget(?object) }" arg:object="{= ?object }" />
Assuming that ?object
is a resource ending with .png
, the
SPIN function ex:selectWidget
may return ex:ImageViewer
.
The SWP engine then essentially produces:
<ex:ImageViewer arg:object="{= ?object }" />
Any additional argument of the ui:dynamicView
element will be passed into
the new element. If the ui:class
argument evaluates to unbound then
nothing gets inserted.
?thisNode
and ?thisParent
(Advanced Topic)Since SWP models themselves are represented in RDF, it is possible to query the document structure itself while the SWP engine is running. Two system variables are available to get pointers into that graph in the current context:
?thisNode
points to the blank node of the current element.?thisParent
points to the blank node of the calling element
if we are inside of a prototype.
Note that in order to traverse the SWP graph itself, you need to switch to the
ui:graph
, e.g. using GRAPH ui:graph { ?root ui:child ?thisParent ... }
or GRAPH ui:unionGraph { ... }
.
ui:graph
consists of the union of all graphs stored in .ui.*
files
in the TopBraid workspace.
ui:unionGraph
is ui:graph
plus the current query graph (data) in the SWP execution.
The property ui:child
represents the hierarchy of SWP elements in the SWP graph, pointing
from a parent to a child node.
Each node also has a ui:childIndex
starting at 0, representing the order.
As this is quite an advanced topic that is rarely needed, we do not provide an example here.
You can find an example by searching for ?thisParent
in the SWA library shipping with TopBraid.
SWP is not limited to creating individual pages or renderings, but is also a framework for creating applications that use linked (RDF) data as their core data models. This section covers a range of topics that, taken together, help developers get started with using SWP for the creation of linked data applications consisting of multiple pages.
For the purpose of this discussion, we assume that we have a SWP server (such as
TopBraid Live) that is able to handle incoming HTTP requests to return SWP pages
(including HTML, SVG and JSON).
In many cases, those renderings are attached (and can be derived from) a given Resource.
The linkage properties such as ui:instanceView
can be used for that purpose.
In the case of TopBraid Live, the URL to get an HTML page for owl:Thing
would look like the following:
http://localhost:8083/tbl/swp?_resource=http%3A%2F%2Fwww.w3.org%2F2002%2F07%2Fowl%23Thing&_base=...&_withImports=true
This particular server uses the following arguments (all optional):
Argument | Description |
---|---|
_base |
The base URI of the default query graph to operate on. |
_withImports |
true to also interpret (transitive) owl:imports from the _base query graph |
_resource |
The URI of the resource to render. |
_matchIds |
A comma-separated list of ui:ids to match against, e.g. "summary,label,*". |
_view |
The URI of a specific view to display (instance of ui:Element ). |
_viewClass |
The URI or qname of a user-defined view class (subclass of ui:Elements )
to display. All other arguments that match the declared arguments of the view class will
be passed into the view, e.g. uispin?_viewClass=ex:MyView&name=Test will
be comparable to <ex:MyView arg:name="Test" /> . |
_snippet |
A boolean to instruct the engine to not wrap the result into a full HTML document (with tags such as
html and head ). |
_fileUpload |
If true then files uploaded as part of a multi-part HTTP request will be copied into
the workspace as temporary files. The name of the corresponding file will be assigned to the
argument variable. For example, define a service with an argument arg:file of type string
and create a web form that uploads files as "file". The variable ?file will then contain the
path to the temp file in the workspace. You can then use the function smf:baseURI to get
the URI of that file, e.g. to open it using <ui:setContext ui:queryGraph="..." /> .
This is particularly useful for Excel files. |
_format |
The mime type for the response, e.g. application/json .
Usually this does not need to specified, because the SWP elements themselves declare their
mime type via ui:responseType . |
_cache |
If set to true , then the server will cache the result of this request.
Subsequent calls to the same URL will return the cached version, significantly boosting
response times. Note that changes to the underlying RDF model or SPARQL queries will not
be detected automatically, which means that this option should only be used for static
content and read-only applications. |
In order to fulfill its requests, a SWP server will operate on a collection of SWP
files that are registered with it. The server will pre-load all files containing .ui.
in their name and combine them into a (virtual) union graph called ui:graph
.
This graph will be used at execution time to identify which visualizations are available
for a given context. The UI graph can also be queried by the SWP documents themselves, e.g.
the following query will enumerate all classes that have an instance view attached to them:
SELECT ?class WHERE { GRAPH ui:graph { ?class ui:instanceView ?view . } }
See also the built-in SPARQL function ui:param
on how
to query additional parameters of the servlet call.
The TopBraid SWP servlet handles both GET, POST and PUT identically.
Starting with 6.3, the servlet maps the body of HTTP POST and PUT requests to the parameter
httpBody
, so scripts declaring such a parameter can further process it.
Starting with 6.4, the servlet also accepts uploaded ZIP files, assuming they end with .zip
.
Use the function smf:isZipFileID($arg)
to test whether such an argument was recognized as ZIP file,
and then the magic property smf:zipFileContents
to walk through the individual files in the archive.
(The same technique can be used from SPARQLMotion scripts).
ui:createLink
In a typical SWP application, generated HTML pages would contain hyperlinks to other pages
from the same information system, and the same server.
Those links may become quite long, and the syntax may be hard to remember and may not portable
between SWP servers.
For this reason, SWP provides a built-in control element ui:createLink
that
generates a URL that can then be turned into a hyperlink, e.g. using href
.
The following example creates a simple link to a page rendering owl:Thing
:
<ui:createLink ui:resource="owl:Thing"> <a href="{= ?link }">Thing</a> </ui:createLink>
ui:createLink
binds a variable called ?link
which can then be used
in the child nodes of the element. You can use the property ui:varName
to specify
a different variable name, to avoid potential name clashes.
Most of the servlet arguments from the table in the subsecion above can
be specified in similar ways, using the built-in properties ui:snippet
,
ui:viewClass
, ui:format
and ui:queryGraph
.
This makes it possible to let the system do the low-level work of turning any combination
of those arguments into a valid URL against the right server.
By default, the generated URLs will be relative to the URL of the SWP service. Absolute
URLs can be forced by adding the ui:absolute="true"
attribute.
To add a fragment, such as #section1
, to the end of the generated URL,
use ui:fragment="section1"
.
There is an optional boolean property ui:sendQueryGraph
that can be set to true
to instruct the engine to propagate the current query graph into the linked page.
The effect of this will be that the linked page will start with the provided named graph
as its default graph. This is typically used in conjunction with
ui:setContext
.
The ui:createLink
element can take any number of other arguments.
All arguments not from the ui:
namespace will be passed into the servlet
as named argument. For example, arg:firstName="Darwin"
will be turned into
&firstName=Darwin
as part of the link.
These additional arguments will be passed into the selected SWP view for execution.
SWP widgets may also want to call other servlets such as SPARQLMotion.
The ui:createLink
element provides a property ui:servlet
for that purpose. For example, set it to sparqlmotion
to call
the TopBraid Live SPARQLMotion handler, and set arg:id
to the name
of the script to call. All other arguments can be passed into the servlet using
the same mechanism.
ui:loadable
Many JavaScript-based applications use Ajax callbacks to replace parts of a page when an event happened. For example, clicking on a tree on the left of the page may replace the form on the right. JavaScript libraries such as jQuery support such load operations relatively conveniently - yet doing the right thing is often a difficult undertaking.
The control element ui:loadable
can be placed around divisions of a page
that shall be re-loadable. The following example illustrates how this works.
<ui:loadable ui:loadId="form"> <ui:if ui:condition="{= bound(?subject) }"> <span>This is the form for {= ui:label(?subject) }</span> </ui:if> </ui:loadable>
When the page with the snippet above loads first, it will simply contain an empty
<div />
. The div carries an internal attribute that provides
enough information to reload the parts inside of the ui:loadable
.
It will get the HTML id specified as ui:loadId
and also have an attribute
uistate
with a partial URL for the server callback. This uistate
includes the values of all currently bound variables, the context variables and
the query graph, as well as a pointer back to the corresponding SWP snippet in the original
source.
The SWP language itself does not define a standard JavaScript function to do the actual
reloading, but TopQuadrant's SWA library does. This includes a function called
swa.load
that can be used for the example above as follows:
<a onclick="swa.load('form', { subject : '<http://example.org/MySubject>' })" href="javascript:void(0)" >
For the example above (and ui:loadable
in general) to work, your SWP
element should subclass swa:Elements
or make sure that your code is
called within an application that uses other SWA elements - this will make sure that
the required imports of jQuery and other libraries are present.
The example above will make an Ajax request to the server to reload the content of the ui:loadable
but this time with the variable ?subject
bound to the example subject.
The ui:if
would then behave differently and insert the nested span
.
The general signature of the swa.load()
function is as follows:
/** * Reloads a ui:loadable using a jQuery Ajax call. * In addition to the id of the loadable, this function can take a JavaScript * object with name-value pairs. The names will become pre-bound variables * in the reloaded view. The values must be parsable RDF nodes (in SPARQL * syntax, e.g. '<http://example.org/MyClass>' or '"2011-11-11"^^xsd:date'. * @param id the id of the loadable * @param args a JavaScript object with additional parameters * @param callback an optional callback that is called after loading * @param withoutIndicator true to bypass the insertion of an animated loading indicator */ swa.load = function(id, args, callback, withoutIndicator) { ... }
The element ui:loadable
may have its attribute ui:cache="true"
to ensure that the request is cached. This is only recommended for read-only snippets.
ui:loadable
may have an attribute ui:loadLater="true"
to indicate that the loadable shall not be executed in the first round, but be lazy loaded
in an Ajax callback as soon as the surrounding page has been finished. This is a convenient
way to break up loading of large documents into smaller transactions, giving more instant feedback
to the users while the lower sections of a page are still loading.
ui:loadable
may also have an attribute ui:loadOnDemand="true"
to indicate that the content of the loadable shall only be inserted if an explicit JavaScript
call has been made to trigger the load. This means that it will only create a placeholder div
and wait until someone calls swa.load
or similar.
See ui:handle
and ui:thenLoadId
for another convenience
feature of SWP related to even-driven reloading of page fragments.
As a general rule of thumb, you should avoid embedding <script>
tags into
SWP body elements. Instead, you should store the JavaScript code in a text (*.js) file and import
it into the HTML document's head (through ui:headIncludes
).
TopBraid includes the function ui:functionCall
that makes it easy to interact
between SWP HTML code and those externally defined JavaScript functions. For example
<ui:group let:arg="Hello, World"> <a onclick="{= ui:functionCall('myapp.function', ?arg) }" ... </ui:group>
creates the following output
<a onclick="myapp.function("Hello, World")" ...
This way the resulting SWP code doesn't get caught up with JavaScript details and remains easier to maintain and modular. Furthermore, the separation into different files is more natural if a development team consists of HTML layout and JavaScript experts.
Many Ajax widgets such as tree viewers require a pointer to a URL from which they get
JSON or XML to dynamically populate content.
The URLs can be created using ui:createLink
, and the arguments expected
by the Ajax widget can be implemented as named SWP arguments.
The resulting JSON can then be created by a view that does not return HTML
but only text nodes. As explained earlier, it is possible to create arbitrary text
documents, as long as the root element is ui:group
.
SWP control structures such as ui:forEach
and ui:if
can be used in either case, also to produce text nodes only.
Any SWP element that is exposed to the outside world via web service requests needs to
declare a ui:responseType
. For HTML elements this would be ui:HTML
,
and similar predefined types exist for JSON etc.
The base class ui:Services
should be used by services that do not generate HTML
but either only have side effects (ui:update
etc) or produce JSON and similar output.
In the case of JSON callbacks, simply subclass ui:JSONService
.
SWP includes a library called swon
that contains pre-defined elements
that make it easy to produce JSON from SPARQL Result Sets. Open the swon file in TopBraid
to see the available elements.
The control element ui:setResponseHeader
can be used to set
HTTP response headers and the HTTP status code.
ui:update
, ui:transaction
and ui:add/remove/setPropertyValue
SWP is often only used to create visualizations.
However, application development often also requires updates to some database,
for example in response to editing a form.
In order to be maximally useful and complete, SWP provides a control element
ui:update
that may contain SPARQL UPDATE requests.
The syntax of ui:update
is illustrated in the example below:
<ui:setContext ui:queryGraph="{= ?teamGraph }"> <ui:update ui:updateQuery="{! INSERT { ?tag a teamwork:Tag . ?tag rdfs:label ?name . ?tag rdfs:comment ?comment . ?tag sioc:has_creator ?user . ?tag teamwork:manager ?user . ?tag teamwork:status teamwork:Uncommitted . ?tag dcterms:created ?timeStamp . } WHERE { BIND (afn:now() AS ?timeStamp) . BIND (smf:userWithName(smf:currentUserName()) AS ?user) . BIND (smf:buildURI("<urn:x-tags:{?name}>") AS ?tag) . } }"/> </ui:setContext>
The example above shows that UPDATE queries must be embedded into a SWP document
using {! ... }
.
Prior to executing such updates, the query graph should be set to the target graph that is
being updated, to help the engine figure out the correct transaction and persistence mechanisms.
If database transactions are supported and needed, multiple ui:update
elements
can be grouped together by a ui:transaction
element.
This will make sure that all updates are regarded as atomic operation by the engine.
Inside of ui:transaction
you can use ui:preCondition
to
evaluate constraints that need to be valid before the updates should happen.
This is illustrated in the following example which creates a new instance of a given
class with a given uri.
<-- ?uri is the URI for the new instance, ?type the rdf:type --> <ui:transaction ui:logMessage="Adding instance of {= ui:label(?type) }"> <ui:preCondition ui:errorMessage="{# SELECT ?message WHERE { FILTER (EXISTS { ?uri ?anyP ?anyO } || EXISTS { ?anyS ?anyP ?uri }) . BIND (fn:concat('The URI ', ?uri, ' is already used' ) AS ?message) . } }"> <ui:update ui:updateQuery="{! INSERT { ?uri a ?type . } WHERE { } }" /> </ui:preCondition> </ui:transaction>
The output of ui:transactions is a JSON object with details about the change that was performed. This information may be used by the client to update the UI. By default, ui:transaction will write this JSON object into the output stream. To switch this off, wrap the transaction as follows:
<ui:setContext ui:silentTransactions="true"> <ui:transaction> .... </ui:transaction> </ui:setContext>
ui:transaction
may have an attribute ui:checkConstraints="true"
, which
is currently only supported on graphs that are under EVN/RDM control.
If set to true, the system will reject transactions that lead to constraint violations.
For simple updates affecting a single subject/predicate combination only, SWP includes several
convenience elements, which internally use ui:update
to do their job.
The elements ui:addPropertyValue
, ui:removePropertyValue
and ui:setPropertyValue
all share the same arguments:
Argument | Description |
---|---|
ui:this |
The subject to update |
arg:property |
The property to add/remove/set |
arg:value |
The object (value) to add/remove/set |
ui:addPropertyValue
will add the provided value (if bound).
ui:removePropertyValue
will remove the produced value (if bound).
ui:setPropertyValue
will replace any existing value with the provided value (if bound).
Finally, the control element ui:removePropertyValues
removes any value
for the given subject/predicate combination.
An example of ui:setPropertyValue
can be found in the section on ui:handle
.
ui:handle
It is a common scenario in rich client (Ajax) interfaces to have callbacks to the server in response to a user action. For example, a click on a hyperlink may need to issue a server call to change the state of a resource, i.e. add or delete a triple. Such scenarios can be achieved "by hand" via JavaScript code that calls a servlet implemented as a custom SWP element. However, writing this JavaScript code is often complicating and leads to a rather ugly mixture of client and server code.
The ui:handle
keyword can help create more maintainable code for such scenarios.
The ui:handle
element needs to be placed within an event source (such as a
<a>
tag) and can have additional children
that are executed only when the specified event is triggered by the user.
This is illustrated in the following example.
<div let:graph="{= ui:createSessionGraph() }" let:id="{= ui:uniqueId() }"> <ui:setContext ui:queryGraph="{= ?graph }"> <ui:setPropertyValue arg:property="owl:versionInfo" arg:value="{= 0 }" ui:this="{= owl:Thing }"/> <ui:loadable ui:loadId="{= ?id }"> <a let:old="{= spl:object(owl:Thing, owl:versionInfo) }"> <span>Click to increment {= ?old }</span> <ui:handle ui:event="onclick" ui:thenLoadId="{= ?id }"> <ui:setPropertyValue arg:property="owl:versionInfo" arg:value="{= ?old + 1 }" ui:this="{= owl:Thing }"/> </ui:handle> </a> </ui:loadable> </ui:setContext> </div>
ui:handle
can have an argument ui:thenLoadId
that will cause it to
reload a given ui:loadable
after the event handling has been finished.
It is also possible to execute additional JavaScript code before the body of the handler is executed.
This code must be specified with the attribute ui:script
of the ui:handle
element.
ui:tempGraph
It is sometimes necessary to keep track of status changes while a SWP document
is being created. For example, assume you want to recursively walk through an RDF tree
structure and need to keep track of which nodes have already been visited.
SWP provides a dedicated named graph ui:tempGraph
that can be
used at execution time to write and query "temporary" RDF triples.
The graph ui:tempGraph
is local to a SWP engine execution, i.e.
it starts empty and will be completely wiped out when the page rendering has been
completed. The following example saves a temporary triple:
<ui:update ui:updateQuery="{! INSERT { GRAPH ui:tempGraph { ?node ex:visited true . } } WHERE { } }"/>
Inside of the same request, you can later query those temporary triples using
<ui:if ui:condition="{= spl:objectInGraph(?node, ex:visited, ui:tempGraph) }">...
An SWP engine may define any number of temporary graphs, but they all must start with
ui:tempGraph
, e.g. ui:tempGraph1
would be another graph.
ui:parse
Some RDF models contain XML mark-up, e.g. stored in strings or XML literals.
In order to insert those "raw" snippets into a SWP document, the control
element ui:parse
can be used.
This takes a string and attempts to parse it into valid XML document with a single
root element.
The following example illustrates this, and also introduces a common problem in
generating HTML pages from ontologies. Assume that a given resource ?this
has an rdfs:comment
consisting of multiple rows.
TopBraid has a built-in function html:encodeBRs
that takes those comments
and replaces native line breaks into proper HTML line breaks with <br />
elements. However, the result of this function is not an XML document snippet but
another string. That string can be inserted into the target document using ui:parse
as shown below:
<ui:group let:comment="{= spl:object(?this, rdfs:comment) }"> <ui:parse ui:str="{= html:encodeBRs(?comment) }"/> </ui:group>
If the ui:parse
element has children, they will be
executed and their output inserted into the last element created
from the parsed string. This can be used to dynamically compute
element or attribute names when generating XML output. In the
following example, an element whose name is provided in the
variable ?elementName
is created, with child content
(requires TopBraid 6.2+):
<ui:parse ui:str="<{= ?elementName }/>" ui:xml="true"> ... child content ... </ui:parse>
ui:bind
and ui:stringify
The following example introduces the control elements ui:bind
and ui:stringify
which turn a sub-tree of the produced DOM elements into a plain string literal.
This can be used, for example, to print HTML into a string so that the string can be distributed as part of an email
(use SPARQLMotion's sml:SendEMails
module for that specific use case), or to turn a JSON structure
created with the SWON namespace into a payload for a web service call.
<ui:bind> <ui:stringify ui:varName="text"> <div>Hello World</div> </ui:stringify> <span>The text is {= ?text }</span> </ui:bind>
In the example above, the elements inside of the ui:stringify
are executed and then turned into
the string literal "<div>Hello World</div>"
. This literal then becomes bound to a variable that can be accessed within the other children
of the ui:bind
element.
By default, ui:stringify
produces HTML output but takes an optional argument ui:format
to specify other mime types such as JSON.
ui:CDATA
CDATA sections can be created using the built-in control element ui:CDATA
.
This is typically used for XML mime types only, not HTML.
To produce complex content including nested elements, combine with ui:bind.
<ui:CDATA ui:data="Hello, {= ?name }" />
ui:return
SWP services can produce RDF files in Turtle serialization.
The control element ui:return
must be used at the point of the SWP script
where the execution shall end, and the service must return the Turtle response type.
The following example produces a temporary RDF graph and produces Turtle output.
<ui:setContext ui:queryGraph="ui:tempGraph"> <ui:update ui:updateQuery="{! INSERT { owl:Thing rdfs:label 'Ding'@de . } WHERE { } }" /> <ui:return /> </ui:setContext>
The snippet above needs to be the ui:prototype
of a subclass of ui:TurtleServices
.
For example, if the subclass is called my:TurtleService
then the servlet call from TBC-ME would be
http://localhost:8083/tbl/swp?_viewClass=my:TurtleService
.
ui:setPrefix
The control element ui:setPrefix
can be used to set a namespace prefix in the currently
active query graph. This assumes that the graph is writable, and should be used with care.
This is defined as a control element - not a function - because functions cannot have side effects.
Example:
<ui:setPrefix ui:prefix="ex" ui:namespace="http://example.org/ns#" />
It is possible (and recommended) to use Cascading Style Sheets (CSSs) for defining the layout of SWP documents.
When a UI element is rendered into an HTML page, then the engine will check
for a CSS file ending with .css
at the location of the SWP
document's base URI.
For example, the CSS file of the namespace
http://example.org/myproject/myrdf
would be expected at
http://example.org/myproject/myrdf.css
.
The SWP engine will automatically include any such stylesheet into
the resulting HTML document.
Individual styles of any HTML element can be set using the normal style
attribute, with semicolon-separated name/value pairs.
In addition to that, the SWP HTML vocabulary defines many RDF properties
in its css
namespace.
If present, the values of those style properties will be concatenated into
a single style
attribute.
This is illustrated in the following example:
<div css:margin-left="16px" />
This will be rendered into the following HTML:
<div style="margin-left:16px" />
The W3C's Scalable Vector Graphics (SVG) is becoming an increasingly popular graphical language. SVG is being natively supported by most web browsers (except for the outdated version of IE that is bundled as internal web browser of Eclipse on Windows). With its SVG support, SWP can be used to generate SVG documents such as box-and-lines diagrams, statistical charts and animations from RDF data.
In order to use SVG with SWP, you need to import the SWP SVG vocabulary from http://uispin.org/svg. This schema defines classes for all SVG Tiny 1.2 elements and properties for all SVG attributes and styles.
The following example ui:instanceView
can be used to
display any RDF resource that has an rdfs:label
as
an oval with a label inside it. Note that uppercase names are needed
for the SVG classes, e.g. svg:Ellipse
.
<svg:G> <svg:Ellipse svg:cx="100" svg:cy="48" svg:rx="60" svg:ry="26" css:fill="yellow" css:stroke="black"/> <svg:Text svg:x="70" svg:y="50">{= ui:label(?this) }</svg:Text> </svg:G>
Generated SVG Source | Screen Rendering |
---|---|
<svg:svg xmlns:svg="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink"> <svg:g> <svg:ellipse cx="100" cy="48" rx="60" ry="26" fill="yellow" stroke="black"/> <svg:text x="70" y="50">Statement</svg:text> </svg:g> </svg:svg> |
ui:chunk
By default, SWP services hold back all output, and only send it to
the client when processing is complete. In services that
return large responses, it is sometimes desirable to return partial
results before processing is complete. This can be achieved with
ui:chunk
.
This element renders its children, and immediately sends the result to the client. The following example sends a JSON object for each row in a result set:
<ui:forEach ui:resultSet="{= ?rs }"> <ui:chunk> <swon:Object> ... </swon:Object> </ui:chunk> </ui:forEach>
Note that the output of parent elements of ui:chunk
will
still be cached until script completion. This
means care must be taken if any prologue or epilogue is required before
the first or after the last chunk (or, indeed, between chunks). To wrap
the example above into a top-level JSON array, we cannot simply wrap the
ui:forEach
into a swon:Array
because its
opening bracket would only be sent after all the chunks. Instead,
we would have to put prologue and epilogue into their own chunks,
resorting to direct literal output:
<ui:chunk> <ui:group>[</ui:group> </ui:chunk> <ui:forEach ui:indexVar="i" ui:resultSet="{= ?rs }"> <ui:chunk> <ui:if ui:condition="{= ?i > 0 }">,</ui:if> <swon:Object> ... </swon:Object> </ui:chunk> </ui:forEach> <ui:chunk> <ui:group>]</ui:group> </ui:chunk>
SWP can be used in an object-oriented style to enhance ontology classes with behavior and views.
By default, all SWP elements are "global", i.e. their life cycle does not depend on any class.
However, as SWP libraries grow, it often becomes increasingly hard to maintain and understand them.
Programming languages have introduced the concept of object-orientation as a means of organizing
data structures into classes that do not only encapsulate state but also behavior.
In many cases, RDF/OWL ontologies provide a natural way of defining a domain, yet they are
limited to describing the state (i.e. properties) of resources, not their behavior.
SWP fills this gap by allowing to attach methods, rules and views to classes.
While the SWP engine by itself does not make use of these structures, they may help organize
your SWP codebase by exploiting the structure of the domain model as a backbone.
By associating SWP snippets with classes from a domain model, it also becomes possible to
query the domain model (in the ui:graph
) and thus dynamically discover available
features, and react on changes to the ontologies.
?this
and ui:this
The variable ?this
has a special meaning in SWP.
Like in SPIN, ?this
points to a context resource - usually the instance of a
class that the execution has started with.
Whenever ?this
has a value, e.g. from an ui:instanceView
, it will be
propagated into the prototypes of any nested element that are called from the current scope.
However, you can change the value of ?this
at any time, either by passing a value
of the argument ui:this
into a prototype, or simply with a let:this="{= ... }"
assignment in a surrounding block - ui:group
is often good for that.
The use of ?this
often saves you from having to explicitly pass argument around.
Instead, "attach" a view element or operation to a class (see below) and let the engine figure
out the context.
ui:DataViews
In many use cases, an SWP view is being created to visualize some aspect of a given resource.
For example, a complex display of instances of ex:Person
may consist of multiple
helper elements, for personal details and family relationships or work history.
The property ui:viewElement
can be used to "attach" such helper view elements
to a given class. Its meaning is simply to state that this element is using instances of
the associated class as values of ?this
.
The information represented by ui:viewElement
can be queried by (generic)
user interface components, e.g. to present a list of available visualizations for objects
of a certain kind.
ui:DataViews
are a special type of views that can be associated with classes.
A data view takes the current context resource ?this
as starting point and fetches
tabular data for it which can then be displayed in various ways including grid displays or charts.
Each ui:DataView
has an argument arg:dataProvider
that must point
to a SPIN template. In that template, the variable ?this
has the same meaning
as in the rest of the SWP and SPIN and points to the currently displayed instance.
SWP libraries such as SWA provide implementations of data views and also allow users to
interactively select which data views to open up for a given resource.
SWP is not just a language to display visualizations, but its control elements such as
ui:update
and ui:forEach
are useful tools to manipulate data, too.
The operations architecture of SWP makes it possible to associate discrete data manipulation
features with classes, similar to how functions or methods are associated with classes in
object-oriented languages.
As an example, assume you have an ontology with a class ex:Person
and a property
ex:knows
. You could define an operation (in particular a ui:Method
)
called ex:addFriend
and associated it with the ex:Person
class via
the property ui:method
.
The ex:addFriend
method would take an argument arg:newFriend
.
In the prototype of this method you could use ui:update
to add the given value of
?newFriend
with the context person ?this
.
The built-in element ui:addPropertyValue
could be used for that purpose, but any
complex combination of updates and nested if conditions would be available too.
A user interface component could then invoke your new method, e.g. as part of a ui:handle
whenever the user drags and drops a friend onto the view.
While ui:Methods
are called on demand and explicitly only, ui:Rules
are
snippets of behaviorial code that are executed by a surrounding rules engine.
For example, you could associate the ex:Person
class with a rule that adds
a ex:sibling
relationship for every instance of ex:Person
that has
at least one shared parent. The rule itself would be implemented through a ui:update
that does its checking in the WHERE clause. The property ui:rule
would be used to
link the rule with the class.
There is currently no default rules engine in SWP, but it is easy to invoke them
even manually. The control element ui:dynamicView
can be used to execute a rule,
using the rule itself as value of ui:class
and ?this
as value of
ui:this
.
SWP attempts to link the worlds of user interface development (e.g. in HTML) with RDF-based data modeling. Bridging the various languages in those areas is not always easy, and some people may prefer to stick to existing infrastructure and tools to maintain their UI files. For this reason, SWP offers two mechanisms for representing SWP snippets: either as text files or as RDF structures. Both are explained in the following subsections.
The starting point of any SWP application is the RDF model that holds the
view references. Properties such as ui:instanceView
and ui:prototype
may be used to link concepts with the XML structures of views.
Any of those properties may actually point to an external text file that contains
the XML code in a format that is easy to edit with conventional tools.
In order to link to an external file, the file needs to be stored on the same
(server or file) location as the SWP file. For example, assume you have a file
snippet.uispin
and an RDF model containing SWP definitions with
the base URI http://example.org/models/mymodel
.
The .uispin
file needs to be placed into the same directory as
the model, and the reference to it (e.g. using ui:instanceView
)
needs to use the URI http://example.org/models/snippet.uispin
as a URI resource.
If a local copy of that file is present in the same folder as the RDF file,
then editing tools like TopBraid Composer will use the local copy instead of
trying to resolve that URL on the web.
SWP not only defines properties that can be used to link RDF-based data models with visualizations, but also includes its own RDF-based data model for representing the visualizations themselves. The basic idea is that any XML document can be represented by RDF (blank) nodes and RDF properties. XML elements are mapped to instances of classes, and the XML attributes become property values. As shown in the class diagram below, SWP utilizes a small collection of classes for representing the various kinds of XML nodes.
The details of this internal representation are usually not relevant for end users. SWP comes with default RDF vocabularies for HTML and SVG, but any other XML vocabulary can be used as well.
The key benefit is that this approach makes it possible to store SWP snippets directly
together with the RDF model that it renders. Often those triples will live in a separate
graph and will be stored in a file ending with .ui.*
to keep model and
view triples cleanly separated.
But with everything in RDF, it becomes possible to track and update dependencies between
the SWP snippets and the model: if a resource is renamed, then an editing tool can
easily also rename all references to that resource in the SWP snippets.
Furthermore, any SWP definitions can be queried using the special named graph ui:graph
which contains the union of all .ui.*
files in the workspace.
Like any programming language, SWP development often requires several iterations, bug fixing and trial-and-error to get the desired output. If your code delivers unexpected results, a common technique is to print out intermediate values to better understand what the script does under the hood. The following built-in control elements are useful for that purpose. Also check out the SWP help page in TopBraid Composer for tool-specific debugging features.
ui:log
The control element ui:log
writes a message to the system log. In TopBraid
Composer, the system log is displayed in the Error Log view. In TopBraid Live, the
system log is a log file.
Log messages have an associated log level:
<ui:log ui:error="This is an error message"/> <ui:log ui:warn="This is a warning message"/> <ui:log ui:info="This is an information message"/> <ui:log ui:debug="This is a debug message"/>
The argument ui:trace="true"
can be set to include a stack trace in the log.
ui:debug
SWP can be executed in debug mode.
In TBC, the easiest way to activate debug mode is to check the corresponding check box in the SWP Preferences page.
Programmatically, debug mode can be activated by setting the context variable debug to true.
For example this can be done via <ui:setContext ui:varName="debug" ui:varValue="{= true }">...
and for web service calls this can be activated by passing in the HTTP parameter _contextdebug=true
.
The control element ui:debug
can be placed around elements that shall only be executed
if the system is in debug mode. This allows developers to place debugging code into the SWP without
impacting the output of the same code in production.
In the following example, ui:debug
is used to print the value of the variable ?var
onto the screen, and only do this if debug mode has been activated.
<div let:var="{= ex:myFunctionCall() }"> <span>This output is always produced</span> <ui:debug> <span>This output is only produced in debug mode. Value of ?var is {= ?var }</span> </ui:debug> </div>
ui:dumpScope
The control element ui:dumpScope
can be used to print all currently visible variable bindings.
ui:dumpScope
does not do anything if the SWP engine is not in debug mode (see above).
In TopBraid Composer, if a file is open, this opens a new Window with a table of variables and their values, that can be sorted etc.
In all other modes, this control element prints an item to the log, which is visible as Error Log view in TBC or from a server log file.
If the following code is executed in debug mode, then a table will be created with all current variable
bindings, including the value for ?var
.
<div let:var="{= ex:myFunctionCall() }"> <ui:dumpScope ui:message="Some optional context message" /> </div>
ui:dumpResultSet
It is a common requirement to understand the content of a SPARQL result set.
The control element ui:dumpResultSet
can be used to print a result set in tabular form.
It takes two arguments: ui:resultSet
points at the result set (e.g. produced via ui:call
)
and an optional ui:message
which may be useful to provide context in the log.
If the following code is executed in debug mode, then a table will be created with all rows of the given result set.
<div letrs:resultSet="{# SELECT * ... }"> <ui:dumpResultSet ui:resultSet="{= ?resultSet }" ui:message="Some optional context message" /> </div>
ui:dumpGraph
Sometimes the easiest way to understand why an SWP snippet behaves as it does is to get a copy
of a query graph and examine it with an editor.
The control element ui:dumpGraph
allows users to save a named graph to a file in
the workspace. This feature makes most sense from within TopBraid Composer only.
If the following code is executed in debug mode, then the given graph will be written into a Turtle file at the given location.
<div> <ui:dumpGraph ui:graph="<http://topbraid.org/examples/kennedys>" ui:filePath="/exampleProject/mycopy.ttl" /> </div>
ui:profile
The ui:profile
element can be placed around other SWP elements to measure their
execution time. This can be used to detect performance bottlenecks.
This element should only be used at development time, and removed before going into production.
It prints the duration together with an optional string into the console.
The following example prints the time that it takes to execute the children.
<ui:profile ui:message="Inserting a header"> <h1>Test Header</h1> </ui:profile>
This section requires TopBraid 5.5 or above.
This section is about support for parsing and processing data represented in JSON. On the topic of generating JSON, and for an alternative way of parsing JSON, see SWON.
TopBraid 5.5 introduces new built-in features of SWP for processing JSON.
Typical use cases of these features are web services that take JSON (strings) as input
and need to traverse the resulting JSON objects, arrays etc in conjunction with
the usual SWP features such as ui:forEach
and ui:update
.
The basic idea of these JSON features is that JSON objects are parsed from a string into a "native" data structure that is kept on the server for the duration of the request. Such native JSON objects can then be accessed via variables - RDF nodes that purely act as identifiers of the JSON objects. The RDF nodes representing JSON objects can be passed into functions as outlined in the following example.
<ul> <ui:json ui:str='[ { "value": 42 } ]' ui:varName="array"> <!-- Parses the given JSON string into variable ?array --> <ui:forEach ui:resultSet="{# SELECT ?object ?value WHERE { ?array ui:jsonArrayMembers ?object . BIND (ui:jsonValue(?object, 'value') AS ?value) . } }"> <ui:if ui:condition="{= bound(?value) }"> <li>Value is: {= ui:jsonString(?value) }</li> </ui:if> </ui:forEach> </ui:json> </ul>
The example above first parses the JSON string into a variable ?array
that
serves as entry point of the "native" JSON object.
This JSON object can be queried with a variety of functions, including the magic property
ui:jsonArrayMembers
that supports traversing the values within a SPARQL query.
In each iteration of the ui:forEach
loop, the variable ?object
will point at the next JSON object in the array.
The function ui:jsonValue
can be used to access name-value pairs of a JSON object.
The results of this function may be other JSON objects, arrays or JSON primitives.
In the example above, the code assumes that the objects have a field "value"
with primitive values that can be turned into xsd:string
literals using
ui:jsonString
.
For details on the various functions mentioned in the example, please consult the documentation
attached to the ui:jsonXY
resources in the SWP namespace.
The currently supported resources are:
ui:json
(SWP control element)ui:jsonArrayMembers
(SPARQL magic property)ui:jsonKeys
(SPARQL magic property)ui:jsonString
(SPARQL function)ui:jsonValue
(SPARQL function)
Starting with TopBraid 6.0, the control element ui:js
can be used to execute server-side
JavaScript, based on the Java Nashorn Engine.
Basically, when a <ui:js>
element is encountered, a specified JavaScript function
from a specified JS file is executed as part of the SWP script.
The JS function can call back into the SWP world, for example by executing ASK or SELECT queries
(as described in later subsections).
To write to the SWP output document, JS code can use the built-in function SWP.element
.
To get started, you need a JavaScript .js
file in the TopBraid workspace.
Let's say we have a file /myproject.org/js/test.js
.
In the SWP file, create an instance of sh:JSLibrary
as follows:
ex:testJSLibrary a sh:JSLibrary ; sh:jsLibrary dash:RDFQueryJSLibrary ; sh:jsLibraryURL "http://myproject.org/js/test.js"^^xsd:anyURI .
In the example above, the URL must align with the local file name, plus http://
.
The reference to the dash:RDFQueryJSLibrary
is in principle not needed but this
library includes various utilities to work with RDF nodes and triples from JavaScript.
You can now call the function testFunction
from the JavaScript file as follows:
<ui:js ui:function="testFunction" ui:library="ex:testJSLibrary" ui:varName="result"> <span>The result value of the function is {= ?result }</span> </ui:js>
Any other argument of ui:js
is mapped to arguments of the function, if the JS
function has parameters where the name matches the local name of the argument.
For example if you have a JS function function testFunction(value)
you can use
<ui:js ui:function="testFunction" arg:value="{= ... }" ... />
to pass arguments
into the JS function.
The JavaScript functions must either return RDF term objects that can be produced
with the TermFactory
or the T
function from RDFQuery, or plain JavaScript
values such as Strings, Booleans and Numbers. The engine will try to translate those plain JS
values into corresponding RDF literals.
The currently active query graph can be accessed using the variable $data
.
All this corresponds to SHACL-JS.
A reasonably complex example of using server-side JavaScript can be found in
TopBraid/Examples/starwars
.
SPARQL.ask
The built-in function SPARQL.ask
can be used to execute any SPARQL ASK query
(defined as a string), possibly with pre-bound variables.
The first argument must be the current query graph (usually $data
).
The second argument is the ASK query string.
You can use prefixes from the query graph to abbreviate the query.
The third (optional) argument is a JavaScript object with name-value pairs of pre-bound
variables for the query.
The function returns a boolean.
The following example goes into the if
if the label of owl:Thing
is indeed "Thing"
.
if(SPARQL.ask($data, "ASK { owl:Thing rdfs:label $label }", { "label" : "Thing" })) { ... }
SPARQL.expr
The built-in function SPARQL.expr
can be used to evaluate any SPARQL expression
(defined as a string), possibly with pre-bound variables.
The first argument must be the current query graph (usually $data
).
The second argument is the expression string.
You can use prefixes from the query graph to abbreviate the query.
The third (optional) argument is a JavaScript object with name-value pairs of pre-bound
variables for the query.
The function returns an RDF term object with the result of the expression.
If that's a literal, you can for example use term.lex
to get the lexical form.
See the SHACL-JS specification on details.
The following function returns the RDF string literal "rolf-michel"
.
function testExpr() { return SPARQL.expr($data, "LCASE($input)", {"input" : "Rolf-Michel"}); }
SPARQL.function
The built-in function SPARQL.function
can be used to invoke any SPARQL function
(defined as a string) and pass in its arguments in the same order as defined by the function.
The first argument must be the current query graph (usually $data
).
The second argument is the qname of the function as a string.
You can use prefixes from the query graph to abbreviate the query.
The other arguments are the argument nodes for the function.
The function returns an RDF term object with the result of the expression.
The following function returns the RDF string literal "Thing"
.
function testFunction() { return SPARQL.function($data, "afn:localname", T("owl:Thing")); }
SPARQL.select
The built-in function SPARQL.select
can be used to execute any SPARQL SELECT query
(defined as a string), possibly with pre-bound variables.
The first argument must be the current query graph (usually $data
).
The second argument is the SELECT query string.
You can use prefixes from the query graph to abbreviate the query.
The third (optional) argument is a JavaScript object with name-value pairs of pre-bound
variables for the query.
The function returns an array of JavaScript objects with name-value pairs for the query solutions.
The following example function returns the concatenation of all local names of instances of
a given class, specified as type
.
function testSelect(type) { var results = SPARQL.select($data, "SELECT ?instance ?label {\n" + " ?instance rdf:type ?type .\n" + " BIND(afn:localname(?instance) AS ?label) .\n" + "} ORDER BY ?label", { "type" : type }); var string = ""; for(var i = 0; i < results.length; i++) { string += i + ": " + results[i].label.lex + ", "; } return string; }
SWP.element
The built-in function SWP.element
can be used to call the prototype of a given
SWP element, with arguments passed in as pre-bound variables.
It basically calls back into the SWP engine to continue the execution there,
in particular to insert output into the resulting document.
The first argument is the qname of the SWP element in the SWP graph.
The second (optional) argument is a JavaScript object with name-value pairs of pre-bound
variables for the query, matching the declared arguments of the SWP element.
The following example function calls an SWP element ex:testElement
and passes in
a xsd:integer
literal.
function testSWP() { SWP.element("ex:testElement", {input: 41}); }
SWP.resultSetToArray
If the surrounding SWP script has created SPARQL result sets (for example using letrs
then the JavaScript function SWP.resultSetToArray
may be used to convert that
result set to plain JavaScript arrays (in the same format as those produced by SPARQL.select
.
The only argument into SWP.resultSetToArray
is the RDF node representing the result set,
which is typically passed into the function as a named parameter.
Any SWP engine must provide the following SPARQL functions.
It is often desirable to render RDF resources by human-readable labels
instead of their qnames or even URIs.
The function ui:label
can be used to get such a label
in an efficient way.
Implementations can chose what to return, but as a general policy the
rdfs:label
(or sub-property thereof) should be used.
This is more flexible than a direct triple match against rdfs:label
because the function may consider the current context, such as the
natural language of the requesting client.
Implementations may chose to optimize this function with native data structures because it will be used frequently.
The magic property (property function) ui:arg
can be used to query the
provided arguments of a SWP element at execution time.
This provides a powerful introspection mechanism for building generic components
that take a dynamic number of arguments.
In the following example, assume that you have a user-defined SWP class
ex:AjaxRequest
that can be used to create server callbacks with an
arbitrary number of arguments. An instance might look as follows:
<ex:AjaxRequest arg:firstName="John" arg:lastName="Doe" />
The class ex:AjaxRequest
may have the following prototype:
<script> ... <ui:forEach ui:resultSet="{# SELECT ?param value WHERE { ?property ui:arg ?value . BIND (afn:localname(?property) AS ?param) . } }"> ... generate JavaScript... </ui:forEach> ... </script>
In this prototype, the result variables of the loop will be bound to "firstName" / "John",
and "lastName / "Doe".
It is common to combine the ui:arg
function with the named graph
ui:graph
to query for additional properties of the argument predicates,
such as their spl:valueType
and spl:defaultValue
.
The SPIN function ui:systemPreference
can be used
to access system settings of the SWP engine.
In some cases (such as to retrieve Google API Keys), components
need to adapt to the local environment and can not create themselves
by just looking at the models.
The function takes a key (string) and returns the value of the
preference (if set).
The SPIN function ui:uniqueId
creates a unique identifier
string that can be then be assigned to components, e.g. as HTML ids.
The function takes no arguments and returns a different string with
each invocation.
The function ui:encodeNode
renders a given RDF node to
a string, using the following conventions:
<@id>
.
The sibling function ui:decodeNode can be used for the reverse direction.
The function ui:decodeNode
creates an RDF node from
a string created by ui:encodeNode
earlier.
This pair of functions is particularly useful in representing
resources across client-server interactions
The function ui:encodeURL
encodes a given string
so that it can be used as part of a URL, e.g. for Ajax callbacks.
The function ui:escapeJSON
converts a given string to a JSON-friendly
string that can be sent across the wire. In particular, this replaces ' with \'
so that string literals can be placed between matching ' braces.
The function ui:graphWithImports
casts a given named graph resource to
an encoded resource that instructs the engine to also include the owl:imports
closure
(and the system graph). In TopBraid, this adds a '+' as in <http://my.com/graph+>.
Returns the original graph unchanged if it already has the additional encoding.
The function ui:graphWithoutImports
does the opposite as ui:graphWithImports
.
The function ui:unionOfGraphs
can be used to create a virtual union graph
of any number of other subgraphs. It takes a variable number of arguments, each of which
need to be URIs of graphs. The result is another graph URI that the SWP engine (and TopBraid
in general) know how to resolve into a graph that has all given graphs as subgraphs.
The sibling function ui:unionOfGraphsFromResultSet
can be used within SWP
if the number of subgraphs is unknown, and will produce a union graph of all graphs
delivered by a given result set.
The function ui:concat
is almost identical to the SPARQL 1.1 built-in
CONCAT
(and its alias fn:concat
). The important distinction
through is that the latter fail if one of the arguments are unbound. For example if you
call CONCAT("Hello, ", ?person)
then the function will output nothing if
?person
is unbound. ui:concat
will still produce what it can
and output Hello
even if ?person
is unbound.
ui:concat
is also used internally by the SWP engine if you are using an inline
expression in an attribute value, e.g. class="my-class {= ?other}"
will be
turned into a call to ui:concat
under the hood. You usually won't have to care
about this because the SWP parser does this automatically.
The function ui:currentQueryGraph
returns the URI resource for the currently
active query graph. This is either set using ui:setContext
or via the
_base
argument of the servlet.
Note that SPARQL's GRAPH { ... }
clause can also change the active query
graph for the enclosed part of the query, but ui:currentQueryGraph()
called inside will not reflect that change.
The function ui:param
can be used to query any additional parameter that has
been passed into the SWP servlet. For example, if the servlet has been called using
uispin?_resource=owl:Thing&name=John
then you can use ui:param('name')
to get the value "John"
. By default this function will create string literals.
If you want to automatically cast into any other datatype, then you can pass this in as second
argument, e.g. ui:param('date', xsd:date)
. If the value is a URI, then you can specify
any rdfs:Class
as second argument, to get a proper resource. In the latter case,
the function will remove '<' and '>' around the URI.
The magic property ui:params
delivers the keys of all parameters that have been passed
into the currently executing SWP servlet. The magic property takes no arguments on the left hand
side and an unbound variable on the right that will contain the names of all parameters.
The function ui:contextValue
provides access to the values of context values
that were previously set using ui:setContext
.
The only argument of this function is the name of the variable to get.
The function ui:functionCall
is the recommended mechanism for creating JavaScript
function calls. In a typical use case, this function is the only value of an event listener,
e.g. the onclick attribute of a hyperlink. The first argument is the name of the function to call,
while the other arguments are inserted as arguments to the function. ui:functionCall
makes sure that characters are correctly escaped, making SWP code much easier to read.
See above for examples.
ui:setGlobalVar
is a built-in control element that sets a "global variable"
that is visible for the duration of the (remaining) SWP service request, and which can be queried using
another function, ui:globalVarValue
.
Global variables work differently from context variables.
The scope of context variables only includes the execution subtree of the ui:setContext
element.
Global variables set with ui:setGlobalVar
are visible to any subsequently evaluated SWP element.
This is illustrated by the following example.
<ui:group> <div> <ui:setGlobalVar ui:name="myVar" ui:value="{= 42 }" /> <ex:doSomething arg:value="{= ui:globalVarValue('myVar') }" /> <ui:setGlobalVar ui:name="myVar" ui:value="{= ui:globalVarValue('myVar') + 1 }" /> </div> <!-- the global variable would still be accessible here --> </ui:group>
Session graphs are a feature of SWP that uses HTTP session variables to track information
that shall be kept alive for as long as a user interacts with the server.
In a nutshell, session graphs can be created using the SPARQL function ui:createSessionGraph
at any point in time.
The function produces a URI for a newly created session graph, which is initially empty.
Each session graph produced by this function can be accessed by that URI as a named graph,
and features such as ui:update
can be used to modify that graph, e.g. to keep
track of some data relevant for the user's session.
Once the graph is no longer needed, applications should make sure to call ui:deleteSessionGraph
to release resources.
Alternatively, the session graphs will be cleaned away by the servlet once the session with the client
ends or times out.
As a result of this architecture, session graphs should be used wisely and only for reasonably small graphs.
The URL of the SWP Core Schema is http://uispin.org/ui
The URL of the (tiny) SWP Link Schema is http://uispin.org/uilink
The URL of the SWP CSS Vocabulary is http://uispin.org/css
The URL of the SWP HTML Vocabulary is http://uispin.org/html
The URL of the SWP SVG Vocabulary is http://uispin.org/svg