[13231] | 1 | /*
|
---|
| 2 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
---|
| 3 | *
|
---|
| 4 | * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved.
|
---|
| 5 | *
|
---|
| 6 | * The contents of this file are subject to the terms of either the GNU
|
---|
| 7 | * General Public License Version 2 only ("GPL") or the Common Development
|
---|
| 8 | * and Distribution License("CDDL") (collectively, the "License"). You
|
---|
| 9 | * may not use this file except in compliance with the License. You can
|
---|
| 10 | * obtain a copy of the License at
|
---|
| 11 | * https://oss.oracle.com/licenses/CDDL+GPL-1.1
|
---|
| 12 | * or LICENSE.txt. See the License for the specific
|
---|
| 13 | * language governing permissions and limitations under the License.
|
---|
| 14 | *
|
---|
| 15 | * When distributing the software, include this License Header Notice in each
|
---|
| 16 | * file and include the License file at LICENSE.txt.
|
---|
| 17 | *
|
---|
| 18 | * GPL Classpath Exception:
|
---|
| 19 | * Oracle designates this particular file as subject to the "Classpath"
|
---|
| 20 | * exception as provided by Oracle in the GPL Version 2 section of the License
|
---|
| 21 | * file that accompanied this code.
|
---|
| 22 | *
|
---|
| 23 | * Modifications:
|
---|
| 24 | * If applicable, add the following below the License Header, with the fields
|
---|
| 25 | * enclosed by brackets [] replaced by your own identifying information:
|
---|
| 26 | * "Portions Copyright [year] [name of copyright owner]"
|
---|
| 27 | *
|
---|
| 28 | * Contributor(s):
|
---|
| 29 | * If you wish your version of this file to be governed by only the CDDL or
|
---|
| 30 | * only the GPL Version 2, indicate your decision by adding "[Contributor]
|
---|
| 31 | * elects to include this software in this distribution under the [CDDL or GPL
|
---|
| 32 | * Version 2] license." If you don't indicate a single choice of license, a
|
---|
| 33 | * recipient has the option to distribute your version of this file under
|
---|
| 34 | * either the CDDL, the GPL Version 2 or to extend the choice of license to
|
---|
| 35 | * its licensees as provided above. However, if you add GPL Version 2 code
|
---|
| 36 | * and therefore, elected the GPL Version 2 license, then the option applies
|
---|
| 37 | * only if the new code is made subject to such option by the copyright
|
---|
| 38 | * holder.
|
---|
| 39 | */
|
---|
| 40 |
|
---|
| 41 | package org.glassfish.json;
|
---|
| 42 |
|
---|
| 43 | import java.io.IOException;
|
---|
| 44 | import java.io.InputStream;
|
---|
| 45 | import java.io.InputStreamReader;
|
---|
| 46 | import java.io.Reader;
|
---|
| 47 | import java.math.BigDecimal;
|
---|
| 48 | import java.nio.charset.Charset;
|
---|
| 49 | import java.util.AbstractMap;
|
---|
| 50 | import java.util.Map;
|
---|
| 51 | import java.util.NoSuchElementException;
|
---|
| 52 | import java.util.Spliterator;
|
---|
| 53 | import java.util.Spliterators;
|
---|
| 54 | import java.util.function.Consumer;
|
---|
| 55 | import java.util.stream.Stream;
|
---|
| 56 | import java.util.stream.StreamSupport;
|
---|
| 57 |
|
---|
| 58 | import javax.json.JsonArray;
|
---|
| 59 | import javax.json.JsonArrayBuilder;
|
---|
| 60 | import javax.json.JsonException;
|
---|
| 61 | import javax.json.JsonObject;
|
---|
| 62 | import javax.json.JsonObjectBuilder;
|
---|
| 63 | import javax.json.JsonValue;
|
---|
| 64 | import javax.json.stream.JsonLocation;
|
---|
| 65 | import javax.json.stream.JsonParser;
|
---|
| 66 | import javax.json.stream.JsonParser.Event;
|
---|
| 67 | import javax.json.stream.JsonParsingException;
|
---|
| 68 |
|
---|
| 69 | import org.glassfish.json.JsonTokenizer.JsonToken;
|
---|
| 70 | import org.glassfish.json.api.BufferPool;
|
---|
| 71 |
|
---|
| 72 | /**
|
---|
| 73 | * JSON parser implementation. NoneContext, ArrayContext, ObjectContext is used
|
---|
| 74 | * to go to next parser state.
|
---|
| 75 | *
|
---|
| 76 | * @author Jitendra Kotamraju
|
---|
| 77 | * @author Kin-man Chung
|
---|
| 78 | */
|
---|
| 79 | public class JsonParserImpl implements JsonParser {
|
---|
| 80 |
|
---|
| 81 | private final BufferPool bufferPool;
|
---|
| 82 | private Context currentContext = new NoneContext();
|
---|
| 83 | private Event currentEvent;
|
---|
| 84 |
|
---|
| 85 | private final Stack stack = new Stack();
|
---|
| 86 | private final JsonTokenizer tokenizer;
|
---|
| 87 |
|
---|
| 88 | public JsonParserImpl(Reader reader, BufferPool bufferPool) {
|
---|
| 89 | this.bufferPool = bufferPool;
|
---|
| 90 | tokenizer = new JsonTokenizer(reader, bufferPool);
|
---|
| 91 | }
|
---|
| 92 |
|
---|
| 93 | public JsonParserImpl(InputStream in, BufferPool bufferPool) {
|
---|
| 94 | this.bufferPool = bufferPool;
|
---|
| 95 | UnicodeDetectingInputStream uin = new UnicodeDetectingInputStream(in);
|
---|
| 96 | tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), bufferPool);
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool) {
|
---|
| 100 | this.bufferPool = bufferPool;
|
---|
| 101 | tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), bufferPool);
|
---|
| 102 | }
|
---|
| 103 |
|
---|
| 104 | @Override
|
---|
| 105 | public String getString() {
|
---|
| 106 | if (currentEvent == Event.KEY_NAME || currentEvent == Event.VALUE_STRING
|
---|
| 107 | || currentEvent == Event.VALUE_NUMBER) {
|
---|
| 108 | return tokenizer.getValue();
|
---|
| 109 | }
|
---|
| 110 | throw new IllegalStateException(
|
---|
| 111 | JsonMessages.PARSER_GETSTRING_ERR(currentEvent));
|
---|
| 112 | }
|
---|
| 113 |
|
---|
| 114 | @Override
|
---|
| 115 | public boolean isIntegralNumber() {
|
---|
| 116 | if (currentEvent != Event.VALUE_NUMBER) {
|
---|
| 117 | throw new IllegalStateException(
|
---|
| 118 | JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(currentEvent));
|
---|
| 119 | }
|
---|
| 120 | return tokenizer.isIntegral();
|
---|
| 121 | }
|
---|
| 122 |
|
---|
| 123 | @Override
|
---|
| 124 | public int getInt() {
|
---|
| 125 | if (currentEvent != Event.VALUE_NUMBER) {
|
---|
| 126 | throw new IllegalStateException(
|
---|
| 127 | JsonMessages.PARSER_GETINT_ERR(currentEvent));
|
---|
| 128 | }
|
---|
| 129 | return tokenizer.getInt();
|
---|
| 130 | }
|
---|
| 131 |
|
---|
| 132 | boolean isDefinitelyInt() {
|
---|
| 133 | return tokenizer.isDefinitelyInt();
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | boolean isDefinitelyLong() {
|
---|
| 137 | return tokenizer.isDefinitelyLong();
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | @Override
|
---|
| 141 | public long getLong() {
|
---|
| 142 | if (currentEvent != Event.VALUE_NUMBER) {
|
---|
| 143 | throw new IllegalStateException(
|
---|
| 144 | JsonMessages.PARSER_GETLONG_ERR(currentEvent));
|
---|
| 145 | }
|
---|
| 146 | return tokenizer.getLong();
|
---|
| 147 | }
|
---|
| 148 |
|
---|
| 149 | @Override
|
---|
| 150 | public BigDecimal getBigDecimal() {
|
---|
| 151 | if (currentEvent != Event.VALUE_NUMBER) {
|
---|
| 152 | throw new IllegalStateException(
|
---|
| 153 | JsonMessages.PARSER_GETBIGDECIMAL_ERR(currentEvent));
|
---|
| 154 | }
|
---|
| 155 | return tokenizer.getBigDecimal();
|
---|
| 156 | }
|
---|
| 157 |
|
---|
| 158 | @Override
|
---|
| 159 | public JsonArray getArray() {
|
---|
| 160 | if (currentEvent != Event.START_ARRAY) {
|
---|
| 161 | throw new IllegalStateException(
|
---|
| 162 | JsonMessages.PARSER_GETARRAY_ERR(currentEvent));
|
---|
| 163 | }
|
---|
| 164 | return getArray(new JsonArrayBuilderImpl(bufferPool));
|
---|
| 165 | }
|
---|
| 166 |
|
---|
| 167 | @Override
|
---|
| 168 | public JsonObject getObject() {
|
---|
| 169 | if (currentEvent != Event.START_OBJECT) {
|
---|
| 170 | throw new IllegalStateException(
|
---|
| 171 | JsonMessages.PARSER_GETOBJECT_ERR(currentEvent));
|
---|
| 172 | }
|
---|
| 173 | return getObject(new JsonObjectBuilderImpl(bufferPool));
|
---|
| 174 | }
|
---|
| 175 |
|
---|
| 176 | @Override
|
---|
| 177 | public JsonValue getValue() {
|
---|
| 178 | switch (currentEvent) {
|
---|
| 179 | case START_ARRAY:
|
---|
| 180 | return getArray(new JsonArrayBuilderImpl(bufferPool));
|
---|
| 181 | case START_OBJECT:
|
---|
| 182 | return getObject(new JsonObjectBuilderImpl(bufferPool));
|
---|
| 183 | case KEY_NAME:
|
---|
| 184 | case VALUE_STRING:
|
---|
| 185 | return new JsonStringImpl(getString());
|
---|
| 186 | case VALUE_NUMBER:
|
---|
| 187 | if (isDefinitelyInt()) {
|
---|
| 188 | return JsonNumberImpl.getJsonNumber(getInt());
|
---|
| 189 | } else if (isDefinitelyLong()) {
|
---|
| 190 | return JsonNumberImpl.getJsonNumber(getLong());
|
---|
| 191 | }
|
---|
| 192 | return JsonNumberImpl.getJsonNumber(getBigDecimal());
|
---|
| 193 | case VALUE_TRUE:
|
---|
| 194 | return JsonValue.TRUE;
|
---|
| 195 | case VALUE_FALSE:
|
---|
| 196 | return JsonValue.FALSE;
|
---|
| 197 | case VALUE_NULL:
|
---|
| 198 | return JsonValue.NULL;
|
---|
| 199 | case END_ARRAY:
|
---|
| 200 | case END_OBJECT:
|
---|
| 201 | default:
|
---|
| 202 | throw new IllegalStateException(JsonMessages.PARSER_GETVALUE_ERR(currentEvent));
|
---|
| 203 | }
|
---|
| 204 | }
|
---|
| 205 |
|
---|
| 206 | @Override
|
---|
| 207 | public Stream<JsonValue> getArrayStream() {
|
---|
| 208 | if (currentEvent != Event.START_ARRAY) {
|
---|
| 209 | throw new IllegalStateException(
|
---|
| 210 | JsonMessages.PARSER_GETARRAY_ERR(currentEvent));
|
---|
| 211 | }
|
---|
| 212 | Spliterator<JsonValue> spliterator =
|
---|
| 213 | new Spliterators.AbstractSpliterator<JsonValue>(Long.MAX_VALUE, Spliterator.ORDERED) {
|
---|
| 214 | @Override
|
---|
| 215 | public Spliterator<JsonValue> trySplit() {
|
---|
| 216 | return null;
|
---|
| 217 | }
|
---|
| 218 | @Override
|
---|
| 219 | public boolean tryAdvance(Consumer<? super JsonValue> action) {
|
---|
| 220 | if (action == null) {
|
---|
| 221 | throw new NullPointerException();
|
---|
| 222 | }
|
---|
| 223 | if (! hasNext()) {
|
---|
| 224 | return false;
|
---|
| 225 | }
|
---|
| 226 | if (next() == JsonParser.Event.END_ARRAY) {
|
---|
| 227 | return false;
|
---|
| 228 | }
|
---|
| 229 | action.accept(getValue());
|
---|
| 230 | return true;
|
---|
| 231 | }
|
---|
| 232 | };
|
---|
| 233 | return StreamSupport.stream(spliterator, false);
|
---|
| 234 | }
|
---|
| 235 |
|
---|
| 236 | @Override
|
---|
| 237 | public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
|
---|
| 238 | if (currentEvent != Event.START_OBJECT) {
|
---|
| 239 | throw new IllegalStateException(
|
---|
| 240 | JsonMessages.PARSER_GETOBJECT_ERR(currentEvent));
|
---|
| 241 | }
|
---|
| 242 | Spliterator<Map.Entry<String, JsonValue>> spliterator =
|
---|
| 243 | new Spliterators.AbstractSpliterator<Map.Entry<String, JsonValue>>(Long.MAX_VALUE, Spliterator.ORDERED) {
|
---|
| 244 | @Override
|
---|
| 245 | public Spliterator<Map.Entry<String,JsonValue>> trySplit() {
|
---|
| 246 | return null;
|
---|
| 247 | }
|
---|
| 248 | @Override
|
---|
| 249 | public boolean tryAdvance(Consumer<? super Map.Entry<String, JsonValue>> action) {
|
---|
| 250 | if (action == null) {
|
---|
| 251 | throw new NullPointerException();
|
---|
| 252 | }
|
---|
| 253 | if (! hasNext()) {
|
---|
| 254 | return false;
|
---|
| 255 | }
|
---|
| 256 | JsonParser.Event e = next();
|
---|
| 257 | if (e == JsonParser.Event.END_OBJECT) {
|
---|
| 258 | return false;
|
---|
| 259 | }
|
---|
| 260 | if (e != JsonParser.Event.KEY_NAME) {
|
---|
| 261 | throw new JsonException(JsonMessages.INTERNAL_ERROR());
|
---|
| 262 | }
|
---|
| 263 | String key = getString();
|
---|
| 264 | if (! hasNext()) {
|
---|
| 265 | throw new JsonException(JsonMessages.INTERNAL_ERROR());
|
---|
| 266 | }
|
---|
| 267 | next();
|
---|
| 268 | JsonValue value = getValue();
|
---|
| 269 | action.accept(new AbstractMap.SimpleImmutableEntry<>(key, value));
|
---|
| 270 | return true;
|
---|
| 271 | }
|
---|
| 272 | };
|
---|
| 273 | return StreamSupport.stream(spliterator, false);
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | @Override
|
---|
| 277 | public Stream<JsonValue> getValueStream() {
|
---|
| 278 | if (! (currentContext instanceof NoneContext)) {
|
---|
| 279 | throw new IllegalStateException(
|
---|
| 280 | JsonMessages.PARSER_GETVALUESTREAM_ERR());
|
---|
| 281 | }
|
---|
| 282 | Spliterator<JsonValue> spliterator =
|
---|
| 283 | new Spliterators.AbstractSpliterator<JsonValue>(Long.MAX_VALUE, Spliterator.ORDERED) {
|
---|
| 284 | @Override
|
---|
| 285 | public Spliterator<JsonValue> trySplit() {
|
---|
| 286 | return null;
|
---|
| 287 | }
|
---|
| 288 | @Override
|
---|
| 289 | public boolean tryAdvance(Consumer<? super JsonValue> action) {
|
---|
| 290 | if (action == null) {
|
---|
| 291 | throw new NullPointerException();
|
---|
| 292 | }
|
---|
| 293 | if (! hasNext()) {
|
---|
| 294 | return false;
|
---|
| 295 | }
|
---|
| 296 | next();
|
---|
| 297 | action.accept(getValue());
|
---|
| 298 | return true;
|
---|
| 299 | }
|
---|
| 300 | };
|
---|
| 301 | return StreamSupport.stream(spliterator, false);
|
---|
| 302 | }
|
---|
| 303 |
|
---|
| 304 | @Override
|
---|
| 305 | public void skipArray() {
|
---|
| 306 | if (currentEvent == Event.START_ARRAY) {
|
---|
| 307 | currentContext.skip();
|
---|
| 308 | currentContext = stack.pop();
|
---|
| 309 | }
|
---|
| 310 | }
|
---|
| 311 |
|
---|
| 312 | @Override
|
---|
| 313 | public void skipObject() {
|
---|
| 314 | if (currentEvent == Event.START_OBJECT) {
|
---|
| 315 | currentContext.skip();
|
---|
| 316 | currentContext = stack.pop();
|
---|
| 317 | }
|
---|
| 318 | }
|
---|
| 319 |
|
---|
| 320 | private JsonArray getArray(JsonArrayBuilder builder) {
|
---|
| 321 | while(hasNext()) {
|
---|
| 322 | JsonParser.Event e = next();
|
---|
| 323 | if (e == JsonParser.Event.END_ARRAY) {
|
---|
| 324 | return builder.build();
|
---|
| 325 | }
|
---|
| 326 | builder.add(getValue());
|
---|
| 327 | }
|
---|
| 328 | throw parsingException(JsonToken.EOF, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL, SQUARECLOSE]");
|
---|
| 329 | }
|
---|
| 330 |
|
---|
| 331 | private JsonObject getObject(JsonObjectBuilder builder) {
|
---|
| 332 | while(hasNext()) {
|
---|
| 333 | JsonParser.Event e = next();
|
---|
| 334 | if (e == JsonParser.Event.END_OBJECT) {
|
---|
| 335 | return builder.build();
|
---|
| 336 | }
|
---|
| 337 | String key = getString();
|
---|
| 338 | next();
|
---|
| 339 | builder.add(key, getValue());
|
---|
| 340 | }
|
---|
| 341 | throw parsingException(JsonToken.EOF, "[STRING, CURLYCLOSE]");
|
---|
| 342 | }
|
---|
| 343 |
|
---|
| 344 | @Override
|
---|
| 345 | public JsonLocation getLocation() {
|
---|
| 346 | return tokenizer.getLocation();
|
---|
| 347 | }
|
---|
| 348 |
|
---|
| 349 | public JsonLocation getLastCharLocation() {
|
---|
| 350 | return tokenizer.getLastCharLocation();
|
---|
| 351 | }
|
---|
| 352 |
|
---|
| 353 | @Override
|
---|
| 354 | public boolean hasNext() {
|
---|
| 355 | return tokenizer.hasNextToken();
|
---|
| 356 | }
|
---|
| 357 |
|
---|
| 358 | @Override
|
---|
| 359 | public Event next() {
|
---|
| 360 | if (!hasNext()) {
|
---|
| 361 | throw new NoSuchElementException();
|
---|
| 362 | }
|
---|
| 363 | return currentEvent = currentContext.getNextEvent();
|
---|
| 364 | }
|
---|
| 365 |
|
---|
| 366 | @Override
|
---|
| 367 | public void close() {
|
---|
| 368 | try {
|
---|
| 369 | tokenizer.close();
|
---|
| 370 | } catch (IOException e) {
|
---|
| 371 | throw new JsonException(JsonMessages.PARSER_TOKENIZER_CLOSE_IO(), e);
|
---|
| 372 | }
|
---|
| 373 | }
|
---|
| 374 |
|
---|
| 375 | // Using the optimized stack impl as we don't require other things
|
---|
| 376 | // like iterator etc.
|
---|
| 377 | private static final class Stack {
|
---|
| 378 | private Context head;
|
---|
| 379 |
|
---|
| 380 | private void push(Context context) {
|
---|
| 381 | context.next = head;
|
---|
| 382 | head = context;
|
---|
| 383 | }
|
---|
| 384 |
|
---|
| 385 | private Context pop() {
|
---|
| 386 | if (head == null) {
|
---|
| 387 | throw new NoSuchElementException();
|
---|
| 388 | }
|
---|
| 389 | Context temp = head;
|
---|
| 390 | head = head.next;
|
---|
| 391 | return temp;
|
---|
| 392 | }
|
---|
| 393 |
|
---|
| 394 | private Context peek() {
|
---|
| 395 | return head;
|
---|
| 396 | }
|
---|
| 397 |
|
---|
| 398 | private boolean isEmpty() {
|
---|
| 399 | return head == null;
|
---|
| 400 | }
|
---|
| 401 | }
|
---|
| 402 |
|
---|
| 403 | private abstract class Context {
|
---|
| 404 | Context next;
|
---|
| 405 | abstract Event getNextEvent();
|
---|
| 406 | abstract void skip();
|
---|
| 407 | }
|
---|
| 408 |
|
---|
| 409 | private final class NoneContext extends Context {
|
---|
| 410 | @Override
|
---|
| 411 | public Event getNextEvent() {
|
---|
| 412 | // Handle 1. { 2. [ 3. value
|
---|
| 413 | JsonToken token = tokenizer.nextToken();
|
---|
| 414 | if (token == JsonToken.CURLYOPEN) {
|
---|
| 415 | stack.push(currentContext);
|
---|
| 416 | currentContext = new ObjectContext();
|
---|
| 417 | return Event.START_OBJECT;
|
---|
| 418 | } else if (token == JsonToken.SQUAREOPEN) {
|
---|
| 419 | stack.push(currentContext);
|
---|
| 420 | currentContext = new ArrayContext();
|
---|
| 421 | return Event.START_ARRAY;
|
---|
| 422 | } else if (token.isValue()) {
|
---|
| 423 | return token.getEvent();
|
---|
| 424 | }
|
---|
| 425 | throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
|
---|
| 426 | }
|
---|
| 427 |
|
---|
| 428 | @Override
|
---|
| 429 | void skip() {
|
---|
| 430 | // no-op
|
---|
| 431 | }
|
---|
| 432 | }
|
---|
| 433 |
|
---|
| 434 | private JsonParsingException parsingException(JsonToken token, String expectedTokens) {
|
---|
| 435 | JsonLocation location = getLastCharLocation();
|
---|
| 436 | return new JsonParsingException(
|
---|
| 437 | JsonMessages.PARSER_INVALID_TOKEN(token, location, expectedTokens), location);
|
---|
| 438 | }
|
---|
| 439 |
|
---|
| 440 | private final class ObjectContext extends Context {
|
---|
| 441 | private boolean firstValue = true;
|
---|
| 442 |
|
---|
| 443 | /*
|
---|
| 444 | * Some more things could be optimized. For example, instead
|
---|
| 445 | * tokenizer.nextToken(), one could use tokenizer.matchColonToken() to
|
---|
| 446 | * match ':'. That might optimize a bit, but will fragment nextToken().
|
---|
| 447 | * I think the current one is more readable.
|
---|
| 448 | *
|
---|
| 449 | */
|
---|
| 450 | @Override
|
---|
| 451 | public Event getNextEvent() {
|
---|
| 452 | // Handle 1. } 2. name:value 3. ,name:value
|
---|
| 453 | JsonToken token = tokenizer.nextToken();
|
---|
| 454 | if (currentEvent == Event.KEY_NAME) {
|
---|
| 455 | // Handle 1. :value
|
---|
| 456 | if (token != JsonToken.COLON) {
|
---|
| 457 | throw parsingException(token, "[COLON]");
|
---|
| 458 | }
|
---|
| 459 | token = tokenizer.nextToken();
|
---|
| 460 | if (token.isValue()) {
|
---|
| 461 | return token.getEvent();
|
---|
| 462 | } else if (token == JsonToken.CURLYOPEN) {
|
---|
| 463 | stack.push(currentContext);
|
---|
| 464 | currentContext = new ObjectContext();
|
---|
| 465 | return Event.START_OBJECT;
|
---|
| 466 | } else if (token == JsonToken.SQUAREOPEN) {
|
---|
| 467 | stack.push(currentContext);
|
---|
| 468 | currentContext = new ArrayContext();
|
---|
| 469 | return Event.START_ARRAY;
|
---|
| 470 | }
|
---|
| 471 | throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
|
---|
| 472 | } else {
|
---|
| 473 | // Handle 1. } 2. name 3. ,name
|
---|
| 474 | if (token == JsonToken.CURLYCLOSE) {
|
---|
| 475 | currentContext = stack.pop();
|
---|
| 476 | return Event.END_OBJECT;
|
---|
| 477 | }
|
---|
| 478 | if (firstValue) {
|
---|
| 479 | firstValue = false;
|
---|
| 480 | } else {
|
---|
| 481 | if (token != JsonToken.COMMA) {
|
---|
| 482 | throw parsingException(token, "[COMMA]");
|
---|
| 483 | }
|
---|
| 484 | token = tokenizer.nextToken();
|
---|
| 485 | }
|
---|
| 486 | if (token == JsonToken.STRING) {
|
---|
| 487 | return Event.KEY_NAME;
|
---|
| 488 | }
|
---|
| 489 | throw parsingException(token, "[STRING]");
|
---|
| 490 | }
|
---|
| 491 | }
|
---|
| 492 |
|
---|
| 493 | @Override
|
---|
| 494 | void skip() {
|
---|
| 495 | JsonToken token;
|
---|
| 496 | int depth = 1;
|
---|
| 497 | do {
|
---|
| 498 | token = tokenizer.nextToken();
|
---|
| 499 | switch (token) {
|
---|
| 500 | case CURLYCLOSE:
|
---|
| 501 | depth--;
|
---|
| 502 | break;
|
---|
| 503 | case CURLYOPEN:
|
---|
| 504 | depth++;
|
---|
| 505 | break;
|
---|
| 506 | }
|
---|
| 507 | } while (!(token == JsonToken.CURLYCLOSE && depth == 0));
|
---|
| 508 | }
|
---|
| 509 |
|
---|
| 510 | }
|
---|
| 511 |
|
---|
| 512 | private final class ArrayContext extends Context {
|
---|
| 513 | private boolean firstValue = true;
|
---|
| 514 |
|
---|
| 515 | // Handle 1. ] 2. value 3. ,value
|
---|
| 516 | @Override
|
---|
| 517 | public Event getNextEvent() {
|
---|
| 518 | JsonToken token = tokenizer.nextToken();
|
---|
| 519 | if (token == JsonToken.SQUARECLOSE) {
|
---|
| 520 | currentContext = stack.pop();
|
---|
| 521 | return Event.END_ARRAY;
|
---|
| 522 | }
|
---|
| 523 | if (firstValue) {
|
---|
| 524 | firstValue = false;
|
---|
| 525 | } else {
|
---|
| 526 | if (token != JsonToken.COMMA) {
|
---|
| 527 | throw parsingException(token, "[COMMA]");
|
---|
| 528 | }
|
---|
| 529 | token = tokenizer.nextToken();
|
---|
| 530 | }
|
---|
| 531 | if (token.isValue()) {
|
---|
| 532 | return token.getEvent();
|
---|
| 533 | } else if (token == JsonToken.CURLYOPEN) {
|
---|
| 534 | stack.push(currentContext);
|
---|
| 535 | currentContext = new ObjectContext();
|
---|
| 536 | return Event.START_OBJECT;
|
---|
| 537 | } else if (token == JsonToken.SQUAREOPEN) {
|
---|
| 538 | stack.push(currentContext);
|
---|
| 539 | currentContext = new ArrayContext();
|
---|
| 540 | return Event.START_ARRAY;
|
---|
| 541 | }
|
---|
| 542 | throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
|
---|
| 543 | }
|
---|
| 544 |
|
---|
| 545 | @Override
|
---|
| 546 | void skip() {
|
---|
| 547 | JsonToken token;
|
---|
| 548 | int depth = 1;
|
---|
| 549 | do {
|
---|
| 550 | token = tokenizer.nextToken();
|
---|
| 551 | switch (token) {
|
---|
| 552 | case SQUARECLOSE:
|
---|
| 553 | depth--;
|
---|
| 554 | break;
|
---|
| 555 | case SQUAREOPEN:
|
---|
| 556 | depth++;
|
---|
| 557 | break;
|
---|
| 558 | }
|
---|
| 559 | } while (!(token == JsonToken.SQUARECLOSE && depth == 0));
|
---|
| 560 | }
|
---|
| 561 | }
|
---|
| 562 |
|
---|
| 563 | }
|
---|