Add various additional Leviathan Functions (JENA-507)

- pythagoras() calculates the length of the hypotenuse of a right
  angled triangle given the lengths of the other two sides
- degrees-to-radians() and radians-to-degrees()
- cos() and cos-1() i.e. acos

As part of this commit some improvements were made to Loader to deal
with class name strings that contain illegal characters to the Java
language specification.  So for example degrees-to-radians would be an
invalid Java class name so it gets escaped to degressToRadians and the
loader looks for that class instead.


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/50de1266
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/50de1266
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/50de1266

Branch: refs/heads/JENA-507
Commit: 50de126659e9a5ab3220da7ffe33de2bf9190c37
Parents: 7cd9e4d
Author: Rob Vesse <[email protected]>
Authored: Thu Oct 9 15:24:36 2014 +0100
Committer: Rob Vesse <[email protected]>
Committed: Thu Oct 9 15:24:36 2014 +0100

----------------------------------------------------------------------
 .../library/leviathan/LeviathanConstants.java   |  14 ++
 .../sparql/function/library/leviathan/cos.java  |  13 ++
 .../sparql/function/library/leviathan/cos1.java |  13 ++
 .../library/leviathan/degreesToRadians.java     |  13 ++
 .../function/library/leviathan/pythagoras.java  |  16 ++
 .../library/leviathan/radiansToDegrees.java     |  13 ++
 .../com/hp/hpl/jena/sparql/util/Loader.java     | 147 ++++++++++++-------
 .../hp/hpl/jena/sparql/util/MappedLoader.java   | 111 +++++++-------
 .../sparql/expr/TestLeviathanFunctions.java     |  80 ++++++----
 9 files changed, 282 insertions(+), 138 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/LeviathanConstants.java
----------------------------------------------------------------------
diff --git 
a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/LeviathanConstants.java
 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/LeviathanConstants.java
index 783b12e..59e9348 100644
--- 
a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/LeviathanConstants.java
+++ 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/LeviathanConstants.java
@@ -18,8 +18,22 @@
 
 package com.hp.hpl.jena.sparql.function.library.leviathan;
 
