Fixes #4892 - Non-blocking JSON parser.
Updates after review. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
e588a1bd9d
commit
d16ce12349
|
@ -47,9 +47,9 @@ import org.eclipse.jetty.util.ajax.JSON.Convertor;
|
|||
*
|
||||
* // Tell the parser that the JSON string content
|
||||
* // is terminated and get the JSON object back.
|
||||
* Map<String, Object> object = parser.eof();
|
||||
* Map<String, Object> object = parser.complete();
|
||||
* </pre>
|
||||
* <p>After the call to {@link #eof()} the parser can be reused to parse
|
||||
* <p>After the call to {@link #complete()} the parser can be reused to parse
|
||||
* another JSON string.</p>
|
||||
* <p>Custom objects can be created by specifying a {@code "class"} or
|
||||
* {@code "x-class"} field:</p>
|
||||
|
@ -64,7 +64,7 @@ import org.eclipse.jetty.util.ajax.JSON.Convertor;
|
|||
* """
|
||||
*
|
||||
* parser.parse(json);
|
||||
* com.acme.Person person = parser.eof();
|
||||
* com.acme.Person person = parser.complete();
|
||||
* </pre>
|
||||
* <p>Class {@code com.acme.Person} must either implement {@link Convertible},
|
||||
* or be mapped with a {@link Convertor} via {@link Factory#putConvertor(String, Convertor)}.</p>
|
||||
|
@ -119,7 +119,7 @@ public class AsyncJSON
|
|||
* @param buffer the buffer to lookup the string from
|
||||
* @return a cached string or {@code null}
|
||||
*/
|
||||
public String cached(ByteBuffer buffer)
|
||||
private String cached(ByteBuffer buffer)
|
||||
{
|
||||
if (cache != null)
|
||||
{
|
||||
|
@ -270,7 +270,7 @@ public class AsyncJSON
|
|||
{
|
||||
int position = buffer.position();
|
||||
byte peek = buffer.get(position);
|
||||
if (Character.isWhitespace((char)peek))
|
||||
if (isWhitespace(peek))
|
||||
buffer.position(position + 1);
|
||||
else
|
||||
throw newInvalidJSON(buffer, "invalid character after JSON data");
|
||||
|
@ -374,7 +374,7 @@ public class AsyncJSON
|
|||
* @throws IllegalArgumentException if the JSON is malformed
|
||||
* @throws IllegalStateException if the no JSON was passed to the {@code parse()} methods
|
||||
*/
|
||||
public <R> R eof()
|
||||
public <R> R complete()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -397,7 +397,7 @@ public class AsyncJSON
|
|||
{
|
||||
if (stack.peek().value == UNSET)
|
||||
throw new IllegalStateException("invalid state " + state);
|
||||
return (R)complete();
|
||||
return (R)end();
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
@ -439,7 +439,7 @@ public class AsyncJSON
|
|||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private Object complete()
|
||||
private Object end()
|
||||
{
|
||||
Object result = stack.peek().value;
|
||||
reset();
|
||||
|
@ -498,7 +498,7 @@ public class AsyncJSON
|
|||
return true;
|
||||
break;
|
||||
default:
|
||||
if (Character.isWhitespace(peek))
|
||||
if (isWhitespace(peek))
|
||||
{
|
||||
buffer.get();
|
||||
break;
|
||||
|
@ -854,7 +854,7 @@ public class AsyncJSON
|
|||
}
|
||||
default:
|
||||
{
|
||||
if (Character.isWhitespace(peek))
|
||||
if (isWhitespace(peek))
|
||||
{
|
||||
buffer.get();
|
||||
break;
|
||||
|
@ -904,7 +904,7 @@ public class AsyncJSON
|
|||
}
|
||||
default:
|
||||
{
|
||||
if (Character.isWhitespace(currentByte))
|
||||
if (isWhitespace(currentByte))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -947,7 +947,7 @@ public class AsyncJSON
|
|||
}
|
||||
default:
|
||||
{
|
||||
if (Character.isWhitespace(peek))
|
||||
if (isWhitespace(peek))
|
||||
{
|
||||
buffer.get();
|
||||
break;
|
||||
|
@ -1002,7 +1002,7 @@ public class AsyncJSON
|
|||
}
|
||||
default:
|
||||
{
|
||||
if (Character.isWhitespace(peek))
|
||||
if (isWhitespace(peek))
|
||||
{
|
||||
buffer.get();
|
||||
break;
|
||||
|
@ -1115,6 +1115,20 @@ public class AsyncJSON
|
|||
return new IllegalArgumentException(builder.toString());
|
||||
}
|
||||
|
||||
private static boolean isWhitespace(byte ws)
|
||||
{
|
||||
switch (ws)
|
||||
{
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The state of JSON parsing.</p>
|
||||
*/
|
||||
|
|
|
@ -78,7 +78,7 @@ public class AsyncJSONTest
|
|||
|
||||
// Parse the whole input.
|
||||
assertTrue(parser.parse(bytes));
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
|
||||
// Parse byte by byte.
|
||||
|
@ -90,7 +90,7 @@ public class AsyncJSONTest
|
|||
else
|
||||
assertFalse(parser.parse(new byte[]{b}));
|
||||
}
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
}
|
||||
|
||||
|
@ -140,12 +140,12 @@ public class AsyncJSONTest
|
|||
|
||||
// Parse the whole input.
|
||||
assertTrue(parser.parse(bytes));
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
assertTrue(parser.parse(buffer));
|
||||
assertFalse(buffer.hasRemaining());
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
|
||||
// Parse byte by byte.
|
||||
|
@ -153,7 +153,7 @@ public class AsyncJSONTest
|
|||
{
|
||||
parser.parse(new byte[]{b});
|
||||
}
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ public class AsyncJSONTest
|
|||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
parser.parse(bytes);
|
||||
parser.eof();
|
||||
parser.complete();
|
||||
});
|
||||
assertTrue(parser.isEmpty());
|
||||
|
||||
|
@ -206,7 +206,7 @@ public class AsyncJSONTest
|
|||
{
|
||||
parser.parse(new byte[]{b});
|
||||
}
|
||||
parser.eof();
|
||||
parser.complete();
|
||||
});
|
||||
assertTrue(parser.isEmpty());
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ public class AsyncJSONTest
|
|||
|
||||
// Parse the whole input.
|
||||
assertTrue(parser.parse(bytes));
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
|
||||
// Parse byte by byte.
|
||||
|
@ -236,7 +236,7 @@ public class AsyncJSONTest
|
|||
assertFalse(parser.parse(new byte[]{b}));
|
||||
}
|
||||
}
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
}
|
||||
|
||||
|
@ -278,7 +278,7 @@ public class AsyncJSONTest
|
|||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
parser.parse(bytes);
|
||||
parser.eof();
|
||||
parser.complete();
|
||||
});
|
||||
assertTrue(parser.isEmpty());
|
||||
|
||||
|
@ -289,7 +289,7 @@ public class AsyncJSONTest
|
|||
{
|
||||
parser.parse(new byte[]{b});
|
||||
}
|
||||
parser.eof();
|
||||
parser.complete();
|
||||
});
|
||||
assertTrue(parser.isEmpty());
|
||||
}
|
||||
|
@ -303,11 +303,11 @@ public class AsyncJSONTest
|
|||
|
||||
// Parse the whole input.
|
||||
parser.parse(bytes);
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
parser.parse(buffer);
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertFalse(buffer.hasRemaining());
|
||||
assertTrue(parser.isEmpty());
|
||||
|
||||
|
@ -316,7 +316,7 @@ public class AsyncJSONTest
|
|||
{
|
||||
parser.parse(new byte[]{b});
|
||||
}
|
||||
assertEquals(expected, parser.eof());
|
||||
assertEquals(expected, parser.complete());
|
||||
assertTrue(parser.isEmpty());
|
||||
}
|
||||
|
||||
|
@ -349,7 +349,7 @@ public class AsyncJSONTest
|
|||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
parser.parse(bytes);
|
||||
parser.eof();
|
||||
parser.complete();
|
||||
});
|
||||
assertTrue(parser.isEmpty());
|
||||
|
||||
|
@ -360,7 +360,7 @@ public class AsyncJSONTest
|
|||
{
|
||||
parser.parse(new byte[]{b});
|
||||
}
|
||||
parser.eof();
|
||||
parser.complete();
|
||||
});
|
||||
assertTrue(parser.isEmpty());
|
||||
}
|
||||
|
@ -379,7 +379,7 @@ public class AsyncJSONTest
|
|||
|
||||
AsyncJSON parser = factory.newAsyncJSON();
|
||||
assertTrue(parser.parse(UTF_8.encode(json)));
|
||||
Map<String, Object> result = parser.eof();
|
||||
Map<String, Object> result = parser.complete();
|
||||
|
||||
Object value1 = result.get("f1");
|
||||
assertTrue(value1 instanceof CustomConvertible);
|
||||
|
@ -389,7 +389,7 @@ public class AsyncJSONTest
|
|||
|
||||
assertSame(convertor, factory.removeConvertor(CustomConvertor.class.getName()));
|
||||
assertTrue(parser.parse(UTF_8.encode(json)));
|
||||
result = parser.eof();
|
||||
result = parser.complete();
|
||||
|
||||
value1 = result.get("f1");
|
||||
assertTrue(value1 instanceof CustomConvertible);
|
||||
|
@ -467,7 +467,7 @@ public class AsyncJSONTest
|
|||
"}]";
|
||||
|
||||
assertTrue(parser.parse(UTF_8.encode(json)));
|
||||
List<CustomMap> messages = parser.eof();
|
||||
List<CustomMap> messages = parser.complete();
|
||||
|
||||
for (CustomMap message : messages)
|
||||
{
|
||||
|
@ -491,7 +491,7 @@ public class AsyncJSONTest
|
|||
|
||||
String json = "{\"foo\": [\"foo\", \"foo\"]}";
|
||||
parser.parse(UTF_8.encode(json));
|
||||
Map<String, Object> object = parser.eof();
|
||||
Map<String, Object> object = parser.complete();
|
||||
|
||||
Map.Entry<String, Object> entry = object.entrySet().iterator().next();
|
||||
assertSame(foo, entry.getKey());
|
||||
|
|
Loading…
Reference in New Issue