private int processExpression(final Expression expression,
                                  final Buffer buffer,
                                  final int parentMatchCount,
                                  final int matchCount,
                                  final int level,
                                  final MatchOrder matchOrder,
                                  final boolean ignoreErrors)
            throws IOException, SAXException {
        int start = -1;
        int end = -1;

        // We don't know if this is a match yet so plus one to the matches so far to
        // get the match number this would be if it matches
        final int potentialMatchNumber = parentMatchCount + 1;

        // Check that we haven't already exceeded the maximum match count for
        // this expression.
        if (expression.checkMaxMatch()
                && expression.checkOnlyMatch(potentialMatchNumber)) {
            // Set the input that this expression will use.
            expression.setInput(buffer);

            // Try to find a match.
            Match match = null;
            try {
                match = expression.match();
            } catch (final ComplexRegexException e) {
                handleComplexRegexException(e, expression);
            } catch (final RuntimeException e) {
                handleRuntimeException(e, expression);
            }

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

                // Make sure the matcher matched from the first character if we
                // are matching in sequence.
                if (matchOrder == MatchOrder.SEQUENCE && !ignoreErrors && start != 0) {
                    messageBuffer.clear();
                    messageBuffer.append("Expression failed to match from the start of the content (matched at char ");
                    messageBuffer.append(start + 1); // make one based
                    messageBuffer.append(" [one based], unmatched content [");
                    appendBufferContents(messageBuffer, buffer.subSequence(0, start));
                    messageBuffer.append("]): ");
                    messageBuffer.append(expression.getDebugId());
                    log(Severity.ERROR, messageBuffer.toString());
                }

                // 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 = match.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());
                    }
                }

                // Make sure we matched some characters and are moving on.
                if (end > 0) {
                    // Make sure we haven't exhausted the reader.
                    if (level == 0 && !reader.isEof() && reader.length() == end) {
                        // We have exhausted the buffer so log an error and
                        // attempt to recover.
                        final int recoveryAdvance = end / 2;
                        end = RECOVERY_MODE;
                        attemptRecovery(expression, recoveryAdvance);

                    } else {
                        // Process sub nodes.
                        process(expression, buffer, match, matchCount, level + 1, matchOrder, ignoreErrors);

                        // Increment the match count.
                        expression.incrementMatchCount();

                        // If we are matching in sequence then we can just move
                        // the char stream on. If not then we need to eliminate
                        // the match from the char stream.
                        if (matchOrder == MatchOrder.SEQUENCE) {
                            // Move the char stream on.
                            buffer.move(end);
                        } else {
                            // Remove the matched section from the buffer.
                            buffer.remove(start, end);
                        }
                    }
                }
            }
        }

        // Make sure the expression matched the minimum number of times.
        if (end != RECOVERY_MODE && !expression.checkMinMatch()) {
            messageBuffer.clear();
            messageBuffer.append("Expression did not match the required number of times (match count: ");
            messageBuffer.append(expression.getMatchCount());
//            messageBuffer.append(" content: [");
//            appendBufferContents(messageBuffer, buffer);
//            messageBuffer.append("]");
            messageBuffer.append("): ");
            messageBuffer.append(expression.getDebugId());
            log(Severity.ERROR, messageBuffer.toString());
        }

        // Return the end match position.
        return end;
    }
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