/*
* Copyright 2009 DuraSpace.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mulgara.query.xpath;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.xpath.XPathFunctionException;
import org.jrdf.graph.BlankNode;
import org.mulgara.query.functions.MulgaraFunction;
import org.mulgara.query.functions.MulgaraFunctionGroup;
import org.mulgara.util.URIUtil;
/**
* Container for functions in the afn domain.
*
* @created Dec 14, 2009
* @author Paula Gearon
* @copyright © 2009 <a href="http://www.duraspace.org/">DuraSpace</a>
*/
public class AfnFunctionGroup implements MulgaraFunctionGroup {
/** The prefix for the afn: namespace */
static final String PREFIX = "afn";
/** The afn: namespace */
static final String NAMESPACE = "http://jena.hpl.hp.com/ARQ/function#";
// static final String NAMESPACE = "http://openjena.org/ARQ/function#";
/**
* Get the prefix used for the namespace of these operations.
* @return The short string used for a prefix in a QName.
*/
public String getPrefix() {
return PREFIX;
}
/**
* Get the namespace of these operations.
* @return The string of the namespace URI.
*/
public String getNamespace() {
return NAMESPACE;
}
/**
* Get the set of SPARQL functions.
* @return A set of MulgaraFunction for this entire group.
*/
public Set<MulgaraFunction> getAllFunctions() {
Set<MulgaraFunction> functions = new HashSet<MulgaraFunction>();
functions.add(new Substring2());
functions.add(new Substring3());
functions.add(new Substr2());
functions.add(new Substr3());
functions.add(new Strjoin());
functions.add(new Sha1sum());
functions.add(new Now());
functions.add(new Bnode());
functions.add(new Localname());
functions.add(new Namespace());
functions.add(new Min());
functions.add(new Max());
functions.add(new Pi());
functions.add(new E());
return functions;
}
/**
* As for {@link org.mulgara.query.xpath.FnFunctionGroup.Substring2}, but with Java semantics.
* @see http://jena.hpl.hp.com/ARQ/function#substr
*/
static private class Substring2 extends MulgaraFunction {
public int getArity() { return 2; }
public String getName() { return "substring/2"; }
public Object eval(List<?> args) throws XPathFunctionException {
String source = args.get(0).toString();
int start = ((Number)args.get(1)).intValue();
// perform boundary checking
int len = source.length();
if (start < 0) start = 0;
if (start > len) start = len;
return source.substring(start);
}
}
/**
* As for {@link org.mulgara.query.xpath.FnFunctionGroup.Substring3} but with Java semantics.
* @see http://jena.hpl.hp.com/ARQ/function#substr
*/
static private class Substring3 extends MulgaraFunction {
public int getArity() { return 3; }
public String getName() { return "substring/3"; }
public Object eval(List<?> args) throws XPathFunctionException {
String source = args.get(0).toString();
int start = ((Number)args.get(1)).intValue();
int end = ((Number)args.get(2)).intValue();
// perform boundary checking
if (start < 0) start = 0;
int len = source.length();
if (start > len) {
start = len;
end = len;
}
if (end > len) end = len;
if (end < start) end = start;
return source.substring(start, end);
}
}
/**
* Synonym for afn:substring
*/
static private class Substr2 extends Substring2 {
public String getName() { return "substr/2"; }
}
/**
* Synonym for afn:substring
*/
static private class Substr3 extends Substring3 {
public String getName() { return "substr/3"; }
}
/**
* Join all the arguments using a separator found in the first parameter.
* afn:strjoin(separator, sequence...)
* @see http://jena.hpl.hp.com/ARQ/function#strjoin
*/
static private class Strjoin extends MulgaraFunction {
public String getName() { return "strjoin/*"; }
public int getArity() { return -1; }
public Object eval(List<?> args) throws XPathFunctionException {
StringBuilder s = new StringBuilder();
int lastIndex = args.size() - 1;
String separator = (String)args.get(0);
for (int i = 1; i < lastIndex; i++) {
if (i != 1) s.append(separator);
s.append(args.get(i));
}
return s.toString();
}
}
/**
* Calculate the SHA1 checkum of a literal or URI
* afn:sha1sum(resource)
* @see http://jena.hpl.hp.com/ARQ/function#sha1sum
*/
static private class Sha1sum extends MulgaraFunction {
public String getName() { return "sha1sum/1"; }
public Object eval(List<?> args) throws XPathFunctionException {
String resource = args.get(0).toString();
try {
byte[] bytes = resource.getBytes("UTF8");
MessageDigest digest = MessageDigest.getInstance("SHA1");
digest.update(bytes, 0, bytes.length);
return toHexString(digest.digest());
} catch (UnsupportedEncodingException e) {
throw new XPathFunctionException("Unable to handle data as UTF-8: " + e.getMessage());
} catch (NoSuchAlgorithmException e) {
throw new XPathFunctionException("SHA1 algorithm is not available: " + e.getMessage());
}
}
static final String toHexString(byte[] data) {
StringBuilder s = new StringBuilder();
for (byte b: data) {
s.append(Integer.toHexString((b & 0xF0) >> 4));
s.append(Integer.toHexString(b & 0x0F));
}
return s.toString();
}
}
/**
* Returns the time this function is called. This will increase throughout a query if the
* query takes any measurable time.
* afn:now()
* @see http://jena.hpl.hp.com/ARQ/function#now
*/
static private class Now extends MulgaraFunction {
public int getArity() { return 0; }
public Object eval(List<?> args) throws XPathFunctionException {
return new Date();
}
}
/**
* Return the blank node label if ?x is a blank node.
* afn:bnode(node)
* @see http://jena.hpl.hp.com/ARQ/function#bnode
*/
static private class Bnode extends MulgaraFunction {
public Object eval(List<?> args) throws XPathFunctionException {
Object resource = args.get(0);
return (resource instanceof BlankNode) ? ((BlankNode)resource).toString() : "";
}
}
/**
* The local name of u if an IRI.
* Based on splitting the IRI, not on any prefixes in the query or dataset
* afn:localname(u)
* @see http://jena.hpl.hp.com/ARQ/function#localname
*/
static private class Localname extends MulgaraFunction {
public Object eval(List<?> args) throws XPathFunctionException {
Object resource = args.get(0);
return (resource instanceof URI) ? localName((URI)resource) : "";
}
static String localName(URI u) {
return URIUtil.parseQName(u).getLocalPart();
}
}
/**
* The namespace of an IRI.
* Based on splitting the IRI, not on any prefixes in the query or dataset
* afn:namespace(u)
* @see http://jena.hpl.hp.com/ARQ/function#namespace
*/
static private class Namespace extends MulgaraFunction {
public Object eval(List<?> args) throws XPathFunctionException {
Object resource = args.get(0);
return (resource instanceof URI) ? namespace((URI)resource) : "";
}
static String namespace(URI u) {
return URIUtil.parseQName(u).getNamespaceURI();
}
}
/**
* Return the minimum of two expressions evaluating to numbers.
* afn:min(x,y)
* @see http://jena.hpl.hp.com/ARQ/function#min
*/
static private class Min extends MulgaraFunction {
public int getArity() { return 2; }
public Object eval(List<?> args) throws XPathFunctionException {
Number x = (Number)args.get(0);
Number y = (Number)args.get(1);
return x.doubleValue() < y.doubleValue() ? x : y;
}
}
/**
* Return the maximum of two expressions evaluating to numbers.
* afn:max(x,y)
* @see http://jena.hpl.hp.com/ARQ/function#max
*/
static private class Max extends MulgaraFunction {
public int getArity() { return 2; }
public Object eval(List<?> args) throws XPathFunctionException {
Number x = (Number)args.get(0);
Number y = (Number)args.get(1);
return x.doubleValue() > y.doubleValue() ? x : y;
}
}
/**
* The value of pi.
* afn:pi()
* @see http://jena.hpl.hp.com/ARQ/function#pi
*/
static private class Pi extends MulgaraFunction {
public int getArity() { return 0; }
public Object eval(List<?> args) throws XPathFunctionException {
return Math.PI;
}
}
/**
* The value of e.
* afn:e()
* @see http://jena.hpl.hp.com/ARQ/function#e
*/
static private class E extends MulgaraFunction {
public int getArity() { return 0; }
public Object eval(List<?> args) throws XPathFunctionException {
return Math.E;
}
}
}