1919
2020import static org .jsonurl .JsonUrlOption .optionEmptyUnquotedKey ;
2121import static org .jsonurl .JsonUrlOption .optionEmptyUnquotedValue ;
22+ import static org .jsonurl .JsonUrlOption .optionNoEmptyComposite ;
2223import static org .jsonurl .JsonUrlOption .optionSkipNulls ;
2324import static org .jsonurl .JsonUrlOption .optionWfuComposite ;
2425import static org .jsonurl .LimitException .Message .MSG_LIMIT_MAX_PARSE_DEPTH ;
2526import static org .jsonurl .LimitException .Message .MSG_LIMIT_MAX_PARSE_VALUES ;
2627import static org .jsonurl .SyntaxException .Message .MSG_BAD_CHAR ;
2728import static org .jsonurl .SyntaxException .Message .MSG_EXPECT_LITERAL ;
2829import static org .jsonurl .SyntaxException .Message .MSG_EXPECT_OBJECT_VALUE ;
29- import static org .jsonurl .SyntaxException .Message .MSG_EXPECT_PAREN ;
3030import static org .jsonurl .SyntaxException .Message .MSG_EXPECT_STRUCT_CHAR ;
3131import static org .jsonurl .SyntaxException .Message .MSG_EXTRA_CHARS ;
3232import static org .jsonurl .SyntaxException .Message .MSG_NO_TEXT ;
@@ -70,6 +70,7 @@ private enum State {
7070 IMPLIED_OBJECT ,
7171 IN_OBJECT ,
7272 OBJECT_HAVE_KEY ,
73+ OBJECT_HAVE_KEY_SEPARATOR ,
7374 OBJECT_AFTER_ELEMENT ,
7475 END_STREAM ,
7576 }
@@ -104,6 +105,11 @@ private enum State {
104105 */
105106 protected static final char VALUE_SEPARATOR = ',' ;
106107
108+ /**
109+ * Generic internal parse error message.
110+ */
111+ private static final String INTERNAL_PARSE_ERROR = "internal parse error" ;
112+
107113 /*
108114 * empty string.
109115 *
@@ -129,6 +135,7 @@ private enum State {
129135 * Buffered "next" event value.
130136 */
131137 private JsonUrlEvent savedEventValue ;
138+ //private Deque<JsonUrlEvent> savedEventStack = new LinkedList<>();
132139
133140 /**
134141 * Current parse/nesting depth.
@@ -189,15 +196,20 @@ protected abstract JsonUrlEvent readBufferedLiteral(
189196 boolean flag ,
190197 boolean isKey );
191198
199+ /**
200+ * Test if the current buffered literal is empty.
201+ */
202+ protected abstract boolean isEmptyBufferedLiteral (boolean flag );
203+
192204 /**
193205 * Read a literal and return its event.
194206 */
195207 protected abstract JsonUrlEvent readLiteral (boolean isKey );
196208
197209 private JsonUrlEvent stateSavedEvent () {
198210 stateStack .pop ();
199- JsonUrlEvent ret = this . savedEventValue ;
200- this . savedEventValue = null ; // NOPMD
211+ final JsonUrlEvent ret = savedEventValue ;
212+ savedEventValue = null ; // NOPMD
201213 return ret ;
202214 }
203215
@@ -219,7 +231,8 @@ private JsonUrlEvent stateStart() {
219231 // non-structural character
220232 break ;
221233 default :
222- throw newSyntaxException (MSG_EXPECT_PAREN );
234+ // can't happen
235+ throw newParseException (INTERNAL_PARSE_ERROR );
223236 }
224237
225238 stateStack .set (0 , State .END_STREAM );
@@ -242,7 +255,8 @@ private JsonUrlEvent stateStart() {
242255 checkResultType (ValueType .NUMBER );
243256 break ;
244257 default :
245- throw newParseException ("interal parse error" );
258+ // can't happen
259+ throw newParseException (INTERNAL_PARSE_ERROR );
246260 }
247261
248262 if (!eof ()) {
@@ -251,9 +265,9 @@ private JsonUrlEvent stateStart() {
251265
252266 return ret ;
253267 }
254-
255- private JsonUrlEvent stateParenStructChar () {
256- final int cval = nextStructChar (true );
268+
269+ private JsonUrlEvent stateParenParen () {
270+ int cval = nextStructChar (true );
257271
258272 switch (cval ) {
259273 case BEGIN_COMPOSITE :
@@ -284,8 +298,6 @@ private JsonUrlEvent stateParenStructChar() {
284298 parseDepth --;
285299 stateStack .pop ();
286300
287- checkResultTypeIsComposite ();
288-
289301 if (parseDepth == PARSE_DEPTH_DONE ) {
290302 if (eof ()) {
291303 stateStack .push (State .END_STREAM );
@@ -294,20 +306,28 @@ private JsonUrlEvent stateParenStructChar() {
294306 throw newSyntaxException (MSG_EXTRA_CHARS );
295307 }
296308 }
297- return JsonUrlEvent .VALUE_EMPTY_COMPOSITE ;
309+
310+ if (optionNoEmptyComposite (options ())) {
311+ checkResultType (ValueType .ARRAY );
312+ savedEventValue = JsonUrlEvent .END_ARRAY ;
313+ stateStack .push (State .SAVED_EVENT );
314+ return JsonUrlEvent .START_ARRAY ;
315+ }
298316
317+ checkResultTypeIsComposite ();
318+ return JsonUrlEvent .VALUE_EMPTY_COMPOSITE ;
299319 default :
300320 return null ;
301321 }
302322 }
303323
304324 @ SuppressWarnings ("PMD.CyclomaticComplexity" )
305325 private JsonUrlEvent stateParen () {
306- //
307- // look for a structural character
308- //
309- JsonUrlEvent ret = stateParenStructChar ();
326+ JsonUrlEvent ret = stateParenParen ();
310327 if (ret != null ) {
328+ //
329+ // found an open or close paren
330+ //
311331 return ret ;
312332 }
313333
@@ -317,7 +337,7 @@ private JsonUrlEvent stateParen() {
317337 //
318338 boolean bufLitFlag = readAndBufferLiteral ();
319339
320- int sep = nextStructChar (true );
340+ final int sep = nextStructChar (true );
321341
322342 switch (sep ) { // NOPMD - false positive
323343 case EOF :
@@ -345,10 +365,26 @@ private JsonUrlEvent stateParen() {
345365 //
346366 // key name for object
347367 //
348- readBufferedLiteral (bufLitFlag , true );
349368 checkResultType (ValueType .OBJECT );
350- stateStack .set (0 , State .OBJECT_HAVE_KEY );
369+ skipChar ();
370+
371+ if (isEmptyObject (sep , bufLitFlag )) {
372+ //
373+ // the empty object sequence (:)
374+ //
375+ // I'm not consuming the close paren because I don't have
376+ // enough context to manage `parseDepth`. But, I can set
377+ // my state to OBJECT_AFTER_ELEMENT in order to produce the
378+ // expected END_OBJECT event. This also handles eof(),
379+ // errors due to extra text, etc.
380+ //
381+ stateStack .set (0 , State .OBJECT_AFTER_ELEMENT );
382+ return JsonUrlEvent .START_OBJECT ;
383+ }
384+
385+ readBufferedLiteral (bufLitFlag , true );
351386 savedEventValue = JsonUrlEvent .KEY_NAME ;
387+ stateStack .set (0 , State .OBJECT_HAVE_KEY_SEPARATOR );
352388 stateStack .push (State .SAVED_EVENT );
353389 return JsonUrlEvent .START_OBJECT ;
354390
@@ -406,8 +442,8 @@ private JsonUrlEvent stateObjectKeySeparator() {
406442 }
407443 }
408444
409- private JsonUrlEvent stateNextValue (boolean isObject , State state ) {
410- if (isObject ) {
445+ private JsonUrlEvent stateNextValue (boolean needKeySep , State state ) {
446+ if (needKeySep ) {
411447 JsonUrlEvent ret = stateObjectKeySeparator ();
412448 if (ret != null ) {
413449 return ret ;
@@ -565,6 +601,11 @@ public JsonUrlEvent next() { // NOPMD - CyclomaticComplexity
565601 true , State .OBJECT_AFTER_ELEMENT ));
566602 break ;
567603
604+ case OBJECT_HAVE_KEY_SEPARATOR :
605+ ret = filterLiteral (stateNextValue (
606+ false , State .OBJECT_AFTER_ELEMENT ));
607+ break ;
608+
568609 case OBJECT_AFTER_ELEMENT :
569610 ret = stateAfterValue (
570611 State .IN_OBJECT ,
@@ -693,6 +734,15 @@ private void checkWfuSeparator(boolean value) {
693734 consumeAmps ();
694735 }
695736 }
737+
738+ private boolean isEmptyObject (int sep , boolean bufLitFlag ) {
739+ final int end = nextStructChar (true );
740+
741+ return sep == NAME_SEPARATOR
742+ && end == END_COMPOSITE
743+ && isEmptyBufferedLiteral (bufLitFlag )
744+ && optionNoEmptyComposite (options ());
745+ }
696746
697747 /**
698748 * Return {@code KEY_NAME} if isKey is true.
0 commit comments