In this section we provide a few more examples of server functions. We have implemented:
some of the XPath functions that did not find their way into X-Query;
particular string functions which might prove useful in both X-Query and XQuery 4;
a function similar to the XSLT document()
function.
We call this server extension module xqx
and name the
package accordingly tamino.sxs.xqx
. The complete source code,
including the file Install.xml and the Javadoc HTML file,
is contained in the directory sxsjxqx in the documentation
set.
Concatenates strings.
public String concat (String op1, String op2)
public String concat3 (String op1, String op2, String op3)
public String concat4 (String op1, String op2, String op3, String op4)
The original XPath concat()
string function allows any
number of arguments (at least 2). We have implemented the functions
concat
, concat3
, and concat4
to cover
cases with two, three, and four arguments. The implementation could hardly be
simpler:
public String concat (String op1, String op2) { return op1+op2; }
public String concat3 (String op1, String op2, String op3) { StringBuffer sb = new StringBuffer(op1); sb.append(op2); sb.append(op3); return sb.toString(); }
public String concat4 (String op1, String op2, String op3, String op4) { StringBuffer sb = new StringBuffer(op1); sb.append(op2); sb.append(op3); sb.append(op4); return sb.toString(); }
xqx.concat(name/last, name/first)
constructs an ID from last name and first name.
xqx.concat3(birthDate,"T","04:30:00")
appends a time to a date.
True if op1 contains op2.
public boolean contains (String op1, String op2)
The string function contains()
is a bit more demanding:
public boolean contains (String op1, String op2) { return (op1.indexOf(op2) >= 0); }
xqx.contains(title,"Moon in June")
is true for all titles containing "Moon in
June". In contrast to the X-Query contains-operator
(~=
), this function is case sensitive.
Returns substring before op2.
public String substringBefore (String op1, String op2)
The original XPath name is substring-before()
.
public String substringBefore (String op1, String op2) { int i = op1.indexOf(op2); if (i > 0) return op1.substring(0,i); return ""; }
xqx.substringBefore(performedAt/time,"T")
returns the date part of dateTime
element
performedAt/time
.
Returns substring after op2.
public String substringAfter (String op1, String op2)
The original XPath name is substring-after()
.
public String substringAfter (String op1, String op2) { int i = op1.indexOf(op2); if (i >= 0) { int b = op2.length()+i; if (b < op1.length()) return op1.substring(b); } return ""; }
xqx.substringAfter(performedAt/time,"T")
returns the time part of dateTime
element
performedAt/time
.
Returns substring from position p with length l.
public String substring (String s, int p, int l)
Two substring
functions are provided in XPath:
substring(s, p, l)
returns a substring of s
starting at position p
with length l
.
substring(s, p)
returns a substring of s
starting at position p
up to the end of the string.
Because Tamino server extensions do not
support method overloading, we implement only the three-argument form. The
length -1
indicates the two-argument version (substring up to end
of string).
public String substring (String s, int p, int l) { p--; // Java counts from 0, Xpath from 1 if (l < 0) return s.substring(p); return s.substring(p,p+l); }
xqx.substring(1999-05-01,6,2)
returns the month ("05"
).
xqx.substring(1999-05-01,6,-1)
returns the month and the day ("05-01"
).
Removes white space from beginning and end.
public String trim (String s)
Here we implement a function that is not available in XPath but works
similarly to the XPath string function normalize-space()
. However,
trim()
only removes leading and trailing white space from a
string, leaving white space in the interior of the string intact. Again, the
implementation is trivial:
public String trim (String s) { return s.trim(); }
If a document instance of document type style
contains an
element:
<name> swing </name>
then the simple path expression:
style/name
returns "swing" with leading and trailing carriage-return characters and blanks. In many cases this is unwanted, for example if we want to use the string as a search string or concatenate it with other strings.
xqx.trim(style/name)
removes these unwanted white space characters.
Normalizes white space.
public String normalizeSpace (String s)
The full implementation of normalize-space()
requires a bit
more coding:
public String normalizeSpace (String s) { StringBuffer sb = new StringBuffer(s.length()); boolean whiteSpace = true; for (int i=0; i < s.length(); i++) { char c = s.charAt(i); if ((c==' ')||(c==13)||(c==10)||(c==9)) { if (!whiteSpace) { whiteSpace = true; sb.append(' '); } } else { sb.append(c); whiteSpace = false; } } if (whiteSpace) { sb.deleteCharAt(sb.length()-1); } return sb.toString(); }
If a document instance of document type album
contains an
element:
<title> Open Up (Whatcha gonna do for the rest of your life?) </title>
then the simple path expression:
album/title
returns the title with all carriage-return, linefeed, tab, and blank characters.
xqx.normalizeSpace(album/title)
normalizes this string to:
Open Up (Whatcha gonna do for the rest of your life?)
Returns length of string.
public int stringLength (String s)
The original XPath name is string-length()
. The
implementation is trivial:
public int stringLength (String s) { return s.length(); }
xqx.stringLength(name/last)
returns the length of the element name/last
including white
space characters.
jazzMusician[xqx.stringLength(./name/last)=9]
returns all jazz musicians whose last name is 9 characters long.
Fetches document specified by path.
public String qdoc (String qpath)
Finally, here is a function that is similar to the
document()
function in XPath, but constrains itself to documents
stored in the same database. This allows us to implement the retrieval of the
document via a Tamino callback function, instead of
retrieving the document via an HTTP request.
The function takes one argument, namely an X-Query expression that identifies the document (or document part). This expression must include the collection name. Before returning a result, it strips off all of Tamino's packaging for query results and returns the data as a vanilla XML node (or node list).
public String qdoc (String qpath) throws java.lang.Exception {
// split operand into collection and relative path int p = qpath.indexOf("/", 0); if (p<0) throw(new Exception("Proper syntax is collection/path")); StringBuffer response = new StringBuffer(1024); // do query to Tamino via SXS callback int ret = SxsXMLXql (qpath.substring(0,p), qpath.substring(p+1), response); if (ret != 0) // Error handling as shown in section Derived elements // .... } // Create document string String docstr = response.toString(); // We have to isolate the returned node(s) int t = docstr.indexOf("<xql:result>", 0)+12; if (t >= 12) { int e = docstr.indexOf("</xql:result>", t); return docstr.substring(t,e); } return ""; }
Since qdoc()
– in contrast to document()
–
does not require the full specification of a URL and removes the
Tamino response wrapping, queries using
qdoc()
are significantly simpler. The following query finds all
collaborations in which jazz musicians whose last name is
"Parker" participated:
collaboration[jazzMusician/@ID=xqx.qdoc( "encyclopedia/jazzMusician[name/last='Parker']" )/@ID]
The next query finds all jazz musician documents taking part in such
collaborations. We see that qdoc()
can be nested. Note that we
have to use the "
notation for the innermost level of
string demarcations.
jazzMusician[@ID = xqx.qdoc( "encyclopedia/collaboration[jazzMusician/@ID=xqx.qdoc( 'encyclopedia/jazzMusician[name/last="Parker"]' )/@ID]")/jazzMusician/@ID]
Similarly, the following query returns all album
documents
that are the result of such a collaboration:
album[@albumNo = xqx.qdoc( "encyclopedia/collaboration[jazzMusician/@ID=xqx.qdoc( 'encyclopedia/jazzMusician[name/last="Parker"]' )/@ID]")/result/@albumNo]