package common.sql;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Stack;
import java.util.StringJoiner;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import core.util.NumberUtil;

/**
 * パース可変項目保持
 *
 * @author Tadashi Nakayama
 */
final class LineParsedNodeItem implements LineParsedNode {

	/** INパターン */
	private static final Pattern PTN_IN = Pattern.compile(".*\\s[iI][nN][\\s]*[(]?[\\s]*$");

	/** クエリ */
	private final String item;
	/** 左要素 */
	private final LineParsedNodeItem left;
	/** 右要素 */
	private final LineParsedNodeItem right;

	/** 括弧フラグ */
	private boolean parenthesis;

	/**
	 * コンストラクタ
	 *
	 * @param val パース対象文字列
	 */
	LineParsedNodeItem(final String val) {
		this.left = getParsedItem(new ItemParser(val));
		this.right = null;
		this.item = null;
		this.parenthesis = false;
	}

	/**
	 * コンストラクタ
	 *
	 * @param str クエリ
	 * @param c 括弧フラグ
	 * @param l 左要素
	 * @param r 右要素
	 */
	private LineParsedNodeItem(final String str, final boolean c,
			final LineParsedNodeItem l, final LineParsedNodeItem r) {
		this.left = l;
		this.right = r;
		this.item = str;
		this.parenthesis = c;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		final UnaryOperator<String> pf = s -> this.parenthesis ? s : "";

		final var sj = new StringJoiner(" ");
		sj.add(Objects.toString(this.left, ""));
		sj.add(Objects.toString(this.item, ""));
		sj.add(Objects.toString(this.right, ""));
		return pf.apply("( ") + sj.toString().trim() + pf.apply(" )");
	}

	/**
	 * クエリ構築
	 *
	 * @param pmap パラメタマップ
	 * @param plist パラメタリスト
	 * @return クエリ
	 */
	@Override
	public String build(final Map<String, ?> pmap, final List<Object> plist) {

		var lstr = "";
		var llist = Collections.emptyList();
		if (this.left != null) {
			llist = new ArrayList<>();
			lstr = this.left.build(pmap, llist);
		}

		var mstr = "";
		var mlist = Collections.emptyList();
		if (this.item != null) {
			mlist = new ArrayList<>();
			mstr = getQuery(pmap, mlist);
		}

		var rstr = "";
		var rlist = Collections.emptyList();
		if (this.right != null) {
			rlist = new ArrayList<>();
			rstr = this.right.build(pmap, rlist);
		}

		final var type = ItemParser.getKeywordType(this.item);
		if (type != null) {
			if ((this.left != null && lstr.isEmpty())
					|| (this.right != null && rstr.isEmpty())) {
				mstr = "";
				mlist = Collections.emptyList();
				if (ItemParser.TYPE_AND.equals(type)) {
					if (llist.isEmpty() && rlist.isEmpty()) {
						lstr = "";
						rstr = "";
					}
				}
			}
		}

		plist.addAll(lstr.isEmpty() ? Collections.emptyList() : llist);
		plist.addAll(mstr.isEmpty() ? Collections.emptyList() : mlist);
		plist.addAll(rstr.isEmpty() ? Collections.emptyList() : rlist);

		return toString(lstr, mstr, rstr);
	}

	/**
	 * 文字列化
	 *
	 * @param lstr 左文字
	 * @param mstr 中文字
	 * @param rstr 右文字
	 * @return 文字列
	 */
	private String toString(final String lstr, final String mstr, final String rstr) {
		final var sb = new StringBuilder();
		if (!lstr.isEmpty() || !mstr.isEmpty() || !rstr.isEmpty()) {
			// root
			if (this.item == null) {
				sb.append(" ");
			}
			if (this.parenthesis) {
				sb.append("(");
			}
			sb.append(lstr);
			if (!mstr.isEmpty() && this.left != null) {
				sb.append(" ");
			}
			sb.append(mstr);
			if (!mstr.isEmpty() && this.right != null) {
				sb.append(" ");
			}
			sb.append(rstr);
			if (this.parenthesis) {
				sb.append(")");
			}
			// root
			if (this.item == null) {
				sb.append(" ");
			}
		}
		return sb.toString();
	}

