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

io.lettuce.core.output.ObjectOutput Maven / Gradle / Ivy

Go to download

Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs and much more.

The newest version!
package io.lettuce.core.output;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import io.lettuce.core.codec.RedisCodec;
import io.lettuce.core.codec.StringCodec;
import io.lettuce.core.internal.LettuceFactories;

/**
 * RESP3-capable command output that represents the RESP3 response as RESP3 primitives including support for nesting. Decodes
 * simple strings through {@link StringCodec#UTF8}.
 *
 * @param  Key type.
 * @param  Value type.
 * @author Mark Paluch
 * @since 6.3.0
 */
public class ObjectOutput extends CommandOutput {

    private final Deque stack;

    private Object current;

    private boolean hasCurrent = false;

    private int depth;

    private boolean initialized;

    public ObjectOutput(RedisCodec codec) {
        super(codec, Collections.emptyList());
        stack = LettuceFactories.newSpScQueue();
        depth = 0;
    }

    @Override
    public void set(long integer) {

        if (!initialized) {
            output = new ArrayList<>();
        }

        setValue(integer);
    }

    @Override
    public void set(double number) {
        setValue(number);
    }

    @Override
    public void setBigNumber(ByteBuffer bytes) {
        setSingle(bytes);
    }

    @Override
    public void set(boolean value) {
        setValue(value);
    }

    @Override
    public void set(ByteBuffer bytes) {
        setValue(bytes == null ? null : codec.decodeValue(bytes));
    }

    @Override
    public void setSingle(ByteBuffer bytes) {
        setValue(bytes == null ? null : StringCodec.UTF8.decodeValue(bytes));
    }

    @SuppressWarnings("unchecked")
    private void setValue(Object value) {

        if (output != null) {

            if (output instanceof Collection) {
                ((Collection) output).add(value);
            } else if (output instanceof Map) {
                if (!hasCurrent) {
                    current = value;
                    hasCurrent = true;
                } else {
                    ((Map) output).put(current, value);
                    current = null;
                    hasCurrent = false;
                }
            } else {
                throw new IllegalStateException(
                        String.format("Output %s is not a supported container type to append a response value", output));
            }
        } else {
            output = value;
        }
    }

    @Override
    public void complete(int depth) {
        if (depth > 0 && depth < this.depth) {
            stack.pop();
            output = stack.peek();
            this.depth--;
        }
    }

    @Override
    public void multi(int count) {

        if (!initialized) {
            output = OutputFactory.newList(Math.max(1, count));
            push(output);
            initialized = true;
        } else {
            List list = OutputFactory.newList(count);
            setValue(list);
            push(list);
            output = list;
        }

        this.depth++;
    }

    @Override
    public void multiMap(int count) {

        if (!initialized) {
            output = new LinkedHashMap<>(count);
            initialized = true;
            push(output);
        } else {

            Map map = new LinkedHashMap<>(count);
            setValue(map);
            push(map);
            output = map;
        }

        this.depth++;
    }

    private void push(Object output) {
        stack.push(output);
    }

}