private void attemptRecovery(final Expression expression, final int recoveryAdvance) throws IOException {
        messageBuffer.clear();
        messageBuffer.append("Top level expression '");
        messageBuffer.append(expression.getDebugId());
        messageBuffer.append("' matched too much content and exhausted the buffer. Attempting recovery...");
        log(Severity.ERROR, messageBuffer.toString());

        // Attempt recovery.
        int start = 0;
        int end = reader.length();
        while (!reader.isEof() && reader.length() == end) {
            reader.move(recoveryAdvance);
            reader.fillBuffer();

            Match recoveryMatch = null;
            try {
                recoveryMatch = expression.match();
            } catch (ComplexRegexException e) {
                handleComplexRegexException(e, expression);
            } catch (final RuntimeException e) {
                handleRuntimeException(e, expression);
            }

            if (recoveryMatch != null) {
                // Find the first and last char position that the matcher found.
                start = recoveryMatch.start();
                end = recoveryMatch.end();

                // We might not want to move the character buffer to the end of
                // the match if advance has been specified. We are calculating
                // end outside of processing matches as we want to abort
                // processing if advance stops the buffer moving on.
                if (expression.getAdvance() != -1) {
                    try {
                        end = recoveryMatch.end(expression.getAdvance());
                    } catch (final IndexOutOfBoundsException e) {
                        messageBuffer.clear();
                        messageBuffer.append("Advance group number ");
                        messageBuffer.append(expression.getAdvance());
                        messageBuffer.append(" not found in expression: ");
                        messageBuffer.append(expression.getDebugId());
                        log(Severity.FATAL_ERROR, messageBuffer.toString());
                    }
                }

                if (start > 0) {
                    // Move to the start of the match for the next pass to
                    // continue from here.
                    reader.move(start);
                } else if (end > 0 && end < reader.length()) {
                    // Move to the end of the match for the next pass to
                    // continue from here.
                    reader.move(end);
                }
            }
        }

        if (reader.isEof() && reader.length() == end) {
            messageBuffer.clear();
            messageBuffer.append("Top level expression '");
            messageBuffer.append(expression.getDebugId());
            messageBuffer.append("' matched too much content and reached the end of the stream without recovering");
            log(Severity.FATAL_ERROR, messageBuffer.toString());

            // Move to the end of the stream so no further matching is
            // attempted.
            reader.move(end);
        } else {
            reader.changeLocation();
            log(Severity.INFO, "Attempting recovery from this position");
        }
    }
Parses a flat file and produces SAX events according to the feed
definition.

@param input The flat input source to process.
@throws IOException  Could be thrown reading the stream.
@throws SAXException Not be thrown as we are not reading an XML file just using
                     the XMLReader interface.
@see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource)
stroom-pipeline/src/main/java/stroom/pipeline/xml/converter/ds3/DS3Parser.java