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

redis.clients.jedis.BuilderFactory Maven / Gradle / Ivy

The newest version!
package redis.clients.jedis;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import redis.clients.jedis.exceptions.JedisDataException;
import redis.clients.jedis.resps.*;
import redis.clients.jedis.resps.LCSMatchResult.MatchedPosition;
import redis.clients.jedis.resps.LCSMatchResult.Position;
import redis.clients.jedis.util.DoublePrecision;
import redis.clients.jedis.util.JedisByteHashMap;
import redis.clients.jedis.util.KeyValue;
import redis.clients.jedis.util.SafeEncoder;

public final class BuilderFactory {

  public static final Builder RAW_OBJECT = new Builder() {
    @Override
    public Object build(Object data) {
      return data;
    }

    @Override
    public String toString() {
      return "Object";
    }
  };

  public static final Builder> RAW_OBJECT_LIST = new Builder>() {
    @Override
    public List build(Object data) {
      return (List) data;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder ENCODED_OBJECT = new Builder() {
    @Override
    public Object build(Object data) {
      return SafeEncoder.encodeObject(data);
    }

    @Override
    public String toString() {
      return "Object";
    }
  };

  public static final Builder> ENCODED_OBJECT_LIST = new Builder>() {
    @Override
    public List build(Object data) {
      return (List) SafeEncoder.encodeObject(data);
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder LONG = new Builder() {
    @Override
    public Long build(Object data) {
      return (Long) data;
    }

    @Override
    public String toString() {
      return "Long";
    }

  };

  public static final Builder> LONG_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }
      return (List) data;
    }

    @Override
    public String toString() {
      return "List";
    }

  };

  public static final Builder DOUBLE = new Builder() {
    @Override
    public Double build(Object data) {
      if (data == null) return null;
      else if (data instanceof Double) return (Double) data;
      else return DoublePrecision.parseFloatingPointNumber(STRING.build(data));
    }

    @Override
    public String toString() {
      return "Double";
    }
  };

  public static final Builder> DOUBLE_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) return null;
      return ((List) data).stream().map(DOUBLE::build).collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder BOOLEAN = new Builder() {
    @Override
    public Boolean build(Object data) {
      if (data == null) return null;
      else if (data instanceof Boolean) return (Boolean) data;
      return ((Long) data) == 1L;
    }

    @Override
    public String toString() {
      return "Boolean";
    }
  };