	/**
	 * ターゲット判断
	 *
	 * @param pmap パラメタマップ
	 * @return ターゲットの場合 true を返す。
	 */
	@Override
	public boolean isTarget(final Map<String, ?> pmap) {
		return true;
	}

	/**
	 * クエリパース処理
	 *
	 * @param ip クエリパース
	 * @return パース可変項目
	 */
	private LineParsedNodeItem getParsedItem(final ItemParser ip) {
		var space = false;
		var sb = new StringBuilder();
		while (true) {
			final var str = ip.getNextToken();
			if (str == null) {
				ip.push(sb.toString());
				break;
			}

			final var trim = str.trim();
			if (ItemParser.isKeyword(trim)) {
				ip.push(sb.toString());
				ip.push(trim);
				sb = new StringBuilder();
			} else if ("(".equals(str)) {
				ip.push("(");
				final var lpi = getParsedItem(ip);
				if (!ItemParser.isKeyword(lpi.item)
						|| ItemParser.getKeywordType(lpi.item) == null) {
					sb.append(lpi.toString());
					space = false;
				} else {
					ip.push(lpi);
				}
			} else if (")".equals(str)) {
				ip.push(sb.toString());
				return popStacked(ip, true);
			} else if ("BETWEEN".equalsIgnoreCase(trim)) {
				throw new IllegalArgumentException(str);
			} else {
				if (!space && 0 < sb.length()) {
					sb.append(" ");
				}
				sb.append(str);
				space = str.endsWith(" ");
			}
		}

		return popStacked(ip, false);
	}

	/**
	 * ポップ処理
	 *
	 * @param ip クエリパース
	 * @param p 括弧までフラグ
	 * @return LineParsedItem
	 */
	private LineParsedNodeItem popStacked(final ItemParser ip, final boolean p) {
		String top = null;
		LineParsedNodeItem r = null;
		LineParsedNodeItem l = null;
		while (!ip.isEmpty()) {
			final var pop = ip.pop();
			if (pop.isLeft() && "(".equals(pop.left())) {
				if (top != null || (l != null && r != null)) {
					l = new LineParsedNodeItem(top, true, l, r);
					r = null;
					top = null;
				} else if (l != null) {
					l.parenthesis = true;
				}
				if (p) {
					break;
				}
			}

			if (pop.isLeft() && ItemParser.isKeyword(pop.left())) {
				if (top != null) {
					r = new LineParsedNodeItem(top, false, l, r);
					l = null;
				} else {
					if (r == null) {
						r = l;
						l = null;
					}
				}
				top = pop.left();
			} else {
				if (l != null) {
					if (r == null) {
						r = l;
					} else {
						r = new LineParsedNodeItem(top, false, l, r);
						top = null;
					}
				}

				if (pop.isRight()) {
					l = pop.right();
				} else {
					l = new LineParsedNodeItem(pop.left(), false, null, null);
				}
			}
		}

		if (top != null || (l != null && r != null)) {
			l = new LineParsedNodeItem(top, false, l, r);
		}

		return l;
	}

	/**
	 * クエリ文字列取得
	 *
	 * @param pmap パラメタマップ
	 * @param plist パラメタリスト
	 * @return クエリ文字列
	 */
	private String getQuery(final Map<String, ?> pmap, final List<Object> plist) {
		StringBuilder sb = null;
		var quote = false;
		var prv = 0;
		var loc = 0;
		while (loc < this.item.length()) {
			final var ch = this.item.codePointAt(loc);
			if (!quote && LineParsedNode.isBindCharacter(ch)) {
				final var bind = getBindString(this.item, loc);
				final var last = this.item.substring(prv, loc);

				final var prepare = prepareParameter(pmap, plist, bind, last);
				if (Objects.toString(prepare, "").isEmpty()) {
					return "";
				}

				if (sb == null) {
					sb = new StringBuilder();
				}
				sb.append(last);
				sb.append(prepare);

				loc = loc + bind.length();
				prv = loc;
			} else {
				loc = this.item.offsetByCodePoints(loc, 1);
				if ('\'' == ch) {
					if (loc < this.item.length()) {
						if ('\'' == this.item.codePointAt(loc)) {
							loc = loc + "'".length();
						} else {
							quote = !quote;
						}
					}
				}
			}
		}

		if (sb != null) {
			sb.append(this.item.substring(prv));
			return sb.toString();
		}

		return this.item;
	}

