@Override
  public String pullNextXmlChunk() throws KettleException {
    Stack<String> elementStack = xmlChunkerState.getElementStack();
    XMLStreamReader xmlStreamReader = xmlChunkerState.getXmlStreamReader();

    try {

      while (xmlStreamReader.hasNext()) {

        switch (xmlStreamReader.next()) {
          case XMLStreamConstants.END_DOCUMENT:
            return null;

          case XMLStreamConstants.END_ELEMENT:
            elementStack.pop();
            break;

          case XMLStreamConstants.START_ELEMENT:
            elementStack.push(xmlStreamReader.getLocalName());

            if (actualElementStackHasExpectedElements(xmlChunkerState)) {
              return pullNextXmlChunkFromTopElementOnStack(xmlChunkerState);
            }

            break;
        }
      }
    } catch (Exception e) {
      throw new KettleException("a problem has arisen reading the xero xml stream", e);
    }

    return null;
  }
  private String pullNextXmlChunkFromTopElementOnStack(XMLChunkerState data)
      throws KettleException {
    Stack<String> elementStack = data.getElementStack();
    XMLStreamReader xmlStreamReader = data.getXmlStreamReader();

    int elementStackDepthOnEntry = elementStack.size();
    StringWriter stringWriter = new StringWriter();

    try {
      XMLStreamWriter xmlStreamWriter =
          data.getXmlOutputFactory().createXMLStreamWriter(stringWriter);

      xmlStreamWriter.writeStartDocument(CharEncoding.UTF_8, "1.0");

      // put the current element on because presumably it's the open element for the one
      // that is being looked for.

      XmlReaderToWriter.write(xmlStreamReader, xmlStreamWriter);

      while (xmlStreamReader.hasNext() & elementStack.size() >= elementStackDepthOnEntry) {

        switch (xmlStreamReader.next()) {
          case XMLStreamConstants.END_DOCUMENT:
            break; // handled below explicitly.

          case XMLStreamConstants.END_ELEMENT:
            elementStack.pop();
            XmlReaderToWriter.write(xmlStreamReader, xmlStreamWriter);
            break;

          case XMLStreamConstants.START_ELEMENT:
            elementStack.push(xmlStreamReader.getLocalName());
            XmlReaderToWriter.write(xmlStreamReader, xmlStreamWriter);
            break;

          default:
            XmlReaderToWriter.write(xmlStreamReader, xmlStreamWriter);
            break;
        }
      }

      xmlStreamWriter.writeEndDocument();
      xmlStreamWriter.close();
    } catch (Exception e) {
      throw new KettleException("unable to process a chunk of the xero xml stream", e);
    }

    return stringWriter.toString();
  }
  /**
   * Checks to see if the expected elements are present at the start of the actual current elements.
   *
   * @return true if the expected elements can be seen at the start of the actual elements.
   */
  private boolean actualElementStackHasExpectedElements(XMLChunkerState data) {
    Stack<String> actual = data.getElementStack();
    Stack<String> expected = data.getExpectedContainerElementsStack();

    if (actual.size() < expected.size()) {
      return false;
    }

    for (int i = 0; i < expected.size(); i++) {
      if (!actual.get(i).equals(expected.get(i))) {
        return false;
      }
    }

    return true;
  }