package com.fiftyonred.mock_jedis;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.ArrayList;
import java.util.Collection;

import redis.clients.jedis.*;
import redis.clients.util.Pool;
import redis.clients.util.Slowlog;

import static redis.clients.jedis.Protocol.Keyword.MATCH;

public class MockJedis extends Jedis {

	public static final String NOT_IMPLEMENTED = "Not implemented";

	private final MockPipeline pipeline;

	public MockJedis(final String host) {
		super(host);
		pipeline = new MockPipeline();
	}

	@Override
	public Long getDB() {
		return (long)pipeline.getCurrentDB();
	}

	@Override
	public Long append(final String key, final String value) {
		return pipeline.append(key, value).get();
	}

	@Override
	public Long append(final byte[] key, final byte[] value) {
		return pipeline.append(key, value).get();
	}

	@Override
	public String getSet(final String key, final String value) {
		return pipeline.getSet(key, value).get();
	}

	@Override
	public byte[] getSet(final byte[] key, final byte[] value) {
		return pipeline.getSet(key, value).get();
	}

	@Override
	public String echo(final String string) {
		return pipeline.echo(string).get();
	}

	@Override
	public byte[] echo(final byte[] string) {
		return pipeline.echo(string).get();
	}

	@Override
	public String randomKey() {
		return pipeline.randomKey().get();
	}

	@Override
	public String rename(final String oldkey, final String newkey) {
		return pipeline.rename(oldkey, newkey).get();
	}

	@Override
	public String rename(final byte[] oldkey, final byte[] newkey) {
		return pipeline.rename(oldkey, newkey).get();
	}

	@Override
	public Long renamenx(final String oldkey, final String newkey) {
		return pipeline.renamenx(oldkey, newkey).get();
	}

	@Override
	public Long renamenx(final byte[] oldkey, final byte[] newkey) {
		return pipeline.renamenx(oldkey, newkey).get();
	}

	@Override
	public byte[] randomBinaryKey() {
		return pipeline.randomKeyBinary().get();
	}

	@Override
	public Long persist(final String key) {
		return pipeline.persist(key).get();
	}

	@Override
	public Long persist(final byte[] key) {
		return pipeline.persist(key).get();
	}

	@Override
	public byte[] get(final byte[] key) {
		return pipeline.get(key).get();
	}

	@Override
	public byte[] dump(final byte[] key) {
		return pipeline.dump(key).get();
	}

	@Override
	public byte[] dump(final String key) {
		return pipeline.dump(key).get();
	}

	@Override
	public String restore(final byte[] key, final int ttl, final byte[] serializedValue) {
		return pipeline.restore(key, ttl, serializedValue).get();
	}

	@Override
	public String restore(final String key, final int ttl, final byte[] serializedValue) {
		return pipeline.restore(key, ttl, serializedValue).get();
	}

	@Override
	public Long del(final byte[]... keys) {
		return pipeline.del(keys).get();
	}

	@Override
	public String flushDB() {
		return pipeline.flushDB().get();
	}

	@Override
	public Set<byte[]> keys(final byte[] pattern) {
		return pipeline.keys(pattern).get();
	}

	@Override
	public List<byte[]> mget(final byte[]... keys) {
		return pipeline.mget(keys).get();
	}

	@Override
	public String mset(final String... keysvalues) {
		return pipeline.mset(keysvalues).get();
	}

	@Override
	public String mset(final byte[]... keysvalues) {
		return pipeline.mset(keysvalues).get();
	}

	@Override
	public Long msetnx(final String... keysvalues) {
		return pipeline.msetnx(keysvalues).get();
	}

	@Override
	public Long msetnx(final byte[]... keysvalues) {
		return pipeline.msetnx(keysvalues).get();
	}

	@Override
	public Long incrBy(final byte[] key, final long integer) {
		return pipeline.incrBy(key, integer).get();
	}

	@Override
	public Double incrByFloat(final String key, final double integer) {
		return pipeline.incrByFloat(key, integer).get();
	}

	@Override
	public Double incrByFloat(final byte[] key, final double integer) {
		return pipeline.incrByFloat(key, integer).get();
	}

	@Override
	public Long strlen(final String key) {
		return pipeline.strlen(key).get();
	}

	@Override
	public Long strlen(final byte[] key) {
		return pipeline.strlen(key).get();
	}

	@Override
	public Long incr(final byte[] key) {
		return pipeline.incr(key).get();
	}

	@Override
	public Long decr(final byte[] key) {
		return pipeline.decr(key).get();
	}

	@Override
	public Long decrBy(final byte[] key, final long integer) {
		return pipeline.decrBy(key, integer).get();
	}

	@Override
	public List<String> sort(final String key) {
		return pipeline.sort(key).get();
	}

	@Override
	public List<byte[]> sort(final byte[] key) {
		return pipeline.sort(key).get();
	}

	@Override
	public Long sort(final String key, final String dstkey) {
		return pipeline.sort(key, dstkey).get();
	}

	@Override
	public Long sort(final byte[] key, final byte[] dstkey) {
		return pipeline.sort(key, dstkey).get();
	}

	@Override
	public List<String> sort(final String key, final SortingParams sortingParameters) {
		return pipeline.sort(key, sortingParameters).get();
	}

	@Override
	public List<byte[]> sort(final byte[] key, final SortingParams sortingParameters) {
		return pipeline.sort(key, sortingParameters).get();
	}

	@Override
	public Long sort(final String key, final SortingParams sortingParameters, final String dstkey) {
		return pipeline.sort(key, sortingParameters, dstkey).get();
	}

	@Override
	public Long sort(final byte[] key, final SortingParams sortingParameters, final byte[] dstkey) {
		return pipeline.sort(key, sortingParameters, dstkey).get();
	}

	@Override
	public Long hset(final byte[] key, final byte[] field, final byte[] value) {
		return pipeline.hset(key, field, value).get();
	}

	@Override
	public byte[] hget(final byte[] key, final byte[] field) {
		return pipeline.hget(key, field).get();
	}

	@Override
	public String hmset(final byte[] key, final Map<byte[], byte[]> hash) {
		return pipeline.hmset(key, hash).get();
	}

	@Override
	public List<byte[]> hmget(final byte[] key, final byte[]... fields) {
		return pipeline.hmget(key, fields).get();
	}

	@Override
	public Long hincrBy(final byte[] key, final byte[] field, final long value) {
		return pipeline.hincrBy(key, field, value).get();
	}

	@Override
	public Boolean hexists(final byte[] key, final byte[] field) {
		return pipeline.hexists(key, field).get();
	}

	@Override
	public Long hdel(final byte[] key, final byte[]... fields) {
		return pipeline.hdel(key, fields).get();
	}

	@Override
	public Long hlen(final byte[] key) {
		return pipeline.hlen(key).get();
	}

	@Override
	public Set<byte[]> hkeys(final byte[] key) {
		return pipeline.hkeys(key).get();
	}

	@Override
	public List<byte[]> hvals(final byte[] key) {
		return pipeline.hvals(key).get();
	}

	@Override
	public Map<byte[], byte[]> hgetAll(final byte[] key) {
		return pipeline.hgetAll(key).get();
	}

	@Override
	public String ping() {
		return pipeline.ping().get();
	}

	@Override
	public Long dbSize() {
		return pipeline.dbSize().get();
	}

	@Override
	public String type(final String key) {
		return pipeline.type(key).get();
	}

	@Override
	public String type(final byte[] key) {
		return pipeline.type(key).get();
	}

	@Override
	public Long move(final String key, final int dbIndex) {
		return pipeline.move(key, dbIndex).get();
	}

	@Override
	public Long move(final byte[] key, final int dbIndex) {
		return pipeline.move(key, dbIndex).get();
	}

	@Override
	public String select(final int dbIndex) {
		return pipeline.select(dbIndex).get();
	}

	@Override
	public String set(final String key, final String value) {
		return pipeline.set(key, value).get();
	}

	@Override
	public String set(final byte[] key, final byte[] value) {
		return pipeline.set(key, value).get();
	}

	@Override
	public Long setnx(final String key, final String value) {
		return pipeline.setnx(key, value).get();
	}

	@Override
	public Long setnx(final byte[] key, final byte[] value) {
		return pipeline.setnx(key, value).get();
	}

	@Override
	public String get(final String key) {
		return pipeline.get(key).get();
	}

	@Override
	public List<String> mget(final String... keys) {
		return pipeline.mget(keys).get();
	}

	@Override
	public String flushAll() {
		return pipeline.flushAll().get();
	}

	@Override
	public Long decrBy(final String key, final long integer) {
		return pipeline.decrBy(key, integer).get();
	}

