All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.pkts.packet.sip.impl.SipMessageBuilder Maven / Gradle / Ivy

package io.pkts.packet.sip.impl;

import io.pkts.buffer.Buffer;
import io.pkts.buffer.Buffers;
import io.pkts.packet.sip.SipMessage;
import io.pkts.packet.sip.SipParseException;
import io.pkts.packet.sip.address.SipURI;
import io.pkts.packet.sip.header.AddressParametersHeader;
import io.pkts.packet.sip.header.CSeqHeader;
import io.pkts.packet.sip.header.CallIdHeader;
import io.pkts.packet.sip.header.ContactHeader;
import io.pkts.packet.sip.header.ContentLengthHeader;
import io.pkts.packet.sip.header.FromHeader;
import io.pkts.packet.sip.header.MaxForwardsHeader;
import io.pkts.packet.sip.header.RecordRouteHeader;
import io.pkts.packet.sip.header.RouteHeader;
import io.pkts.packet.sip.header.SipHeader;
import io.pkts.packet.sip.header.ToHeader;
import io.pkts.packet.sip.header.ViaHeader;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author [email protected]
 */
public abstract class SipMessageBuilder implements SipMessage.Builder {

    /**
     * These are all the headers that the user has added to this builder.
     * These headers may have been added to this list through any of the
     * withXXX-methods or they could have been copied from the
     * template if one was used.
     */
    private final List headers;

    /**
     * All headers added to this builder is subject
     * to filtering.
     */
    private Predicate filter;

    private Function onRequestURIFunction;

    private CSeqHeader cseq;
    private CSeqHeader.Builder cseqBuilder;

    private Consumer onMaxForwardsBuilder;

    private Consumer> onToBuilder;

    private Consumer> onFromBuilder;

    private Consumer> onContactBuilder;

    private List viaHeaders;
    private Consumer onTopMostViaBuilder;
    private BiConsumer onViaBuilder;

    private List recordRouteHeaders;
    private Consumer> onTopMostRecordRouteBuilder;
    private Consumer> onRecordRouteBuilder;

    private List routeHeaders;
    private Consumer> onTopMostRouteBuilder;
    private Consumer> onRouteBuilder;

    private Function onHeaderFunction;

    /**
     * TODO: should probably allow to pass in an object as well.
     */
    private Buffer body;

    private SipHeader toHeader;
    private SipHeader fromHeader;
    private SipHeader cSeqHeader;
    private SipHeader callIdHeader;
    private SipHeader maxForwardsHeader;
    private SipHeader viaHeader;
    private SipHeader routeHeader;
    private SipHeader recordRouteHeader;
    private SipHeader contactHeader;

    /**
     * By default, this builder will add certain headers if missing
     * but if the user wish to turn off this behavior then she can
     * do so by flipping this flag.
     */
    private boolean useDefaults = true;

    protected SipMessageBuilder(final int headerSizeHint) {
        headers = new ArrayList<>(headerSizeHint);
    }

    protected SipMessageBuilder() {
        this(15);
    }

    @Override
    public SipMessage.Builder withNoDefaults() {
        useDefaults = false;
        return this;
    }

    @Override
    public SipMessage.Builder onHeader(final Function f) throws IllegalStateException {
        if (this.onHeaderFunction == null) {
            this.onHeaderFunction = f;
        } else {
            this.onHeaderFunction = this.onHeaderFunction.andThen(f);
        }
        return this;
    }

    private void processHeader(final SipHeader header) {
        if (header.isContactHeader()) {
            addHeader(header);
            contactHeader = header;
        } else if (header.isCSeqHeader()) {
            addHeader(header);
            cSeqHeader = header;
        } else if (header.isMaxForwardsHeader()) {
            addHeader(header);
            maxForwardsHeader = header;
        } else if (header.isFromHeader()) {
            addHeader(header);
            fromHeader = header;
        } else if (header.isToHeader()) {
            addHeader(header);
            toHeader = header;
        } else if (header.isViaHeader()) {
            viaHeaders = ensureList(viaHeaders);
            viaHeaders.add(header.ensure().toViaHeader());
            viaHeader = header;
        } else if (header.isCallIdHeader()) {
            addHeader(header);
            callIdHeader = header;
        } else if (header.isRouteHeader()) {
            routeHeaders = ensureList(routeHeaders);
            routeHeaders.add(header.ensure().toRouteHeader());
            routeHeader = header;
        } else if (header.isRecordRouteHeader()) {
            recordRouteHeaders = ensureList(recordRouteHeaders);
            recordRouteHeaders.add(header.ensure().toRecordRouteHeader());
            recordRouteHeader = header;
        } else {
            addHeader(header);
        }
    }