	/**
	 * 項目文字列取得
	 *
	 * @param val 文字列
	 * @param loc 開始位置
	 * @return 項目文字列
	 */
	String getBindString(final String val, final int loc) {
		var e = loc;
		do {
			e = separatorNext(val, e);
		} while (e < val.length() && LineParsedNode.isBindCharacter(val.codePointAt(e)));

		while (e < val.length() && Character.isWhitespace(val.codePointAt(e))) {
			e = val.offsetByCodePoints(e, 1);
		}

		if (e < val.length() && val.codePointAt(e) == '(') {
			e = endEnclosure(val, e) + 1;
		} else if (e < val.length() && val.codePointAt(e) == '\'') {
			e = val.indexOf('\'', e + "'".length()) + "'".length();
		} else if (LineParsedNode.isDate(val.substring(e))
				|| LineParsedNode.isTimestamp(val.substring(e))) {
			e = val.indexOf(')', e) + ")".length();
		} else if (LineParsedNode.isReplace(val.codePointAt(loc))) {
			e = val.length();
		} else {
			while (e < val.length()) {
				final var cp = val.codePointAt(e);
				if (!(Character.isDigit(cp) && cp < Byte.MAX_VALUE) && cp != '-' && cp != '.') {
					break;
				}
				e = val.offsetByCodePoints(e, 1);
			}
		}

		return val.substring(loc, e);
	}

	/**
	 * 終了括弧位置取得
	 *
	 * @param val 文字列
	 * @param begin 括弧開始位置
	 * @return 終了括弧位置
	 */
	private int endEnclosure(final String val, final int begin) {
		var parenthsis = 0;
		for (var i = begin; i < val.length(); i = val.offsetByCodePoints(i, 1)) {
			final var cp = val.codePointAt(i);
			if (cp == '(') {
				parenthsis++;
			} else if (cp == ')') {
				parenthsis--;
				if (parenthsis == 0) {
					return i;
				}
			}
		}
		return val.length();
	}

	/**
	 * パラメタリスト準備とパラメタ文字列返却
	 *
	 * @param pmap パラメタマップ
	 * @param plist パラメタリスト
	 * @param bind bind値
	 * @param last bind直前文字列
	 * @return パラメタ文字列
	 */
	private String prepareParameter(final Map<String, ?> pmap, final List<Object> plist,
			final String bind, final String last) {
		return LineParsedNode.isMultiKey(bind)
			? prepareMulti(pmap, plist, bind) : prepare(pmap, plist, bind, last);
	}

	/**
	 * パラメタリスト準備とパラメタ文字列返却
	 *
	 * @param pmap パラメタマップ
	 * @param plist パラメタリスト
	 * @param bind bind値
	 * @return パラメタ文字列
	 */
	private String prepareMulti(final Map<String, ?> pmap,
			final List<Object> plist, final String bind) {
		String ret = null;
		final var keys = getMultiKeys(bind);
		if (!keys.isEmpty()) {
			final var vl = getValueList(keys, pmap);
			if (vl != null) {
				ret = prepareMultiString(vl, plist);
			} else {
				final var val = getLiteral(bind);
				if (LineParsedNode.isSearchless(bind)) {
					plist.addAll(Arrays.asList(new String[keys.size()]));
					ret = String.join(",", bindVars(keys.size()));
				} else if (LineParsedNode.isDefault(bind)) {
					final var list = getDefault(val);
					plist.addAll(list);

					ret = String.join(",", bindVars(list.size()));
				}

				if (ret != null) {
					ret = "( " + ret + " )";
				}
			}
		}
		return ret;
	}

	/**
	 * 値リスト取得
	 *
	 * @param keys キーリスト
	 * @param pmap パラメタマップ
	 * @return 値リスト
	 */
	private List<List<Object>> getValueList(final List<String> keys, final Map<String, ?> pmap) {
		final var ret = new ArrayList<List<Object>>();
		for (final var key : keys) {
			final var obj = pmap.get(key);
			if (!LineParsedNode.isValid(obj)) {
				return null;
			}
			ret.add(toParamCollection(obj, true));
		}
		return ret;
	}

