private void addRangeState(final PlanBDoc doc) {
        // If key is provided then from/to are the same.
        if (key != null) {
            if (rangeFrom != null) {
                error(LogUtil.message("Range state 'key` provided plus `from' for {}", mapName));
            } else if (rangeTo != null) {
                error(LogUtil.message("Range state 'key` provided plus `to' for {}", mapName));
            } else {
                try {
                    final long longKey = Long.parseLong(key);
                    if (longKey < 0) {
                        // negative values cause problems for the ordering of data in LMDB so prevent
                        // their use when using byteBuffer.putLong, -10, 0 & 10 will be stored in LMDB
                        // as 0, 10, -10
                        error(LogUtil.message(
                                "Range state only supports non-negative numbers (key: {}) for {}",
                                longKey, mapName));
                    } else {
                        final RangeState.Key k = RangeState.Key.builder()
                                .keyStart(longKey)
                                .keyEnd(longKey)
                                .build();
                        final Val v = getVal();
                        catchLmdbError(() -> writer.addRangeState(doc, new RangeState(k, v)));
                    }
                } catch (final RuntimeException e) {
                    error("Unable to parse string \"" + key + "\" as long for range", e);
                }
            }

        } else {
            if (rangeFrom == null) {
                error(LogUtil.message("Range state 'from' is null for {}", mapName));
            } else if (rangeTo == null) {
                error(LogUtil.message("Range state 'to' is null for {}", mapName));
            } else if (rangeFrom > rangeTo) {
                error(LogUtil.message(
                        "Range 'from' must be less than or equal to range 'to' " +
                        "(from: {}, to: {}) for {}",
                        rangeFrom, rangeTo, mapName));
            } else if (rangeFrom < 0) {
                // negative values cause problems for the ordering of data in LMDB so prevent their use
                // when using byteBuffer.putLong, -10, 0 & 10 will be stored in LMDB as 0, 10, -10
                error(LogUtil.message(
                        "Range state only supports non-negative numbers (from: {}, to: {}) for {}",
                        rangeFrom, rangeTo, mapName));
            } else {
                final RangeState.Key k = RangeState.Key.builder()
                        .keyStart(rangeFrom)
                        .keyEnd(rangeTo)
                        .build();
                final Val v = getVal();
                catchLmdbError(() -> writer.addRangeState(doc, new RangeState(k, v)));
            }
        }
    }
This method applies a post processing function if we are currently within
a function element. At this stage we should have details of the function
to apply from the corresponding start element and content to apply it to
from the characters event.

@param uri       The element's Namespace URI, or the empty string.
@param localName The element's local key, or the empty string.
@param qName     The element's qualified (prefixed) key, or the empty string.
@throws SAXException The client may throw an exception during processing.
@see AbstractXMLFilter#endElement(String,
String, String)
stroom-state/stroom-planb-impl/src/main/java/stroom/planb/impl/pipeline/PlanBFilter.java