	@Override
	public Long decr(final String key) {
		return pipeline.decr(key).get();
	}

	@Override
	public Long expire(final String key, final int seconds) {
		return pipeline.expire(key, seconds).get();
	}

	@Override
	public Long expire(final byte[] key, final int seconds) {
		return pipeline.expire(key, seconds).get();
	}

	@Override
	public Long expireAt(final String key, final long unixTime) {
		return pipeline.expireAt(key, unixTime).get();
	}

	@Override
	public Long expireAt(final byte[] key, final long unixTime) {
		return pipeline.expireAt(key, unixTime).get();
	}

	@Override
	public String psetex(final String key, final int milliseconds, final String value) {
		return pipeline.psetex(key, milliseconds, value).get();
	}

	@Override
	public String set(final String key, final String value, final String nxxx) {
		return pipeline.set(key, value, nxxx).get();
	}

	@Override
	public String set(final String key, final String value, final String nxxx, final String expx, final int time) {
		return pipeline.set(key, value, nxxx, expx, time).get();
	}

	@Override
	public String migrate(final String host, final int port, final String key, final int destinationDb, final int timeout) {
		return pipeline.migrate(host, port, key, destinationDb, timeout).get();
	}

	@Override
	public String psetex(final byte[] key, final int milliseconds, final byte[] value) {
		return pipeline.psetex(key, milliseconds, value).get();
	}

	@Override
	public Long ttl(final String key) {
		return pipeline.ttl(key).get();
	}

	@Override
	public Long ttl(final byte[] key) {
		return pipeline.ttl(key).get();
	}

	@Override
	public Long pttl(final String key) {
		return pipeline.pttl(key).get();
	}

	@Override
	public Long pttl(final byte[] key) {
		return pipeline.pttl(key).get();
	}

	@Override
	public Long pexpire(final String key, final int milliseconds) {
		return pipeline.pexpire(key, milliseconds).get();
	}

	@Override
	public Long pexpire(final String key, final long milliseconds) {
		return pipeline.pexpire(key, milliseconds).get();
	}

	@Override
	public Long pexpire(final byte[] key, final int milliseconds) {
		return pipeline.pexpire(key, milliseconds).get();
	}

	@Override
	public Long pexpireAt(final String key, final long millisecondsTimestamp) {
		return pipeline.pexpireAt(key, millisecondsTimestamp).get();
	}

	@Override
	public Long pexpireAt(final byte[] key, final long millisecondsTimestamp) {
		return pipeline.pexpireAt(key, millisecondsTimestamp).get();
	}

	@Override
	public Boolean exists(final String key) {
		return pipeline.exists(key).get();
	}

	@Override
	public Boolean exists(final byte[] key) {
		return pipeline.exists(key).get();
	}

	@Override
	public Long incr(final String key) {
		return pipeline.incr(key).get();
	}

	@Override
	public Long incrBy(final String key, final long integer) {
		return pipeline.incrBy(key, integer).get();
	}

	@Override
	public String setex(final String key, final int seconds, final String value) {
		return pipeline.setex(key, seconds, value).get();
	}

	@Override
	public String setex(final byte[] key, final int seconds, final byte[] value) {
		return pipeline.setex(key, seconds, value).get();
	}

	@Override
	public Long del(final String... keys) {
		return pipeline.del(keys).get();
	}

	@Override
	public Long del(final String key) {
		return pipeline.del(key).get();
	}

	@Override
	public Long del(final byte[] key) {
		return pipeline.del(key).get();
	}

	@Override
	public Long hset(final String key, final String field, final String value) {
		return pipeline.hset(key, field, value).get();
	}

	@Override
	public Long hsetnx(final String key, final String field, final String value) {
		return pipeline.hsetnx(key, field, value).get();
	}

	@Override
	public Long hsetnx(final byte[] key, final byte[] field, final byte[] value) {
		return pipeline.hsetnx(key, field, value).get();
	}

	@Override
	public String hget(final String key, final String field) {
		return pipeline.hget(key, field).get();
	}

	@Override
	public Map<String, String> hgetAll(final String key) {
		return pipeline.hgetAll(key).get();
	}

	@Override
	public Set<String> hkeys(final String key) {
		return pipeline.hkeys(key).get();
	}

	@Override
	public List<String> hvals(final String key) {
		return pipeline.hvals(key).get();
	}

	@Override
	public String hmset(final String key, final Map<String, String> hash) {
		return pipeline.hmset(key, hash).get();
	}

	@Override
	public List<String> hmget(final String key, final String... fields) {
		return pipeline.hmget(key, fields).get();
	}

	@Override
	public Long hincrBy(final String key, final String field, final long value) {
		return pipeline.hincrBy(key, field, value).get();
	}

	@Override
	public Double hincrByFloat(final String key, final String field, final double value) {
		return pipeline.hincrByFloat(key, field, value).get();
	}

	@Override
	public Long hdel(final String key, final String... fields) {
		return pipeline.hdel(key, fields).get();
	}

	@Override
	public Boolean hexists(final String key, final String field) {
		return pipeline.hexists(key, field).get();
	}

	@Override
	public Long hlen(final String key) {
		return pipeline.hlen(key).get();
	}

	@Override
	public Long lpush(final String key, final String... strings) {
		return pipeline.lpush(key, strings).get();
	}

	@Override
	public Long lpush(final byte[] key, final byte[]... strings) {
		return pipeline.lpush(key, strings).get();
	}

	@Override
	public Long sadd(final String key, final String... members) {
		return pipeline.sadd(key, members).get();
	}

	@Override
	public Long sadd(final byte[] key, final byte[]... members) {
		return pipeline.sadd(key, members).get();
	}

	@Override
	public Long scard(final String key) {
		return pipeline.scard(key).get();
	}

	@Override
	public Long scard(final byte[] key) {
		return pipeline.scard(key).get();
	}

	@Override
	public Set<String> sdiff(final String... keys) {
		return pipeline.sdiff(keys).get();
	}

	@Override
	public Set<byte[]> sdiff(final byte[]... keys) {
		return pipeline.sdiff(keys).get();
	}

	@Override
	public Long sdiffstore(final String dstKey, final String... keys) {
		return pipeline.sdiffstore(dstKey, keys).get();
	}

	@Override
	public Long sdiffstore(final byte[] dstKey, final byte[]... keys) {
		return pipeline.sdiffstore(dstKey, keys).get();
	}

	@Override
	public Set<String> sinter(final String... keys) {
		return pipeline.sinter(keys).get();
	}

	@Override
	public Set<byte[]> sinter(final byte[]... keys) {
		return pipeline.sinter(keys).get();
	}

	@Override
	public Long sinterstore(final String dstKey, final String... keys) {
		return pipeline.sinterstore(dstKey, keys).get();
	}

	@Override
	public Long sinterstore(final byte[] dstKey, final byte[]... keys) {
		return pipeline.sinterstore(dstKey, keys).get();
	}

	@Override
	public Boolean sismember(final String key, final String member) {
		return pipeline.sismember(key, member).get();
	}

	@Override
	public Boolean sismember(final byte[] key, final byte[] member) {
		return pipeline.sismember(key, member).get();
	}

	@Override
	public Set<String> smembers(final String key) {
		return pipeline.smembers(key).get();
	}

	@Override
	public Set<byte[]> smembers(final byte[] key) {
		return pipeline.smembers(key).get();
	}

	@Override
	public Long smove(final String srcKey, final String dstKey, final String member) {
		return pipeline.smove(srcKey, dstKey, member).get();
	}

	@Override
	public Long smove(final byte[] srcKey, final byte[] dstKey, final byte[] member) {
		return pipeline.smove(srcKey, dstKey, member).get();
	}

	@Override
	public String spop(final String key) {
		return pipeline.spop(key).get();
	}

	@Override
	public byte[] spop(final byte[] key) {
		return pipeline.spop(key).get();
	}

	@Override
	public String srandmember(final String key) {
		return pipeline.srandmember(key).get();
	}

	@Override
	public byte[] srandmember(final byte[] key) {
		return pipeline.srandmember(key).get();
	}

	@Override
	public Long srem(final String key, final String... members) {
		return pipeline.srem(key, members).get();
	}

	@Override
	public Long srem(final byte[] key, final byte[]... members) {
		return pipeline.srem(key, members).get();
	}

	@Override
	public Set<String> sunion(final String... keys) {
		return pipeline.sunion(keys).get();
	}

	@Override
	public Set<byte[]> sunion(final byte[]... keys) {
		return pipeline.sunion(keys).get();
	}

	@Override
	public Long sunionstore(final String dstKey, final String... keys) {
		return pipeline.sunionstore(dstKey, keys).get();
	}