  public static final Builder> BOOLEAN_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) return null;
      return ((List) data).stream().map(BOOLEAN::build).collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder> BOOLEAN_WITH_ERROR_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) return null;
      return ((List) data).stream()
          //.map((val) -> (val instanceof JedisDataException) ? val : BOOLEAN.build(val))
          .map((val) -> (val instanceof JedisDataException) ? null : BOOLEAN.build(val))
          .collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder BINARY = new Builder() {
    @Override
    public byte[] build(Object data) {
      return (byte[]) data;
    }

    @Override
    public String toString() {
      return "byte[]";
    }
  };

  public static final Builder> BINARY_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      return (List) data;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder> BINARY_SET = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Set build(Object data) {
      if (null == data) {
        return null;
      }
      List l = BINARY_LIST.build(data);
      return SetFromList.of(l);
    }

    @Override
    public String toString() {
      return "Set";
    }
  };

  public static final Builder>> BINARY_PAIR_LIST
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public List> build(Object data) {
      final List flatHash = (List) data;
      final List> pairList = new ArrayList<>();
      final Iterator iterator = flatHash.iterator();
      while (iterator.hasNext()) {
        pairList.add(new AbstractMap.SimpleEntry<>(iterator.next(), iterator.next()));
      }

      return pairList;
    }

    @Override
    public String toString() {
      return "List>";
    }
  };

  public static final Builder>> BINARY_PAIR_LIST_FROM_PAIRS
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public List> build(Object data) {
      final List list = (List) data;
      final List> pairList = new ArrayList<>();
      for (Object object : list) {
        final List flat = (List) object;
        pairList.add(new AbstractMap.SimpleEntry<>(flat.get(0), flat.get(1)));
      }

      return pairList;
    }

    @Override
    public String toString() {
      return "List>";
    }
  };

  public static final Builder STRING = new Builder() {
    @Override
    public String build(Object data) {
      return data == null ? null : SafeEncoder.encode((byte[]) data);
    }

    @Override
    public String toString() {
      return "String";
    }
  };

  public static final Builder> STRING_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) return null;
      return ((List) data).stream().map(STRING::build).collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder> STRING_SET = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Set build(Object data) {
      if (null == data) return null;
      return ((List) data).stream().map(STRING::build).collect(Collectors.toSet());
    }

    @Override
    public String toString() {
      return "Set";
    }
  };

  public static final Builder> BINARY_MAP = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Map build(Object data) {
      final List list = (List) data;
      if (list.isEmpty()) return Collections.emptyMap();

      if (list.get(0) instanceof KeyValue) {
        final Map map = new JedisByteHashMap();
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          KeyValue kv = (KeyValue) iterator.next();
          map.put(BINARY.build(kv.getKey()), BINARY.build(kv.getValue()));
        }
        return map;
      } else {
        final Map map = new JedisByteHashMap();
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          map.put(BINARY.build(iterator.next()), BINARY.build(iterator.next()));
        }
        return map;
      }
    }

    @Override
    public String toString() {
      return "Map";
    }
  };

  public static final Builder> STRING_MAP = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Map build(Object data) {
      final List list = (List) data;
      if (list.isEmpty()) return Collections.emptyMap();

      if (list.get(0) instanceof KeyValue) {
        final Map map = new HashMap<>(list.size(), 1f);
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          KeyValue kv = (KeyValue) iterator.next();
          map.put(STRING.build(kv.getKey()), STRING.build(kv.getValue()));
        }
        return map;
      } else {
        final Map map = new HashMap<>(list.size() / 2, 1f);
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          map.put(STRING.build(iterator.next()), STRING.build(iterator.next()));
        }
        return map;
      }
    }

    @Override
    public String toString() {
      return "Map";
    }
  };

  public static final Builder> ENCODED_OBJECT_MAP = new Builder>() {
    @Override
    public Map build(Object data) {
      if (data == null) return null;
      final List list = (List) data;
      if (list.isEmpty()) return Collections.emptyMap();

      if (list.get(0) instanceof KeyValue) {
        final Map map = new HashMap<>(list.size(), 1f);
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          KeyValue kv = (KeyValue) iterator.next();
          map.put(STRING.build(kv.getKey()), ENCODED_OBJECT.build(kv.getValue()));
        }
        return map;
      } else {
        final Map map = new HashMap<>(list.size() / 2, 1f);
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          map.put(STRING.build(iterator.next()), ENCODED_OBJECT.build(iterator.next()));
        }
        return map;
      }
    }
  };

  public static final Builder AGGRESSIVE_ENCODED_OBJECT = new Builder() {
    @Override
    public Object build(Object data) {
      if (data == null) return null;

      if (data instanceof List) {
        final List list = (List) data;
        if (list.isEmpty()) return Collections.emptyMap();

        if (list.get(0) instanceof KeyValue) {
          return ((List) data).stream()
              .filter(kv -> kv != null && kv.getKey() != null && kv.getValue() != null)
              .collect(Collectors.toMap(kv -> STRING.build(kv.getKey()),
                  kv -> this.build(kv.getValue())));
        } else {
          return list.stream().map(this::build).collect(Collectors.toList());
        }
      } else if (data instanceof byte[]) {
        return STRING.build(data);
      }
      return data;
    }
  };

  public static final Builder> AGGRESSIVE_ENCODED_OBJECT_MAP = new Builder>() {
    @Override
    public Map build(Object data) {
      return (Map) AGGRESSIVE_ENCODED_OBJECT.build(data);
    }
  };

  public static final Builder>> STRING_PAIR_LIST
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public List> build(Object data) {
      final List flatHash = (List) data;
      final List> pairList = new ArrayList<>(flatHash.size() / 2);
      final Iterator iterator = flatHash.iterator();
      while (iterator.hasNext()) {
        pairList.add(KeyValue.of(STRING.build(iterator.next()), STRING.build(iterator.next())));
      }

      return pairList;
    }

    @Override
    public String toString() {
      return "List>";
    }
  };

  public static final Builder>> STRING_PAIR_LIST_FROM_PAIRS
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public List> build(Object data) {
      return ((List) data).stream().map(o -> (List) o)
          .map(l -> KeyValue.of(STRING.build(l.get(0)), STRING.build(l.get(1))))
          .collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List>";
    }
  };

  public static final Builder> STRING_LONG_MAP = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Map build(Object data) {
      final List list = (List) data;
      if (list.isEmpty()) return Collections.emptyMap();

      if (list.get(0) instanceof KeyValue) {
        final Map map = new LinkedHashMap<>(list.size(), 1f);
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          KeyValue kv = (KeyValue) iterator.next();
          map.put(STRING.build(kv.getKey()), LONG.build(kv.getValue()));
        }
        return map;
      } else {
        final Map map = new LinkedHashMap<>(list.size() / 2, 1f);
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          map.put(STRING.build(iterator.next()), LONG.build(iterator.next()));
        }
        return map;
      }
    }

    @Override
    public String toString() {
      return "Map";
    }
  };

  public static final Builder> KEYED_ELEMENT = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue build(Object data) {
      if (data == null) return null;
      List l = (List) data;
      return KeyValue.of(STRING.build(l.get(0)), STRING.build(l.get(1)));
    }

    @Override
    public String toString() {
      return "KeyValue";
    }
  };

  public static final Builder> BINARY_KEYED_ELEMENT = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue build(Object data) {
      if (data == null) return null;
      List l = (List) data;
      return KeyValue.of(BINARY.build(l.get(0)), BINARY.build(l.get(1)));
    }

    @Override
    public String toString() {
      return "KeyValue";
    }
  };

  public static final Builder> ZRANK_WITHSCORE_PAIR = new Builder>() {
    @Override
    public KeyValue build(Object data) {
      if (data == null) {
        return null;
      }
      List l = (List) data;
      return new KeyValue<>(LONG.build(l.get(0)), DOUBLE.build(l.get(1)));
    }

    @Override
    public String toString() {
      return "KeyValue";
    }
  };

  public static final Builder>> KEYED_STRING_LIST
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue> build(Object data) {
      if (data == null) return null;
      List l = (List) data;
      return new KeyValue<>(STRING.build(l.get(0)), STRING_LIST.build(l.get(1)));
    }

    @Override
    public String toString() {
      return "KeyValue>";
    }
  };

  public static final Builder> LONG_LONG_PAIR = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue build(Object data) {
      if (data == null) return null;
      List dataList = (List) data;
      return new KeyValue<>(LONG.build(dataList.get(0)), LONG.build(dataList.get(1)));
    }
  };

  public static final Builder>>> KEYED_STRING_LIST_LIST
      = new Builder>>>() {
    @Override
    public List>> build(Object data) {
      List list = (List) data;
      return list.stream().map(KEYED_STRING_LIST::build).collect(Collectors.toList());
    }
  };

  public static final Builder>> KEYED_BINARY_LIST
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue> build(Object data) {
      if (data == null) return null;
      List l = (List) data;
      return new KeyValue<>(BINARY.build(l.get(0)), BINARY_LIST.build(l.get(1)));
    }

    @Override
    public String toString() {
      return "KeyValue>";
    }
  };

  public static final Builder TUPLE = new Builder() {
    @Override
    @SuppressWarnings("unchecked")
    public Tuple build(Object data) {
      List l = (List) data; // never null
      if (l.isEmpty()) {
        return null;
      }
      return new Tuple(l.get(0), DOUBLE.build(l.get(1)));
    }

    @Override
    public String toString() {
      return "Tuple";
    }
  };

  public static final Builder> KEYED_TUPLE = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue build(Object data) {
      List l = (List) data; // never null
      if (l.isEmpty()) {
        return null;
      }
      return KeyValue.of(STRING.build(l.get(0)), new Tuple(BINARY.build(l.get(1)), DOUBLE.build(l.get(2))));
    }

    @Override
    public String toString() {
      return "KeyValue";
    }
  };

  public static final Builder> BINARY_KEYED_TUPLE = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue build(Object data) {
      List l = (List) data; // never null
      if (l.isEmpty()) {
        return null;
      }
      return KeyValue.of(BINARY.build(l.get(0)), new Tuple(BINARY.build(l.get(1)), DOUBLE.build(l.get(2))));
    }

    @Override
    public String toString() {
      return "KeyValue";
    }
  };

  public static final Builder> TUPLE_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }
      List l = (List) data;
      final List result = new ArrayList<>(l.size() / 2);
      Iterator iterator = l.iterator();
      while (iterator.hasNext()) {
        result.add(new Tuple(iterator.next(), DOUBLE.build(iterator.next())));
      }
      return result;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder> TUPLE_LIST_RESP3 = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) return null;
      return ((List) data).stream().map(TUPLE::build).collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder> TUPLE_ZSET = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Set build(Object data) {
      if (null == data) {
        return null;
      }
      List l = (List) data;
      final Set result = new LinkedHashSet<>(l.size() / 2, 1);
      Iterator iterator = l.iterator();
      while (iterator.hasNext()) {
        result.add(new Tuple(iterator.next(), DOUBLE.build(iterator.next())));
      }
      return result;
    }

    @Override
    public String toString() {
      return "ZSet";
    }
  };

  public static final Builder> TUPLE_ZSET_RESP3 = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Set build(Object data) {
      if (null == data) return null;
      return ((List) data).stream().map(TUPLE::build).collect(Collectors.toCollection(LinkedHashSet::new));
    }

    @Override
    public String toString() {
      return "ZSet";
    }
  };

  private static final Builder> TUPLE_LIST_FROM_PAIRS = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (data == null) return null;
      return ((List>) data).stream().map(TUPLE::build).collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder>> KEYED_TUPLE_LIST
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue> build(Object data) {
      if (data == null) return null;
      List l = (List) data;
      return new KeyValue<>(STRING.build(l.get(0)), TUPLE_LIST_FROM_PAIRS.build(l.get(1)));
    }

    @Override
    public String toString() {
      return "KeyValue>";
    }
  };

  public static final Builder>> BINARY_KEYED_TUPLE_LIST
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public KeyValue> build(Object data) {
      if (data == null) return null;
      List l = (List) data;
      return new KeyValue<>(BINARY.build(l.get(0)), TUPLE_LIST_FROM_PAIRS.build(l.get(1)));
    }

    @Override
    public String toString() {
      return "KeyValue>";
    }
  };

  public static final Builder> SCAN_RESPONSE = new Builder>() {
    @Override
    public ScanResult build(Object data) {
      List result = (List) data;
      String newcursor = new String((byte[]) result.get(0));
      List rawResults = (List) result.get(1);
      List results = new ArrayList<>(rawResults.size());
      for (byte[] bs : rawResults) {
        results.add(SafeEncoder.encode(bs));
      }
      return new ScanResult<>(newcursor, results);
    }
  };

  public static final Builder>> HSCAN_RESPONSE
      = new Builder>>() {
    @Override
    public ScanResult> build(Object data) {
      List result = (List) data;
      String newcursor = new String((byte[]) result.get(0));
      List rawResults = (List) result.get(1);
      List> results = new ArrayList<>(rawResults.size() / 2);
      Iterator iterator = rawResults.iterator();
      while (iterator.hasNext()) {
        results.add(new AbstractMap.SimpleEntry<>(SafeEncoder.encode(iterator.next()),
            SafeEncoder.encode(iterator.next())));
      }
      return new ScanResult<>(newcursor, results);
    }
  };

  public static final Builder> SSCAN_RESPONSE = new Builder>() {
    @Override
    public ScanResult build(Object data) {
      List result = (List) data;
      String newcursor = new String((byte[]) result.get(0));
      List rawResults = (List) result.get(1);
      List results = new ArrayList<>(rawResults.size());
      for (byte[] bs : rawResults) {
        results.add(SafeEncoder.encode(bs));
      }
      return new ScanResult<>(newcursor, results);
    }
  };

  public static final Builder> ZSCAN_RESPONSE = new Builder>() {
    @Override
    public ScanResult build(Object data) {
      List result = (List) data;
      String newcursor = new String((byte[]) result.get(0));
      List rawResults = (List) result.get(1);
      List results = new ArrayList<>(rawResults.size() / 2);
      Iterator iterator = rawResults.iterator();
      while (iterator.hasNext()) {
        results.add(new Tuple(iterator.next(), BuilderFactory.DOUBLE.build(iterator.next())));
      }
      return new ScanResult<>(newcursor, results);
    }
  };

  public static final Builder> SCAN_BINARY_RESPONSE = new Builder>() {
    @Override
    public ScanResult build(Object data) {
      List result = (List) data;
      byte[] newcursor = (byte[]) result.get(0);
      List rawResults = (List) result.get(1);
      return new ScanResult<>(newcursor, rawResults);
    }
  };

  public static final Builder>> HSCAN_BINARY_RESPONSE
      = new Builder>>() {
    @Override
    public ScanResult> build(Object data) {
      List result = (List) data;
      byte[] newcursor = (byte[]) result.get(0);
      List rawResults = (List) result.get(1);
      List> results = new ArrayList<>(rawResults.size() / 2);
      Iterator iterator = rawResults.iterator();
      while (iterator.hasNext()) {
        results.add(new AbstractMap.SimpleEntry<>(iterator.next(), iterator.next()));
      }
      return new ScanResult<>(newcursor, results);
    }
  };

  public static final Builder> SSCAN_BINARY_RESPONSE = new Builder>() {
    @Override
    public ScanResult build(Object data) {
      List result = (List) data;
      byte[] newcursor = (byte[]) result.get(0);
      List rawResults = (List) result.get(1);
      return new ScanResult<>(newcursor, rawResults);
    }
  };

  public static final Builder> PUBSUB_NUMSUB_MAP = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Map build(Object data) {
      final List flatHash = (List) data;
      final Map hash = new HashMap<>(flatHash.size() / 2, 1f);
      final Iterator iterator = flatHash.iterator();
      while (iterator.hasNext()) {
        hash.put(SafeEncoder.encode((byte[]) iterator.next()), (Long) iterator.next());
      }
      return hash;
    }

    @Override
    public String toString() {
      return "PUBSUB_NUMSUB_MAP";
    }
  };

  public static final Builder> GEO_COORDINATE_LIST = new Builder>() {
    @Override
    public List build(Object data) {
      if (null == data) {
        return null;
      }
      return interpretGeoposResult((List) data);
    }

    @Override
    public String toString() {
      return "List";
    }

    private List interpretGeoposResult(List responses) {
      List responseCoordinate = new ArrayList<>(responses.size());
      for (Object response : responses) {
        if (response == null) {
          responseCoordinate.add(null);
        } else {
          List respList = (List) response;
          GeoCoordinate coord = new GeoCoordinate(DOUBLE.build(respList.get(0)),
              DOUBLE.build(respList.get(1)));
          responseCoordinate.add(coord);
        }
      }
      return responseCoordinate;
    }
  };

  public static final Builder> GEORADIUS_WITH_PARAMS_RESULT = new Builder>() {
    @Override
    public List build(Object data) {
      if (data == null) {
        return null;
      }

      List objectList = (List) data;

      List responses = new ArrayList<>(objectList.size());
      if (objectList.isEmpty()) {
        return responses;
      }

      if (objectList.get(0) instanceof List) {
        // list of members with additional informations
        GeoRadiusResponse resp;
        for (Object obj : objectList) {
          List informations = (List) obj;

          resp = new GeoRadiusResponse((byte[]) informations.get(0));

          int size = informations.size();
          for (int idx = 1; idx < size; idx++) {
            Object info = informations.get(idx);
            if (info instanceof List) {
              // coordinate
              List coord = (List) info;

              resp.setCoordinate(new GeoCoordinate(DOUBLE.build(coord.get(0)),
                  DOUBLE.build(coord.get(1))));
            } else if (info instanceof Long) {
              // score
              resp.setRawScore(LONG.build(info));
            } else {
              // distance
              resp.setDistance(DOUBLE.build(info));
            }
          }

          responses.add(resp);
        }
      } else {
        // list of members
        for (Object obj : objectList) {
          responses.add(new GeoRadiusResponse((byte[]) obj));
        }
      }

      return responses;
    }

    @Override
    public String toString() {
      return "GeoRadiusWithParamsResult";
    }
  };

  public static final Builder> COMMAND_DOCS_RESPONSE = new Builder>() {
    @Override
    public Map build(Object data) {
      if (data == null) return null;
      List list = (List) data;
      if (list.isEmpty()) return Collections.emptyMap();

      if (list.get(0) instanceof KeyValue) {
        final Map map = new HashMap<>(list.size(), 1f);
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          KeyValue kv = (KeyValue) iterator.next();
          map.put(STRING.build(kv.getKey()), new CommandDocument(ENCODED_OBJECT_MAP.build(kv.getValue())));
        }
        return map;
      } else {
        final Map map = new HashMap<>(list.size() / 2, 1f);
        final Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
          map.put(STRING.build(iterator.next()), new CommandDocument(ENCODED_OBJECT_MAP.build(iterator.next())));
        }
        return map;
      }
    }
  };

  public static final Builder> COMMAND_INFO_RESPONSE = new Builder>() {
    @Override
    public Map build(Object data) {
      if (data == null) {
        return null;
      }

      List rawList = (List) data;
      Map map = new HashMap<>(rawList.size());

      for (Object rawCommandInfo : rawList) {
        if (rawCommandInfo == null) {
          continue;
        }

        List commandInfo = (List) rawCommandInfo;
        String name = STRING.build(commandInfo.get(0));
        CommandInfo info = CommandInfo.COMMAND_INFO_BUILDER.build(commandInfo);
        map.put(name, info);
      }

      return map;
    }
  };

  private static final Builder>> CLUSTER_SHARD_SLOTS_RANGES = new Builder>>() {

    @Override
    public List> build(Object data) {
      if (null == data) {
        return null;
      }

      List rawSlots = (List) data;
      List> slotsRanges = new ArrayList<>();
      for (int i = 0; i < rawSlots.size(); i += 2) {
        slotsRanges.add(Arrays.asList(rawSlots.get(i), rawSlots.get(i + 1)));
      }
      return slotsRanges;
    }
  };

  private static final Builder> CLUSTER_SHARD_NODE_INFO_LIST
      = new Builder>() {

    final Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {

      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(ClusterShardNodeInfo.ID, STRING);
      tempMappingFunctions.put(ClusterShardNodeInfo.ENDPOINT, STRING);
      tempMappingFunctions.put(ClusterShardNodeInfo.IP, STRING);
      tempMappingFunctions.put(ClusterShardNodeInfo.HOSTNAME, STRING);
      tempMappingFunctions.put(ClusterShardNodeInfo.PORT, LONG);
      tempMappingFunctions.put(ClusterShardNodeInfo.TLS_PORT, LONG);
      tempMappingFunctions.put(ClusterShardNodeInfo.ROLE, STRING);
      tempMappingFunctions.put(ClusterShardNodeInfo.REPLICATION_OFFSET, LONG);
      tempMappingFunctions.put(ClusterShardNodeInfo.HEALTH, STRING);

      return tempMappingFunctions;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }

      List response = new ArrayList<>();

      List clusterShardNodeInfos = (List) data;
      for (Object clusterShardNodeInfoObject : clusterShardNodeInfos) {
        List clusterShardNodeInfo = (List) clusterShardNodeInfoObject;
        Iterator iterator = clusterShardNodeInfo.iterator();
        response.add(new ClusterShardNodeInfo(createMapFromDecodingFunctions(iterator, mappingFunctions)));
      }

      return response;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder> CLUSTER_SHARD_INFO_LIST
          = new Builder>() {

    final Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {

      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(ClusterShardInfo.SLOTS, CLUSTER_SHARD_SLOTS_RANGES);
      tempMappingFunctions.put(ClusterShardInfo.NODES, CLUSTER_SHARD_NODE_INFO_LIST);

      return tempMappingFunctions;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }

      List response = new ArrayList<>();

      List clusterShardInfos = (List) data;
      for (Object clusterShardInfoObject : clusterShardInfos) {
        List clusterShardInfo = (List) clusterShardInfoObject;
        Iterator iterator = clusterShardInfo.iterator();
        response.add(new ClusterShardInfo(createMapFromDecodingFunctions(iterator, mappingFunctions)));
      }

      return response;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder> MODULE_LIST = new Builder>() {
    @Override
    public List build(Object data) {
      if (data == null) {
        return null;
      }

      List> objectList = (List>) data;

      List responses = new ArrayList<>(objectList.size());
      if (objectList.isEmpty()) {
        return responses;
      }

      for (List moduleResp : objectList) {
        if (moduleResp.get(0) instanceof KeyValue) {
          responses.add(new Module(STRING.build(((KeyValue) moduleResp.get(0)).getValue()),
              LONG.build(((KeyValue) moduleResp.get(1)).getValue()).intValue()));
          continue;
        }
        Module m = new Module(SafeEncoder.encode((byte[]) moduleResp.get(1)),
            ((Long) moduleResp.get(3)).intValue());
        responses.add(m);
      }

      return responses;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  /**
   * Create a AccessControlUser object from the ACL GETUSER reply.
   */
  public static final Builder ACCESS_CONTROL_USER = new Builder() {
    @Override
    public AccessControlUser build(Object data) {
      Map map = ENCODED_OBJECT_MAP.build(data);
      if (map == null) return null;
      return new AccessControlUser(map);
    }

    @Override
    public String toString() {
      return "AccessControlUser";
    }
  };

  /**
   * Create an Access Control Log Entry Result of ACL LOG command
   */
  public static final Builder> ACCESS_CONTROL_LOG_ENTRY_LIST
      = new Builder>() {

    private final Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {

      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(AccessControlLogEntry.COUNT, LONG);
      tempMappingFunctions.put(AccessControlLogEntry.REASON, STRING);
      tempMappingFunctions.put(AccessControlLogEntry.CONTEXT, STRING);
      tempMappingFunctions.put(AccessControlLogEntry.OBJECT, STRING);
      tempMappingFunctions.put(AccessControlLogEntry.USERNAME, STRING);
      tempMappingFunctions.put(AccessControlLogEntry.AGE_SECONDS, DOUBLE);
      tempMappingFunctions.put(AccessControlLogEntry.CLIENT_INFO, STRING);
      tempMappingFunctions.put(AccessControlLogEntry.ENTRY_ID, LONG);
      tempMappingFunctions.put(AccessControlLogEntry.TIMESTAMP_CREATED, LONG);
      tempMappingFunctions.put(AccessControlLogEntry.TIMESTAMP_LAST_UPDATED, LONG);

      return tempMappingFunctions;
    }

    @Override
    public List build(Object data) {

      if (null == data) {
        return null;
      }

      List list = new ArrayList<>();
      List> logEntries = (List>) data;
      for (List logEntryData : logEntries) {
        Iterator logEntryDataIterator = logEntryData.iterator();
        AccessControlLogEntry accessControlLogEntry = new AccessControlLogEntry(
            createMapFromDecodingFunctions(logEntryDataIterator, mappingFunctions,
                BACKUP_BUILDERS_FOR_DECODING_FUNCTIONS));
        list.add(accessControlLogEntry);
      }
      return list;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  // Stream Builders -->

  public static final Builder STREAM_ENTRY_ID = new Builder() {
    @Override
    public StreamEntryID build(Object data) {
      if (null == data) {
        return null;
      }
      String id = SafeEncoder.encode((byte[]) data);
      return new StreamEntryID(id);
    }

    @Override
    public String toString() {
      return "StreamEntryID";
    }
  };

  public static final Builder> STREAM_ENTRY_ID_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }
      List objectList = (List) data;
      List responses = new ArrayList<>(objectList.size());
      if (!objectList.isEmpty()) {
        for(Object object : objectList) {
          responses.add(STREAM_ENTRY_ID.build(object));
        }
      }
      return responses;
    }
  };

  public static final Builder STREAM_ENTRY = new Builder() {
    @Override
    @SuppressWarnings("unchecked")
    public StreamEntry build(Object data) {
      if (null == data) {
        return null;
      }
      List objectList = (List) data;

      if (objectList.isEmpty()) {
        return null;
      }

      String entryIdString = SafeEncoder.encode((byte[]) objectList.get(0));
      StreamEntryID entryID = new StreamEntryID(entryIdString);
      List hash = (List) objectList.get(1);

      Iterator hashIterator = hash.iterator();
      Map map = new HashMap<>(hash.size() / 2, 1f);
      while (hashIterator.hasNext()) {
        map.put(SafeEncoder.encode(hashIterator.next()), SafeEncoder.encode(hashIterator.next()));
      }
      return new StreamEntry(entryID, map);
    }

    @Override
    public String toString() {
      return "StreamEntry";
    }
  };

  public static final Builder> STREAM_ENTRY_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }
      List> objectList = (List>) data;

      List responses = new ArrayList<>(objectList.size() / 2);
      if (objectList.isEmpty()) {
        return responses;
      }

      for (ArrayList res : objectList) {
        if (res == null) {
          responses.add(null);
          continue;
        }
        String entryIdString = SafeEncoder.encode((byte[]) res.get(0));
        StreamEntryID entryID = new StreamEntryID(entryIdString);
        List hash = (List) res.get(1);
        if (hash == null) {
          responses.add(new StreamEntry(entryID, null));
          continue;
        }

        Iterator hashIterator = hash.iterator();
        Map map = new HashMap<>(hash.size() / 2, 1f);
        while (hashIterator.hasNext()) {
          map.put(SafeEncoder.encode(hashIterator.next()), SafeEncoder.encode(hashIterator.next()));
        }
        responses.add(new StreamEntry(entryID, map));
      }

      return responses;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder>> STREAM_AUTO_CLAIM_RESPONSE
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public Map.Entry> build(Object data) {
      if (null == data) {
        return null;
      }

      List objectList = (List) data;
      return new AbstractMap.SimpleEntry<>(STREAM_ENTRY_ID.build(objectList.get(0)),
          STREAM_ENTRY_LIST.build(objectList.get(1)));
    }

    @Override
    public String toString() {
      return "Map.Entry>";
    }
  };

  public static final Builder>> STREAM_AUTO_CLAIM_JUSTID_RESPONSE
      = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public Map.Entry> build(Object data) {
      if (null == data) {
        return null;
      }

      List objectList = (List) data;
      return new AbstractMap.SimpleEntry<>(STREAM_ENTRY_ID.build(objectList.get(0)),
          STREAM_ENTRY_ID_LIST.build(objectList.get(1)));
    }

    @Override
    public String toString() {
      return "Map.Entry>";
    }
  };

  /**
   * @deprecated Use {@link BuilderFactory#STREAM_AUTO_CLAIM_JUSTID_RESPONSE}.
   */
  @Deprecated
  public static final Builder>> STREAM_AUTO_CLAIM_ID_RESPONSE
      = STREAM_AUTO_CLAIM_JUSTID_RESPONSE;

  public static final Builder>>> STREAM_READ_RESPONSE
      = new Builder>>>() {
    @Override
    public List>> build(Object data) {
      if (data == null) return null;
      List list = (List) data;
      if (list.isEmpty()) return Collections.emptyList();

      if (list.get(0) instanceof KeyValue) {
        return ((List) list).stream()
            .map(kv -> new KeyValue<>(STRING.build(kv.getKey()),
                STREAM_ENTRY_LIST.build(kv.getValue())))
            .collect(Collectors.toList());
      } else {
        List>> result = new ArrayList<>(list.size());
        for (Object streamObj : list) {
          List stream = (List) streamObj;
          String streamKey = STRING.build(stream.get(0));
          List streamEntries = STREAM_ENTRY_LIST.build(stream.get(1));
          result.add(KeyValue.of(streamKey, streamEntries));
        }
        return result;
      }
    }

    @Override
    public String toString() {
      return "List>>";
    }
  };

  public static final Builder> STREAM_PENDING_ENTRY_LIST = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }

      List streamsEntries = (List) data;
      List result = new ArrayList<>(streamsEntries.size());
      for (Object streamObj : streamsEntries) {
        List stream = (List) streamObj;
        String id = SafeEncoder.encode((byte[]) stream.get(0));
        String consumerName = SafeEncoder.encode((byte[]) stream.get(1));
        long idleTime = BuilderFactory.LONG.build(stream.get(2));
        long deliveredTimes = BuilderFactory.LONG.build(stream.get(3));
        result.add(new StreamPendingEntry(new StreamEntryID(id), consumerName, idleTime,
            deliveredTimes));
      }
      return result;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder STREAM_INFO = new Builder() {

    Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {

      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(StreamInfo.LAST_GENERATED_ID, STREAM_ENTRY_ID);
      tempMappingFunctions.put(StreamInfo.FIRST_ENTRY, STREAM_ENTRY);
      tempMappingFunctions.put(StreamInfo.LENGTH, LONG);
      tempMappingFunctions.put(StreamInfo.RADIX_TREE_KEYS, LONG);
      tempMappingFunctions.put(StreamInfo.RADIX_TREE_NODES, LONG);
      tempMappingFunctions.put(StreamInfo.LAST_ENTRY, STREAM_ENTRY);
      tempMappingFunctions.put(StreamInfo.GROUPS, LONG);

      return tempMappingFunctions;
    }

    @Override
    @SuppressWarnings("unchecked")
    public StreamInfo build(Object data) {
      if (null == data) {
        return null;
      }

      List streamsEntries = (List) data;
      Iterator iterator = streamsEntries.iterator();

      return new StreamInfo(createMapFromDecodingFunctions(iterator, mappingFunctions));
    }

    @Override
    public String toString() {
      return "StreamInfo";
    }
  };

  public static final Builder> STREAM_GROUP_INFO_LIST = new Builder>() {

    Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {

      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(StreamGroupInfo.NAME, STRING);
      tempMappingFunctions.put(StreamGroupInfo.CONSUMERS, LONG);
      tempMappingFunctions.put(StreamGroupInfo.PENDING, LONG);
      tempMappingFunctions.put(StreamGroupInfo.LAST_DELIVERED, STREAM_ENTRY_ID);

      return tempMappingFunctions;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }

      List list = new ArrayList<>();
      List streamsEntries = (List) data;
      Iterator groupsArray = streamsEntries.iterator();

      while (groupsArray.hasNext()) {

        List groupInfo = (List) groupsArray.next();

        Iterator groupInfoIterator = groupInfo.iterator();

        StreamGroupInfo streamGroupInfo = new StreamGroupInfo(createMapFromDecodingFunctions(
          groupInfoIterator, mappingFunctions));
        list.add(streamGroupInfo);

      }
      return list;

    }

    @Override
    public String toString() {
      return "List";
    }
  };

  /**
   * @deprecated Use {@link BuilderFactory#STREAM_CONSUMER_INFO_LIST}.
   */
  @Deprecated
  public static final Builder> STREAM_CONSUMERS_INFO_LIST
      = new Builder>() {

    Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {
      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(StreamConsumersInfo.NAME, STRING);
      tempMappingFunctions.put(StreamConsumersInfo.IDLE, LONG);
      tempMappingFunctions.put(StreamConsumersInfo.PENDING, LONG);
      return tempMappingFunctions;

    }

    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }

      List list = new ArrayList<>();
      List streamsEntries = (List) data;
      Iterator groupsArray = streamsEntries.iterator();

      while (groupsArray.hasNext()) {

        List groupInfo = (List) groupsArray.next();

        Iterator consumerInfoIterator = groupInfo.iterator();

        StreamConsumersInfo streamGroupInfo = new StreamConsumersInfo(
            createMapFromDecodingFunctions(consumerInfoIterator, mappingFunctions));
        list.add(streamGroupInfo);

      }
      return list;

    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder> STREAM_CONSUMER_INFO_LIST
      = new Builder>() {

    Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {
      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(StreamConsumerInfo.NAME, STRING);
      tempMappingFunctions.put(StreamConsumerInfo.IDLE, LONG);
      tempMappingFunctions.put(StreamConsumerInfo.PENDING, LONG);
      return tempMappingFunctions;

    }

    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }

      List list = new ArrayList<>();
      List streamsEntries = (List) data;
      Iterator groupsArray = streamsEntries.iterator();

      while (groupsArray.hasNext()) {

        List groupInfo = (List) groupsArray.next();

        Iterator consumerInfoIterator = groupInfo.iterator();

        StreamConsumerInfo streamConsumerInfo = new StreamConsumerInfo(
            createMapFromDecodingFunctions(consumerInfoIterator, mappingFunctions));
        list.add(streamConsumerInfo);
      }

      return list;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  private static final Builder> STREAM_CONSUMER_FULL_INFO_LIST
      = new Builder>() {

    final Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {

      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(StreamConsumerFullInfo.NAME, STRING);
      tempMappingFunctions.put(StreamConsumerFullInfo.SEEN_TIME, LONG);
      tempMappingFunctions.put(StreamConsumerFullInfo.PEL_COUNT, LONG);
      tempMappingFunctions.put(StreamConsumerFullInfo.PENDING, ENCODED_OBJECT_LIST);

      return tempMappingFunctions;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }

      List list = new ArrayList<>();
      List streamsEntries = (List) data;

      for (Object streamsEntry : streamsEntries) {
        List consumerInfoList = (List) streamsEntry;
        Iterator consumerInfoIterator = consumerInfoList.iterator();
        StreamConsumerFullInfo consumerInfo = new StreamConsumerFullInfo(
            createMapFromDecodingFunctions(consumerInfoIterator, mappingFunctions));
        list.add(consumerInfo);
      }
      return list;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  private static final Builder> STREAM_GROUP_FULL_INFO_LIST
      = new Builder>() {

    final Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {

      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(StreamGroupFullInfo.NAME, STRING);
      tempMappingFunctions.put(StreamGroupFullInfo.CONSUMERS, STREAM_CONSUMER_FULL_INFO_LIST);
      tempMappingFunctions.put(StreamGroupFullInfo.PENDING, ENCODED_OBJECT_LIST);
      tempMappingFunctions.put(StreamGroupFullInfo.LAST_DELIVERED, STREAM_ENTRY_ID);
      tempMappingFunctions.put(StreamGroupFullInfo.PEL_COUNT, LONG);

      return tempMappingFunctions;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List build(Object data) {
      if (null == data) {
        return null;
      }

      List list = new ArrayList<>();
      List streamsEntries = (List) data;

      for (Object streamsEntry : streamsEntries) {

        List groupInfo = (List) streamsEntry;

        Iterator groupInfoIterator = groupInfo.iterator();

        StreamGroupFullInfo groupFullInfo = new StreamGroupFullInfo(
            createMapFromDecodingFunctions(groupInfoIterator, mappingFunctions));
        list.add(groupFullInfo);

      }
      return list;
    }

    @Override
    public String toString() {
      return "List";
    }
  };

  public static final Builder STREAM_FULL_INFO = new Builder() {

    final Map mappingFunctions = createDecoderMap();

    private Map createDecoderMap() {

      Map tempMappingFunctions = new HashMap<>();
      tempMappingFunctions.put(StreamFullInfo.LAST_GENERATED_ID, STREAM_ENTRY_ID);
      tempMappingFunctions.put(StreamFullInfo.LENGTH, LONG);
      tempMappingFunctions.put(StreamFullInfo.RADIX_TREE_KEYS, LONG);
      tempMappingFunctions.put(StreamFullInfo.RADIX_TREE_NODES, LONG);
      tempMappingFunctions.put(StreamFullInfo.GROUPS, STREAM_GROUP_FULL_INFO_LIST);
      tempMappingFunctions.put(StreamFullInfo.ENTRIES, STREAM_ENTRY_LIST);

      return tempMappingFunctions;
    }

    @Override
    @SuppressWarnings("unchecked")
    public StreamFullInfo build(Object data) {
      if (null == data) {
        return null;
      }

      List streamsEntries = (List) data;
      Iterator iterator = streamsEntries.iterator();

      return new StreamFullInfo(createMapFromDecodingFunctions(iterator, mappingFunctions));
    }

    @Override
    public String toString() {
      return "StreamFullInfo";
    }
  };

  /**
   * @deprecated Use {@link BuilderFactory#STREAM_FULL_INFO}.
   */
  @Deprecated
  public static final Builder STREAM_INFO_FULL = STREAM_FULL_INFO;

  public static final Builder STREAM_PENDING_SUMMARY = new Builder() {
    @Override
    @SuppressWarnings("unchecked")
    public StreamPendingSummary build(Object data) {
      if (null == data) {
        return null;
      }

      List objectList = (List) data;
      long total = BuilderFactory.LONG.build(objectList.get(0));
      String minId = SafeEncoder.encode((byte[]) objectList.get(1));
      String maxId = SafeEncoder.encode((byte[]) objectList.get(2));
      List> consumerObjList = (List>) objectList.get(3);
      Map map = new HashMap<>(consumerObjList.size());
      for (List consumerObj : consumerObjList) {
        map.put(SafeEncoder.encode((byte[]) consumerObj.get(0)), Long.parseLong(SafeEncoder.encode((byte[]) consumerObj.get(1))));
      }
      return new StreamPendingSummary(total, new StreamEntryID(minId), new StreamEntryID(maxId), map);
    }

    @Override
    public String toString() {
      return "StreamPendingSummary";
    }
  };

  private static final List BACKUP_BUILDERS_FOR_DECODING_FUNCTIONS
      = Arrays.asList(STRING, LONG, DOUBLE);

  private static Map createMapFromDecodingFunctions(Iterator iterator,
      Map mappingFunctions) {
    return createMapFromDecodingFunctions(iterator, mappingFunctions, null);
  }

  private static Map createMapFromDecodingFunctions(Iterator iterator,
      Map mappingFunctions, Collection backupBuilders) {

    if (!iterator.hasNext()) {
      return Collections.emptyMap();
    }

    Map resultMap = new HashMap<>();
    while (iterator.hasNext()) {
      final Object tempObject = iterator.next();
      final String mapKey;
      final Object rawValue;

      if (tempObject instanceof KeyValue) {
        KeyValue kv = (KeyValue) tempObject;
        mapKey = STRING.build(kv.getKey());
        rawValue = kv.getValue();
      } else {
        mapKey = STRING.build(tempObject);
        rawValue = iterator.next();
      }

      if (mappingFunctions.containsKey(mapKey)) {
        resultMap.put(mapKey, mappingFunctions.get(mapKey).build(rawValue));
      } else { // For future - if we don't find an element in our builder map
        Collection builders = backupBuilders != null ? backupBuilders : mappingFunctions.values();
        for (Builder b : builders) {
          try {
            resultMap.put(mapKey, b.build(rawValue));
            break;
          } catch (ClassCastException e) {
            // We continue with next builder
          }
        }
      }
    }
    return resultMap;
  }

  // <-- Stream Builders

  public static final Builder STR_ALGO_LCS_RESULT_BUILDER = new Builder() {
    @Override
    public LCSMatchResult build(Object data) {
      if (data == null) {
        return null;
      }

      if (data instanceof byte[]) {
        return new LCSMatchResult(STRING.build(data));
      } else if (data instanceof Long) {
        return new LCSMatchResult(LONG.build(data));
      } else {
        long len = 0;
        List matchedPositions = new ArrayList<>();

        List objectList = (List) data;
        if (objectList.get(0) instanceof KeyValue) {
          Iterator iterator = objectList.iterator();
          while (iterator.hasNext()) {
            KeyValue kv = (KeyValue) iterator.next();
            if ("matches".equalsIgnoreCase(STRING.build(kv.getKey()))) {
              addMatchedPosition(matchedPositions, kv.getValue());
            } else if ("len".equalsIgnoreCase(STRING.build(kv.getKey()))) {
              len = LONG.build(kv.getValue());
            }
          }
        } else {
          for (int i = 0; i < objectList.size(); i += 2) {
            if ("matches".equalsIgnoreCase(STRING.build(objectList.get(i)))) {
              addMatchedPosition(matchedPositions, objectList.get(i + 1));
            } else if ("len".equalsIgnoreCase(STRING.build(objectList.get(i)))) {
              len = LONG.build(objectList.get(i + 1));
            }
          }
        }

        return new LCSMatchResult(matchedPositions, len);
      }
    }

    private void addMatchedPosition(List matchedPositions, Object o) {
      List matches = (List) o;
      for (Object obj : matches) {
        if (obj instanceof List) {
          List positions = (List) obj;
          Position a = new Position(
              LONG.build(((List) positions.get(0)).get(0)),
              LONG.build(((List) positions.get(0)).get(1))
          );
          Position b = new Position(
              LONG.build(((List) positions.get(1)).get(0)),
              LONG.build(((List) positions.get(1)).get(1))
          );
          long matchLen = 0;
          if (positions.size() >= 3) {
            matchLen = LONG.build(positions.get(2));
          }
          matchedPositions.add(new MatchedPosition(a, b, matchLen));
        }
      }
    }
  };

  public static final Builder> STRING_MAP_FROM_PAIRS = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Map build(Object data) {
      final List list = (List) data;
      if (list.isEmpty()) return Collections.emptyMap();

      if (list.get(0) instanceof KeyValue) {
        return ((List) list).stream()
            .collect(Collectors.toMap(kv -> STRING.build(kv.getKey()),
                kv -> STRING.build(kv.getValue())));
      }

      final Map map = new HashMap<>(list.size());
      for (Object object : list) {
        if (object == null) continue;
        final List flat = (List) object;
        if (flat.isEmpty()) continue;
        map.put(STRING.build(flat.get(0)), STRING.build(flat.get(1)));
      }
      return map;
    }

    @Override
    public String toString() {
      return "Map";
    }
  };

  public static final Builder> ENCODED_OBJECT_MAP_FROM_PAIRS = new Builder>() {
    @Override
    @SuppressWarnings("unchecked")
    public Map build(Object data) {
      final List list = (List) data;
      if (list.isEmpty()) return Collections.emptyMap();

      if (list.get(0) instanceof KeyValue) {
        return ((List) list).stream()
            .collect(Collectors.toMap(kv -> STRING.build(kv.getKey()),
                kv -> ENCODED_OBJECT.build(kv.getValue())));
      }

      final Map map = new HashMap<>(list.size());
      for (Object object : list) {
        if (object == null) continue;
        final List flat = (List) object;
        if (flat.isEmpty()) continue;
        map.put(STRING.build(flat.get(0)), STRING.build(flat.get(1)));
      }
      return map;
    }

    @Override
    public String toString() {
      return "Map";
    }
  };

  /**
   * @deprecated Use {@link LibraryInfo#LIBRARY_INFO_LIST}.
   */
  @Deprecated
  public static final Builder> LIBRARY_LIST = LibraryInfo.LIBRARY_INFO_LIST;

  public static final Builder>> STRING_LIST_LIST = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public List> build(Object data) {
      if (null == data) return null;
      return ((List) data).stream().map(STRING_LIST::build).collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List>";
    }
  };

  public static final Builder>> ENCODED_OBJECT_LIST_LIST = new Builder>>() {
    @Override
    @SuppressWarnings("unchecked")
    public List> build(Object data) {
      if (null == data) return null;
      return ((List) data).stream().map(ENCODED_OBJECT_LIST::build).collect(Collectors.toList());
    }

    @Override
    public String toString() {
      return "List>";
    }
  };

  /**
   * A decorator to implement Set from List. Assume that given List do not contains duplicated
   * values. The resulting set displays the same ordering, concurrency, and performance
   * characteristics as the backing list. This class should be used only for Redis commands which
   * return Set result.
   */
  protected static class SetFromList extends AbstractSet implements Serializable {
    private static final long serialVersionUID = -2850347066962734052L;
    private final List list;

    private SetFromList(List list) {
      this.list = list;
    }

    @Override
    public void clear() {
      list.clear();
    }

    @Override
    public int size() {
      return list.size();
    }

    @Override
    public boolean isEmpty() {
      return list.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
      return list.contains(o);
    }

    @Override
    public boolean remove(Object o) {
      return list.remove(o);
    }

    @Override
    public boolean add(E e) {
      return !contains(e) && list.add(e);
    }

    @Override
    public Iterator iterator() {
      return list.iterator();
    }

    @Override
    public Object[] toArray() {
      return list.toArray();
    }

    @Override
    public  T[] toArray(T[] a) {
      return list.toArray(a);
    }

    @Override
    public String toString() {
      return list.toString();
    }

    @Override
    public int hashCode() {
      return list.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o == null) return false;
      if (o == this) return true;
      if (!(o instanceof Set)) return false;

      Collection c = (Collection) o;
      if (c.size() != size()) {
        return false;
      }

      return containsAll(c);
    }

    @Override
    public boolean containsAll(Collection c) {
      return list.containsAll(c);
    }

    @Override
    public boolean removeAll(Collection c) {
      return list.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection c) {
      return list.retainAll(c);
    }

    protected static  SetFromList of(List list) {
      if (list == null) {
        return null;
      }
      return new SetFromList<>(list);
    }
  }

  private BuilderFactory() {
    throw new InstantiationError("Must not instantiate this class");
  }
}