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) ; }