	@Override
	public Long sunionstore(final byte[] dstKey, final byte[]... keys) {
		return pipeline.sunionstore(dstKey, keys).get();
	}

	@Override
	public String lpop(final String key) {
		return pipeline.lpop(key).get();
	}

	@Override
	public byte[] lpop(final byte[] key) {
		return pipeline.lpop(key).get();
	}

	@Override
	public Long llen(final String key) {
		return pipeline.llen(key).get();
	}

	@Override
	public Long llen(final byte[] key) {
		return pipeline.llen(key).get();
	}

	@Override
	public List<String> lrange(final String key, final long start, final long end) {
		return pipeline.lrange(key, start, end).get();
	}

	@Override
	public List<byte[]> lrange(final byte[] key, final long start, final long end) {
		return pipeline.lrange(key, start, end).get();
	}

	@Override
	public Pipeline pipelined() {
		return pipeline;
	}

	@Override
	public Set<String> keys(final String pattern) {
		return pipeline.keys(pattern).get();
	}

	@Override
	public void connect() {
		// do nothing
	}

	@Override
	public void disconnect() {
		// do nothing
	}

	@Override
	public String quit() {
		return "OK";
	}

	// ************************************************************************************
	// Unsupported operations
	// ************************************************************************************

	@Override
	public ScanResult<Tuple> zscan(byte[] key, byte[] cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String set(String key, String value, String nxxx, String expx, long time) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String substr(String key, int start, int end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long rpush(String key, String... strings) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String ltrim(String key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String lindex(String key, long index) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String lset(String key, long index, String value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long lrem(String key, long count, String value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String rpop(String key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String rpoplpush(String srckey, String dstkey) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> srandmember(String key, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zadd(String key, double score, String member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zadd(String key, Map<String, Double> scoreMembers) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrange(String key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zrem(String key, String... members) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Double zincrby(String key, double score, String member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zrank(String key, String member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zrevrank(String key, String member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrevrange(String key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeWithScores(String key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeWithScores(String key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zcard(String key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Double zscore(String key, String member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String watch(String... keys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> blpop(int timeout, String... keys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> blpop(String... args) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> brpop(String... args) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> blpop(String arg) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> brpop(String arg) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> brpop(int timeout, String... keys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zcount(String key, double min, double max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zcount(String key, String min, String max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrangeByScore(String key, double min, double max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrangeByScore(String key, String min, String max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrangeByScore(String key, double min, double max, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrangeByScore(String key, String min, String max, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeByScoreWithScores(String key, double min, double max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeByScoreWithScores(String key, String min, String max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeByScoreWithScores(String key, double min, double max, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeByScoreWithScores(String key, String min, String max, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrevrangeByScore(String key, double max, double min) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrevrangeByScore(String key, String max, String min) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrevrangeByScore(String key, double max, double min, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeByScoreWithScores(String key, double max, double min) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<String> zrevrangeByScore(String key, String max, String min, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeByScoreWithScores(String key, String max, String min) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zremrangeByRank(String key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zremrangeByScore(String key, double start, double end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zremrangeByScore(String key, String start, String end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zunionstore(String dstkey, String... sets) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zunionstore(String dstkey, ZParams params, String... sets) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zinterstore(String dstkey, String... sets) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zinterstore(String dstkey, ZParams params, String... sets) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long lpushx(String key, String... string) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long rpushx(String key, String... string) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long linsert(String key, BinaryClient.LIST_POSITION where, String pivot, String value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String brpoplpush(String source, String destination, int timeout) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Boolean setbit(String key, long offset, boolean value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Boolean setbit(String key, long offset, String value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Boolean getbit(String key, long offset) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long setrange(String key, long offset, String value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String getrange(String key, long startOffset, long endOffset) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitpos(String key, boolean value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitpos(String key, boolean value, BitPosParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> configGet(String pattern) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String configSet(String parameter, String value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object eval(String script, int keyCount, String... params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public void subscribe(JedisPubSub jedisPubSub, String... channels) {
		super.subscribe(jedisPubSub, channels);
	}

	@Override
	public Long publish(String channel, String message) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public void psubscribe(JedisPubSub jedisPubSub, String... patterns) {
		super.psubscribe(jedisPubSub, patterns);
	}

	@Override
	public Object eval(String script, List<String> keys, List<String> args) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object eval(String script) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object evalsha(String script) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object evalsha(String sha1, List<String> keys, List<String> args) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object evalsha(String sha1, int keyCount, String... params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Boolean scriptExists(String sha1) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<Boolean> scriptExists(String... sha1) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String scriptLoad(String script) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<Slowlog> slowlogGet() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<Slowlog> slowlogGet(long entries) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long objectRefcount(String string) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String objectEncoding(String string) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long objectIdletime(String string) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitcount(String key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitcount(String key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitop(BitOP op, String destKey, String... srcKeys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<Map<String, String>> sentinelMasters() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> sentinelGetMasterAddrByName(String masterName) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long sentinelReset(String pattern) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<Map<String, String>> sentinelSlaves(String masterName) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String sentinelFailover(String masterName) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String sentinelMonitor(String masterName, String ip, int port, int quorum) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String sentinelRemove(String masterName) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String sentinelSet(String masterName, Map<String, String> parameterMap) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clientKill(String client) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clientSetname(String name) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<String> scan(int cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<String> scan(int cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Map.Entry<String, String>> hscan(String key, int cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Map.Entry<String, String>> hscan(String key, int cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<String> sscan(String key, int cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<String> sscan(String key, int cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Tuple> zscan(String key, int cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Tuple> zscan(String key, int cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<String> scan(String cursor) {
		return scan(cursor, (new ScanParams()).match("*"));
	}

	/**
	 * In this simple proposal, we're not testing complex iterations
	 * of scan cursor. SCAN is simply a wrapper for KEYS, and the result
	 * is given in one single response, no matter the COUNT argument.
	 */
	@Override
	public ScanResult<String> scan(String cursor, ScanParams params) {
		// We need to extract the MATCH argument from the scan params
		Collection<byte[]> rawParams = params.getParams();

		// Raw collection is a list of byte[], made of: key1, value1, key2, value2, etc.
		boolean isKey = true;
		String match = null;
		boolean foundMatchKey = false;

		// So, we run over the list, where any even index is a key, and the following data is its value.
		for (byte[] raw : rawParams) {
			if (isKey) {
				String key = new String(raw);
				if (key.equals(new String(MATCH.raw))) {
					// What really interests us is the MATCH key.
					foundMatchKey = true;
				}
			}
			// As soon as we've found the MATCH key, we can stop searching.
			else if (foundMatchKey) {
				match = new String(raw);
				break;
			}
			isKey = !isKey;
		}

		// Our simple implementation of SCAN is really a plain wrapper for KEYS,
		// relying on the current mock implementation of the pattern search.
		return new ScanResult<String>("0", new ArrayList<String>(keys(match)));
	}

	@Override
	public ScanResult<Map.Entry<String, String>> hscan(String key, String cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Map.Entry<String, String>> hscan(String key, String cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<String> sscan(String key, String cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<String> sscan(String key, String cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Tuple> zscan(String key, String cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Tuple> zscan(String key, String cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterNodes() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterMeet(String ip, int port) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterAddSlots(int... slots) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterDelSlots(int... slots) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterInfo() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> clusterGetKeysInSlot(int slot, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterSetSlotNode(int slot, String nodeId) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterSetSlotMigrating(int slot, String nodeId) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterSetSlotImporting(int slot, String nodeId) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterSetSlotStable(int slot) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterForget(String nodeId) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterFlushSlots() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long clusterKeySlot(String key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long clusterCountKeysInSlot(int slot) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterSaveConfig() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterReplicate(String nodeId) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> clusterSlaves(String nodeId) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clusterFailover() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String asking() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> pubsubChannels(String pattern) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long pubsubNumPat() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Map<String, String> pubsubNumSub(String... channels) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public void close() {
		super.close();
	}

	@Override
	public void setDataSource(Pool<Jedis> jedisPool) {
		super.setDataSource(jedisPool);
	}

	@Override
	public Long pfadd(String key, String... elements) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public long pfcount(String key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public long pfcount(String... keys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String pfmerge(String destkey, String... sourcekeys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String set(byte[] key, byte[] value, byte[] nxxx, byte[] expx, long time) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] substr(byte[] key, int start, int end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Double hincrByFloat(byte[] key, byte[] field, double value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long rpush(byte[] key, byte[]... strings) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String ltrim(byte[] key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] lindex(byte[] key, long index) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String lset(byte[] key, long index, byte[] value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long lrem(byte[] key, long count, byte[] value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] rpop(byte[] key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] rpoplpush(byte[] srckey, byte[] dstkey) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> srandmember(byte[] key, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zadd(byte[] key, double score, byte[] member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zadd(byte[] key, Map<byte[], Double> scoreMembers) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrange(byte[] key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zrem(byte[] key, byte[]... members) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Double zincrby(byte[] key, double score, byte[] member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zrank(byte[] key, byte[] member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zrevrank(byte[] key, byte[] member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrevrange(byte[] key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeWithScores(byte[] key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeWithScores(byte[] key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zcard(byte[] key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Double zscore(byte[] key, byte[] member) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Transaction multi() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<Object> multi(TransactionBlock jedisTransaction) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	protected void checkIsInMulti() {
		super.checkIsInMulti();
	}

	@Override
	public void resetState() {
		super.resetState();
	}

	@Override
	public String watch(byte[]... keys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String unwatch() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> blpop(int timeout, byte[]... keys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> brpop(int timeout, byte[]... keys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> blpop(byte[] arg) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> brpop(byte[] arg) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> blpop(byte[]... args) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> brpop(byte[]... args) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String auth(String password) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<Object> pipelined(PipelineBlock jedisPipeline) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zcount(byte[] key, double min, double max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zcount(byte[] key, byte[] min, byte[] max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrangeByScore(byte[] key, double min, double max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrangeByScore(byte[] key, byte[] min, byte[] max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrangeByScore(byte[] key, double min, double max, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeByScoreWithScores(byte[] key, double min, double max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrevrangeByScore(byte[] key, double max, double min) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrevrangeByScore(byte[] key, byte[] max, byte[] min) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrevrangeByScore(byte[] key, double max, double min, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<byte[]> zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeByScoreWithScores(byte[] key, double max, double min) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Set<Tuple> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zremrangeByRank(byte[] key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zremrangeByScore(byte[] key, double start, double end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zremrangeByScore(byte[] key, byte[] start, byte[] end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zunionstore(byte[] dstkey, byte[]... sets) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zunionstore(byte[] dstkey, ZParams params, byte[]... sets) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zinterstore(byte[] dstkey, byte[]... sets) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long zinterstore(byte[] dstkey, ZParams params, byte[]... sets) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String save() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String bgsave() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String bgrewriteaof() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long lastsave() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String shutdown() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String info() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String info(String section) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public void monitor(JedisMonitor jedisMonitor) {
		super.monitor(jedisMonitor);
	}

	@Override
	public String slaveof(String host, int port) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String slaveofNoOne() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> configGet(byte[] pattern) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String configResetStat() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] configSet(byte[] parameter, byte[] value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public boolean isConnected() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public void sync() {
		super.sync();
	}

	@Override
	public Long lpushx(byte[] key, byte[]... string) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long rpushx(byte[] key, byte[]... string) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long linsert(byte[] key, BinaryClient.LIST_POSITION where, byte[] pivot, byte[] value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String debug(DebugParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Client getClient() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] brpoplpush(byte[] source, byte[] destination, int timeout) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Boolean setbit(byte[] key, long offset, boolean value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Boolean setbit(byte[] key, long offset, byte[] value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Boolean getbit(byte[] key, long offset) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitpos(byte[] key, boolean value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitpos(byte[] key, boolean value, BitPosParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long setrange(byte[] key, long offset, byte[] value) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] getrange(byte[] key, long startOffset, long endOffset) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long publish(byte[] channel, byte[] message) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public void subscribe(BinaryJedisPubSub jedisPubSub, byte[]... channels) {
		super.subscribe(jedisPubSub, channels);
	}

	@Override
	public void psubscribe(BinaryJedisPubSub jedisPubSub, byte[]... patterns) {
		super.psubscribe(jedisPubSub, patterns);
	}

	@Override
	public Object eval(byte[] script, List<byte[]> keys, List<byte[]> args) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object eval(byte[] script, byte[] keyCount, byte[]... params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object eval(byte[] script, int keyCount, byte[]... params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object eval(byte[] script) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object evalsha(byte[] sha1) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object evalsha(byte[] sha1, List<byte[]> keys, List<byte[]> args) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Object evalsha(byte[] sha1, int keyCount, byte[]... params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String scriptFlush() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<Long> scriptExists(byte[]... sha1) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] scriptLoad(byte[] script) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String scriptKill() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String slowlogReset() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long slowlogLen() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> slowlogGetBinary() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<byte[]> slowlogGetBinary(long entries) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long objectRefcount(byte[] key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public byte[] objectEncoding(byte[] key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long objectIdletime(byte[] key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitcount(byte[] key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitcount(byte[] key, long start, long end) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long bitop(BitOP op, byte[] destKey, byte[]... srcKeys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long pexpire(byte[] key, long milliseconds) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String set(byte[] key, byte[] value, byte[] nxxx) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String set(byte[] key, byte[] value, byte[] nxxx, byte[] expx, int time) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clientKill(byte[] client) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clientGetname() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clientList() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String clientSetname(byte[] name) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public List<String> time() {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String migrate(byte[] host, int port, byte[] key, int destinationDb, int timeout) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long waitReplicas(int replicas, long timeout) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long pfadd(byte[] key, byte[]... elements) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public long pfcount(byte[] key) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public String pfmerge(byte[] destkey, byte[]... sourcekeys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public Long pfcount(byte[]... keys) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<byte[]> scan(byte[] cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<byte[]> scan(byte[] cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Map.Entry<byte[], byte[]>> hscan(byte[] key, byte[] cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Map.Entry<byte[], byte[]>> hscan(byte[] key, byte[] cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<byte[]> sscan(byte[] key, byte[] cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<byte[]> sscan(byte[] key, byte[] cursor, ScanParams params) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}

	@Override
	public ScanResult<Tuple> zscan(byte[] key, byte[] cursor) {
		throw new UnsupportedOperationException(NOT_IMPLEMENTED);
	}
}


package com.fiftyonred.utils;

import java.util.regex.Pattern;

public class WildcardMatcher {
	private final Pattern REGEX_STAR = Pattern.compile("\\*");

	/**
	 * Performs a wildcard matching for the text and pattern
	 * provided.
	 * Source: http://www.adarshr.com/papers/wildcard
	 *
	 * @param text    the text to be tested for matches.
	 * @param pattern the pattern to be matched for.
	 *                This can contain the wildcard character '*' (asterisk).
	 * @return <tt>true</tt> if a match is found, <tt>false</tt>
	 * otherwise.
	 */
	public boolean match(String text, final String pattern) {
		// Create the cards by splitting using a RegEx. If more speed
		// is desired, a simpler character based splitting can be done.
		final String[] cards = REGEX_STAR.split(pattern);
		final int numCards = cards.length;

		// Iterate over the cards.
		for (int i = 0; i < numCards; ++i) {
			final String card = cards[i];
			final int idx = text.indexOf(card);

			// Card not detected in the text.
			if (idx == -1) {
				return false;
			}
			if (idx != 0 && i == 0) {
				return false; // test needs to start from 'card'
			}

			// Move ahead, towards the right of the text.
			text = text.substring(idx + card.length());
		}

		return true;
	}
}


package io.cucumber.pro.environment;

import java.util.Map;

public class CIEnvironment {

    private final String ciName;
    private final String revision;
    private final String branch;
    private final String tag;
    private final String projectName;

    CIEnvironment(String ciName, String revision, String branch, String tag, String projectName) {
        this.ciName = ciName;
        this.revision = revision;
        this.branch = branch;
        this.tag = tag;
        this.projectName = projectName;
    }

    public static CIEnvironment detect(Map<String, String> env) {
        CIEnvironment ci;
        if ((ci = detectBamboo(env)) != null) return ci;
        if ((ci = detectCircle(env)) != null) return ci;
        if ((ci = detectJenkins(env)) != null) return ci;
        if ((ci = detectTfs(env)) != null) return ci;
        if ((ci = detectTravis(env)) != null) return ci;
        if ((ci = detectWercker(env)) != null) return ci;
        return null;
    }

    // https://confluence.atlassian.com/bamboo/bamboo-variables-289277087.html
    private static CIEnvironment detectBamboo(Map<String, String> env) {
        String revision = env.get("bamboo_planRepository_revision");
        String branch = env.get("bamboo_repository_git_branch");
        if (revision == null || branch == null) return null;
        String tag = null;
        String projectName = env.get("bamboo_planRepository_name");
        return new CIEnvironment(
                "Bamboo",
                revision,
                branch,
                tag,
                projectName
        );
    }

    // https://circleci.com/docs/2.0/env-vars/#circleci-environment-variable-descriptions
    private static CIEnvironment detectCircle(Map<String, String> env) {
        String revision = env.get("CIRCLE_SHA1");
        String branch = env.get("CIRCLE_BRANCH");
        if (revision == null || branch == null) return null;
        String tag = env.get("CIRCLE_TAG");
        String projectName = env.get("CIRCLE_PROJECT_REPONAME");
        return new CIEnvironment(
                "Circle CI",
                revision,
                branch,
                tag,
                projectName
        );
    }

    // https://wiki.jenkins.io/display/JENKINS/Git+Plugin#GitPlugin-Environmentvariables
    private static CIEnvironment detectJenkins(Map<String, String> env) {
        String revision = env.get("GIT_COMMIT");
        String branch = env.get("GIT_BRANCH");
        if (revision == null || branch == null) return null;
        // https://wiki.jenkins.io/display/JENKINS/Git+Tag+Message+Plugin
        String tag = env.get("GIT_TAG_NAME");
        String projectName = null;
        return new CIEnvironment(
                "Jenkins",
                revision,
                branch,
                tag,
                projectName
        );
    }

    // https://docs.microsoft.com/en-us/vsts/build-release/concepts/definitions/build/variables?tabs=batch#predefined-variables
    private static CIEnvironment detectTfs(Map<String, String> env) {
        String revision = env.get("BUILD_SOURCEVERSION");
        String branch = env.get("BUILD_SOURCEBRANCHNAME");
        if (revision == null || branch == null) return null;
        String tag = null;
        String projectName = env.get("SYSTEM_TEAMPROJECT");
        return new CIEnvironment(
                "TFS",
                revision,
                branch,
                tag,
                projectName
        );
    }

    // https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
    private static CIEnvironment detectTravis(Map<String, String> env) {
        String revision = env.get("TRAVIS_COMMIT");
        String branch = env.get("TRAVIS_BRANCH");
        if (revision == null || branch == null) return null;
        String tag = null;
        String repoSlug = env.get("TRAVIS_REPO_SLUG");
        String projectName = repoSlug.split("/")[1];
        return new CIEnvironment(
                "Travis CI",
                revision,
                branch,
                tag,
                projectName
        );
    }

    // http://devcenter.wercker.com/docs/environment-variables/available-env-vars
    private static CIEnvironment detectWercker(Map<String, String> env) {
        String revision = env.get("WERCKER_GIT_COMMIT");
        String branch = env.get("WERCKER_GIT_BRANCH");
        if (revision == null || branch == null) return null;
        String tag = null;
        String projectName = env.get("WERCKER_GIT_REPOSITORY");
        return new CIEnvironment(
                "Wercker",
                revision,
                branch,
                tag,
                projectName
        );
    }

    public String getCiName() {
        return ciName;
    }

    public String getRevision() {
        return revision;
    }

    public String getBranch() {
        return branch;
    }

    public String getTag() {
        return tag;
    }

    public String getProjectName() {
        return projectName;
    }

}


package io.cucumber.pro.config;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

/**
 * Nested configuration. Keys are hierarchical.
 */
public class Config {
    private final Map<String, Value> valueByProperty = new TreeMap<>();
    private final Map<String, Config> configByProperty = new TreeMap<>();

    public String getString(String key) {
        return getIn(normalize(key), false).getString();
    }

    public Boolean getBoolean(String key) {
        return getIn(normalize(key), false).getBoolean();
    }

    public Integer getInteger(String key) {
        return getIn(normalize(key), false).getInt();
    }

    public boolean isNull(String key) {
        return getIn(normalize(key), true).isNull();
    }

    public void setNull(String key) {
        setIn(normalize(key), new NullValue());
    }

    public void set(String key, String value) {
        setIn(normalize(key), RealValue.fromString(value));
    }

    public void set(String key, int value) {
        setIn(normalize(key), RealValue.fromInteger(value));
    }

    public void set(String key, boolean value) {
        setIn(normalize(key), RealValue.fromBoolean(value));
    }

    // Use by loaders
    public void setValue(String property, Value value) {
        this.valueByProperty.put(property.toLowerCase(), value);
    }

    public Config getChild(String property) {
        if (!this.configByProperty.containsKey(property.toLowerCase())) {
            this.configByProperty.put(property.toLowerCase(), new Config());
        }
        return this.configByProperty.get(property.toLowerCase());
    }

    private Value getValue(String property) {
        return this.valueByProperty.get(property.toLowerCase());
    }

    private Value getIn(String normalizedKey, boolean allowNull) {
        List<String> path = toPath(normalizedKey);
        Config config = this;
        for (int i = 0; i < path.size(); i++) {
            String property = path.get(i);
            if (i == path.size() - 1) {
                Value value = config.getValue(property);
                if (value != null) return value;
                if (allowNull) return new NullValue();
                throw new UndefinedKeyException(normalizedKey);
            } else {
                config = config.getChild(property.toLowerCase());
                if (config == null) {
                    if (allowNull) return new NullValue();
                    throw new UndefinedKeyException(normalizedKey);
                }
            }
        }
        throw new RuntimeException("path cannot be empty");
    }

    private void setIn(String normalizedKey, Value value) {
        List<String> path = toPath(normalizedKey);
        Config config = this;
        for (int i = 0; i < path.size(); i++) {
            String property = path.get(i);
            if (i == path.size() - 1) {
                config.setValue(property, value);
                return;
            } else {
                config = config.getChild(property.toLowerCase());
            }
        }
    }

    public String toYaml(String rootKey) {
        try {
            StringBuilder out = new StringBuilder();
            this.print(0, rootKey, out);
            return out.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void print(int depth, String rootKey, Appendable out) throws IOException {
        for (Map.Entry<String, Value> entry : valueByProperty.entrySet()) {
            if (rootKey == null || rootKey.equals(entry.getKey())) {
                String key = entry.getKey();

                // Print the key/value
                indent(depth, out);
                out.append(entry.getKey()).append(":");
                if (!entry.getValue().isNull()) {
                    out.append(" ").append(entry.getValue().getString());
                }
                out.append("\n");
            }
        }
        for (Map.Entry<String, Config> entry : configByProperty.entrySet()) {
            if (rootKey == null || rootKey.equals(entry.getKey())) {
                indent(depth, out);
                out.append(entry.getKey()).append(":\n");
                Config config = entry.getValue();
                config.print(depth + 1, null, out);
            }
        }
    }

    private void indent(int depth, Appendable out) throws IOException {
        for (int i = 0; i < depth; i++) {
            out.append("  ");
        }
    }

    private List<String> toPath(String key) {
        return Arrays.asList(normalize(key).split("\\."));
    }

    private String normalize(String key) {
        return key.replace('_', '.').toLowerCase(Locale.ENGLISH);
    }
}


/*
 * Copyright (c) 2016-2017 by OpenText Corporation. All Rights Reserved.
 */
package com.opentext.ia.yaml.configuration.zip;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import com.opentext.ia.yaml.resource.ResourceResolver;
import com.opentext.ia.yaml.resource.UnknownResourceException;

/**
 *  Build a zip from files on the file system.
 *  @since 9.6.0
 */
public class ZipBuilder {

  private static final Path TEMP_DIR = Paths.get(System.getProperty("java.io.tmpdir")).toAbsolutePath().normalize();

  private final Map<Object, String> entries = new HashMap<>();
  private final String basePath;
  private final Map<String, Integer> numExternalFilesByExtension = new HashMap<>();
  private final ZipCustomization customization;

  public ZipBuilder(File baseDir) {
    this(baseDir, ZipCustomization.none());
  }

  public ZipBuilder(File baseDir, ZipCustomization customization) {
    if (!baseDir.isDirectory()) {
      throw new IllegalArgumentException("Not a directory: " + baseDir);
    }
    this.basePath = baseDir.getAbsolutePath() + File.separator;
    this.customization = customization;
  }

  /**
   * Add a file to the ZIP.
   * @param file The file to add
   * @return The name of the ZIP entry
   */
  public String add(File file) {
    return add(file, true);
  }

  /**
   * Add a file to the ZIP.
   * @param file The file to add
   * @param preserveExternalFileName Whether external file names are to be preserved as much as possible, or whether
   * they should be replaced with unique numbers
   * @return The name of the ZIP entry
   */
  public String add(File file, boolean preserveExternalFileName) {
    File existingFile = checkFileExists(file);
    String result = zipPathFor(existingFile, preserveExternalFileName);
    entries.put(existingFile, result);
    return result;
  }

  private static File checkFileExists(File file) {
    File result = file.getAbsoluteFile();
    if (!result.isFile()) {
      throw new CannotReadZipEntryException(null, result);
    }
    return result;
  }

  private String zipPathFor(File file, boolean preserveExternalFileName) {
    String result = file.getAbsolutePath();
    if (result.startsWith(basePath)) {
      // Inside base directory => use relative path (which will be unique)
      result = FilenameUtils.separatorsToUnix(result.substring(basePath.length()));
    } else {
      // Outside base directory => ensure unique name
      String ext = FilenameUtils.getExtension(result);
      int index = numExternalFilesByExtension.getOrDefault(ext, 0);
      numExternalFilesByExtension.put(ext, 1 + index);
      if (preserveExternalFileName) {
        result = String.format("%d-%s", index, file.getName());
      } else {
        result = String.format("%d.%s", index, ext);
      }
    }
    return result;
  }

  /**
   * Replace the contents of a file with a different text.
   * @param file The file for which to replace the content
   * @param text The new content to include in the ZIP
   */
  public void replace(File file, String text) {
    replace(file, true, text);
  }

  /**
   * Replace the contents of a file with a different text.
   * @param file The file for which to replace the content
   * @param preserveExternalFileName Whether external file names are to be preserved as much as possible, or whether
   * they should be replaced with unique numbers
   * @param text The new content to include in the ZIP
   */
  public void replace(File file, boolean preserveExternalFileName, String text) {
    String path = entries.containsKey(file) ? entries.remove(file) : zipPathFor(file, preserveExternalFileName);
    entries.put(text, path);
  }

  /**
   * Build a ZIP file containing the added entries.
   * @return A ZIP file containing the added entries
   * @throws IOException In case an I/O error occurs
   */
  public File build() throws IOException {
    if (entries.isEmpty()) {
      throw new EmptyZipException();
    }
    String fileName = "import_configuration" + System.currentTimeMillis() + ".zip";
    File result = new File(TEMP_DIR.toFile(), fileName);

    try (ZipOutputStream zip = new ZipOutputStream(
        Files.newOutputStream(result.toPath(), StandardOpenOption.CREATE_NEW))) {
      customization.init(entries.values(), this::streamFor);
      for (Entry<Object, String> entry : entries.entrySet()) {
        try (InputStream input = toInputStream(entry.getKey())) {
          addEntry(ExtraZipEntry.of(entry.getValue(),
              customization.customize(entry.getValue(),  input)), zip);
        }
      }
      customization.extraEntries().forEach(entry -> addEntry(entry, zip));
      zip.closeEntry();
    }

    return result;
  }

  private InputStream streamFor(String name) {
    return entries.entrySet().stream()
        .filter(e -> name.equals(e.getValue()))
        .map(Entry::getKey)
        .map(source -> {
          try {
            return toInputStream(source);
          } catch (IOException e1) {
            return null;
          }
        })
        .filter(Objects::nonNull)
        .findFirst()
        .orElse(null);
  }

  private InputStream toInputStream(Object source) throws IOException {
    if (source instanceof String) {
      return IOUtils.toInputStream((String)source, StandardCharsets.UTF_8);
    }
    return Files.newInputStream(((File)source).toPath(), StandardOpenOption.READ);
  }

  private void addEntry(ExtraZipEntry entry, ZipOutputStream zip) {
    try {
      zip.putNextEntry(new ZipEntry(entry.getName()));
      try (InputStream input = entry.getContent()) {
        IOUtils.copy(input, zip);
      }
    } catch (IOException e) {
      throw new InvalidZipEntryException(entry.getName(), e);
    }
  }

  public ResourceResolver getResourceResolver() {
    return name -> {
      Object source = entries.entrySet().stream()
          .filter(e -> e.getValue().equals(name))
          .map(Entry::getKey)
          .findFirst()
          .orElseThrow(() -> new UnknownResourceException(name, null));
      if (source instanceof String) {
        return (String)source;
      }
      try (InputStream input = toInputStream(source)) {
        return IOUtils.toString(input, StandardCharsets.UTF_8);
      } catch (IOException e) {
        throw new UnknownResourceException(name, e);
      }
    };
  }

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

}


/*
 * Copyright (c) 2016-2017 by OpenText Corporation. All Rights Reserved.
 */
package com.opentext.ia.sdk.sip;

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;

import com.opentext.ia.sdk.support.io.DataBuffer;

/**
 * {@linkplain #generate(Iterable, DataBuffer) Generate} a product by {@linkplain Assembler assembling} components.
 * @param <C> The type of components to assemble
 */
public class Generator<C> {

  private final Assembler<C> assembler;

  /**
   * Assemble a product using the given {@linkplain Assembler}.
   * @param assembler The assembler to build up the product
   */
  public Generator(Assembler<C> assembler) {
    this.assembler = assembler;
  }

  /**
   * Generate a product by assembling components.
   * @param components The components to assemble
   * @param product The output to write the assembled product to
   * @return Metrics about the generation process
   * @throws IOException When an I/O error occurs
   */
  public Metrics generate(Iterable<C> components, DataBuffer product) throws IOException {
    return generate(components.iterator(), product);
  }

  /**
   * Generate a product by assembling components.
   * @param components The components to assemble
   * @param product Storage for the assembled product
   * @return Metrics about the generation process
   * @throws IOException When an I/O error occurs
   */
  public Metrics generate(Iterator<C> components, DataBuffer product) throws IOException {
    assembler.start(product);
    try {
      while (components.hasNext()) {
        assembler.add(components.next());
      }
    } finally {
      assembler.end();
    }
    return assembler.getMetrics();
  }

  /**
   * Generate a product by assembling components.
   * @param components The components to assemble
   * @param product The output to write the assembled product to
   * @return Metrics about the generation process
   * @throws IOException When an I/O error occurs
   */
  public Metrics generate(Enumeration<C> components, DataBuffer product) throws IOException {
    return generate(new EnumerationIterator<C>(components), product);
  }

  /**
   * Generate a product from a single piece.
   * @param component The component to generate the product from
   * @param product The output to write the assembled product to
   * @return Metrics about the generation process
   * @throws IOException When an I/O error occurs
   */
  public Metrics generate(C component, DataBuffer product) throws IOException {
    return generate(Collections.singletonList(component), product);
  }

}


/*
 * Copyright (c) 2016-2017 by OpenText Corporation. All Rights Reserved.
 */
package com.opentext.ia.sdk.client.impl;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import org.apache.http.client.utils.URIBuilder;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeCreator;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.opentext.ia.sdk.client.api.ArchiveClient;
import com.opentext.ia.sdk.client.api.ContentResult;
import com.opentext.ia.sdk.client.api.InfoArchiveLinkRelations;
import com.opentext.ia.sdk.dto.IngestionResponse;
import com.opentext.ia.sdk.dto.OrderItem;
import com.opentext.ia.sdk.dto.ReceptionResponse;
import com.opentext.ia.sdk.dto.Row;
import com.opentext.ia.sdk.dto.SearchComposition;
import com.opentext.ia.sdk.dto.SearchDataBuilder;
import com.opentext.ia.sdk.dto.SearchResult;
import com.opentext.ia.sdk.dto.SearchResults;
import com.opentext.ia.sdk.dto.export.ExportConfiguration;
import com.opentext.ia.sdk.dto.export.ExportTransformation;
import com.opentext.ia.sdk.dto.query.Comparison;
import com.opentext.ia.sdk.dto.query.SearchQuery;
import com.opentext.ia.sdk.support.http.BinaryPart;
import com.opentext.ia.sdk.support.http.MediaTypes;
import com.opentext.ia.sdk.support.http.ResponseFactory;
import com.opentext.ia.sdk.support.http.TextPart;
import com.opentext.ia.sdk.support.http.rest.LinkContainer;
import com.opentext.ia.sdk.support.http.rest.RestClient;

/**
 * Implementation of {@linkplain ArchiveClient} that uses the REST API of a running InfoArchive server.
 */
public class InfoArchiveRestClient implements ArchiveClient, InfoArchiveLinkRelations {

  private final ResponseFactory<ContentResult> contentResultFactory = new ContentResultFactory();

  private final RestClient restClient;
  private final ApplicationIngestionResourcesCache resourceCache;

  public InfoArchiveRestClient(RestClient restClient, ApplicationIngestionResourcesCache resourceCache) {
    this.restClient = restClient;
    this.resourceCache = resourceCache;
  }

  @Override
  public String ingest(InputStream sip) throws IOException {
    ReceptionResponse response = restClient.post(resourceCache.getAipResourceUri(), ReceptionResponse.class,
        new TextPart("format", "sip_zip"), new BinaryPart("sip", sip, "IASIP.zip"));
    String ingestUri = response.getUri(LINK_INGEST);
    // There was a backwards incompatible change between 4.2 and 4.3 from PUT to POST
    if (resourceCache.isVersionOrEarlier("4.2")) {
      return restClient.put(ingestUri, IngestionResponse.class).getAipId();
    }
    return restClient.post(ingestUri, IngestionResponse.class).getAipId();
  }

  @Override
  public String ingestDirect(InputStream sip) throws IOException {
    String ingestDirectUri = resourceCache.getAipIngestDirectResourceUri();
    if (ingestDirectUri == null) {
      // Fall back to previous approach
      return ingest(sip);
    }
    return restClient.post(ingestDirectUri, IngestionResponse.class, new TextPart("format", "sip_zip"),
        new BinaryPart("sip", sip, "IASIP.zip")).getAipId();
  }

  /**
   * Fetch the content for the specified content id.
   * @param contentId The id of the content to fetch.
   * @return A ContentResult
   * @throws IOException When an I/O error occurs
   * @deprecated Will be removed without replacement in a future version
   */
  @Override
  @Deprecated
  public ContentResult fetchContent(String contentId) throws IOException {
    try {
      String contentResource = resourceCache.getCiResourceUri();
      URIBuilder builder = new URIBuilder(contentResource);
      builder.setParameter("cid", contentId);
      URI uri = builder.build();
      return restClient.get(uri.toString(), contentResultFactory);
    } catch (URISyntaxException e) {
      throw new IllegalStateException("Failed to create content resource uri.", e);
    }
  }

  /**
   * Fetch the content for the specified order item.
   * @param orderItem The order item.
   * @return A ContentResult
   * @throws IOException When an I/O error occurs
   * @deprecated Will be removed without replacement in a future version
   */
  @Override
  @Deprecated
  public ContentResult fetchOrderContent(OrderItem orderItem) throws IOException {
    String downloadUri = Objects.requireNonNull(
        Objects.requireNonNull(orderItem, "Missing order item").getUri(LINK_DOWNLOAD), "Missing download URI");
    String fetchUri = restClient.uri(downloadUri)
        .addParameter("downloadToken", "")
        .build();
    return restClient.get(fetchUri, contentResultFactory);
  }

  @Override
  public SearchResults search(SearchQuery searchQuery, SearchComposition searchComposition) throws IOException {
    String uri = searchComposition.getSelfUri();
    String searchCriteria = toSearchCriteria(searchQuery);
    SearchResults result = restClient.post(uri, SearchResults.class, searchCriteria, MediaTypes.XML);
    uri = result.getUri("next");
    while (uri != null) {
      SearchResults onePageOfSearchResults = restClient.post(uri, SearchResults.class, searchCriteria, MediaTypes.XML);
      onePageOfSearchResults.getResults().forEach(result::addResult);
      uri = onePageOfSearchResults.getUri("next");
    }
    return result;
  }

  private String toSearchCriteria(SearchQuery searchQuery) {
    SearchDataBuilder searchDataBuilder = SearchDataBuilder.builder();
    searchQuery.getItems().stream()
        .filter(item -> item instanceof Comparison)
        .map(Comparison.class::cast)
        .forEach(comparison -> addComparison(comparison, searchDataBuilder));
    return searchDataBuilder.build()
        .replaceFirst("<\\?.*\\?>", "")
        .replace(" ", "")
        .replace(System.lineSeparator(), "");
  }

  private void addComparison(Comparison comparison, SearchDataBuilder searchDataBuilder) {
    switch (comparison.getOperator()) {
      case EQUAL: searchDataBuilder.isEqual(comparison.getName(), comparison.getValue().get(0));
        break;
      case NOT_EQUAL:
        searchDataBuilder.isNotEqual(comparison.getName(), comparison.getValue().get(0));
        break;
      case STARTS_WITH:
        searchDataBuilder.startsWith(comparison.getName(), comparison.getValue().get(0));
        break;
      case BETWEEN:
        searchDataBuilder.between(comparison.getName(), comparison.getValue().get(0), comparison.getValue().get(1));
        break;
      default:
        break;
    }
  }

  @Override
  public OrderItem export(SearchResults searchResults, ExportConfiguration exportConfiguration, String outputName)
      throws IOException {
    String fullOutputName = outputName + '_' + Long.toString(new Date().getTime());
    String exportUri = restClient.uri(searchResults.getUri(LINK_EXPORT))
        .addParameter("name", fullOutputName)
        .build();
    String exportRequestBody = getValidJsonRequestForExport(exportConfiguration.getSelfUri(),
        searchResults.getResults());
    return restClient.post(exportUri, OrderItem.class, exportRequestBody);
  }

  @Override
  public OrderItem exportAndWaitForDownloadLink(SearchResults searchResults, ExportConfiguration exportConfiguration,
      String outputName, TimeUnit timeUnit, long timeOut) throws IOException {
    OrderItem result = export(searchResults, exportConfiguration, outputName);
    long maxWaitTime = System.currentTimeMillis() + timeUnit.toMillis(timeOut);
    while (result.getUri(LINK_DOWNLOAD) == null && System.currentTimeMillis() < maxWaitTime) {
      try {
        TimeUnit.SECONDS.sleep(2);
      } catch (InterruptedException ignored) {
        Thread.currentThread().interrupt();
      }
      result = restClient.refresh(result);
    }
    return result;
  }

  private String getValidJsonRequestForExport(String exportConfigurationUri, List<SearchResult> searchResults) {
    JsonNodeFactory jsonNodeFactory = new ObjectMapper().getNodeFactory();
    ObjectNode root = jsonNodeFactory.objectNode();
    root.set("exportConfiguration", jsonNodeFactory.textNode(exportConfigurationUri));
    root.set("includedRows", getIncludedRows(searchResults, jsonNodeFactory));
    return root.toString();
  }

  private ArrayNode getIncludedRows(List<SearchResult> searchResults, JsonNodeCreator jsonNodeCreator) {
    ArrayNode result = jsonNodeCreator.arrayNode();
    searchResults.stream()
        .map(SearchResult::getRows)
        .flatMap(Collection::stream)
        .map(Row::getId)
        .forEach(result::add);
    return result;
  }

  /**
   * Upload the transformation zip with the stylesheet into Archive.
   * @param exportTransformation The export transformation.
   * @param zip The input stream of zip with stylesheet.
   * @throws IOException When an I/O error occurs
   * @return The uploaded transformation
   * @deprecated Use declarative configuration to define transformations
   */
  @Override
  @Deprecated
  public LinkContainer uploadTransformation(ExportTransformation exportTransformation, InputStream zip)
      throws IOException {
    String uri = exportTransformation.getUri(LINK_EXPORT_TRANSFORMATION_ZIP);
    return restClient.post(uri, LinkContainer.class, new BinaryPart("file", zip, "stylesheet.zip"));
  }

}


/*
 * Copyright (c) 2016-2017 by OpenText Corporation. All Rights Reserved.
 */
package com.opentext.ia.sdk.sip;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.function.Supplier;

import com.opentext.ia.sdk.support.io.FileBuffer;
import com.opentext.ia.sdk.support.io.FileSupplier;

/**
 * {@linkplain #generate(Iterable) Generate} a file by {@linkplain Assembler assembling} components.
 * @param <C> The type of components to assemble
 */
public class FileGenerator<C> extends Generator<C> {

  private final Supplier<File> fileSupplier;

  /**
   * Assemble a file using the given assembler into a temporary directory.
   * @param assembler The assembler to build up the file
   */
  public FileGenerator(Assembler<C> assembler) {
    this(assembler, FileSupplier.fromTemporaryDirectory());
  }

  /**
   * Assemble a file using the given assembler into the given directory.
   * @param assembler The assembler to build up the file
   * @param dir The directory in which to generate the file
   */
  public FileGenerator(Assembler<C> assembler, File dir) {
    this(assembler, FileSupplier.fromDirectory(dir));
  }

  /**
   * Assemble a file using the given assembler into the given directory.
   * @param assembler The assembler to build up the file
   * @param fileSupplier A function that supplies the file to write to
   */
  public FileGenerator(Assembler<C> assembler, Supplier<File> fileSupplier) {
    super(assembler);
    this.fileSupplier = fileSupplier;
  }

  /**
   * Generate a file by assembling components.
   * @param components The components to assemble
   * @return The generated file and metrics about the generation process
   * @throws IOException When an I/O error occurs
   */
  public FileGenerationMetrics generate(Iterable<C> components) throws IOException {
    return generate(components.iterator());
  }

  /**
   * Generate a file by assembling components.
   * @param components The components to assemble
   * @return The generated file and metrics about the generation process
   * @throws IOException When an I/O error occurs
   */
  public FileGenerationMetrics generate(Iterator<C> components) throws IOException {
    File result = fileSupplier.get();
    return new FileGenerationMetrics(result, generate(components, new FileBuffer(result)));
  }

  /**
   * Generate a file by assembling components.
   * @param components The components to assemble
   * @return The generated file and metrics about the generation process
   * @throws IOException When an I/O error occurs
   */
  public FileGenerationMetrics generate(Enumeration<C> components) throws IOException {
    return generate(new EnumerationIterator<>(components));
  }

  /**
   * Generate a file from a single component.
   * @param component The component to generate the file from
   * @return The generated file and metrics about the generation process
   * @throws IOException When an I/O error occurs
   */
  public FileGenerationMetrics generate(C component) throws IOException {
    return generate(Collections.singletonList(component));
  }

}


/*
 * Copyright (c) 2016-2017 by OpenText Corporation. All Rights Reserved.
 */
package com.opentext.ia.sdk.support.io;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

/**
 * Create uniquely named directories in a given parent directory.
 */
public final class UniqueDirectory {

  private UniqueDirectory() {
    // Utility class
  }

  /**
   * Create a new directory in the given parent directory.
   * @param parentDir The parent directory in which to create a new directory
   * @return The newly created directory
   */
  public static File in(File parentDir) {
    File result = new File(parentDir, UUID.randomUUID()
      .toString());
    if (!result.mkdirs()) {
      throw new RuntimeIoException(new IOException("Could not create directory: " + result));
    }
    return result;
  }

}


/*
 * Copyright (c) 2016-2017 by OpenText Corporation. All Rights Reserved.
 */
package com.opentext.ia.configuration;

import java.util.UUID;

import org.atteo.evo.inflector.English;

/**
 * Base class for all configuration builders.
 * @author Ray Sinnema
 * @since 9.4.0
 *
 * @param <P> The type of parent builder
 * @param <C> The type of configuration to build
 */
public abstract class BaseBuilder<P extends BaseBuilder<?, C>, C extends Configuration<?>> {

  private final ConfigurationObject object;
  private final ConfigurationProducer<C> producer;
  private final P parent;

  protected BaseBuilder(P parent, String type) {
    this.producer = null;
    this.parent = parent;
    this.object = new ConfigurationObject(type);
  }

  protected BaseBuilder(ConfigurationProducer<C> producer, String type) {
    this.producer = producer;
    this.parent = null;
    this.object = new ConfigurationObject(type);
  }

  protected String someName() {
    return UUID.randomUUID().toString().replaceAll("-", "");
  }

  protected Object getProperty(String name) {
    return object.getProperties().get(name);
  }

  protected void setProperty(String name, Object value) {
    object.setProperty(name, value);
  }

  protected void setUninitialized(String... properties) {
    for (String property : properties) {
      setProperty(property, null);
    }
  }

  protected boolean hasProperty(String name) {
    return object.hasProperty(name);
  }

  /**
   * End this builder.
   * @return The parent builder
   */
  public P end() {
    parent.addChildObject(English.plural(object.getType()), object);
    return parent;
  }

  protected void addChildObject(String collection, ConfigurationObject childObject) {
    String parentProp = parentProperty();

    if (parentProp != null) {
      childObject.setProperty(parentProp, object.getProperties().optString("name"));
    }

    object.addChildObject(collection, childObject);
  }

  protected String parentProperty() {
    return object.getType();
  }

  /**
   * Build the configuration.
   * @return The configuration that was built
   */
  public C build() {
    if (parent == null) {
      return producer.produce(object);
    }
    return end().build();
  }

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

}


/*
 * Copyright (c) 2016-2017 by OpenText Corporation. All Rights Reserved.
 */
package com.opentext.ia.sip.assembly.stringtemplate;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Map;

import javax.annotation.Nullable;

import org.stringtemplate.v4.*;

import com.opentext.ia.sdk.sip.ContentInfo;
import com.opentext.ia.sdk.sip.DigitalObject;
import com.opentext.ia.sdk.sip.FixedHeaderAndFooterTemplate;
import com.opentext.ia.sdk.sip.Template;

/**
 * {@linkplain Template} using the StringTemplate template engine.
 * Templates have access to the following objects:
 *

 *
model

 *
The domain object (of type D)

 *
content

 *
The reference information and the encoded hashes of the {@linkplain DigitalObject}s associated with the domain object, if any

 *

 * @param <D> The type of domain object to replace with text
 */
public class StringTemplate<D> extends FixedHeaderAndFooterTemplate<D> {

  private static final char DEFAULT_DELIMETER_START_CHAR = '$';
  private static final char DEFAULT_DELIMETER_END_CHAR = '$';
  private static final String TEMPLATE_NAME = "template";
  private static final String MODEL_VARIABLE = "model";
  private static final String CONTENT_VARIABLE = "content";

  private final ST templatePrototype;

  /**
   * Create an instance.
   * @param header The fixed header
   * @param footer The fixed footer
   * @param row The template for the rows
   */
  public StringTemplate(InputStream header, InputStream footer, InputStream row) {
    this(toString(header), toString(footer), toString(row));
  }

  /**
   * Create an instance with the default start and end delimeters.
   * @param header The fixed header
   * @param footer The fixed footer
   * @param row The template for the rows
   */
  public StringTemplate(String header, String footer, String row) {
    this(header, footer, row, DEFAULT_DELIMETER_START_CHAR, DEFAULT_DELIMETER_END_CHAR);

  }

  /**
   * Create an instance.
   * @param header The fixed header
   * @param footer The fixed footer
   * @param row The template for the rows
   * @param delimeterStart The character that starts a StringTemplate expression.
   * @param delimeterEnd The character that ends a StringTemplate expression.
   */
  public StringTemplate(String header, String footer, String row, char delimeterStart, char delimeterEnd) {
    super(header, footer);
    this.templatePrototype = compileTemplate(row, delimeterStart, delimeterEnd);
  }

  @Nullable
  private ST compileTemplate(String row, char delimeterStartChar, char delimeterEndChar) {
    STGroup group = new STGroup(delimeterStartChar, delimeterEndChar);
    prepareGroup(group);
    group.defineTemplate(TEMPLATE_NAME, MODEL_VARIABLE + ',' + CONTENT_VARIABLE, row);
    return group.getInstanceOf(TEMPLATE_NAME);
  }

  /**
   * Prepares a group by adding renderers, adaptors and sub templates. Override this if you want to add additional
   * renderers. By default adds:
   *

   *
an {@linkplain XmlDateRenderer} which renders Date instances into the standard XML date and time format

   *
an {@linkplain MapModelAdaptor} which allows {@linkplain java.util.Map} to be used transparently as domain
   * objects.

   *

   * @param group The template group
   */
  protected void prepareGroup(STGroup group) {
    registerAdaptor(group, Map.class, new MapModelAdaptor());
    registerRenderer(group, Date.class, new XmlDateRenderer());
  }

  /**
   * Registers a ModelAdaptor with the group. Override this method if you want to suppress one OOTB adaptor but not all.
   * @param <S> The domain object type
   * @param group The template group
   * @param type The domain object class
   * @param adaptor The adaptor that will be used to extract properties of objects of type S
   */
  protected <S> void registerAdaptor(STGroup group, Class<S> type, ModelAdaptor adaptor) {
    group.registerModelAdaptor(type, adaptor);
  }

  /**
   * Registers a renderer with the group. Override this method if you want to suppress one OOTB renderer but not all.
   * @param <S> The domain object type
   * @param group The template group
   * @param type The domain object class
   * @param attributeRenderer The renderer that will be used to render objects of type S into a String
   */
  protected <S> void registerRenderer(STGroup group, Class<S> type, AttributeRenderer attributeRenderer) {
    group.registerRenderer(type, attributeRenderer);
  }

  @Override
  public void writeRow(D domainObject, Map<String, ContentInfo> contentInfo, PrintWriter writer) throws IOException {
    ST template = prepareTemplate(templatePrototype, domainObject, contentInfo);
    template.write(new NoIndentWriter(writer));
  }

  /**
   * Prepares the template by adding the variables.
   * @param prototype The template prototype
   * @param domainObject The domain object
   * @param contentInfo The reference information and the encoded content hashes
   * @return The prepared template instance
   */
  protected ST prepareTemplate(ST prototype, D domainObject, Map<String, ContentInfo> contentInfo) {
    ST template = new ST(prototype);
    template.add(MODEL_VARIABLE, domainObject);
    template.add(CONTENT_VARIABLE, contentInfo);
    return template;
  }

}