class RrdpParser {

    public void parseDelta(InputStream inputStream, Consumer<DeltaHeader> processDeltaHeader, Consumer<DeltaElement> processDeltaElement) {
        try {
            final XMLInputFactory factory = XMLInputFactory.newInstance();
            final XMLEventReader eventReader = factory.createXMLEventReader(inputStream);

            String sessionId = null;
            BigInteger serial = null;
            String uri = null;
            String hash = null;
            StringBuilder base64 = new StringBuilder();
            boolean inPublishElement = false;
            boolean deltaHeaderProcessed = false;

            final Base64.Decoder decoder = Base64.getDecoder();

            while (eventReader.hasNext()) {
                final XMLEvent event = eventReader.nextEvent();

                switch (event.getEventType()) {
                    case XMLStreamConstants.START_ELEMENT:
                        final StartElement startElement = event.asStartElement();
                        final String qName = startElement.getName().getLocalPart();

                        switch (qName) {
                            case "publish":
                                if (!deltaHeaderProcessed) {
                                    throw new RrdpException(ErrorCodes.RRDP_PARSE_ERROR, "delta header not present before elements");
                                }
                                uri = getAttr(startElement, "uri", "Uri is not present in 'publish' element");
                                hash = getAttr(startElement, "hash");
                                inPublishElement = true;
                                break;
                            case "withdraw":
                                if (!deltaHeaderProcessed) {
                                    throw new RrdpException(ErrorCodes.RRDP_PARSE_ERROR, "delta header not present before elements");
                                }
                                uri = getAttr(startElement, "uri", "Uri is not present in 'publish' element");
                                hash = getAttr(startElement, "hash", "Hash is not present in 'withdraw' element");
                                break;
                            case "delta":
                                serial = new BigInteger(getAttr(startElement, "serial", "Notification serial is not present"));
                                sessionId = getAttr(startElement, "session_id", "Session id is not present");
                                processDeltaHeader.accept(new DeltaHeader(sessionId, serial));
                                deltaHeaderProcessed = true;
                                break;
                        }
                        break;

                    case XMLStreamConstants.CHARACTERS:
                        final Characters characters = event.asCharacters();
                        if (inPublishElement) {
                            final String thisBase64 = characters.getData();
                            base64.append(thisBase64.replaceAll("\\s", ""));
                        }
                        break;

                    case XMLStreamConstants.END_ELEMENT:
                        final EndElement endElement = event.asEndElement();
                        final String qqName = endElement.getName().getLocalPart();

                        switch (qqName) {
                            case "publish":
                                final byte[] decoded = decoder.decode(base64.toString());
                                base64 = new StringBuilder();
                                processDeltaElement.accept(new DeltaPublish(decoded, uri, Hex.parse(hash)));
                                break;
                            case "withdraw":
                                processDeltaElement.accept(new DeltaWithdraw(uri, Hex.parse(hash)));
                                break;
                        }
                        break;
                }
            }
        } catch (XMLStreamException e) {
            throw new RrdpException("Couldn't parse delta: ", e);
        }
    }

}