+/**
+ * Constants related to support for the <a href=
+ * 
"https://bitbucket.org/dotnetrdf/dotnetrdf/wiki/DeveloperGuide/SPARQL/Leviathan%20Functions";
+ * >Leviathan Function Library</a> which is a library of extension functions
+ * originally implemented by the <a href="http://www.dotnetrdf.org";>dotNetRDF 
Project</a>.
+ * 
+ */
 public class LeviathanConstants {
 
+    /**
+     * Leviathan Function Library URI
+     */
     public static final String LeviathanFunctionLibraryURI = 
"http://www.dotnetrdf.org/leviathan#";;
+    
+    /**
+     * Leviathan Function Library package
+     */
     public static final String LeviathanFunctionLibrary = 
"java:com.hp.hpl.jena.sparql.function.library.leviathan.";
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cos.java
----------------------------------------------------------------------
diff --git 
a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cos.java
 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cos.java
new file mode 100644
index 0000000..cf54f5f
--- /dev/null
+++ 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cos.java
@@ -0,0 +1,13 @@
+package com.hp.hpl.jena.sparql.function.library.leviathan;
+
+import com.hp.hpl.jena.sparql.expr.NodeValue;
+import com.hp.hpl.jena.sparql.function.FunctionBase1;
+
+public class cos extends FunctionBase1 {
+
+    @Override
+    public NodeValue exec(NodeValue v) {
+        return NodeValue.makeDouble(Math.cos(v.getDouble()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cos1.java
----------------------------------------------------------------------
diff --git 
a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cos1.java
 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cos1.java
new file mode 100644
index 0000000..3e3cc0f
--- /dev/null
+++ 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/cos1.java
@@ -0,0 +1,13 @@
+package com.hp.hpl.jena.sparql.function.library.leviathan;
+
+import com.hp.hpl.jena.sparql.expr.NodeValue;
+import com.hp.hpl.jena.sparql.function.FunctionBase1;
+
+public class cos1 extends FunctionBase1 {
+
+    @Override
+    public NodeValue exec(NodeValue v) {
+        return NodeValue.makeDouble(Math.acos(v.getDouble()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/degreesToRadians.java
----------------------------------------------------------------------
diff --git 
a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/degreesToRadians.java
 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/degreesToRadians.java
new file mode 100644
index 0000000..c629c7b
--- /dev/null
+++ 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/degreesToRadians.java
@@ -0,0 +1,13 @@
+package com.hp.hpl.jena.sparql.function.library.leviathan;
+
+import com.hp.hpl.jena.sparql.expr.NodeValue;
+import com.hp.hpl.jena.sparql.function.FunctionBase1;
+
+public class degreesToRadians extends FunctionBase1 {
+
+    @Override
+    public NodeValue exec(NodeValue v) {
+        return NodeValue.makeDouble(Math.toRadians(v.getDouble()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/pythagoras.java
----------------------------------------------------------------------
diff --git 
a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/pythagoras.java
 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/pythagoras.java
new file mode 100644
index 0000000..2a8d488
--- /dev/null
+++ 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/pythagoras.java
@@ -0,0 +1,16 @@
+package com.hp.hpl.jena.sparql.function.library.leviathan;
+
+import com.hp.hpl.jena.sparql.expr.NodeValue;
+import com.hp.hpl.jena.sparql.function.FunctionBase2;
+
+public class pythagoras extends FunctionBase2 {
+
+    @Override
+    public NodeValue exec(NodeValue v1, NodeValue v2) {
+        double a = v1.getDouble();
+        double b = v2.getDouble();
+
+        return NodeValue.makeDouble(Math.sqrt(Math.pow(a, 2) + Math.pow(b, 
2)));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/radiansToDegrees.java
----------------------------------------------------------------------
diff --git 
a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/radiansToDegrees.java
 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/radiansToDegrees.java
new file mode 100644
index 0000000..3cf3d93
--- /dev/null
+++ 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/function/library/leviathan/radiansToDegrees.java
@@ -0,0 +1,13 @@
+package com.hp.hpl.jena.sparql.function.library.leviathan;
+
+import com.hp.hpl.jena.sparql.expr.NodeValue;
+import com.hp.hpl.jena.sparql.function.FunctionBase1;
+
+public class radiansToDegrees extends FunctionBase1 {
+
+    @Override
+    public NodeValue exec(NodeValue v) {
+        return NodeValue.makeDouble(Math.toDegrees(v.getDouble()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/Loader.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/Loader.java 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/Loader.java
index 50b8dfe..3a74ea5 100644
--- a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/Loader.java
+++ b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/Loader.java
@@ -18,64 +18,109 @@
 
 package com.hp.hpl.jena.sparql.util;
 
-import org.apache.jena.atlas.logging.Log ;
-
-import com.hp.hpl.jena.sparql.ARQConstants ;
-import com.hp.hpl.jena.sparql.ARQInternalErrorException ;
-
-
-public class Loader
-{
-    static public Class<?> loadClass(String classNameOrURI) { return 
loadClass(classNameOrURI, null) ; }
-    
-    static public Class<?> loadClass(String classNameOrURI, Class<?> 
requiredClass)
-    {
-        if ( classNameOrURI == null )
-            throw new ARQInternalErrorException("Null classNameorIRI") ;
-        
-        if ( classNameOrURI.startsWith("http:") )
-            return null ;
-        if ( classNameOrURI.startsWith("urn:") )
-            return null ;
-
-        String className = classNameOrURI ;
-        
-        if ( classNameOrURI.startsWith(ARQConstants.javaClassURIScheme) )
-            className = 
classNameOrURI.substring(ARQConstants.javaClassURIScheme.length()) ;
-        
-        Class<?> classObj = null ;
-        
+import org.apache.jena.atlas.logging.Log;
+
+import com.hp.hpl.jena.sparql.ARQConstants;
+import com.hp.hpl.jena.sparql.ARQInternalErrorException;
+
+public class Loader {
+    static public Class<?> loadClass(String classNameOrURI) {
+        return loadClass(classNameOrURI, null);
+    }
+
+    static public Class<?> loadClass(String classNameOrURI, Class<?> 
requiredClass) {
+        if (classNameOrURI == null)
+            throw new ARQInternalErrorException("Null classNameorIRI");
+
+        if (classNameOrURI.startsWith("http:"))
+            return null;
+        if (classNameOrURI.startsWith("urn:"))
+            return null;
+
+        String className = classNameOrURI;
+
+        if (classNameOrURI.startsWith(ARQConstants.javaClassURIScheme))
+            className = 
classNameOrURI.substring(ARQConstants.javaClassURIScheme.length());
+
+        Class<?> classObj = null;
+
         try {
             classObj = Class.forName(className);
-        } catch (ClassNotFoundException ex)
-        {
-            Log.warn(Loader.class, "Class not found: "+className);
-            return null ;
+        } catch (ClassNotFoundException ex) {
+            // It is possible that when coming from a URI we might have
+            // characters which aren't valid as Java identifiers
+            // We should see if we can load the class with the escaped class
+            // name instead
+            String baseUri = className.substring(0, className.lastIndexOf('.') 
+ 1);
+            String escapedClassName = 
escape(className.substring(className.lastIndexOf('.') + 1));
+            try {
+                classObj = Class.forName(baseUri + escapedClassName);
+            } catch (ClassNotFoundException innerEx) {
+                // Ignore, handled in the outer catch
+            }
+
+            if (classObj == null) {
+                Log.warn(Loader.class, "Class not found: " + className);
+                return null;
+            }
         }
-        
-        if ( requiredClass != null && ! 
requiredClass.isAssignableFrom(classObj) )
-        {
-            Log.warn(Loader.class, "Class '"+className+"' found but not a 
"+Utils.classShortName(requiredClass)) ;
-            return null ;
+
+        if (requiredClass != null && 
!requiredClass.isAssignableFrom(classObj)) {
+            Log.warn(Loader.class, "Class '" + className + "' found but not a 
" + Utils.classShortName(requiredClass));
+            return null;
         }
-        return classObj ;
+        return classObj;
     }
 
-    static public Object loadAndInstantiate(String uri, Class<?> requiredClass)
-    {
-        Class<?> classObj = loadClass(uri, requiredClass) ;
-        if ( classObj == null )
-            return null ;
-        
-        Object module = null ;
+    static public Object loadAndInstantiate(String uri, Class<?> 
requiredClass) {
+        Class<?> classObj = loadClass(uri, requiredClass);
+        if (classObj == null)
+            return null;
+
+        Object module = null;
         try {
-            module = classObj.newInstance() ;
-        } catch (Exception ex)
-        {
-            String className = 
uri.substring(ARQConstants.javaClassURIScheme.length()) ;
-            Log.warn(Loader.class, "Exception during instantiation 
'"+className+"': "+ex.getMessage()) ;
-            return null ;
+            module = classObj.newInstance();
+        } catch (Exception ex) {
+            String className = 
uri.substring(ARQConstants.javaClassURIScheme.length());
+            Log.warn(Loader.class, "Exception during instantiation '" + 
className + "': " + ex.getMessage());
+            return null;
         }
-        return module ;
+        return module;
+    }
+
+    static public String escape(String className) {
+        StringBuilder builder = new StringBuilder();
+        boolean upgrade = false;
+
+        for (int offset = 0; offset < className.length();) {
+            int cp = className.codePointAt(offset);
+            if (builder.length() == 0) {
+                if (Character.isJavaIdentifierStart(cp)) {
+                    // Start character is valid
+                    builder.append(Character.toChars(cp));
+                } else {
+                    // Illegal start character so use F_ as prefix
+                    builder.append("F_");
+                }
+            } else {
+                if (Character.isJavaIdentifierPart(cp)) {
+                    // Upgrade to upper case if previous character was illegal
+                    if (upgrade) {
+                        cp = Character.toUpperCase(cp);
+                        upgrade = false;
+                    }
+                    // Valid character
+                    builder.append(Character.toChars(cp));
+                } else {
+                    // Skip illegal characters, the next valid character will 
be
+                    // upgraded to upper case
+                    upgrade = true;
+                }
+            }
+
+            offset += Character.charCount(cp);
+        }
+
+        return builder.toString();
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/MappedLoader.java
----------------------------------------------------------------------
diff --git 
a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/MappedLoader.java 
b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/MappedLoader.java
index 992e759..581104d 100644
--- a/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/MappedLoader.java
+++ b/jena-arq/src/main/java/com/hp/hpl/jena/sparql/util/MappedLoader.java
@@ -18,90 +18,77 @@
 
 package com.hp.hpl.jena.sparql.util;
 
-import java.util.HashMap ;
-import java.util.Map ;
+import java.util.HashMap;
+import java.util.Map;
 
-import com.hp.hpl.jena.sparql.ARQConstants ;
+import com.hp.hpl.jena.sparql.ARQConstants;
 import com.hp.hpl.jena.sparql.function.library.leviathan.LeviathanConstants;
 
+public class MappedLoader {
+    // Map string => string of prefixes
+    // e.g. http://jena.hpl.hp.com/ARQ/property# =>
+    // java:com.hp.hpl.jena.sparql.pfunction.
+
+    static Map<String, String> uriMap = new HashMap<>();
 
-public class MappedLoader
-{
-    // Map string => string of prefixes 
-    //   e.g. http://jena.hpl.hp.com/ARQ/property# => 
java:com.hp.hpl.jena.sparql.pfunction.
-    
-    static Map<String, String> uriMap = new HashMap<>() ;
-    
     static {
         // ARQ library
-        uriMap.put(ARQConstants.ARQFunctionLibraryURI,
-                   ARQConstants.ARQFunctionLibrary) ;
-        uriMap.put(ARQConstants.ARQPropertyFunctionLibraryURI,
-                   ARQConstants.ARQPropertyFunctionLibrary) ;
-        uriMap.put(ARQConstants.ARQProcedureLibraryURI,
-                   ARQConstants.ARQProcedureLibrary) ;
-        
+        uriMap.put(ARQConstants.ARQFunctionLibraryURI, 
ARQConstants.ARQFunctionLibrary);
+        uriMap.put(ARQConstants.ARQPropertyFunctionLibraryURI, 
ARQConstants.ARQPropertyFunctionLibrary);
+        uriMap.put(ARQConstants.ARQProcedureLibraryURI, 
ARQConstants.ARQProcedureLibrary);
+
         // Old name, new name
-        uriMap.put("java:com.hp.hpl.jena.query.function.library.",
-                   "java:com.hp.hpl.jena.sparql.function.library.") ;
-        
-        uriMap.put("java:com.hp.hpl.jena.query.pfunction.library.",
-                   "java:com.hp.hpl.jena.sparql.pfunction.library.") ;
-        
+        uriMap.put("java:com.hp.hpl.jena.query.function.library.", 
"java:com.hp.hpl.jena.sparql.function.library.");
+
+        uriMap.put("java:com.hp.hpl.jena.query.pfunction.library.", 
"java:com.hp.hpl.jena.sparql.pfunction.library.");
+
         // Leviathan library
-        uriMap.put(LeviathanConstants.LeviathanFunctionLibraryURI, 
-                   LeviathanConstants.LeviathanFunctionLibrary) ;
+        uriMap.put(LeviathanConstants.LeviathanFunctionLibraryURI, 
LeviathanConstants.LeviathanFunctionLibrary);
     }
-    
-    public static boolean isPossibleDynamicURI(String uri, Class<?> 
expectedClass)
-    {
-        uri = mapDynamicURI(uri) ;
-        if ( uri == null )
-            return false ;
+
+    public static boolean isPossibleDynamicURI(String uri, Class<?> 
expectedClass) {
+        uri = mapDynamicURI(uri);
+        if (uri == null)
+            return false;
         // Need to force the load to check everything.
         // Callers (who are expectedClass sensitive) should have
         // an "alreadyLoaded" cache
-        return loadClass(uri, expectedClass) != null ;
+        return loadClass(uri, expectedClass) != null;
     }
 
-    public static String mapDynamicURI(String uri)
-    {
-        Map.Entry<String, String> e = find(uri) ;
-        if ( e == null )
-        {
-            if ( uri.startsWith(ARQConstants.javaClassURIScheme) )
-                return uri ;
-            return null ;
+    public static String mapDynamicURI(String uri) {
+        Map.Entry<String, String> e = find(uri);
+        if (e == null) {
+            if (uri.startsWith(ARQConstants.javaClassURIScheme))
+                return uri;
+            return null;
         }
-        
-        String k = e.getKey() ;
+
+        String k = e.getKey();
         String v = e.getValue();
 
-        uri = uri.substring(k.length()) ;
-        uri = v + uri ;
-        return uri ;
+        uri = uri.substring(k.length());
+        uri = v + uri;
+        return uri;
     }
-    
-    private static Map.Entry<String,String> find(String uri)
-    {
-        for ( Map.Entry<String, String> e : uriMap.entrySet() )
-        {
+
+    private static Map.Entry<String, String> find(String uri) {
+        for (Map.Entry<String, String> e : uriMap.entrySet()) {
             String k = e.getKey();
-            if ( uri.startsWith( k ) )
-            {
+            if (uri.startsWith(k)) {
                 return e;
             }
         }
-        return null ;
+
+        return null;
     }
-    
-    public static Class<?> loadClass(String uri, Class<?> expectedClass)
-    {
-        uri = mapDynamicURI(uri) ;
-        if ( uri == null )
-            return null ;
-        
-        return Loader.loadClass(uri, expectedClass) ;
+
+    public static Class<?> loadClass(String uri, Class<?> expectedClass) {
+        uri = mapDynamicURI(uri);
+        if (uri == null)
+            return null;
+
+        return Loader.loadClass(uri, expectedClass);
     }
-    
+
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/50de1266/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java
----------------------------------------------------------------------
diff --git 
a/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java
 
b/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java
index 346cc48..d519bde 100644
--- 
a/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java
+++ 
b/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestLeviathanFunctions.java
@@ -21,7 +21,6 @@ package com.hp.hpl.jena.sparql.expr;
 import org.apache.jena.atlas.junit.BaseTest;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import com.hp.hpl.jena.graph.Node;
@@ -30,12 +29,12 @@ import com.hp.hpl.jena.shared.impl.PrefixMappingImpl;
 import com.hp.hpl.jena.sparql.ARQConstants;
 import com.hp.hpl.jena.sparql.function.FunctionEnvBase;
 import com.hp.hpl.jena.sparql.function.library.leviathan.LeviathanConstants;
-import com.hp.hpl.jena.sparql.serializer.SerializationContext;
 import com.hp.hpl.jena.sparql.util.ExprUtils;
 import com.hp.hpl.jena.sparql.util.NodeFactoryExtra;
 
 public class TestLeviathanFunctions extends BaseTest {
 
+    private static final double DELTA = 0.0000000001d;
     static boolean warnOnBadLexicalForms = true;
 
     @BeforeClass
@@ -50,8 +49,6 @@ public class TestLeviathanFunctions extends BaseTest {
     }
 
     private static PrefixMapping pmap = new PrefixMappingImpl();
-    private static SerializationContext serContext = new 
SerializationContext(false);
-
     static {
         pmap.setNsPrefixes(ARQConstants.getGlobalPrefixMap());
         pmap.setNsPrefix("lfn", 
LeviathanConstants.LeviathanFunctionLibraryURI);
@@ -146,88 +143,97 @@ public class TestLeviathanFunctions extends BaseTest {
     public void log_03() {
         test("lfn:log(-1)", NodeFactoryExtra.doubleToNode(Double.NaN));
     }
-    
+
     @Test
     public void log_04() {
         test("lfn:log(4, 2)", "2");
     }
-    
+
     @Test
     public void log_05() {
         test("lfn:log(4, 16)", "0.5");
     }
-    
+
     @Test
     public void log_06() {
         test("lfn:log(16, 4)", "2");
     }
-    
+
     @Test
     public void reciprocal_01() {
         test("lfn:reciprocal(1)", "1");
     }
-    
+
     @Test
     public void reciprocal_02() {
         test("lfn:reciprocal(2)", "0.5");
     }
-    
+
     @Test
     public void reciprocal_03() {
         test("lfn:reciprocal(lfn:reciprocal(2))", "2");
     }
-    
+
     @Test
     public void root_01() {
         test("lfn:root(4,2)", "2");
     }
-    
+
     @Test
     public void root_02() {
         test("lfn:root(2,1)", "2");
     }
-    
+
     @Test
-    @Ignore // Unfortunately Java floating point precision is awful and I get 
3.999999999999996 when running on Oracle JVM
     public void root_03() {
-        test("lfn:root(64,3)", "4");
+        testDouble("lfn:root(64,3)", "4", DELTA);
     }
-    
+
     @Test
     public void sqrt_01() {
         test("lfn:sqrt(4)", "2");
     }
-    
+
     @Test
     public void sqrt_02() {
         test("lfn:sqrt(144)", "12");
     }
-    
+
     @Test
     public void cartesian_01() {
         test("lfn:cartesian(0, 0, 0, 0)", "0");
     }
-    
+
     @Test
     public void cartesian_02() {
         test("lfn:cartesian(0, 0, 3, 4)", "5");
     }
-    
+
     @Test
     public void cartesian_03() {
         test("lfn:cartesian(0, 0, 0, 3, 4, 0)", "5");
     }
-    
+
     @Test
     public void cartesian_04() {
         test("lfn:cartesian(0, 0, 0, 0, 3, 4)", "5");
     }
-    
+
     @Test
     public void cartesian_05() {
         test("lfn:cartesian(0, 0, 0, 3, 0, 4)", "5");
     }
+
+    @Test
+    public void cos_01() {
+        testDouble("lfn:cos(lfn:degrees-to-radians(60))", "0.5", DELTA);
+    }
     
+    @Test
+    public void acos_01() {
+        
testDouble("lfn:radians-to-degrees(lfn:cos-1(lfn:cos(lfn:degrees-to-radians(60))))",
 "60", DELTA);
+    }
+
     private static void test(String exprString, String result) {
         Node r = NodeFactoryExtra.parseNode(result);
         test(exprString, r);
@@ -235,12 +241,36 @@ public class TestLeviathanFunctions extends BaseTest {
 
     private static void test(String exprString, Node result) {
         Expr expr = ExprUtils.parse(exprString, pmap);
-        NodeValue nv = expr.eval(null, new FunctionEnvBase());
-        NodeValue nvr = NodeValue.makeNode(result);
+        NodeValue actual = expr.eval(null, new FunctionEnvBase());
+        NodeValue expected = NodeValue.makeNode(result);
+
+        // Note that we don't test lexical form because we can get mismatches
+        // between how things like doubles are expressed
+        assertTrue("Not same value: Expected = " + expected + " : Actual = " + 
actual,
+                NodeValue.sameAs(expected, actual));
+    }
+
+    private static void testDouble(String exprString, String result, double 
delta) {
+        Node r = NodeFactoryExtra.parseNode(result);
+        testDouble(exprString, r, delta);
+    }
+
+    private static void testDouble(String exprString, Node result, double 
delta) {
+        Expr expr = ExprUtils.parse(exprString, pmap);
+        NodeValue actual = expr.eval(null, new FunctionEnvBase());
+        NodeValue expected = NodeValue.makeNode(result);
 
         // Note that we don't test lexical form because we can get mismatches
         // between how things like doubles are expressed
-        assertTrue("Not same value: Expected: " + nvr + " : Actual = " + nv, 
NodeValue.sameAs(nvr, nv));
+        if (NodeValue.sameAs(expected, actual))
+            return;
+
+        // Because Java floating point calculations are woefully imprecise we
+        // are in many cases simply testing that the differences between the
+        // values are within a given delta
+        double difference = Math.abs(actual.getDouble() - 
expected.getDouble());
+        assertTrue("Values not within given delta " + delta + ": Expected = " 
+ expected + " : Actual = " + actual,
+                difference <= delta);
     }
 
     private static void testError(String exprString) {

Reply via email to