static TimelineFilterList parseKVFilters(String expr, boolean valueAsString)
      throws TimelineParseException {
    return parseFilters(new TimelineParserForKVFilters(expr, valueAsString));
  }
@Test
  void testConfigFiltersParsing() throws Exception {
    String expr = "(((key11 ne 234 AND key12 eq val12) AND " +
        "(key13 ene val13 OR key14 eq 567)) OR (key21 eq val_21 OR key22 eq " +
        "val.22))";
    TimelineFilterList expectedList = new TimelineFilterList(
        Operator.OR,
        new TimelineFilterList(
            Operator.AND,
            new TimelineFilterList(
                Operator.AND,
                new TimelineKeyValueFilter(TimelineCompareOp.NOT_EQUAL,
                    "key11", "234", false),
                new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
                    "key12", "val12", true)
            ),
            new TimelineFilterList(
                Operator.OR,
                new TimelineKeyValueFilter(TimelineCompareOp.NOT_EQUAL,
                    "key13", "val13", true),
                new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
                    "key14", "567", true)
            )
        ),
        new TimelineFilterList(
            Operator.OR,
            new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
                "key21", "val_21", true),
            new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
                "key22", "val.22", true)
        )
    );
    verifyFilterList(expr, TimelineReaderWebServicesUtils.
        parseKVFilters(expr, true), expectedList);

    expr = "abc ne 234 AND def eq 23 OR rst ene 24 OR xyz eq 456 AND pqr eq 2";
    expectedList = new TimelineFilterList(
        new TimelineFilterList(
            Operator.OR,
            new TimelineFilterList(
                new TimelineKeyValueFilter(TimelineCompareOp.NOT_EQUAL,
                    "abc", "234", false),
                new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
                    "def", "23", true)
            ),
            new TimelineKeyValueFilter(TimelineCompareOp.NOT_EQUAL,
                "rst", "24", true),
            new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
                "xyz", "456", true)
        ),
        new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
            "pqr", "2", true)
    );
    verifyFilterList(expr, TimelineReaderWebServicesUtils.
        parseKVFilters(expr, true), expectedList);

    // Test with unnecessary spaces.
    expr = "  abc ne   234       AND       def           eq 23 OR     rst ene " +
        "           24     OR xyz     eq     456    AND pqr eq 2        ";
    expectedList = new TimelineFilterList(
        new TimelineFilterList(
            Operator.OR,
            new TimelineFilterList(
                new TimelineKeyValueFilter(TimelineCompareOp.NOT_EQUAL,
                    "abc", "234", false),
                new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
                    "def", "23", true)
            ),
            new TimelineKeyValueFilter(TimelineCompareOp.NOT_EQUAL,
                "rst", "24", true),
            new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
                "xyz", "456", true)
        ),
        new TimelineKeyValueFilter(TimelineCompareOp.EQUAL,
            "pqr", "2", true)
    );
    verifyFilterList(expr, TimelineReaderWebServicesUtils.
        parseKVFilters(expr, true), expectedList);

    expr = "abc gt 234 AND def eq 23 OR rst ene 24 OR xyz eq 456 AND pqr eq 2";
    try {
      TimelineReaderWebServicesUtils.parseKVFilters(expr, true);
      fail("Invalid compareop specified for config filters. Should be either" +
          " eq,ne or ene and exception should have been thrown.");
    } catch (TimelineParseException e) {
    }
  }