	/**
	 * パラメタ文字列返却
	 *
	 * @param vl パラメタ値
	 * @param plist パラメタリスト
	 * @return パラメタ文字列
	 */
	private String prepareMultiString(final List<List<Object>> vl, final List<Object> plist) {
		final var ret = new StringJoiner(",");

		final var list = new ArrayList<>();
		for (var i = 0; i < vl.get(0).size(); i++) {
			final var sj = new StringJoiner(",");
			for (final var l : vl) {
				final var obj = l.get(i);
				if (!Objects.toString(obj, "").isEmpty() || 1 == l.size()) {
					list.add(obj);
					sj.add("?");
				}
			}
			ret.add("( " + sj.toString() + " )");
		}
		plist.addAll(list);

		return ret.toString();
	}

	/**
	 * パラメタリスト準備とパラメタ文字列返却
	 *
	 * @param pmap パラメタマップ
	 * @param plist パラメタリスト
	 * @param bind bind値
	 * @param last bind直前文字列
	 * @return パラメタ文字列
	 */
	private String prepare(final Map<String, ?> pmap, final List<Object> plist,
			final String bind, final String last) {
		final var key = getKey(bind);
		final var val = getLiteral(bind);

		if (LineParsedNode.isReplace(bind)) {
			final var loc = key.indexOf('/');
			if (loc < 0) {
				final var obj = pmap.get(key);
				if (LineParsedNode.isValid(obj)) {
					return toCsvString(obj);
				}
			} else {
				final var obj = pmap.get(key.substring(0, loc));
				if (LineParsedNode.isValid(obj)) {
					final var rep = key.substring(key.offsetByCodePoints(loc, 1));
					final var csv = toCsvString(obj);
					return val.replace(rep, csv);
				}
			}
			return val;
		}

		String ret = null;
		final var obj = pmap.get(key);
		if (LineParsedNode.isValid(obj)) {
			final var in = PTN_IN.matcher(last).matches();
			ret = prepareString(val, toParamCollection(obj, in), plist);
		} else {
			if (LineParsedNode.isSearchless(bind)) {
				plist.add(null);
				ret = "?";
			} else if (LineParsedNode.isDefault(bind)) {
				final var list = getDefault(val);
				plist.addAll(list);
				ret = String.join(",", bindVars(list.size()));
			}
		}

		if (ret != null && LineParsedNode.isEnclosed(val)) {
			ret = "( " + ret + " )";
		}
		return ret;
	}

	/**
	 * バインド変数配列取得
	 *
	 * @param size サイズ
	 * @return 配列
	 */
	private String[] bindVars(final int size) {
		final var ary = new String[size];
		Arrays.fill(ary, "?");
		return ary;
	}

	/**
	 * 文字列化
	 *
	 * @param obj パラメタ値
	 * @return コレクションの文字列
	 */
	private String toCsvString(final Object obj) {
		return toList(obj).stream().filter(Objects::nonNull).
							map(Objects::toString).
							map(s -> s.replaceAll("[^0-9A-Za-z_, .*]", "")).
							collect(Collectors.joining(", "));
	}

	/**
	 * パラメタ文字列返却
	 *
	 * @param val リテラル値
	 * @param l パラメタ値リスト
	 * @param plist パラメタリスト
	 * @return パラメタ文字列
	 */
	private String prepareString(final String val, final List<?> l, final List<Object> plist) {
		final var ret = new StringJoiner(",");

		final var list = new ArrayList<>();
		for (final var o : l) {
			final var object = toValidClass(o, val);
			if (!Objects.toString(object, "").isEmpty() || 1 == l.size()) {
				list.add(object);
				ret.add("?");
			}
		}
		plist.addAll(list);

		return ret.toString();
	}

	/**
	 * SQLファイル記述値取得
	 *
	 * @param val リテラル値
	 * @return SQLファイル記述値
	 */
	private List<Object> getDefault(final String val) {
		final var ret = new ArrayList<>();

		final var str = getInnerValue(val);
		if (LineParsedNode.isString(str)) {
			for (final var s : str.split(",")) {
				final var v = s.trim();
				ret.add(v.substring("'".length(), v.length() - "'".length()));
			}
		} else if (LineParsedNode.isDate(str)) {
			for (final var line : str.split("[tT][oO]_[dD][aA][tT][eE]")) {
				final var date = getDateValue(line);
				if (!Objects.toString(date, "").isEmpty()) {
					ret.add(toDate(date));
				}
			}
		} else if (LineParsedNode.isTimestamp(str)) {
			for (final var line : str.split("[tT][oO]_[tT][iI][mM][eE][sS][tT][aA][mM][pP]")) {
				final var date = getDateValue(line);
				if (!Objects.toString(date, "").isEmpty()) {
					ret.add(toTimestamp(date, line));
				}
			}
		} else {
			for (final var s : str.split(",")) {
				ret.add(toNumber(s));
			}
		}

		return ret;
	}

