You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2021/11/10 22:29:19 UTC

[jena] branch main updated: JENA-2197: Whitespace facet on XSD literals

This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/main by this push:
     new 165fac4  JENA-2197: Whitespace facet on XSD literals
     new 3fa5214  Merge pull request #1107 from afs/ws-facet
165fac4 is described below

commit 165fac4a6ed0d1ad1d8332a70e8c026f9e4dd295
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Mon Nov 8 20:48:16 2021 +0000

    JENA-2197: Whitespace facet on XSD literals
---
 .../org/apache/jena/sparql/expr/NodeValue.java     | 85 ++++++++++------------
 .../sparql/expr/nodevalue/NodeValueDateTime.java   | 16 ++--
 .../org/apache/jena/sparql/expr/TestNodeValue.java | 63 +++++++++++++++-
 3 files changed, 111 insertions(+), 53 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
index 32e928c..41ecb54 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
@@ -38,7 +38,6 @@ import org.apache.jena.datatypes.DatatypeFormatException ;
 import org.apache.jena.datatypes.RDFDatatype ;
 import org.apache.jena.datatypes.TypeMapper ;
 import org.apache.jena.datatypes.xsd.XSDDatatype;
-import org.apache.jena.datatypes.xsd.XSDDateTime ;
 import org.apache.jena.ext.xerces.DatatypeFactoryInst;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.NodeFactory ;
@@ -210,30 +209,26 @@ public abstract class NodeValue extends ExprNode
     public static NodeValue makeDate(String lexicalForm)
     { return NodeValue.makeNode(lexicalForm, XSDdate) ; }
 
