Author: andy
Date: Sat Jul  5 15:49:24 2014
New Revision: 1608039

URL: http://svn.apache.org/r1608039
Log:
JENA-740 : REPLACE to handle empty string consistently

Modified:
    
jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/nodevalue/XSDFuncOp.java
    
jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestFunctions.java

Modified: 
jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/nodevalue/XSDFuncOp.java
URL: 
http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/nodevalue/XSDFuncOp.java?rev=1608039&r1=1608038&r2=1608039&view=diff
==============================================================================
--- 
jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/nodevalue/XSDFuncOp.java
 (original)
+++ 
jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/nodevalue/XSDFuncOp.java
 Sat Jul  5 15:49:24 2014
@@ -36,6 +36,7 @@ import java.math.BigInteger ;
 import java.util.HashSet ;
 import java.util.List ;
 import java.util.Set ;
+import java.util.regex.Matcher ;
 import java.util.regex.Pattern ;
 
 import javax.xml.datatype.DatatypeConstants ;
@@ -375,17 +376,45 @@ public class XSDFuncOp
             String flagsStr = checkAndGetStringLiteral("replace", 
nvFlags).getLiteralLexicalForm() ;
             flags = RegexJava.makeMask(flagsStr) ;
         }
-
         return strReplace(nvStr, Pattern.compile(pat, flags), nvReplacement) ;
     }
 
     public static NodeValue strReplace(NodeValue nvStr, Pattern pattern, 
NodeValue nvReplacement) {
         String n = checkAndGetStringLiteral("replace", 
nvStr).getLiteralLexicalForm() ;
         String rep = checkAndGetStringLiteral("replace", 
nvReplacement).getLiteralLexicalForm() ;
-        String x = pattern.matcher(n).replaceAll(rep) ;
+        String x = replaceAll(pattern.matcher(n), rep) ;
+        if ( x == null )
+            // No replacement.
+            return nvStr ; 
         return calcReturn(x, nvStr.asNode()) ;
     }
 
+    // Jena's replaceAll and xsd:func-replace differ in the handling of 
matching
+    // an empty string.
+    // Java:  ("", ".*", "x") --> "x" and ("notEmpty", ".*", "x") --> "xx"
+    // F&O: [err:FORX0003] (in F&O, a global error; SPARQL does not have 
global execution errors)
+    // http://www.w3.org/TR/xpath-functions/#func-replace
+    // ARQ 
+    
+    private static String replaceAll(Matcher matcher, String rep) {
+        // Follow Java -- return matcher.replaceAll(rep) ;
+        StringBuffer sb = null ;   // Delay until needed
+        while(matcher.find()) {
+            if ( sb == null )
+                sb = new StringBuffer() ;
+            else {
+                // Do one match of zerolength string otherwise filter out.
+                if (matcher.start() == matcher.end() )
+                    continue ;
+            }
+            matcher.appendReplacement(sb, rep);
+        }
+        if ( sb == null )
+            return null ;
+        matcher.appendTail(sb);
+        return sb.toString();
+    }
+
     public static NodeValue strReplace(NodeValue nvStr, NodeValue nvPattern, 
NodeValue nvReplacement) {
         return strReplace(nvStr, nvPattern, nvReplacement, null) ;
     }

Modified: 
jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestFunctions.java
URL: 
http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestFunctions.java?rev=1608039&r1=1608038&r2=1608039&view=diff
==============================================================================
--- 
jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestFunctions.java
 (original)
+++ 
jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/expr/TestFunctions.java
 Sat Jul  5 15:49:24 2014
@@ -157,14 +157,24 @@ public class TestFunctions
     @Test public void exprContains17() { testEvalException("Contains(123, 
'ab'@fr)") ; }
     @Test public void exprContains18() { 
testEvalException("STRENDS('123'^^xsd:string, 12.3)") ; }
 
-    @Test public void exprReplace1()   { test("REPLACE('abc', 'b', 'Z')", 
NodeValue.makeString("aZc")) ; }
-    @Test public void exprReplace2()   { test("REPLACE('abc', 'b.', 'Z')", 
NodeValue.makeString("aZ")) ; }
-    @Test public void exprReplace3()   { test("REPLACE('abcbd', 'b.', 'Z')", 
NodeValue.makeString("aZZ")) ; }
-    
-    @Test public void exprReplace4()   { test("REPLACE('abcbd'^^xsd:string, 
'b.', 'Z')", NodeValue.makeNode("aZZ", XSDDatatype.XSDstring)) ; }
-    @Test public void exprReplace5()   { test("REPLACE('abcbd'@en, 'b.', 
'Z')", NodeValue.makeNode("aZZ", "en", (String)null)) ; }
-    @Test public void exprReplace6()   { test("REPLACE('abcbd', 'B.', 'Z', 
'i')", NodeValue.makeString("aZZ")) ; }
-    
+    @Test public void exprReplace01()  { test("REPLACE('abc', 'b', 'Z')", 
NodeValue.makeString("aZc")) ; }
+    @Test public void exprReplace02()  { test("REPLACE('abc', 'b.', 'Z')", 
NodeValue.makeString("aZ")) ; }
+    @Test public void exprReplace03()  { test("REPLACE('abcbd', 'b.', 'Z')", 
NodeValue.makeString("aZZ")) ; }
+    
+    @Test public void exprReplace04()  { test("REPLACE('abcbd'^^xsd:string, 
'b.', 'Z')", NodeValue.makeNode("aZZ", XSDDatatype.XSDstring)) ; }
+    @Test public void exprReplace05()  { test("REPLACE('abcbd'@en, 'b.', 
'Z')", NodeValue.makeNode("aZZ", "en", (String)null)) ; }
+    @Test public void exprReplace06()  { test("REPLACE('abcbd', 'B.', 'Z', 
'i')", NodeValue.makeString("aZZ")) ; }
+    
+    // See JENA-740
+    // ARQ provides replacement of the potentially empty string.
+    @Test public void exprReplace07()  { test("REPLACE('abc', '.*', 'Z')", 
NodeValue.makeString("Z")) ; }
+    @Test public void exprReplace08()  { test("REPLACE('', '.*', 'Z')",    
NodeValue.makeString("Z")) ; }
+    @Test public void exprReplace09()  { test("REPLACE('abc', '.?', 'Z')", 
NodeValue.makeString("ZZZ")) ; }
+    
+    @Test public void exprReplace10()  { test("REPLACE('abc', 'XXX', 'Z')", 
NodeValue.makeString("abc")) ; }
+    @Test public void exprReplace11()  { test("REPLACE('', '.', 'Z')",      
NodeValue.makeString("")) ; }
+    @Test public void exprReplace12()  { test("REPLACE('', '(a|b)?', 'Z')", 
NodeValue.makeString("Z")) ; }
+
     @Test public void exprBoolean1()    { test("fn:boolean('')", FALSE) ; }
     @Test public void exprBoolean2()    { test("fn:boolean(0)", FALSE) ; }
     @Test public void exprBoolean3()    { test("fn:boolean(''^^xsd:string)", 
FALSE) ; }


Reply via email to