	/**
	 * SQLとデータを考慮して集合化
	 *
	 * @param obj 値オブジェクト
	 * @return 集合
	 */
	private List<Object> toList(final Object obj) {
		if (List.class.isInstance(obj)) {
			return List.class.cast(obj);
		} else if (obj.getClass().isArray()) {
			return Arrays.asList(Object[].class.cast(obj));
		}
		return Collections.singletonList(obj);
	}

	/**
	 * SQLとデータを考慮して集合化
	 *
	 * @param obj 値オブジェクト
	 * @param in IN含フラグ
	 * @return 集合
	 */
	private List<Object> toParamCollection(final Object obj, final boolean in) {
		var list = toList(obj);
		if (in) {
			if (list.size() == 1) {
				if (list.get(0) != null) {
					final var vals = String.valueOf(list.get(0)).split(",");
					list = Arrays.asList(Object[].class.cast(vals));
				} else {
					list = Collections.emptyList();
				}
			}
		} else {
			if (!list.isEmpty()) {
				list = Collections.singletonList(list.get(0));
			}
		}
		return list;
	}

	/**
	 * 型変換
	 *
	 * @param obj 値
	 * @param val リテラル値
	 * @return 変換後値
	 */
	private Object toValidClass(final Object obj, final String val) {
		final var inner = getInnerValue(val);
		if (LineParsedNode.isString(inner)) {
			return addLikePlace(inner, obj);
		} else if (LineParsedNode.isDate(inner)) {
			return toDate(obj);
		} else if (LineParsedNode.isTimestamp(inner)) {
			return toTimestamp(obj, inner);
		}
		return toNumber(obj);
	}

	/**
	 * LIKEワイルドカード追加
	 *
	 * @param val リテラル値
	 * @param str 追加対象文字列
	 * @return 追加後文字列
	 */
	private String addLikePlace(final String val, final Object str) {
		var ret = Objects.toString(str, "");
		if (val.startsWith("'%")) {
			ret = "%" + ret;
		}
		if (val.endsWith("%'")) {
			ret = ret + "%";
		}
		return ret;
	}

	/**
	 * 数値化
	 *
	 * @param obj オブジェクト
	 * @return 数値
	 */
	private Number toNumber(final Object obj) {
		return Number.class.isInstance(obj)
			? Number.class.cast(obj) : NumberUtil.toBigDecimal(Objects.toString(obj, null));
	}


	/**
	 * クエリパース
	 *
	 * @author Tadashi Nakayama
	 */
	private static final class ItemParser {
		/** キーワードタイプ（両方） */
		public static final String TYPE_AND = "AND";
		/** キーワードタイプ（片方） */
		public static final String TYPE_OR = "OR";

		/** キーワードマップ */
		private static final Map<String, String> KEYWORDS;

		/** 括弧スタック */
		private final Stack<Either<String, LineParsedNodeItem>> stack = new Stack<>();
		/** クエリ文字列 */
		private final String qry;
		/** パース位置 */
		private int loc;

		static {
			final var map = new HashMap<String, String>();
			map.put("AND", TYPE_AND);
			map.put("OR", TYPE_OR);
			map.put("WHERE", null);
			KEYWORDS = Collections.unmodifiableMap(map);
		}

		/**
		 * コンストラクタ
		 *
		 * @param query クエリ文字列
		 */
		ItemParser(final String query) {
			this.qry = query;
			this.stack.clear();
			this.loc = 0;
		}

		/**
		 * スタックプッシュ
		 *
		 * @param obj 値
		 */
		public void push(final LineParsedNodeItem obj) {
			if (obj != null) {
				this.stack.push(Either.right(obj));
			}
		}

