List<BlockInfo> parseLine(String line) throws IOException {
    if (currentState == State.DEFAULT) {
      if (line.contains("<INodeSection>")) {
        transitionTo(State.INODE_SECTION);
      } else {
        return Collections.emptyList();
      }
    }
    if (line.contains("<inode>")) {
      transitionTo(State.INODE);
    }
    if (line.contains("<type>FILE</type>")) {
      transitionTo(State.FILE);
    }
    List<String> replicationStrings = valuesFromXMLString(line, "replication");
    if (!replicationStrings.isEmpty()) {
      if (replicationStrings.size() > 1) {
        throw new IOException(String.format("Found %s replication strings",
            replicationStrings.size()));
      }
      transitionTo(State.FILE_WITH_REPLICATION);
      currentReplication = Short.parseShort(replicationStrings.get(0));
    }
    Matcher blockMatcher = BLOCK_PATTERN.matcher(line);
    List<BlockInfo> blockInfos = new ArrayList<>();
    while (blockMatcher.find()) {
      if (currentState != State.FILE_WITH_REPLICATION) {
        throw new IOException(
            "Found a block string when in state: " + currentState);
      }
      long id = Long.parseLong(blockMatcher.group(1));
      long gs = Long.parseLong(blockMatcher.group(2));
      long size = Long.parseLong(blockMatcher.group(3));
      blockInfos.add(new BlockInfo(id, gs, size, currentReplication));
    }
    if (line.contains("</inode>")) {
      transitionTo(State.INODE_SECTION);
    }
    if (line.contains("</INodeSection>")) {
      transitionTo(State.DEFAULT);
    }
    return blockInfos;
  }
@Test
  public void testBlocksFromLine() throws Exception {
    String[] lines = {
        "<INodeSection><lastInodeId>1"
            + "</lastInodeId><inode><id>2</id><type>FILE</type>"
            + "<name>fake-file</name>"
            + "<replication>3</replication><mtime>3</mtime>"
            + "<atime>4</atime>" + "<perferredBlockSize>5</perferredBlockSize>"
            + "<permission>hdfs:hdfs:rw-------</permission>"
            + "<blocks><block><id>6</id><genstamp>7</genstamp>"
            + "<numBytes>8</numBytes></block>"
            + "<block><id>9</id><genstamp>10</genstamp>"
            + "<numBytes>11</numBytes></block></inode>",
        "<inode><type>DIRECTORY</type></inode>", "<inode><type>FILE</type>",
        "<replication>12</replication>",
        "<blocks><block><id>13</id><genstamp>14</genstamp>"
            + "<numBytes>15</numBytes></block>",
        "</inode>",
        "</INodeSection>"
    };

    short replCount = 0; // This is ignored
    Map<BlockInfo, Short> expectedBlockCount = new HashMap<>();
    expectedBlockCount.put(new BlockInfo(6, 7, 8, replCount), (short) 3);
    expectedBlockCount.put(new BlockInfo(9, 10, 11, replCount), (short) 3);
    expectedBlockCount.put(new BlockInfo(13, 14, 15, replCount), (short) 12);

    final Map<BlockInfo, Short> actualBlockCount = new HashMap<>();
    XMLParser parser = new XMLParser();
    for (String line : lines) {
      for (BlockInfo info : parser.parseLine(line)) {
        actualBlockCount.put(info, info.getReplication());
      }
    }

    for (Map.Entry<BlockInfo, Short> expect : expectedBlockCount.entrySet()) {
      assertEquals(expect.getValue(), actualBlockCount.get(expect.getKey()));
    }
  }