-    public static NodeValue makeDateTime(Calendar cal)
-    {
-        String lex = DateTimeUtils.calendarToXSDDateTimeString(cal) ;
-        return NodeValue.makeNode(lex, XSDdateTime) ;
+    public static NodeValue makeDateTime(Calendar cal) {
+        String lex = DateTimeUtils.calendarToXSDDateTimeString(cal);
+        return NodeValue.makeNode(lex, XSDdateTime);
     }
 
-    public static NodeValue makeDateTime(XMLGregorianCalendar cal)
-    {
-        String lex = cal.toXMLFormat() ;
-        Node node = org.apache.jena.graph.NodeFactory.createLiteral(lex, XSDdateTime) ;
-        return new NodeValueDateTime(lex, node) ;
+    public static NodeValue makeDateTime(XMLGregorianCalendar cal) {
+        String lex = cal.toXMLFormat();
+        Node node = NodeFactory.createLiteral(lex, XSDdateTime);
+        return NodeValueDateTime.create(lex, node);
     }
 
-    public static NodeValue makeDate(Calendar cal)
-    {
-        String lex = DateTimeUtils.calendarToXSDDateString(cal) ;
-        return NodeValue.makeNode(lex, XSDdate) ;
+    public static NodeValue makeDate(Calendar cal) {
+        String lex = DateTimeUtils.calendarToXSDDateString(cal);
+        return NodeValue.makeNode(lex, XSDdate);
     }
 
-    public static NodeValue makeDate(XMLGregorianCalendar cal)
-    {
-        String lex = cal.toXMLFormat() ;
-        Node node = org.apache.jena.graph.NodeFactory.createLiteral(lex, XSDdate) ;
-        return new NodeValueDateTime(lex, node) ;
+    public static NodeValue makeDate(XMLGregorianCalendar cal) {
+        String lex = cal.toXMLFormat();
+        Node node = NodeFactory.createLiteral(lex, XSDdate);
+        return NodeValueDateTime.create(lex, node);
     }
 
     public static NodeValue makeDuration(String lexicalForm)
@@ -1037,13 +1032,16 @@ public abstract class NodeValue extends ExprNode
 
             // Order here is promotion order integer-decimal-float-double
 
-            if ( ! datatype.equals(XSDdecimal) ) {
+            // XSD allows whitespace. Java String.trim removes too much
+            // so must test for validity on the untrimmed lexical form.
+            String lexTrimmed = lex.trim();
+
+            if ( ! datatype.equals(XSDdecimal) ) { // ! decimal is short for integers and all derived types.
                 // XSD integer and derived types
                 if ( XSDinteger.isValidLiteral(lit) )
                 {
-                    // .trim() implements the facet of whitespace collapse.
                     // BigInteger does not accept such whitespace.
-                    String s = node.getLiteralLexicalForm().trim() ;
+                    String s = lexTrimmed;
                     if ( s.startsWith("+") )
                         // BigInteger does not accept leading "+"
                         s = s.substring(1) ;
@@ -1055,7 +1053,7 @@ public abstract class NodeValue extends ExprNode
             }
 
             if ( datatype.equals(XSDdecimal) && XSDdecimal.isValidLiteral(lit) ) {
-                BigDecimal decimal = new BigDecimal(lit.getLexicalForm()) ;
+                BigDecimal decimal = new BigDecimal(lexTrimmed) ;
                 return new NodeValueDecimal(decimal, node) ;
             }
 
@@ -1071,59 +1069,56 @@ public abstract class NodeValue extends ExprNode
                 return new NodeValueDouble(d, node) ;
             }
 
+            if ( datatype.equals(XSDboolean) && XSDboolean.isValidLiteral(lit) ) {
+                boolean b = (Boolean) lit.getValue();
+                return new NodeValueBoolean(b, node) ;
+            }
+
             if ( (datatype.equals(XSDdateTime) || datatype.equals(XSDdateTimeStamp)) && XSDdateTime.isValid(lex) ) {
-                return new NodeValueDateTime(lex, node) ;
+                return NodeValueDateTime.create(lexTrimmed, node) ;
             }
 
             if ( datatype.equals(XSDdate) && XSDdate.isValidLiteral(lit) ) {
-                // Jena datatype support works on masked dataTimes.
-                //XSDDateTime dateTime = (XSDDateTime)lit.getValue() ;
-                return new NodeValueDateTime(lex, node) ;
+                return NodeValueDateTime.create(lexTrimmed, node) ;
             }
 
             if ( datatype.equals(XSDtime) && XSDtime.isValidLiteral(lit) ) {
-                return new NodeValueDateTime(lex, node) ;
+                return NodeValueDateTime.create(lexTrimmed, node) ;
             }
 
             if ( datatype.equals(XSDgYear) && XSDgYear.isValidLiteral(lit) ) {
-                return new NodeValueDateTime(lex, node) ;
+                return NodeValueDateTime.create(lexTrimmed, node) ;
             }
             if ( datatype.equals(XSDgYearMonth) && XSDgYearMonth.isValidLiteral(lit) ) {
-                return new NodeValueDateTime(lex, node) ;
+                return NodeValueDateTime.create(lexTrimmed, node) ;
             }
             if ( datatype.equals(XSDgMonth) && XSDgMonth.isValidLiteral(lit) ) {
-                XSDDateTime time = (XSDDateTime)lit.getValue() ;
-                return new NodeValueDateTime(lex, node) ;
+                return NodeValueDateTime.create(lexTrimmed, node) ;
             }
 
             if ( datatype.equals(XSDgMonthDay) && XSDgMonthDay.isValidLiteral(lit) ) {
-                XSDDateTime time = (XSDDateTime)lit.getValue() ;
-                return new NodeValueDateTime(lex, node) ;
+                return NodeValueDateTime.create(lexTrimmed, node) ;
             }
             if ( datatype.equals(XSDgDay) && XSDgDay.isValidLiteral(lit) ) {
-                XSDDateTime time = (XSDDateTime)lit.getValue() ;
-                return new NodeValueDateTime(lex, node) ;
+                return NodeValueDateTime.create(lexTrimmed, node) ;
             }
 
+            // -- Duration
+
             if ( datatype.equals(XSDduration) && XSDduration.isValid(lex) ) {
-                Duration duration = xmlDatatypeFactory.newDuration(lex) ;
+                Duration duration = xmlDatatypeFactory.newDuration(lexTrimmed) ;
                 return new NodeValueDuration(duration, node) ;
             }
 
             if ( datatype.equals(XSDyearMonthDuration) && XSDyearMonthDuration.isValid(lex) ) {
-                Duration duration = xmlDatatypeFactory.newDuration(lex) ;
+                Duration duration = xmlDatatypeFactory.newDuration(lexTrimmed) ;
                 return new NodeValueDuration(duration, node) ;
             }
             if ( datatype.equals(XSDdayTimeDuration) && XSDdayTimeDuration.isValid(lex) ) {
-                Duration duration = xmlDatatypeFactory.newDuration(lex) ;
+                Duration duration = xmlDatatypeFactory.newDuration(lexTrimmed) ;
                 return new NodeValueDuration(duration, node) ;
             }
 
-            if ( datatype.equals(XSDboolean) && XSDboolean.isValidLiteral(lit) ) {
-                boolean b = (Boolean) lit.getValue();
-                return new NodeValueBoolean(b, node) ;
-            }
-
             // If wired into the TypeMapper via RomanNumeralDatatype.enableAsFirstClassDatatype
 //            if ( RomanNumeralDatatype.get().isValidLiteral(lit) )
 //            {
@@ -1136,7 +1131,7 @@ public abstract class NodeValue extends ExprNode
             {
                 if ( lit.getDatatypeURI().equals(RomanNumeralDatatype.get().getURI()) )
                 {
-                    Object obj = RomanNumeralDatatype.get().parse(lit.getLexicalForm()) ;
+                    Object obj = RomanNumeralDatatype.get().parse(lexTrimmed) ;
                     if ( obj instanceof Integer )
                         return new NodeValueInteger(((Integer)obj).longValue()) ;
                     if ( obj instanceof RomanNumeral )
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDateTime.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDateTime.java
index 1efdf91..80d06bc 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDateTime.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDateTime.java
@@ -30,21 +30,27 @@ public class NodeValueDateTime extends NodeValue
 {
     final private XMLGregorianCalendar datetime ;
 
-    public NodeValueDateTime(String lex, Node n)
-    {
-        super(n) ;
+    /** Lex - caller removes leading and trailing whitespace. */
+    public static NodeValueDateTime create(String lex, Node n) {
+        XMLGregorianCalendar datetime;
         // Java bug : Java6, Java8: gMonth with a timezone of Z causes IllegalArgumentException
-        if ( XSDgMonth.equals(getNode().getLiteralDatatype()) )
+        if ( XSDgMonth.equals(n.getLiteralDatatype()) )
         {
             if ( lex.endsWith("Z") )
             {
                 lex = lex.substring(0, lex.length()-1) ;
                 datetime = NodeValue.xmlDatatypeFactory.newXMLGregorianCalendar(lex) ;
                 datetime.setTimezone(0) ;
-                return ;
+                return new NodeValueDateTime(datetime, n);
             }
         }
         datetime = NodeValue.xmlDatatypeFactory.newXMLGregorianCalendar(lex) ;
+        return new NodeValueDateTime(datetime, n);
+    }
+
+    public NodeValueDateTime(XMLGregorianCalendar datetime, Node n) {
+        super(n);
+        this.datetime = datetime;
     }
 
     // Look at datatype.
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValue.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValue.java
index edb0e87..3c1e300 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValue.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValue.java
@@ -386,7 +386,7 @@ public class TestNodeValue
 
     @Test
     public void testNodeFloat1() {
-        // Theer is no SPARQL representation in short form of a float.
+        // There is no SPARQL representation in short form of a float.
         NodeValue v = NodeValue.makeNode("57.0", XSDDatatype.XSDfloat);
         assertTrue("Not a number: " + v, v.isNumber());
         assertTrue("Not a float: " + v, v.isFloat());
@@ -398,8 +398,22 @@ public class TestNodeValue
     }
 
     @Test
+    public void testNodeFloat2() {
+        // WhiteSpace facet
+        NodeValue v = NodeValue.makeNode(" 57.0 ", XSDDatatype.XSDfloat);
+        assertTrue("Not a number: " + v, v.isNumber());
+        assertTrue("Not a float: " + v, v.isFloat());
+        assertTrue("Not a double(float): " + v, v.isDouble());
+        assertTrue("Not a node: " + v, v.hasNode());
+        String actualStr = v.asQuotedString();
+
+        assertEquals("Print form mismatch", "\" 57.0 \"^^<" + XSDDatatype.XSDfloat.getURI() + ">", actualStr);
+    }
+
+
+    @Test
     public void testNodeDouble1() {
-        // Note input form is legal and canomical as a lexical form double
+        // Note input form is legal and canonical as a lexical form double
         NodeValue v = NodeValue.makeNode("57.0e0", XSDDatatype.XSDdouble);
         assertTrue("Not a number: " + v, v.isNumber());
         assertTrue("Not a double: " + v, v.isDouble());
@@ -444,7 +458,6 @@ public class TestNodeValue
         assertTrue("Not a node: " + v, v.hasNode());
     }
 
-
     @Test
     public void testNodeBool1() {
         NodeValue v = NodeValue.makeNode("true", XSDDatatype.XSDboolean);
@@ -479,6 +492,50 @@ public class TestNodeValue
         assertFalse("Not false: " + v, XSDFuncOp.booleanEffectiveValue(v));
     }
 
+    @Test
+    public void testNodeDateTime1() {
+        NodeValue v = NodeValue.makeNode("2021-11-08T20:37:25+01:00", XSDDatatype.XSDdateTime);
+        assertTrue("Not a dateTime: " + v, v.isDateTime());
+    }
+
+    @Test
+    public void testNodeDateTime2() {
+        NodeValue v = NodeValue.makeNode("\t2021-11-08T20:37:26+01:00\t", XSDDatatype.XSDdateTime);
+        assertTrue("Not a dateTime: " + v, v.isDateTime());
+    }
+
+    @Test
+    public void testNodeGYear1() {
+        NodeValue v = NodeValue.makeNode("2021", XSDDatatype.XSDgYear);
+        assertTrue("Not a gYear: " + v, v.isGYear());
+    }
+
+    @Test
+    public void testNodeGYear2() {
+        NodeValue v = NodeValue.makeNode("\t2021\t", XSDDatatype.XSDgYear);
+        assertTrue("Not a gYear: " + v, v.isGYear());
+    }
+
+    @Test
+    public void testNodeDuration1() {
+        NodeValue v = NodeValue.makeNode("P1Y", XSDDatatype.XSDyearMonthDuration);
+        assertTrue("Not a yearMonthDuration: " + v, v.isYearMonthDuration());
+        assertTrue("Not a duration: " + v, v.isDuration());
+    }
+
+    @Test
+    public void testNodeDuration2() {
+        NodeValue v = NodeValue.makeNode("P1Y  ", XSDDatatype.XSDduration);
+        assertTrue("Not a yearMonthDuration: " + v, v.isDuration());
+    }
+
+    @Test
+    public void testNodeDuration3() {
+        // Internal whiespace -> invalide.
+        NodeValue v = NodeValue.makeNode("P1Y  10S", XSDDatatype.XSDduration);
+        assertFalse("Is a valid duration: " + v, v.isDuration());
+    }
+
     static NodeValue make(String str) {
         Node n = NodeFactoryExtra.parseNode(str);
         NodeValue nv = NodeValue.makeNode(n);