[Mulgara-svn] r1797 - in trunk/src/jar/query/java/org/mulgara/query: . xpath

pag at mulgara.org pag at mulgara.org
Tue Oct 6 06:29:59 UTC 2009


Author: pag
Date: 2009-10-05 23:29:59 -0700 (Mon, 05 Oct 2009)
New Revision: 1797

Added:
   trunk/src/jar/query/java/org/mulgara/query/xpath/
   trunk/src/jar/query/java/org/mulgara/query/xpath/FnFunctionGroup.java
   trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraFunction.java
   trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraFunctionGroup.java
   trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraXPathFunctionResolver.java
   trunk/src/jar/query/java/org/mulgara/query/xpath/OpFunctionGroup.java
   trunk/src/jar/query/java/org/mulgara/query/xpath/SparqlFunctionGroup.java
Log:
Initial implementation of a set of functions for Mulgara, and the XPathFunctionResolver

Added: trunk/src/jar/query/java/org/mulgara/query/xpath/FnFunctionGroup.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/xpath/FnFunctionGroup.java	                        (rev 0)
+++ trunk/src/jar/query/java/org/mulgara/query/xpath/FnFunctionGroup.java	2009-10-06 06:29:59 UTC (rev 1797)
@@ -0,0 +1,367 @@
+/*
+ * 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.URLEncoder;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.xpath.XPathFunctionException;
+
+import org.apache.xerces.impl.xpath.regex.RegularExpression;
+
+/**
+ * Container for functions in the fn domain.
+ *
+ * @created Oct 5, 2009
+ * @author Paul Gearon
+ * @copyright &copy; 2009 <a href="http://www.duraspace.org/">DuraSpace</a>
+ */
+ at SuppressWarnings("unchecked")
+public class FnFunctionGroup extends MulgaraFunctionGroup {
+
+  /** The prefix for the fn: namespace */
+  static final String PREFIX = "fn";
+
+  /** The fn: namespace */
+  static final String NAMESPACE = "http://www.w3.org/2005/xpath-functions/#";
+
+  /** The name of the UTF-8 encoding scheme */
+  static private final String UTF8 = "UTF-8";
+
+  /**
+   * 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 Matches2());
+    functions.add(new Matches3());
+    functions.add(new FnBoolean());
+    functions.add(new Not());
+    functions.add(new Concat());
+    functions.add(new Substring2());
+    functions.add(new Substring3());
+    functions.add(new StringLength());
+    functions.add(new NormalizeSpace());
+    functions.add(new UpperCase());
+    functions.add(new LowerCase());
+    functions.add(new Translate());
+    functions.add(new EncodeForUri());
+    functions.add(new IriToUri());
+    functions.add(new EscapeHtmlUri());
+    return functions;
+  }
+
+  /**
+   * Function to evaluate if a string matches a pattern.
+   * @see http://www.w3.org/TR/xpath-functions/#func-matches
+   */
+  static private class Matches2 extends MulgaraFunction {
+    public String getName() { return "matches/2"; }
+    public int getArity() { return 2; }
+    public Object eval(List args) {
+      String str = (String)args.get(0);
+      String pattern = (String)args.get(1);
+      return new RegularExpression(pattern).matches(str);
+    }
+  }
+
+  /**
+   * Function to evaluate if a string matches a pattern, with a set of modifying flags.
+   * @see http://www.w3.org/TR/xpath-functions/#func-matches
+   */
+  static private class Matches3 extends MulgaraFunction {
+    public String getName() { return "matches/3"; }
+    public int getArity() { return 3; }
+    public Object eval(List args) {
+      String str = (String)args.get(0);
+      String pattern = (String)args.get(1);
+      String flags = (String)args.get(2);
+      return new RegularExpression(pattern, flags).matches(str);
+    }
+  }
+
+  /**
+   * Function to compute the EBV of a sequence. Unfortunately, no sequence info is available at this level.
+   * @see http://www.w3.org/TR/xpath-functions/#func-boolean
+   */
+  static private class FnBoolean extends MulgaraFunction {
+    public String getName() { return "boolean"; }
+    public int getArity() { return 1; }
+    public Object eval(List args) throws XPathFunctionException {
+      // no sequence info available here. Look at singleton only for the moment
+      return toBool(args.get(0));
+    }
+  }
+
+  // No implementation of fn:compare
+
+  /**
+   * Function to compute the inverse EBV of a sequence. See FnBoolean for restrictions.
+   * @see http://www.w3.org/TR/xpath-functions/#func-not
+   */
+  static private class Not extends MulgaraFunction {
+    public int getArity() { return 1; }
+    public Object eval(List args) throws XPathFunctionException {
+      return !toBool(args.get(0));
+    }
+  }
+
+  /**
+   * Concatenates two or more arguments cast to string.
+   * @see http://www.w3.org/TR/xpath-functions/#func-concat
+   */
+  static private class Concat extends MulgaraFunction {
+    public int getArity() { return -1; }
+    public String getName() { return "concat/*"; }
+    public Object eval(List args) throws XPathFunctionException {
+      StringBuilder s = new StringBuilder();
+      for (Object o: args) s.append(o);
+      return s.toString();
+    }
+  }
+
+  /**
+   * Returns the string located at a specified place within an argument string.
+   * @see http://www.w3.org/TR/xpath-functions/#func-substring
+   */
+  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);
+    }
+  }
+
+  /**
+   * Returns the string located at a specified place within an argument string.
+   * @see http://www.w3.org/TR/xpath-functions/#func-substring
+   */
+  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() - 1;
+      int end = ((Number)args.get(2)).intValue() + start;
+      // perform boundary checking
+      int len = source.length();
+      if (start < 0) start = 0;
+      if (start > len) {
+        start = len;
+        end = len;
+      }
+      if (end > len) end = len;
+      if (end < start) end = start;
+      
+      return source.substring(start, end);
+    }
+  }
+
+  /**
+   * Returns the length of the argument.
+   * @see http://www.w3.org/TR/xpath-functions/#func-string-length
+   */
+  static private class StringLength extends MulgaraFunction {
+    public String getName() { return "string-length/1"; }
+    public Object eval(List args) throws XPathFunctionException {
+      return args.get(0).toString().length();
+    }
+  }
+
+  /**
+   * Returns the whitespace-normalized value of the argument.
+   * @see http://www.w3.org/TR/xpath-functions/#func-normalize-space
+   */
+  static private class NormalizeSpace extends MulgaraFunction {
+    public String getName() { return "normalize-space/1"; }
+    public Object eval(List args) throws XPathFunctionException {
+      String str = args.get(0).toString().trim();
+      return str.replaceAll(" +", " ");
+    }
+  }
+
+  /**
+   * Returns the upper-cased value of the argument.
+   * @see http://www.w3.org/TR/xpath-functions/#func-upper-case
+   */
+  static private class UpperCase extends MulgaraFunction {
+    public String getName() { return "upper-case/1"; }
+    public Object eval(List args) throws XPathFunctionException {
+      return args.get(0).toString().toUpperCase();
+    }
+  }
+
+  /**
+   * Returns the lower-cased value of the argument.
+   * @see http://www.w3.org/TR/xpath-functions/#func-lower-case
+   */
+  static private class LowerCase extends MulgaraFunction {
+    public String getName() { return "lower-case/1"; }
+    public Object eval(List args) throws XPathFunctionException {
+      return args.get(0).toString().toLowerCase();
+    }
+  }
+
+  /**
+   * Returns the first xs:string argument with occurrences of characters contained
+   * in the second argument replaced by the character at the corresponding position
+   * in the third argument.
+   */
+  static private class Translate extends MulgaraFunction {
+    public int getArity() { return 3; }
+    public Object eval(List args) throws XPathFunctionException {
+      String str = args.get(0).toString();
+      String mapStr = args.get(1).toString();
+      String transStr = args.get(2).toString();
+      // iterate through the map chars
+      for (int i = 0; i < mapStr.length(); i++) {
+        char c = mapStr.charAt(i);
+        if (i < transStr.length()) str = replaceChars(str, c, transStr.charAt(i));
+        else str = removeChars(str, c);
+      }
+      return str;
+    }
+
+    private static String replaceChars(String str, char c, char r) {
+      StringBuilder s = new StringBuilder(str);
+      for (int i = 0; i < s.length(); i++) {
+        if (s.charAt(i) == c) s.setCharAt(i, r);
+      }
+      return s.toString();
+    }
+
+    private static String removeChars(String str, char c) {
+      StringBuilder s = new StringBuilder(str);
+      for (int i = 0; i < s.length(); i++) {
+        if (s.charAt(i) == c) {
+          s.replace(i, i + 1, "");
+          i--;
+        }
+      }
+      return s.toString();
+    }
+  }
+
+  /**
+   * Returns the xs:string argument with certain characters escaped to enable the
+   * resulting string to be used as a path segment in a URI.
+   * @see http://www.w3.org/TR/xpath-functions/#func-encode-for-uri
+   */
+  static private class EncodeForUri extends MulgaraFunction {
+    public String getName() { return "encode-for-uri/1"; }
+    public Object eval(List args) throws XPathFunctionException {
+      try {
+        return URLEncoder.encode(args.get(0).toString(), UTF8);
+      } catch (UnsupportedEncodingException e) {
+        throw new XPathFunctionException("Unable to encode string for URL: " + e.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Returns the xs:string argument with certain characters escaped to enable the
+   * resulting string to be used as (part of) a URI.
+   * @see http://www.w3.org/TR/xpath-functions/#func-iri-to-uri
+   */
+  static private class IriToUri extends MulgaraFunction {
+    public String getName() { return "iri-to-uri/1"; }
+    public Object eval(List args) throws XPathFunctionException {
+      StringBuilder str = new StringBuilder(args.get(0).toString());
+      for (int i = 0; i < str.length(); i++) {
+        char c = str.charAt(i);
+        if (outOfRange(c)) str.replace(i, i + 1, escape(str.substring(i, i + 1)));
+      }
+      return str.toString();
+    }
+    /**
+     * Test for URI compatibility. See Errata note:
+     * @see http://www.w3.org/XML/2007/qt-errata/xpath-functions-errata.html#E8
+     * @param c The character to test.
+     * @return <code>true</code> if the character is out of range for a URI and must be encoded.
+     */
+    static private final boolean outOfRange(char c) {
+      return c < 0x20 || c > 0x7E || c == '<' || c == '>' || c == '"' ||
+             c == '{' || c == '}' || c == '|' || c == '\\' || c == '^' || c == '`';
+    }
+  }
+
+  /**
+   * Returns the string argument with certain characters escaped in the manner that html user agents
+   * handle attribute values that expect URIs.
+   * @see http://www.w3.org/TR/xpath-functions/#func-escape-html-uri
+   */
+  static private class EscapeHtmlUri extends MulgaraFunction {
+    public String getName() { return "escape-html-uri/1"; }
+    public Object eval(List args) throws XPathFunctionException {
+      StringBuilder str = new StringBuilder(args.get(0).toString());
+      for (int i = 0; i < str.length(); i++) {
+        char c = str.charAt(i);
+        if (outOfRange(c)) str.replace(i, i + 1, escape(str.substring(i, i + 1)));
+      }
+      return str.toString();
+    }
+    static private final boolean outOfRange(char c) {
+      return c < 0x20 || c > 0x7E;
+    }
+  }
+
+  /**
+   * Converts a single character to a string containing escaped UTF-8 encoding.
+   * A utility used by both EscapeHtmlUri and IriToUri.
+   * @param c The character to escape.
+   * @return A string containing a sequence of %HH representing the UTF-8 encoding of <var>c</var>
+   * @throws XPathFunctionException If UTF-8 encoding fails.
+   */
+  static private final String escape(String c) throws XPathFunctionException {
+    byte[] bytes = null;
+    try {
+      bytes = c.getBytes(UTF8);
+    } catch (UnsupportedEncodingException e) {
+      throw new XPathFunctionException("Unable to encode string for URL: " + e.getMessage());
+    }
+    StringBuilder result = new StringBuilder();
+    for (int i = 0; i < bytes.length; i++) result.append(String.format("%%%02X", bytes[i]));
+    return result.toString();
+  }
+
+}

Added: trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraFunction.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraFunction.java	                        (rev 0)
+++ trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraFunction.java	2009-10-06 06:29:59 UTC (rev 1797)
@@ -0,0 +1,120 @@
+/*
+ * 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.util.List;
+
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+
+import org.jrdf.graph.Literal;
+import org.mulgara.query.QueryException;
+import org.mulgara.query.filter.value.SimpleLiteral;
+
+/**
+ * General class for providing management information for XPath function implementations.
+ *
+ * @created Oct 5, 2009
+ * @author Paul Gearon
+ * @copyright &copy; 2009 <a href="http://www.duraspace.org/">DuraSpace</a>
+ */
+ at SuppressWarnings("unchecked")
+public abstract class MulgaraFunction implements XPathFunction {
+
+  /**
+   * Evaluate the method represented by this class, using the supplied arguments.
+   * @see javax.xml.xpath.XPathFunction#evaluate(java.util.List)
+   */
+  public Object evaluate(List args) throws XPathFunctionException {
+    checkArgs(getArity(), args);
+    try {
+      return eval(args);
+    } catch (ClassCastException e) {
+      throw new XPathFunctionException("Incorrect parameter types passed to function: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Test that the correct number of arguments were provided to the function call.
+   * @param expected The expected number of arguments.
+   * @param args The list of arguments.
+   * @throws XPathFunctionException Thrown if the argument list is the wrong length.
+   */
+  protected void checkArgs(int expected, List args) throws XPathFunctionException {
+    if (expected >= 0 && args.size() != expected) {
+      throw new XPathFunctionException("Incorrect number of parameters. Should be " + expected + ", but was: " + args.size());
+    }
+  }
+
+  /**
+   * Returns the number of arguments required to use this function. Default is 1. 
+   * @return The number of arguments for this function.
+   */
+  protected int getArity() { return 1; }
+
+  /**
+   * The name of this function, followed by its arity.
+   * If not overridden then this will be the function class's name, starting with a lower-case letter.
+   * @return The name/arity of this function.
+   */
+  protected String getName() {
+    StringBuilder name = new StringBuilder(getClass().getSimpleName());
+    char c = name.charAt(0);
+    if (Character.isUpperCase(c)) {
+      name.setCharAt(0, Character.toLowerCase(c));
+    }
+    for (int i = 1; i < name.length(); i++) {
+      c = name.charAt(0);
+      if (Character.isUpperCase(c)) name.replace(i, i + 1, "-" + Character.toLowerCase(c));
+    }
+    name.append("/");
+    int arity = getArity();
+    if (arity >= 0) name.append(getArity());
+    else name.append("*");
+    return name.toString();
+  }
+
+  /**
+   * Evaluates the operation of the function. The argument list will be the correct length.
+   * @param args The arguments for the function.
+   * @return The return value of the function.
+   * @throws XPathFunctionException If there was an error executing the function.
+   */
+  protected abstract Object eval(List<?> args) throws XPathFunctionException;
+
+  /**
+   * Convert a singleton value into its effective boolean value (EBV).
+   * @param o The singleton to test.
+   * @return The EBV of the value.
+   * @throws XPathFunctionException If a complex evaluation throws an exception.
+   */
+  protected static final boolean toBool(Object o) throws XPathFunctionException {
+    if (o == null) return false;
+    if (o instanceof String) return ((String)o).length() != 0;
+    if (o instanceof Number) return ((Number)o).doubleValue() != 0 && ((Number)o).doubleValue() != Double.NaN;
+    if (o instanceof Boolean) return ((Boolean)o).booleanValue();
+    try {
+      if (o instanceof Literal) return new SimpleLiteral(((Literal)o).getLexicalForm(), ((Literal)o).getLanguage()).test(null);
+    } catch (NullPointerException e) {
+      throw new XPathFunctionException("Conversion of data to a simple literal requires a context: " + e.getMessage());
+    } catch (QueryException e) {
+      throw new XPathFunctionException("Unable to convert data to a simple literal: " + e.getMessage());
+    }
+    throw new XPathFunctionException("Type error: " + o + " [" + o.getClass() + "]");
+  }
+
+}

Added: trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraFunctionGroup.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraFunctionGroup.java	                        (rev 0)
+++ trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraFunctionGroup.java	2009-10-06 06:29:59 UTC (rev 1797)
@@ -0,0 +1,49 @@
+/*
+ * 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.net.URI;
+import java.util.Set;
+
+/**
+ * Represents a group of functions exposed as XPathFunctions in a single namespace.
+ *
+ * @created Oct 5, 2009
+ * @author Paul Gearon
+ * @copyright &copy; 2009 <a href="http://www.duraspace.org/">DuraSpace</a>
+ */
+public abstract class MulgaraFunctionGroup {
+
+  /**
+   * Get the prefix used for the namespace of these operations.
+   * @return The short string used for a prefix in a QName.
+   */
+  public abstract String getPrefix();
+
+  /**
+   * Get the namespace of these operations.
+   * @return The string of the namespace URI.
+   */
+  public abstract String getNamespace();
+
+  /**
+   * Get the set of function in this group.
+   * @return A set of MulgaraFunction for this entire group.
+   */
+  public abstract Set<MulgaraFunction> getAllFunctions();
+
+}

Added: trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraXPathFunctionResolver.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraXPathFunctionResolver.java	                        (rev 0)
+++ trunk/src/jar/query/java/org/mulgara/query/xpath/MulgaraXPathFunctionResolver.java	2009-10-06 06:29:59 UTC (rev 1797)
@@ -0,0 +1,81 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionResolver;
+
+/**
+ * Retrieves functions for SPARQL expressions.
+ *
+ * @created Oct 5, 2009
+ * @author Paul Gearon
+ * @copyright &copy; 2009 <a href="http://www.duraspace.org/">DuraSpace</a>
+ */
+public class MulgaraXPathFunctionResolver implements XPathFunctionResolver {
+
+  /**
+   * @see javax.xml.xpath.XPathFunctionResolver#resolveFunction(javax.xml.namespace.QName, int)
+   */
+  public XPathFunction resolveFunction(QName functionName, int arity) {
+    XPathFunction result = null;
+    String namespace = functionName.getNamespaceURI();
+    if (namespace != null) {
+      Map<String,XPathFunction> fnGroupMap = functionGroups.get(namespace);
+      if (fnGroupMap != null) {
+        result = fnGroupMap.get(functionName.getLocalPart() + "/" + arity);
+        // fall back to multiple arity
+        if (result == null) result = fnGroupMap.get(functionName.getLocalPart() + "/*");
+      }
+    }
+    return result;
+  }
+
+
+  /**
+   * A mapping of namespace URIs to the map of (name->functions) in that namespace.
+   * This is used to look up the requested function object.
+   */
+  private static final Map<String,Map<String,XPathFunction>> functionGroups = new HashMap<String,Map<String,XPathFunction>>();
+
+
+  // initialize the maps of requested parameters to the function object being asked for
+  static {
+    addFunctionGroup(new SparqlFunctionGroup());
+    addFunctionGroup(new FnFunctionGroup());
+    addFunctionGroup(new OpFunctionGroup());
+  }
+
+  /**
+   * A helper method to create a mapping of function names to their implementing classes,
+   * and of namespaces to these mappings.
+   * @param fnGroup A group of functions to be added into a single namespace.
+   *        This group also provides that namespace.
+   */
+  private static final void addFunctionGroup(MulgaraFunctionGroup fnGroup) {
+    // map the function names to the functions
+    Map<String,XPathFunction> functionMap = new HashMap<String,XPathFunction>();
+    for (MulgaraFunction fn: fnGroup.getAllFunctions()) functionMap.put(fn.getName(), fn);
+    // map the namespace to the name->function map
+    functionGroups.put(fnGroup.getNamespace(), functionMap);
+  }
+
+}

Added: trunk/src/jar/query/java/org/mulgara/query/xpath/OpFunctionGroup.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/xpath/OpFunctionGroup.java	                        (rev 0)
+++ trunk/src/jar/query/java/org/mulgara/query/xpath/OpFunctionGroup.java	2009-10-06 06:29:59 UTC (rev 1797)
@@ -0,0 +1,168 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Container for functions in the op pseudo-domain.
+ *
+ * @created Oct 5, 2009
+ * @author Paul Gearon
+ * @copyright &copy; 2009 <a href="http://www.duraspace.org/">DuraSpace</a>
+ */
+ at SuppressWarnings("unchecked")
+public class OpFunctionGroup extends MulgaraFunctionGroup {
+
+  /** The prefix for the fn: namespace */
+  static final String PREFIX = "op";
+
+  /** The op: namespace */
+  static final String NAMESPACE = PREFIX;
+
+  /**
+   * 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 NumericEqual());
+    functions.add(new NumericLessThan());
+    functions.add(new NumericGreaterThan());
+    functions.add(new NumericIntegerDivision());
+    functions.add(new NumericMod());
+    return functions;
+  }
+
+  /**
+   * Function to evaluate if two numbers are equal.
+   * @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal
+   */
+  static private class NumericEqual extends MulgaraFunction {
+    public String getName() { return "numeric-equal/2"; }
+    public int getArity() { return 2; }
+    public Object eval(List args) {
+      Number left = (Number)args.get(0);
+      Number right = (Number)args.get(1);
+      return left.doubleValue() == right.doubleValue();
+    }
+  }
+
+  /**
+   * Function to evaluate if one number is less than another.
+   * @see http://www.w3.org/TR/xpath-functions/#func-numeric-less-than
+   */
+  static private class NumericLessThan extends MulgaraFunction {
+    public String getName() { return "numeric-less-than/2"; }
+    public int getArity() { return 2; }
+    public Object eval(List args) {
+      Number left = (Number)args.get(0);
+      Number right = (Number)args.get(1);
+      return left.doubleValue() < right.doubleValue();
+    }
+  }
+
+  /**
+   * Function to evaluate if one number is greater than another.
+   * @see http://www.w3.org/TR/xpath-functions/#func-numeric-greater-than
+   */
+  static private class NumericGreaterThan extends MulgaraFunction {
+    public String getName() { return "numeric-greater-than/2"; }
+    public int getArity() { return 2; }
+    public Object eval(List args) {
+      Number left = (Number)args.get(0);
+      Number right = (Number)args.get(1);
+      return left.doubleValue() > right.doubleValue();
+    }
+  }
+
+  /**
+   * Function to evaluate integer division between numbers. This does not meet the XPath semantics
+   * perfectly, but will work for many situations.
+   * @see http://www.w3.org/TR/xpath-functions/#func-numeric-integer-divide
+   */
+  static private class NumericIntegerDivision extends MulgaraFunction {
+    public String getName() { return "numeric-integer-divide/2"; }
+    public int getArity() { return 2; }
+    public Object eval(List args) {
+      Number left = (Number)args.get(0);
+      Number right = (Number)args.get(1);
+      if (left instanceof BigDecimal) {
+        return ((BigDecimal)left).divide(
+            (right instanceof BigDecimal) ? (BigDecimal)right : new BigDecimal(right.toString())
+        );
+      }
+      if (left instanceof BigInteger) {
+        return ((BigInteger)left).divide(
+            (right instanceof BigInteger) ? (BigInteger)right : new BigInteger(right.toString())
+        );
+      }
+      return Double.valueOf(left.doubleValue() / right.doubleValue()).longValue();
+    }
+  }
+
+  /**
+   * Function to evaluate the numeric mod operation. This does not meet the XPath semantics
+   * perfectly, but will work for most situations.
+   * @see http://www.w3.org/TR/xpath-functions/#func-numeric-mod
+   */
+  static private class NumericMod extends MulgaraFunction {
+    public String getName() { return "numeric-mod/2"; }
+    public int getArity() { return 2; }
+    public Object eval(List args) {
+      Number left = (Number)args.get(0);
+      Number right = (Number)args.get(1);
+      if (left instanceof Byte || left instanceof Short || left instanceof Integer || left instanceof Long) {
+        return left.longValue() % right.longValue();
+      }
+      if (left instanceof Float || left instanceof Double) {
+        return left.doubleValue() % right.doubleValue();
+      }
+      if (left instanceof BigDecimal) {
+        return ((BigDecimal)left).remainder(
+            (right instanceof BigDecimal) ? (BigDecimal)right : new BigDecimal(right.toString())
+        );
+      }
+      if (left instanceof BigInteger) {
+        return ((BigInteger)left).remainder(
+            (right instanceof BigInteger) ? (BigInteger)right : new BigInteger(right.toString())
+        );
+      }
+      return Double.valueOf(left.doubleValue() % right.doubleValue()).longValue();
+    }
+  }
+}

Added: trunk/src/jar/query/java/org/mulgara/query/xpath/SparqlFunctionGroup.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/xpath/SparqlFunctionGroup.java	                        (rev 0)
+++ trunk/src/jar/query/java/org/mulgara/query/xpath/SparqlFunctionGroup.java	2009-10-06 06:29:59 UTC (rev 1797)
@@ -0,0 +1,179 @@
+/*
+ * 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.net.URI;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.xpath.XPathFunctionException;
+
+import org.jrdf.graph.BlankNode;
+import org.jrdf.graph.Literal;
+import org.mulgara.query.QueryException;
+import org.mulgara.query.filter.value.SimpleLiteral;
+
+/**
+ * Container for functions in the SPARQL domain.
+ *
+ * @created Oct 5, 2009
+ * @author Paul Gearon
+ * @copyright &copy; 2009 <a href="http://www.duraspace.org/">DuraSpace</a>
+ */
+ at SuppressWarnings("unchecked")
+public class SparqlFunctionGroup extends MulgaraFunctionGroup {
+
+  /** The prefix for the sparql: namespace */
+  static final String PREFIX = "sparql";
+
+  /** The sparql: namespace */
+  static final String NAMESPACE = "http://www.w3.org/2006/sparql-functions#";
+
+  /**
+   * 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 Bound());
+    functions.add(new IsIRI());
+    functions.add(new IsURI());
+    functions.add(new IsBlank());
+    functions.add(new IsLiteral());
+    functions.add(new Str());
+    functions.add(new Lang());
+    functions.add(new Datatype());
+    functions.add(new LangMatches());
+    functions.add(new LogicalOr());
+    functions.add(new LogicalAnd());
+    return functions;
+  }
+
+  /** Function to evaluate if a parameter is bound. */
+  static private class Bound extends MulgaraFunction {
+    public Object eval(List args) { return args.get(0) != null; }
+  }
+
+  /** Function to evaluate if a parameter is an IRI. */
+  static private class IsIRI extends MulgaraFunction {
+    public Object eval(List args) { return args.get(0) instanceof URI; }
+  }
+
+  /** Function to evaluate if a parameter is a URI. */
+  static private class IsURI extends MulgaraFunction {
+    public Object eval(List args) { return args.get(0) instanceof URI; }
+  }
+
+  /** Function to evaluate if a parameter is a blank node. */
+  static private class IsBlank extends MulgaraFunction {
+    public Object eval(List args) { return args.get(0) instanceof BlankNode; }
+  }
+
+  /** Function to evaluate if a parameter is a literal. */
+  static private class IsLiteral extends MulgaraFunction {
+    public Object eval(List args) {
+      Object o = args.get(0);
+      return o instanceof Literal || o instanceof String || o instanceof Number;
+    }
+  }
+
+  /** Function to convert a value to a string. */
+  static private class Str extends MulgaraFunction {
+    public Object eval(List args) { return args.get(0).toString(); }
+  }
+
+  /** Function to get the language of a literal. This information is probably not available for this implementation. */
+  static private class Lang extends MulgaraFunction {
+    public Object eval(List args) {
+      Object o = args.get(0);
+      return o instanceof Literal ? ((Literal)o).getLanguage() : "";
+    }
+  }
+    
+  /** Function to get the datatype of a literal. This information is probably not available for this implementation. */
+  static private class Datatype extends MulgaraFunction {
+    public Object eval(List args) {
+      Object o = args.get(0);
+      return o instanceof Literal ? ((Literal)o).getDatatype() : null;
+    }
+  }
+  
+  /**
+   * Function to test if a language code matches a pattern.
+   * This information is probably not available for this implementation.
+   */
+  static private class LangMatches extends MulgaraFunction {
+    protected int getArity() { return 2; }
+    public Object eval(List args) throws XPathFunctionException {
+      Object o = args.get(0);
+      if (o instanceof Literal) {
+        Literal l = (Literal)o;
+        if (l.getLanguage() == null || l.getLanguage().length() == 0) return false;
+        org.mulgara.query.filter.LangMatches match = new org.mulgara.query.filter.LangMatches(new SimpleLiteral(l.getLexicalForm(), l.getLanguage()), new SimpleLiteral((String)args.get(1)));
+        try {
+          return match.getValue();
+        } catch (QueryException e) {
+          throw new XPathFunctionException("Unable to get data from lang matching test: " + e.getMessage());
+        }
+      }
+      return false;
+    }
+  }
+
+  /** Common operations required for a logical operation. */
+  static private abstract class LogicOp extends MulgaraFunction {
+    protected int getArity() { return 2; }
+    protected abstract boolean op(Object left, Object right) throws XPathFunctionException;
+    public Object eval(List args) throws XPathFunctionException {
+      return op(args.get(0), args.get(1));
+    }
+  }
+
+  /** Function to perform a logical OR between 2 operands. */
+  static private class LogicalOr extends LogicOp {
+    protected String getName() { return "logical-or"; }
+    public boolean op(Object left, Object right) throws XPathFunctionException {
+      return toBool(left) || toBool(right);
+    }
+  }
+
+  /** Function to perform a logical AND between 2 operands. */
+  static private class LogicalAnd extends LogicOp {
+    protected String getName() { return "logical-and"; }
+    public boolean op(Object left, Object right) throws XPathFunctionException {
+      return toBool(left) && toBool(right);
+    }
+  }
+
+}




More information about the Mulgara-svn mailing list