		/**
		 * スタックプッシュ
		 *
		 * @param val 値
		 */
		public void push(final String val) {
			final var str = Objects.toString(val, "").trim();
			if (!str.isEmpty()) {
				this.stack.push(Either.left(str));
			}
		}

		/**
		 * スタックポップ
		 *
		 * @return 値
		 */
		public Either<String, LineParsedNodeItem> pop() {
			return this.stack.pop();
		}

		/**
		 * スタック空確認
		 *
		 * @return からの場合 true を返す。
		 */
		public boolean isEmpty() {
			return this.stack.isEmpty();
		}

		/**
		 * キーワード判断
		 *
		 * @param val 判断対象文字列
		 * @return キーワードの場合 true を返す。
		 */
		public static boolean isKeyword(final String val) {
			return val != null && KEYWORDS.containsKey(val.toUpperCase(Locale.ENGLISH));
		}

		/**
		 * キーワード判断
		 *
		 * @param val 判断対象文字列
		 * @return キーワードの場合 true を返す。
		 */
		public static String getKeywordType(final String val) {
			return Optional.ofNullable(val).
					map(v -> KEYWORDS.get(v.toUpperCase(Locale.ENGLISH))).orElse(null);
		}

		/**
		 * 次トークン取得
		 *
		 * @return 次トークン
		 */
		public String getNextToken() {
			int s = this.loc;
			while (s < this.qry.length() && Character.isWhitespace(this.qry.codePointAt(s))) {
				s = this.qry.offsetByCodePoints(s, 1);
			}

			var literal = false;
			var e = s;
			while (e < this.qry.length()) {
				final var cp = this.qry.codePointAt(e);
				if (cp == '\'') {
					literal = !literal;
				} else if (Character.isWhitespace(cp)) {
					if (!literal) {
						if (s == e) {
							s = this.qry.offsetByCodePoints(e, 1);
						} else {
							e = this.qry.offsetByCodePoints(e, 1);
							break;
						}
					}
				} else if (cp == '(' || cp == ')') {
					if (s == e) {
						e = this.qry.offsetByCodePoints(e, 1);
					}
					break;
				}
				e = this.qry.offsetByCodePoints(e, 1);
			}

			if (s < e) {
				this.loc = e;
				return this.qry.substring(s, e);
			}

			return null;
		}
	}

	/**
	 * Ether
	 *
	 * @author Tadashi Nakayama
	 * @param <L> 左要素ジェネリクス
	 * @param <R> 右要素ジェネリクス
	 */
	private static final class Either<L, R> implements Serializable {

		/** serialVersionUID */
		private static final long serialVersionUID = 1L;

		/** 左要素 */
		private final L left;
		/** 右要素 */
		private final R right;

		/**
		 * コンストラクタ
		 *
		 * @param l 左要素
		 * @param r 右要素
		 */
		private Either(final L l, final R r) {
			if (l == r) {
				throw new IllegalArgumentException();
			}

			this.left = l;
			this.right = r;
		}

		/**
		 * 左要素Either作成
		 *
		 * @param <L> 左要素ジェネリクス
		 * @param <R> 右要素ジェネリクス
		 * @param l 左要素
		 * @return 左要素Either
		 */
		public static <L, R> Either<L, R> left(final L l) {
			return new Either<>(l, null);
		}

		/**
		 * 左要素存在取得
		 *
		 * @return 左要素
		 */
		public L left() {
			return this.left;
		}

		/**
		 * 左要素存在確認
		 *
		 * @return 左要素が存在する場合 true を返す。
		 */
		public boolean isLeft() {
			return this.left != null;
		}

		/**
		 * 右要素取得
		 *
		 * @return 右要素
		 */
		public R right() {
			return this.right;
		}

		/**
		 * 右要素存在確認
		 *
		 * @return 右要素が存在する場合 true を返す。
		 */
		public boolean isRight() {
			return this.right != null;
		}

		/**
		 * 右要素Either作成
		 *
		 * @param <L> 左要素ジェネリクス
		 * @param <R> 右要素ジェネリクス
		 * @param r 右要素
		 * @return 右要素Either
		 */
		public static <L, R> Either<L, R> right(final R r) {
			return new Either<>(null, r);
		}

		/**
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			return Objects.toString(isLeft() ? left() : right(), "");
		}
	}
}
