private void addTemporalRangeState(final PlanBDoc doc) {
        final Instant time = this.time != null
                ? this.time
                : this.effectiveTime;
        if (time == null) {
            error(LogUtil.message("Temporal range range 'time' is null for {}", mapName));

        } else {
            // If key is provided then from/to are the same.
            if (key != null) {
                if (rangeFrom != null) {
                    error(LogUtil.message("Temporal range state 'key` provided plus `from' for {}",
                            mapName));
                } else if (rangeTo != null) {
                    error(LogUtil.message("Temporal 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(
                                    "Temporal range state only supports non-negative numbers " +
                                    "(key: {}) for {}",
                                    longKey,
                                    mapName));
                        } else {
                            final TemporalRangeState.Key k = TemporalRangeState.Key.builder()
                                    .keyStart(longKey)
                                    .keyEnd(longKey)
                                    .time(time)
                                    .build();
                            final Val v = getVal();
                            catchLmdbError(() -> writer.addTemporalRangeState(doc, new TemporalRangeState(k, v)));
                        }
                    } catch (final RuntimeException e) {
                        error("Unable to parse string \"" + key + "\" as long for range", e);
                    }
                }

            } else {
                if (rangeFrom == null) {
                    error(LogUtil.message("Temporal range 'from' is null for {}", mapName));
                } else if (rangeTo == null) {
                    error(LogUtil.message("Temporal range 'to' is null for {}", mapName));
                } else if (rangeFrom > rangeTo) {
                    error(LogUtil.message(
                            "Temporal 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(
                            "Temporal range only supports non-negative numbers " +
                            "(from: {}, to: {}) for {}",
                            rangeFrom,
                            rangeTo,
                            mapName));
                } else {
                    final TemporalRangeState.Key k = TemporalRangeState.Key.builder()
                            .keyStart(rangeFrom)
                            .keyEnd(rangeTo)
                            .time(time)
                            .build();
                    final Val v = getVal();
                    catchLmdbError(() -> writer.addTemporalRangeState(doc, new TemporalRangeState(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