Skip to content

Commit e3e3efe

Browse files
committed
fix: Fix bug writing number-like strings that include plus, with AQF
A string like "1e+6" must be represented as 1e!+6, where the plus is escaped. Otherwise, the plus will be decoded as a space when it's later parsed.
1 parent cfa3b3a commit e3e3efe

File tree

3 files changed

+45
-16
lines changed

3 files changed

+45
-16
lines changed

module/jsonurl-core/src/main/java/org/jsonurl/text/JsonUrlTextAppender.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public abstract class JsonUrlTextAppender<A extends Appendable, R> // NOPMD
8989
* @param dest JSON&#x2192;URL text destination
9090
* @param options Set of JsonUrlOptions
9191
*/
92-
public JsonUrlTextAppender(
92+
protected JsonUrlTextAppender(
9393
A dest,
9494
CompositeType impliedType,
9595
Set<JsonUrlOption> options) {
@@ -504,7 +504,12 @@ private static <T extends Appendable> boolean appendLiteral(
504504
dest.append(text, start, end);
505505

506506
} else if (optionAQF(options)) {
507-
dest.append('!').append(text, start, end);
507+
if (contains(text, start, end, '+')) {
508+
encodeAqf(dest, text, start, end);
509+
510+
} else {
511+
dest.append('!').append(text, start, end);
512+
}
508513

509514
} else if (contains(text, start, end, '+')) {
510515
encode(dest, text, start, end, false, false);

module/jsonurl-core/src/test/java/org/jsonurl/text/JsonUrlStringBuilderTest.java

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -576,57 +576,67 @@ void testException() {
576576
"n", "nu", "nul", "Null", "nUll", "nuLl", "nulL",
577577
})
578578
void testNonQuotedString(String text) throws IOException {
579-
testValue(text, text, text, text, text);
579+
testValue(text, text, text, text, text, text);
580580
}
581581

582582
@ParameterizedTest
583583
@ValueSource(strings = {
584584
"true", "false", "null", "1", "1.0", "1e3", "1e-3",
585585
})
586586
void testQuotedString(String text) throws IOException {
587-
testValue(text, text, '\'' + text + '\'', text, '!' + text);
587+
testValue(text, text, '\'' + text + '\'', text, '!' + text, text);
588588
}
589589

590590
@ParameterizedTest
591591
@CsvSource({
592592
//
593593
// text - a string literal
594+
//
594595
// expected - with no options it determines that the plus needs to
595596
// be encoded so that it doesn't look like a number
597+
//
596598
// expectedISL - Plus needs to be encoded so it isn't decoded as a
597599
// space
600+
//
598601
// expectedAqf - this is a valid literal in the AQF syntax so
599602
// the first character must be escaped.
600603
//
601-
"1e+3,1e%2B3,1e%2B3,!1e+3",
604+
// expectedISLAqf - the leading one is not escaped because it's
605+
// an implied string. However, the plus needs to be escaped so it
606+
// isn't interpreted as a space.
607+
//
608+
"1e+3,1e%2B3,1e%2B3,1e!+3,1e!+3",
602609
})
603610
void testEncodedString(
604611
String text,
605612
String expected,
606613
String expectedImpliedStringLiteral,
607-
String expectedAqf) throws IOException {
614+
String expectedAqf,
615+
String expectedImpliedStringAqf) throws IOException {
608616

609617
testValue(
610618
text,
611619
text,
612620
expected,
613621
expectedImpliedStringLiteral,
614-
expectedAqf);
622+
expectedAqf,
623+
expectedImpliedStringAqf);
615624
}
616625

617626
@Test
618627
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
619628
void testEncodedString() throws IOException { // NOPMD - JUnitTestsShouldIncludeAssert
620-
testValue("'hello", "%27hello", "%27hello", "'hello", "'hello");
621-
testValue("hello,", "'hello,'", "'hello,'", "hello%2C", "hello!,");
622-
testValue("hello, ", "'hello,+'", "'hello,+'", "hello%2C+", "hello!,+");
623-
testValue("a b", "a+b", "a+b", "a+b", "a+b");
624-
testValue("a(", "'a('", "'a('", "a%28", "a!(");
629+
testValue("'hello", "%27hello", "%27hello", "'hello", "'hello", null);
630+
testValue("hello,", "'hello,'", "'hello,'", "hello%2C", "hello!,", null);
631+
testValue("hello, ", "'hello,+'", "'hello,+'", "hello%2C+", "hello!,+", null);
632+
testValue("a b", "a+b", "a+b", "a+b", "a+b", null);
633+
testValue("a(", "'a('", "'a('", "a%28", "a!(", null);
625634
testValue("bob's burgers",
626635
"bob's+burgers",
627636
"bob's+burgers",
628637
"bob's+burgers",
629-
"bob's+burgers");
638+
"bob's+burgers",
639+
null);
630640
}
631641

632642
@ParameterizedTest
@@ -666,7 +676,8 @@ private void testValue(
666676
String keyOutput,
667677
String nonKeyOutput,
668678
String nonKeyImpliedStringLiteralOutput,
669-
String nonKeyAqfOutput) throws IOException {
679+
String nonKeyAqfOutput,
680+
String nonKeyImpliedStringAqfOutput) throws IOException {
670681

671682
assertEquals(
672683
keyOutput,
@@ -698,5 +709,18 @@ private void testValue(
698709
.build(),
699710
nonKeyAqfOutput);
700711

712+
assertEquals(
713+
coalesce(nonKeyImpliedStringAqfOutput, nonKeyAqfOutput),
714+
new JsonUrlStringBuilder(
715+
JsonUrlOption.AQF,
716+
JsonUrlOption.IMPLIED_STRING_LITERALS)
717+
.add(text, 0, text.length())
718+
.build(),
719+
coalesce(nonKeyImpliedStringAqfOutput, nonKeyAqfOutput));
720+
721+
}
722+
723+
private static String coalesce(String a, String b) { // NOPMD - ShortVariable
724+
return a == null ? b : a;
701725
}
702726
}

module/jsonurl-factory/src/test/java/org/jsonurl/factory/AbstractParseTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,9 +651,9 @@ void testLiteral2(JsonUrlOption option) throws IOException {
651651
newParser(newOptions(option)).parse("1e%2B1"),
652652
desc);
653653

654-
// stringify("1e+1") -> string("1e%2B1")
654+
// stringify("1e+1") -> string("1e!+1")
655655
assertEquals(
656-
"!1e+1",
656+
"1e!+1",
657657
new JsonUrlStringBuilder(
658658
newOptions(option)).add("1e+1").build(),
659659
desc);

0 commit comments

Comments
 (0)