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

io.pkts.packet.sip.header.impl.ParametersSupport Maven / Gradle / Ivy

There is a newer version: 3.0.10
Show newest version
/**
 * 
 */
package io.pkts.packet.sip.header.impl;

import io.pkts.buffer.Buffer;
import io.pkts.buffer.Buffers;
import io.pkts.packet.sip.SipParseException;
import io.pkts.packet.sip.impl.SipParser;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import static io.pkts.buffer.Buffers.wrap;
import static io.pkts.packet.sip.impl.PreConditions.ifNull;

/**
 * @author [email protected]
 */
public final class ParametersSupport {

    /**
     * This buffer is the full original slice of the parameters as we received them. We keep this
     * one around since it is very common in applications such as proxies etc that you only look at
     * the parameters but never actually change them so we want to keep this one around for
     * performance reasons. However, if there ever is a change we nullify this buffer so that we no
     * longer can use it ever.
     */
    private Buffer originalParams;

    /**
     * The buffer that contains all our parameters but as we consume them, they will be (well,
     * consumed) moved over to the parameter map for fast future access. Once all parameters have
     * been consumed, this buffer will actually be empty.
     * 
     * If we are asked to produce a buffer again through {@link #toBuffer()} or through
     * {@link #transferValue(Buffer)} we will serialize all parameters back to buffer form and
     * re-assign this params buffer. The {@link #paramMap} will be left untouched but unless there
     * is another change to the parameters we will just return the same params buffer again.
     */
    private Buffer params;

    private Map paramMap;

    /**
     * If there is a change to the parameters that will force us to re-generate the full buffer
     * again.
     */
    private boolean isDirty;

    private static final int estimatedSize = 0;

    public ParametersSupport() {
        this(null);
    }

    /**
     * @param params
     */
    public ParametersSupport(final Buffer params) {
        if (params != null) {
            this.originalParams = params.slice();
            this.params = params;
        } else {
            this.originalParams = null;
            this.params = Buffers.EMPTY_BUFFER;
        }
    }

    /**
     * Check if there are any parameters specified at all.
     *
     * @return
     */
    public boolean hasParameters() {
        return (this.originalParams != null && !this.originalParams.isEmpty())
                || (this.paramMap != null && !this.paramMap.isEmpty());
    }

    public boolean hasParameter(final Buffer name) {
        return this.paramMap != null && this.paramMap.containsKey(name);
    }

    public Buffer getParameter(final Buffer name) throws SipParseException {
        if (name == null) {
            throw new IllegalArgumentException("The name of the parameter cannot be null");
        }

        if (this.paramMap != null && this.paramMap.containsKey(name)) {
            final Buffer value = this.paramMap.get(name);
            if (value == null) {
                return null;
            }
            return value.slice();
        }

        return consumeUntil(name);
    }

    /**
     * WARNING: should really only be used by internal implementations.
     * 
     * @return
     */
    public Set> getAllParameters() {
        consumeUntil(null);
        if (this.paramMap != null) {
            return this.paramMap.entrySet();
        }
        return null;
    }

    /**
     * Internal helper method that will consume all raw parameters until we find the specified name
     * or if the name is null, then that will be the same as "consume all".
     * 
     * @param name
     * @return
     */
    private Buffer consumeUntil(final Buffer name) {
        try {
            while (this.params.hasReadableBytes()) {
                final int startIndex = params.getReaderIndex();
                SipParser.consumeSEMI(params);
                final Buffer[] keyValue = SipParser.consumeGenericParam(this.params);
                final Buffer key = keyValue[0];
                final Buffer value = keyValue[1] == null ? Buffers.EMPTY_BUFFER : keyValue[1];

                if (startIndex == params.getReaderIndex()) {
                    throw new SipParseException("Issue-106 unable to make progress consuming parameters. " +
                            "Possible bad input that was not properly detected before identified as being parameters");
                }

                if (key != null) {
                    ensureParamsMap();
                    this.paramMap.put(key, value);

                    if (name != null && name.equals(key)) {
                        return value;
                    }
                }

            }
            return null;
        } catch (final IndexOutOfBoundsException e) {
            throw new SipParseException(this.params.getReaderIndex(),
                    "Unable to process the value due to a IndexOutOfBoundsException", e);
        } catch (final IOException e) {
            throw new SipParseException(this.params.getReaderIndex(),
                    "Could not read from the underlying stream while parsing the value");
        }

    }

    private void ensureParamsMap() {
        if (this.paramMap == null) {
            // default map size is 16 but params are rarely more than a few
            this.paramMap = new LinkedHashMap(8);
        }
    }

    public void removeParameter(final Buffer name) {
        if (this.paramMap != null) {
            this.paramMap.remove(name);
        }
    }

    public Buffer getParameter(final String name) throws SipParseException {
        return getParameter(Buffers.wrap(name));
    }

    public void setParameter(final String name, final String value) throws SipParseException,
    IllegalArgumentException {
        setParameter(wrap(name), value == null ? Buffers.EMPTY_BUFFER : wrap(value));
    }

    public void setParameter(final Buffer name, final Buffer value) throws SipParseException,
    IllegalArgumentException {
        getParameter(name);
        ensureParamsMap();
        this.paramMap.put(name, ifNull(value, Buffers.EMPTY_BUFFER));
        this.isDirty = true;
        this.originalParams = null;
    }

    public Buffer toBuffer() {
        if (this.originalParams != null) {
            return this.originalParams.slice();
        }

        // hmmm... very side effect programming. Not nice at all.
        ensureParams();
        return this.params.slice();
    }

    /**
     * Make sure that the internal params buffer is actually valid etc.
     */
    private void ensureParams() {
        if (this.isDirty) {
            // note, it would only be dirty if we actually have inserted a value
            // so therefore no need to check that the parammap is null
            final Buffer restOfParams = this.params;
            this.params = allcoateNewParamBuffer();
            for (final Map.Entry entry : this.paramMap.entrySet()) {
                this.params.write(SipParser.SEMI);
                final Buffer key = entry.getKey();
                final Buffer value = entry.getValue();
                key.getBytes(0, this.params);
                if (value != null && !value.isEmpty()) {
                    this.params.write(SipParser.EQ);
                    value.getBytes(0, this.params);
                }
            }
            this.paramMap.clear();
            restOfParams.getBytes(this.params);
            this.originalParams = this.params.slice();
            this.isDirty = false;
        }
    }

    public void transferValue(final Buffer dst) {
        if (this.isDirty) {
            ensureParams();
        }
        if (this.originalParams != null) {
            this.originalParams.getBytes(0, dst);
        } else {
            this.params.getBytes(0, dst);
        }
    }

    /**
     * Will create an appropriate sized buffer that will fit all parameters
     * 
     * @return
     */
    private Buffer allcoateNewParamBuffer() {
        // TODO: actually do what we claim that we are doing
        // and figure out how big of a buffer we need.
        return Buffers.createBuffer(256);
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy