private void parseField(
      List<String> fieldNames,
      List<ColumnMetaData.Rep> fieldTypes,
      Row.RowBuilder rowBuilder,
      JsonParser parser)
      throws IOException {
    final String fieldName = parser.getCurrentName();

    // Move to next token, which is name's value
    JsonToken token = parser.nextToken();
    int i = fieldNames.indexOf(fieldName);
    if (i < 0) {
      return;
    }
    ColumnMetaData.Rep type = fieldTypes.get(i);
    switch (token) {
      case VALUE_NUMBER_INT:
        if (type == null) {
          type = ColumnMetaData.Rep.INTEGER;
        }
        // fall through
      case VALUE_NUMBER_FLOAT:
        if (type == null) {
          type = ColumnMetaData.Rep.FLOAT;
        }
        switch (type) {
          case BYTE:
            rowBuilder.set(i, parser.getByteValue());
            break;
          case SHORT:
            rowBuilder.set(i, parser.getShortValue());
            break;
          case INTEGER:
            rowBuilder.set(i, parser.getIntValue());
            break;
          case LONG:
            rowBuilder.set(i, parser.getLongValue());
            break;
          case FLOAT:
            rowBuilder.set(i, parser.getFloatValue());
            break;
          case DOUBLE:
            rowBuilder.set(i, parser.getDoubleValue());
            break;
        }
        break;
      case VALUE_TRUE:
        rowBuilder.set(i, true);
        break;
      case VALUE_FALSE:
        rowBuilder.set(i, false);
        break;
      case VALUE_NULL:
        break;
      case VALUE_STRING:
      default:
        if (type == ColumnMetaData.Rep.JAVA_SQL_TIMESTAMP) {
          try {
            final Date parse = UTC_TIMESTAMP_FORMAT.parse(parser.getText());
            rowBuilder.set(i, parse.getTime());
          } catch (ParseException e) {
            // ignore bad value
          }
        } else {
          rowBuilder.set(i, parser.getText());
        }
        break;
    }
  }
  /** Parses the output of a {@code topN} query, sending the results to a {@link Sink}. */
  private void parse(
      QueryType queryType,
      InputStream in,
      Sink sink,
      List<String> fieldNames,
      List<ColumnMetaData.Rep> fieldTypes,
      Page page) {
    final JsonFactory factory = new JsonFactory();
    final Row.RowBuilder rowBuilder = Row.newBuilder(fieldNames.size());

    if (CalcitePrepareImpl.DEBUG) {
      try {
        final byte[] bytes = AvaticaUtils.readFullyToBytes(in);
        System.out.println("Response: " + new String(bytes));
        in = new ByteArrayInputStream(bytes);
      } catch (IOException e) {
        throw Throwables.propagate(e);
      }
    }

    try (final JsonParser parser = factory.createParser(in)) {
      switch (queryType) {
        case TOP_N:
          if (parser.nextToken() == JsonToken.START_ARRAY
              && parser.nextToken() == JsonToken.START_OBJECT) {
            expectScalarField(parser, "timestamp");
            if (parser.nextToken() == JsonToken.FIELD_NAME
                && parser.getCurrentName().equals("result")
                && parser.nextToken() == JsonToken.START_ARRAY) {
              while (parser.nextToken() == JsonToken.START_OBJECT) {
                // loop until token equal to "}"
                parseFields(fieldNames, fieldTypes, rowBuilder, parser);
                sink.send(rowBuilder.build());
                rowBuilder.reset();
              }
            }
          }
          break;

        case SELECT:
          if (parser.nextToken() == JsonToken.START_ARRAY
              && parser.nextToken() == JsonToken.START_OBJECT) {
            page.pagingIdentifier = null;
            page.offset = -1;
            expectScalarField(parser, "timestamp");
            if (parser.nextToken() == JsonToken.FIELD_NAME
                && parser.getCurrentName().equals("result")
                && parser.nextToken() == JsonToken.START_OBJECT) {
              if (parser.nextToken() == JsonToken.FIELD_NAME
                  && parser.getCurrentName().equals("pagingIdentifiers")
                  && parser.nextToken() == JsonToken.START_OBJECT) {
                switch (parser.nextToken()) {
                  case FIELD_NAME:
                    page.pagingIdentifier = parser.getCurrentName();
                    if (parser.nextToken() == JsonToken.VALUE_NUMBER_INT) {
                      page.offset = parser.getIntValue();
                    }
                    expect(parser, JsonToken.END_OBJECT);
                    break;
                  case END_OBJECT:
                }
              }
              if (parser.nextToken() == JsonToken.FIELD_NAME
                  && parser.getCurrentName().equals("events")
                  && parser.nextToken() == JsonToken.START_ARRAY) {
                while (parser.nextToken() == JsonToken.START_OBJECT) {
                  expectScalarField(parser, "segmentId");
                  expectScalarField(parser, "offset");
                  if (parser.nextToken() == JsonToken.FIELD_NAME
                      && parser.getCurrentName().equals("event")
                      && parser.nextToken() == JsonToken.START_OBJECT) {
                    parseFields(fieldNames, fieldTypes, rowBuilder, parser);
                    sink.send(rowBuilder.build());
                    rowBuilder.reset();
                  }
                  expect(parser, JsonToken.END_OBJECT);
                }
                parser.nextToken();
              }
            }
          }
          break;

        case GROUP_BY:
          if (parser.nextToken() == JsonToken.START_ARRAY) {
            while (parser.nextToken() == JsonToken.START_OBJECT) {
              expectScalarField(parser, "version");
              expectScalarField(parser, "timestamp");
              if (parser.nextToken() == JsonToken.FIELD_NAME
                  && parser.getCurrentName().equals("event")
                  && parser.nextToken() == JsonToken.START_OBJECT) {
                parseFields(fieldNames, fieldTypes, rowBuilder, parser);
                sink.send(rowBuilder.build());
                rowBuilder.reset();
              }
              expect(parser, JsonToken.END_OBJECT);
            }
          }
      }
    } catch (IOException | InterruptedException e) {
      throw Throwables.propagate(e);
    }
  }