Skip to content

Commit bc8b3ef

Browse files
committed
feat: Add support for parsing implied arrays and objects
BREAKING CHANGE The primary feature included here is support for implied arrays, objects, including support for www-form-urlencoded separators to be used at the top-level for implied objects. There are a number of smaller changes that help support that feature. LimitException and SyntaxException now define inner-enums named Message. These replace the final Strings that were there before. This is not a breaking change as those strings were not public. The ParseResultFacade interface was added to separate the implementation of building a result from the parse() method. I also provides a clean interface for the parser to support targeting something other than a pre-modeled JSON API. ValueFactory has been removed from Parser. A ValueFactory is now provided to the parser as a method argument. Additionally, the new ValueFactoryParser may be used if there is a need for a Parser instance that maintains its own reference to a ValueFactory.
1 parent 41a2fca commit bc8b3ef

35 files changed

+3092
-764
lines changed

module/jsonurl-core/src/main/java/org/jsonurl/CharUtil.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ final class CharUtil {
149149
CHARBITS[')'] = IS_STRUCTCHAR | IS_QSCHAR | IS_ENC_QSTRSAFE;
150150
CHARBITS[','] = IS_STRUCTCHAR | IS_QSCHAR | IS_ENC_QSTRSAFE;
151151
CHARBITS[':'] = IS_STRUCTCHAR | IS_QSCHAR | IS_ENC_QSTRSAFE;
152+
CHARBITS['&'] = IS_STRUCTCHAR;
153+
CHARBITS['='] = IS_STRUCTCHAR;
152154
CHARBITS[' '] = IS_SPACE;
153155

154156
for (int i = 'A'; i <= 'F'; i++) {

module/jsonurl-core/src/main/java/org/jsonurl/JsonUrl.java

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
import static org.jsonurl.CharUtil.IS_QUOTE;
2424
import static org.jsonurl.CharUtil.IS_STRUCTCHAR;
2525
import static org.jsonurl.CharUtil.hexDecode;
26-
import static org.jsonurl.SyntaxException.ERR_MSG_BADCHAR;
27-
import static org.jsonurl.SyntaxException.ERR_MSG_BADPCTENC;
28-
import static org.jsonurl.SyntaxException.ERR_MSG_BADQSTR;
29-
import static org.jsonurl.SyntaxException.ERR_MSG_BADUTF8;
30-
import static org.jsonurl.SyntaxException.ERR_MSG_EXPECT_LITERAL;
31-
import static org.jsonurl.SyntaxException.ERR_MSG_NOTEXT;
26+
import static org.jsonurl.SyntaxException.Message.MSG_BAD_CHAR;
27+
import static org.jsonurl.SyntaxException.Message.MSG_BAD_PCT_ENC;
28+
import static org.jsonurl.SyntaxException.Message.MSG_BAD_QSTR;
29+
import static org.jsonurl.SyntaxException.Message.MSG_BAD_UTF8;
30+
import static org.jsonurl.SyntaxException.Message.MSG_EXPECT_LITERAL;
31+
import static org.jsonurl.SyntaxException.Message.MSG_NO_TEXT;
3232

3333
import java.io.IOException;
3434
import java.nio.charset.MalformedInputException;
@@ -61,7 +61,7 @@ static final NumberBuilder newNumberBuilder(
6161
//
6262
// use the factory as a BigMathProvider
6363
//
64-
ret.setMathContextProvider((BigMathProvider)factory);
64+
ret.setBigMathProvider((BigMathProvider)factory);
6565
}
6666

6767
return ret;
@@ -73,14 +73,14 @@ private static final int percentDecode(
7373
int len) {
7474

7575
if (off + 2 > len) {
76-
throw new SyntaxException(ERR_MSG_BADPCTENC, off);
76+
throw new SyntaxException(MSG_BAD_PCT_ENC, off);
7777
}
7878

7979
int c1 = hexDecode(s.charAt(off));
8080
int c2 = hexDecode(s.charAt(off + 1));
8181

8282
if (c1 < 0 || c2 < 0) {
83-
throw new SyntaxException(ERR_MSG_BADPCTENC, off);
83+
throw new SyntaxException(MSG_BAD_PCT_ENC, off);
8484
}
8585

8686
return ((c1 << 4) | c2);
@@ -150,7 +150,8 @@ private static final String string(
150150
CharSequence s,
151151
int start,
152152
int stop,
153-
boolean quoted) {
153+
boolean quoted,
154+
boolean isEmptyUnquotedStringOK) {
154155

155156
boolean needEndQuote = quoted;
156157

@@ -229,10 +230,16 @@ private static final String string(
229230
}
230231
}
231232
if (needEndQuote) {
232-
throw new SyntaxException(ERR_MSG_BADQSTR, stop);
233+
throw new SyntaxException(MSG_BAD_QSTR, stop);
233234
}
234235
if (more > 0) {
235-
throw new SyntaxException(ERR_MSG_BADUTF8, stop);
236+
throw new SyntaxException(MSG_BAD_UTF8, stop);
237+
}
238+
239+
if (!quoted && !isEmptyUnquotedStringOK && buf.length() == 0) {
240+
throw new SyntaxException(
241+
SyntaxException.Message.MSG_EXPECT_LITERAL,
242+
start);
236243
}
237244

238245
return buf.toString();
@@ -251,21 +258,23 @@ private static final String string(
251258
* @param start the start index
252259
* @param stop the stop index
253260
*/
254-
@SuppressWarnings("PMD")
255261
static final String literalToJavaString(
256262
StringBuilder buf,
257263
NumberBuilder num,
258264
CharSequence s,
259265
int start,
260-
int stop) {
266+
int stop,
267+
boolean isEmptyUnquotedStringOK) {
261268

262-
String ret;
269+
263270

264271
if (s.charAt(start) == '\'') {
265-
return string(buf, s, start + 1, stop, true);
272+
return string(buf, s, start + 1, stop, true, isEmptyUnquotedStringOK);
266273
}
267274

268-
if ((ret = getTrueFalseNull(s, start, stop)) != null) {
275+
String ret = getTrueFalseNull(s, start, stop);
276+
277+
if (ret != null) {
269278
return ret;
270279
}
271280

@@ -282,10 +291,10 @@ static final String literalToJavaString(
282291
}
283292

284293
if (num.parse(s, start, stop)) {
285-
return num.toString();
294+
return s.subSequence(start, stop).toString();
286295
}
287296

288-
return string(buf, s, start, stop, false);
297+
return string(buf, s, start, stop, false, isEmptyUnquotedStringOK);
289298
}
290299

291300
/**
@@ -309,10 +318,12 @@ static final <V> V literal(
309318
CharSequence s,
310319
int start,
311320
int stop,
312-
ValueFactory<V,?,?,?,?,?,?,?,?,?> factory) {
321+
ValueFactory<V,?,?,?,?,?,?,?,?,?> factory,
322+
boolean isEmptyUnquotedStringOK) {
313323

314324
if (s.charAt(start) == '\'') {
315-
return factory.getString(string(buf, s, start + 1, stop, true));
325+
return factory.getString(string(
326+
buf, s, start + 1, stop, true, isEmptyUnquotedStringOK));
316327
}
317328

318329
V ret = factory.getTrueFalseNull(s, start, stop);
@@ -332,7 +343,8 @@ static final <V> V literal(
332343
return factory.getNumber(num);
333344
}
334345

335-
return factory.getString(string(buf, s, start, stop, false));
346+
return factory.getString(string(
347+
buf, s, start, stop, false, isEmptyUnquotedStringOK));
336348
}
337349
}
338350

@@ -497,7 +509,7 @@ private JsonUrl() {
497509
* Determine the length of a literal value.
498510
*
499511
* <p>This simply calls
500-
* {@link #parseLiteralLength(CharSequence, int, int, String)
512+
* {@link #parseLiteralLength(CharSequence, int, int, org.jsonurl.SyntaxException.Message)
501513
* parseLiteralLength(s, start, stop, null)}.
502514
*
503515
* @param s text to be parsed
@@ -522,7 +534,8 @@ public static final int parseLiteralLength(
522534
* <p>This method will stop when the first structural character
523535
* is found or when ``stop'' is reached, whichever comes first.
524536
* Note, because it is static no limits are enforced as is with
525-
* {@link org.jsonurl.Parser#parse(CharSequence, int, int) parse}.
537+
* {@link org.jsonurl.Parser#parse(CharSequence, ValueFactory)
538+
* parse}.
526539
*
527540
* @param s text to be parsed
528541
* @param start start position in text
@@ -535,7 +548,7 @@ public static final int parseLiteralLength(
535548
CharSequence s,
536549
int start,
537550
int stop,
538-
String errmsg) {
551+
SyntaxException.Message errmsg) {
539552

540553
int ret = 0;
541554

@@ -561,9 +574,9 @@ public static final int parseLiteralLength(
561574
break;
562575
}
563576
}
564-
throw new SyntaxException(ERR_MSG_BADCHAR, start);
577+
throw new SyntaxException(MSG_BAD_CHAR, start);
565578
}
566-
throw new SyntaxException(ERR_MSG_BADQSTR, start);
579+
throw new SyntaxException(MSG_BAD_QSTR, start);
567580
}
568581

569582
for (; start < stop; start++) {
@@ -580,7 +593,7 @@ public static final int parseLiteralLength(
580593
break;
581594
}
582595
}
583-
throw new SyntaxException(ERR_MSG_BADCHAR, start);
596+
throw new SyntaxException(MSG_BAD_CHAR, start);
584597
}
585598

586599
if (start != stop && errmsg != null) {
@@ -591,7 +604,7 @@ public static final int parseLiteralLength(
591604

592605
/**
593606
* Determine the length of a literal value.
594-
* @see #parseLiteralLength(CharSequence, int, int, String)
607+
* @see #parseLiteralLength(CharSequence, int, int, org.jsonurl.SyntaxException.Message)
595608
*/
596609
public static final int parseLiteralLength(CharSequence s) {
597610
return parseLiteralLength(s, 0, s.length(), null);
@@ -608,8 +621,8 @@ public static final int parseLiteralLength(CharSequence s) {
608621
* <li>string
609622
* </ul>
610623
* No limits are enforced (as is with
611-
* {@link org.jsonurl.Parser#parse(CharSequence, int, int) parse})
612-
* because this method is static.
624+
* {@link org.jsonurl.Parser#parse(CharSequence, ValueFactory)
625+
* parse}) because this method is static.
613626
*
614627
* <p>Note, the third argument is a length not a position. It indicates
615628
* the number of characters to be parsed.
@@ -623,55 +636,62 @@ public static final <V> V parseLiteral(
623636
CharSequence s,
624637
int start,
625638
int length,
626-
ValueFactory<V,?,?,?,?,?,?,?,?,?> factory) {
639+
ValueFactory<V,?,?,?,?,?,?,?,?,?> factory,
640+
boolean isEmptyUnquotedStringOK) {
627641

628642
if (length == 0) {
629-
throw new SyntaxException(ERR_MSG_NOTEXT);
643+
throw new SyntaxException(MSG_NO_TEXT);
630644
}
631645

632646
int stop = start + length;
633-
parseLiteralLength(s, start, stop, ERR_MSG_EXPECT_LITERAL);
647+
parseLiteralLength(s, start, stop, MSG_EXPECT_LITERAL);
634648

635-
return Parse.literal(null, null, s, start, stop, factory);
649+
return Parse.literal(null, null, s, start, stop, factory, isEmptyUnquotedStringOK);
636650
}
637651

638652
/**
639653
* Parse a single literal value.
640654
*
641655
* <p>This simply calls
642-
* {@link #parseLiteral(CharSequence, int, int, ValueFactory)
643-
* parseLiteral(s, start, length, JavaValueFactory.PRIMITIVE)}.
644-
* @see JsonUrl#parseLiteral(CharSequence, int, int, ValueFactory)
656+
* {@link #parseLiteral(CharSequence, int, int, ValueFactory, boolean)
657+
* parseLiteral(s, start, length, JavaValueFactory.PRIMITIVE, false)}.
658+
*
659+
* @see JsonUrl#parseLiteral(CharSequence, int, int, ValueFactory, boolean)
645660
* @see JavaValueFactory#PRIMITIVE
646661
*/
647662
public static final Object parseLiteral(
648663
CharSequence s,
649664
int start,
650665
int length) {
651-
return parseLiteral(s, start, length, JavaValueFactory.PRIMITIVE);
666+
return parseLiteral(s, start, length, JavaValueFactory.PRIMITIVE, false);
652667
}
653668

654669
/**
655670
* Parse a single literal value.
656671
*
657672
* <p>This simply calls
658-
* {@link #parseLiteral(CharSequence, int, int, ValueFactory)
659-
* parseLiteral(s, 0, s.length(), JavaValueFactory.PRIMITIVE)}.
660-
* @see JsonUrl#parseLiteral(CharSequence, int, int, ValueFactory)
673+
* {@link #parseLiteral(CharSequence, int, int, ValueFactory, boolean)
674+
* parseLiteral(s, 0, s.length(), JavaValueFactory.PRIMITIVE, false)}.
675+
*
676+
* @see JsonUrl#parseLiteral(CharSequence, int, int, ValueFactory, boolean)
677+
* @see JavaValueFactory#PRIMITIVE
661678
*/
662679
public static final Object parseLiteral(CharSequence s) {
663-
return parseLiteral(s, 0, s.length(), JavaValueFactory.PRIMITIVE);
680+
return parseLiteral(s, 0, s.length(), JavaValueFactory.PRIMITIVE, false);
664681
}
665682

666683
/**
667684
* Parse a single literal value.
668685
*
669-
* @see JsonUrl#parseLiteral(CharSequence, int, int, ValueFactory)
686+
* <p>This simply calls
687+
* {@link #parseLiteral(CharSequence, int, int, ValueFactory, boolean)
688+
* parseLiteral(s, 0, s.length(), factory, false)}.
689+
* @see JsonUrl#parseLiteral(CharSequence, int, int, ValueFactory, boolean)
670690
*/
671691
public static final <V> V parseLiteral(
672692
CharSequence s,
673693
ValueFactory<V,?,?,?,?,?,?,?,?,?> factory) {
674-
return parseLiteral(s, 0, s.length(), factory);
694+
return parseLiteral(s, 0, s.length(), factory, false);
675695
}
676696

677697
/**

0 commit comments

Comments
 (0)