    private short addHeader(final SipHeader header) {
        headers.add(header);
        return (short)(headers.size() - 1);
    }

    @Override
    public SipMessage.Builder withHeader(final SipHeader header) {
        if (header != null) {
            processHeader(header);
        }
        return this;
    }

    @Override
    public SipMessage.Builder withHeaders(final List headers) {
        if (headers != null) {
            headers.forEach(this::processHeader);
        }
        return this;
    }

    @Override
    public SipMessage.Builder withPushHeader(final SipHeader header) {
        return this;
    }

    @Override
    public SipMessage.Builder onFromHeader(final Consumer> f) {
        if (this.onFromBuilder != null) {
            this.onFromBuilder = this.onFromBuilder.andThen(f);
        } else {
            this.onFromBuilder = f;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withFromHeader(final FromHeader from) {
        if (from != null) {
            addHeader(from);
            fromHeader = from;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withFromHeader(final String from) {
        return withFromHeader(FromHeader.frame(Buffers.wrap(from)));
    }

    @Override
    public SipMessage.Builder onToHeader(final Consumer> f) {
        if (this.onToBuilder != null) {
            this.onToBuilder = this.onToBuilder.andThen(f);
        } else {
            this.onToBuilder = f;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withToHeader(final ToHeader to) {
        if (to != null) {
            addHeader(to);
            toHeader = to;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withToHeader(final String to) {
        return withToHeader(ToHeader.frame(Buffers.wrap(to)));
    }

    @Override
    public SipMessage.Builder onContactHeader(final Consumer> f) {
        if (this.onContactBuilder != null) {
            this.onContactBuilder = this.onContactBuilder.andThen(f);
        } else {
            this.onContactBuilder = f;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withContactHeader(final ContactHeader contact) {
        if (contact != null) {
            addHeader(contact);
            contactHeader = contact;
        }
        return this;
    }

    @Override
    public SipMessage.Builder onCSeqHeader(final Consumer f) {
        return this;
    }

    @Override
    public SipMessage.Builder withCSeqHeader(final CSeqHeader cseq) {
        if (cseq != null) {
            addHeader(cseq);
            cSeqHeader = cseq;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withCallIdHeader(final CallIdHeader callID) {
        if (callID != null) {
            addHeader(callID);
            callIdHeader = callID;
        }
        return this;
    }

    @Override
    public SipMessage.Builder onMaxForwardsHeader(final Consumer f) {
        if (this.onMaxForwardsBuilder != null) {
            this.onMaxForwardsBuilder.andThen(f);
        } else {
            this.onMaxForwardsBuilder = f;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withMaxForwardsHeader(final MaxForwardsHeader maxForwards) {
        addHeader(maxForwards);
        maxForwardsHeader = maxForwards;
        return this;
    }

    @Override
    public SipMessage.Builder onTopMostViaHeader(final Consumer f) {
        this.onTopMostViaBuilder = chainConsumers(this.onTopMostViaBuilder, f);
        return this;
    }

    @Override
    public SipMessage.Builder onViaHeader(final BiConsumer f) {
        this.onViaBuilder = chainConsumers(this.onViaBuilder, f);
        return this;
    }

    @Override
    public SipMessage.Builder onTopMostRouteHeader(final Consumer> f) {
        this.onTopMostRouteBuilder = chainConsumers(this.onTopMostRouteBuilder, f);
        return this;
    }

    @Override
    public SipMessage.Builder onRouteHeader(final Consumer> f) {
        this.onRouteBuilder = chainConsumers(this.onRouteBuilder, f);
        return this;
    }

    @Override
    public SipMessage.Builder withRouteHeader(final RouteHeader route) {
        if (route != null) {
            this.routeHeaders = ensureList(this.routeHeaders);
            this.routeHeaders.clear();
            this.routeHeaders.add(route);
        }

        return this;
    }

    @Override
    public SipMessage.Builder withRouteHeaders(final RouteHeader... routes) {
        if (routes != null && routes.length > 0) {
            this.routeHeaders = ensureList(this.routeHeaders);
            this.routeHeaders.clear();
            routeHeader = routes[0];
            this.routeHeaders.addAll(Arrays.asList(routes));
        }

        return this;
    }

    @Override
    public SipMessage.Builder withRouteHeaders(final List routes) {
        if (routes != null && !routes.isEmpty()) {
            this.routeHeaders = ensureList(this.routeHeaders);
            this.routeHeaders.clear();
            routeHeader = routes.get(0);
            this.routeHeaders.addAll(routes);
        }

        return this;
    }

    @Override
    public SipMessage.Builder withTopMostRouteHeader(final RouteHeader route) {
        if (route != null) {
            this.routeHeaders = ensureList(this.routeHeaders);
            this.routeHeaders.add(0, route);
            routeHeader = route;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withPoppedRoute() {
        if (this.routeHeaders != null) {
            this.routeHeaders.remove(0);
        }
        return this;
    }

    @Override
    public SipMessage.Builder withNoRoutes() {
        if (this.routeHeaders != null) {
            this.routeHeaders.clear();
        }
        return this;
    }

    @Override
    public SipMessage.Builder onTopMostRecordRouteHeader(final Consumer> f) {
        this.onTopMostRecordRouteBuilder = chainConsumers(this.onTopMostRecordRouteBuilder, f);
        return this;
    }

    @Override
    public SipMessage.Builder onRecordRouteHeader(final Consumer> f) {
        this.onRecordRouteBuilder = chainConsumers(this.onRecordRouteBuilder, f);
        return this;
    }

    @Override
    public SipMessage.Builder withRecordRouteHeader(final RecordRouteHeader recordRoute) {
        if (recordRoute != null) {
            this.recordRouteHeaders = ensureList(this.recordRouteHeaders);
            this.recordRouteHeaders.clear();
            this.recordRouteHeaders.add(recordRoute);
            recordRouteHeader = recordRoute;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withRecordRouteHeaders(final RecordRouteHeader... recordRoutes) {
        if (recordRoutes != null && recordRoutes.length > 0) {
            this.recordRouteHeaders = ensureList(this.recordRouteHeaders);
            this.recordRouteHeaders.clear();
            recordRouteHeader = recordRoutes[0];
            this.recordRouteHeaders.addAll(Arrays.asList(recordRoutes));
        }

        return this;
    }

    @Override
    public SipMessage.Builder withRecordRouteHeaders(final List recordRoutes) {
        if (recordRoutes != null && !recordRoutes.isEmpty()) {
            this.recordRouteHeaders = ensureList(this.recordRouteHeaders);
            this.recordRouteHeaders.clear();
            recordRouteHeader = recordRoutes.get(0);
            this.recordRouteHeaders.addAll(recordRoutes);
        }

        return this;
    }

    @Override
    public SipMessage.Builder withTopMostRecordRouteHeader(final RecordRouteHeader recordRoute) {
        if (recordRoute != null) {
            this.recordRouteHeaders = ensureList(this.recordRouteHeaders);
            this.recordRouteHeaders.add(0, recordRoute);
            recordRouteHeader = recordRoute;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withViaHeader(final ViaHeader via) {
        if (via != null) {
            this.viaHeaders = ensureList(this.viaHeaders);
            this.viaHeaders.clear();
            this.viaHeaders.add(via);
            viaHeader = via;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withViaHeaders(final ViaHeader... vias) {
        if (vias != null && vias.length > 0) {
            this.viaHeaders = ensureList(this.viaHeaders);
            this.viaHeaders.clear();
            viaHeader = vias[0];
            this.viaHeaders.addAll(Arrays.asList(vias));
        }
        return this;
    }

    @Override
    public SipMessage.Builder withViaHeaders(final List vias) {
        if (vias != null && !vias.isEmpty()) {
            this.viaHeaders = ensureList(this.viaHeaders);
            this.viaHeaders.clear();
            viaHeader = vias.get(0);
            this.viaHeaders.addAll(vias);
        }
        return this;
    }

    @Override
    public SipMessage.Builder withTopMostViaHeader(final ViaHeader via) {
        if (via != null) {
            this.viaHeaders = ensureList(this.viaHeaders);
            this.viaHeaders.add(0, via);
            viaHeader = via;
        }
        return this;
    }

    @Override
    public SipMessage.Builder withTopMostViaHeader() {
        this.viaHeaders = ensureList(this.viaHeaders);
        this.viaHeaders.add(0, null);
        return this;
    }

    @Override
    public SipMessage.Builder withPoppedVia() {
        if (this.viaHeaders != null) {
            this.viaHeaders.remove(0);
        }
        return this;
    }

    protected final Function getRequestURIFunction() {
        return this.onRequestURIFunction;
    }

    private  List ensureList(final List list) {
        if (list != null)  {
            return list;
        }

        // TODO: the initial size of this array could have an impact on performance.
        // TODO: Need to do some performance testing...
        return new ArrayList(4);
    }

    @Override
    public SipMessage.Builder onRequestURI(final Function f) {
        if (this.onRequestURIFunction == null) {
            this.onRequestURIFunction = f;
        } else {
            this.onRequestURIFunction = this.onRequestURIFunction.andThen(f);
        }
        return this;
    }

    @Override
    public SipMessage.Builder withBody(final Buffer body) {
        if (body != null) {
            this.body = body.slice();
        }
        return this;
    }

    /**
     * Special size of method that checks the size of the headers
     * we keep track of as a list. The reason we count size() - 1 is
     * because the list already occupy one
     * @param list
     * @return
     */
    private final int sizeOf(final List list) {
        return list == null ? 0 : list.size() - 1;
    }

    /**
     * See {@link SipMessage.Builder#withNoDefaults()}, which describes what defaults will
     * be pushed. They are copied here for reference:
     *
     * 
    *
  • {@link ToHeader} - the request-uri will be used to construct the to-header
  • *
  • {@link CSeqHeader} - a new CSeq header will be added where the * method is the same as this message and the sequence number is set to 1
  • *
  • {@link CallIdHeader} - a new random call-id will be added
  • *
  • {@link MaxForwardsHeader} - if we are building a request, then a max forwards of 70 will be added
  • *
  • {@link ContentLengthHeader} - Will be added if there is a body * on the message and the length set to the correct length.
  • *
* */ private void enforceDefaults() { if (toHeader == null) { withToHeader(generateDefaultToHeader()); } if (isBuildingRequest() && maxForwardsHeader == null) { withMaxForwardsHeader(MaxForwardsHeader.create()); } if (callIdHeader == null) { withCallIdHeader(CallIdHeader.create()); } if (cSeqHeader == null) { withCSeqHeader(generateDefaultCSeqHeader()); } } /** * Indicates whether or not we are building a request. Must be overridden by * the request builder. Used for e.g. {@link SipHeaderBuilderWrapper#enforceDefaults()} * * @return */ protected boolean isBuildingRequest() { return false; } /** * Indicates whether or not we are building a response. Must be overridden by * the response builder. Used for e.g. {@link SipHeaderBuilderWrapper#enforceDefaults()} * * @return */ protected boolean isBuildingResponse() { return false; } protected abstract ToHeader generateDefaultToHeader(); protected abstract CSeqHeader generateDefaultCSeqHeader(); @Override public T build() { int msgSize = 2; final int headerCount = this.headers.size() + sizeOf(viaHeaders) + sizeOf(recordRouteHeaders) + sizeOf(routeHeaders); final Map> finalHeaders = new HashMap<>(headerCount); SipHeader contentLengthHeader = null; if (useDefaults) { enforceDefaults(); } // TODO: redo this, it's ugly. Bloody side effect programming & ugly ugly copy-paste crap toHeader = null; fromHeader = null; cSeqHeader = null; callIdHeader = null; maxForwardsHeader = null; viaHeader = null; routeHeader = null; recordRouteHeader = null; contactHeader = null; for (int i = 0; i < this.headers.size(); ++i) { final SipHeader header = this.headers.get(i); if (header != null) { final SipHeader finalHeader = processFinalHeader((short) finalHeaders.size(), header); if (finalHeader != null) { if (finalHeader.isContentLengthHeader()) { // not that it actually matters but pretty much // every implementation put the content-length header // last so we'll do that too... contentLengthHeader = finalHeader; } else { msgSize += finalHeader.getBufferSize() + 2; finalHeaders.computeIfAbsent(finalHeader.getName().toString(), k -> new ArrayList<>()).add(finalHeader); } } } } if (this.viaHeaders != null) { for (int j = 0; j < this.viaHeaders.size(); ++j) { final ViaHeader finalVia = processVia(j, this.viaHeaders.get(j)); msgSize += finalVia.getBufferSize() + 2; if (viaHeader == null) { viaHeader = finalVia; } finalHeaders.computeIfAbsent(finalVia.getName().toString(), k -> new ArrayList<>()).add(finalVia); } } if (this.recordRouteHeaders != null) { for (int j = 0; j < this.recordRouteHeaders.size(); ++j) { final Consumer> f = j == 0 ? this.onTopMostRecordRouteBuilder : this.onRecordRouteBuilder; final RecordRouteHeader finalRR = invokeAddressBuilderFunction(f, this.recordRouteHeaders.get(j).ensure().toRecordRouteHeader()); msgSize += finalRR.getBufferSize() + 2; if (recordRouteHeader == null) { recordRouteHeader = finalRR; } finalHeaders.computeIfAbsent(finalRR.getName().toString(), k -> new ArrayList<>()).add(finalRR); } } if (this.routeHeaders != null) { for (int j = 0; j < this.routeHeaders.size(); ++j) { final Consumer> f = j == 0 ? this.onTopMostRouteBuilder : this.onRouteBuilder; final RouteHeader finalRoute = invokeAddressBuilderFunction(f, this.routeHeaders.get(j).ensure().toRouteHeader()); msgSize += finalRoute.getBufferSize() + 2; if (routeHeader == null) { routeHeader = finalRoute; } finalHeaders.computeIfAbsent(finalRoute.getName().toString(), k -> new ArrayList<>()).add(finalRoute); } } // TODO: Body - should probably have a onBody as well and we may want // to allow a "raw" body as well as an object. final Buffer body = this.body; // if we are to use defaults then we will adjust the value // of the Content-Length header. If not, then the CL will be // whatever the user decided it should be. if (isBuildingRequest() && useDefaults) { contentLengthHeader = ContentLengthHeader.create(body == null ? 0 : body.capacity()); } if (contentLengthHeader != null) { msgSize += contentLengthHeader.getBufferSize() + 2; finalHeaders.computeIfAbsent(contentLengthHeader.getName().toString(), k -> new ArrayList<>()).add(contentLengthHeader); } // TODO: not correct but will do for now... final SipInitialLine initialLine = buildInitialLine(); final Buffer initialLineBuffer = initialLine.getBuffer(); msgSize += initialLineBuffer.getReadableBytes() + 2; if (body != null) { msgSize += body.getReadableBytes(); } // TODO: instead of copying over the bytes like this create // a composite buffer... final Buffer msg = Buffers.createBuffer(msgSize); initialLineBuffer.getBytes(msg); msg.write(SipParser.CR); msg.write(SipParser.LF); for (final List headerValues : finalHeaders.values()) { for (final SipHeader header : headerValues) { header.getBytes(msg); msg.write(SipParser.CR); msg.write(SipParser.LF); } } msg.write(SipParser.CR); msg.write(SipParser.LF); if (body != null) { body.getBytes(msg); } return internalBuild(msg, initialLine, finalHeaders, toHeader, fromHeader, cSeqHeader, callIdHeader, maxForwardsHeader, viaHeader, routeHeader, recordRouteHeader, contactHeader, body); } private ViaHeader processVia(final int index, final SipHeader header) throws SipParseException { if (index > 0 && this.onViaBuilder == null) { if (header == null) { throw new SipParseException("You cannot register an empty Via-header and " + "then not also register a function for that via. Please refer to javadoc"); } return header.ensure().toViaHeader(); } if (index == 0 && this.onTopMostViaBuilder == null) { if (header == null) { throw new SipParseException("You cannot register an empty top-most Via-header and " + "then not also register a function for that top-most via. Please refer to the javadoc"); } return header.ensure().toViaHeader(); } final ViaHeader.Builder builder = header == null ? ViaHeader.builder() : header.ensure().toViaHeader().copy(); if (index == 0) { this.onTopMostViaBuilder.accept(builder); } else { this.onViaBuilder.accept(index, builder); } return builder.build(); } protected abstract SipInitialLine buildInitialLine() throws SipParseException; protected abstract T internalBuild(final Buffer message, final SipInitialLine initialLine, final Map> headers, final SipHeader toHeader, final SipHeader fromHeader, final SipHeader cSeqHeader, final SipHeader callIdHeader, final SipHeader maxForwardsHeader, final SipHeader viaHeader, final SipHeader routeHeader, final SipHeader recordRouteHeader, final SipHeader contactHeader, final Buffer body); private SipHeader processFinalHeader(final short index, final SipHeader header) { final SipHeader finalHeader; if (header.isContactHeader()) { finalHeader = invokeContactHeaderFunction(header.ensure().toContactHeader()); contactHeader = finalHeader; } else if (header.isCSeqHeader()) { finalHeader = header.ensure().toCSeqHeader(); cSeqHeader = finalHeader; } else if (header.isMaxForwardsHeader()) { finalHeader = invokeMaxForwardsFunction(header.ensure().toMaxForwardsHeader()); maxForwardsHeader = finalHeader; } else if (header.isFromHeader()) { finalHeader = invokeFromHeaderFunction(header.ensure().toFromHeader()); fromHeader = finalHeader; } else if (header.isToHeader()) { finalHeader = invokeToHeaderFunction(header.ensure().toToHeader()); toHeader = finalHeader; } else if (header.isCallIdHeader()) { finalHeader = header.ensure().toCallIdHeader(); callIdHeader = finalHeader; } else { finalHeader = processGenericHeader(header); } return finalHeader; } private T invokeSipHeaderBuilderFunction(final Consumer> f, final T header) { if (header != null && f != null) { final SipHeader.Builder b = header.copy(); f.accept(b); return b.build(); } return header; } private T invokeAddressBuilderFunction(final Consumer> f, final T header) { if (header != null && f != null) { final AddressParametersHeader.Builder b = header.copy(); f.accept(b); return b.build(); } return header; } private SipHeader processGenericHeader(final SipHeader header) { if (this.onHeaderFunction != null) { return this.onHeaderFunction.apply(header); } return header; } private ToHeader invokeToHeaderFunction(final ToHeader to) { if (to != null && onToBuilder != null) { final AddressParametersHeader.Builder b = to.copy(); onToBuilder.accept(b); return b.build(); } return to; } private ContactHeader invokeContactHeaderFunction(final ContactHeader contact) { if (contact != null && onContactBuilder != null) { final AddressParametersHeader.Builder b = contact.copy(); onContactBuilder.accept(b); return b.build(); } return contact; } private FromHeader invokeFromHeaderFunction(final FromHeader from) { if (from != null && onFromBuilder != null) { final AddressParametersHeader.Builder b = from.copy(); onFromBuilder.accept(b); return b.build(); } return from; } private MaxForwardsHeader invokeMaxForwardsFunction(final MaxForwardsHeader max) { if (max != null && onMaxForwardsBuilder != null) { final MaxForwardsHeader.Builder b = max.copy(); onMaxForwardsBuilder.accept(b); return b.build(); } return max; } @Override public SipMessage.Builder onCommit(final Consumer f) { return this; } /** * Helper function to chain consumers together or return the new one if the current consumer * isn't set. * * @param currentConsumer the current consumer, which may be null * @param consumer the new consumer to chain with the current one. * @param * @return the chained consumer (or the new consumer if there previously wasn't one around) */ private Consumer chainConsumers(final Consumer currentConsumer, final Consumer consumer) { if (currentConsumer != null) { return currentConsumer.andThen(consumer); } return consumer; } private BiConsumer chainConsumers(final BiConsumer currentConsumer, final BiConsumer consumer) { if (currentConsumer != null) { return currentConsumer.andThen(consumer); } return consumer; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy