package org.nutz.lang.util; import org.nutz.castor.Castors; import org.nutz.lang.Mirror; import org.nutz.lang.Strings; /** * 描述了一个区间 * <ul> * <li>T 对象必须实现有意义的 toString,并且字符串中不能包含半角逗号 * <li>T 对象必须可比较 * <li>T 对象必须可以被 Castors 正确的从字符串转换 * <li>T 对象的 toString() 和 Castors 的转换必须可逆 * </ul> * * 任何区间的字符串描述都符合: * * <pre> * 全封闭的区间 : [T0, T1] * 左开右闭区间 : (T0, T1] * 左闭右开区间 : [T0, T1) * 左开右闭区间 : (T0, T1] * 全开放的区间 : (T0, T1) * 精确等于某值 : (T0) 或 [T0) # 总之开闭区间无所谓了 * </pre> * * 比如对于数字: * * <pre> * [4,10] // >=4 && <=10 * (6,54] // >=6 && <54 * (,78) // <78 * </pre> * * 对于日期 * * <pre> * [2012-09-10 12:33:24, 2013-08-14] // 会自动交换大小值,可以是日期或者时间 * </pre> * * @author zozoh(zozohtnt@gmail.com) */ public abstract class Region<T extends Comparable<T>> { public static IntRegion Int(String str) { return new IntRegion(str); } public static LongRegion Long(String str) { return new LongRegion(str); } public static FloatRegion Float(String str) { return new FloatRegion(str); } public static DoubleRegion Double(String str) { return new DoubleRegion(str); } public static DateRegion Date(String str) { return new DateRegion(str); } protected Class<T> eleType; protected T left; protected T right; protected boolean leftOpen; protected boolean rightOpen; public T left() { return left; } public T right() { return right; } public boolean isLeftOpen() { return leftOpen; } public boolean isRightOpen() { return rightOpen; } /** * @return 是区间还是一个精确匹配的值 */ public boolean isRegion() { return left != right && !isNull(); } /** * @return 当前区间是否为空 */ public boolean isNull() { return null == left && null == right; } /** * 根据左边开闭区间的情况返回一个正确的左边比较的运算符 * * @param gt * 大于的运算符,开区间时采用 * @param gte * 大于等于的运算符,闭区间时采用 * @return 运算符 */ public String leftOpt(String gt, String gte) { if (null == left) return null; return leftOpen ? gt : gte; } /** * 根据右边开闭区间的情况返回一个正确的右边比较的运算符 * * @param lt * 小于的运算符,开区间时采用 * @param lte * 小于等于的运算符,闭区间时采用 * @return 运算符 */ public String rightOpt(String lt, String lte) { if (null == right) return null; return rightOpen ? lt : lte; } /** * @param obj * 对象 * @return 对象是否在这个区间 */ public boolean match(T obj) { if (null == obj) return false; if (!isRegion()) { return left.compareTo(obj) == 0; } if (null != left) { int c = obj.compareTo(left); if (c < 0 || c == 0 && leftOpen) { return false; } } if (null != right) { int c = obj.compareTo(right); if (c > 0 || c == 0 && rightOpen) { return false; } } return true; } @SuppressWarnings({"unchecked", "rawtypes"}) public Region() { eleType = (Class<T>) (Class) Mirror.getTypeParam(getClass(), 0); } /** * 解析 * * @param str * 字符串 * @return 自身 */ public Region<T> valueOf(String str) { String[] ss = str.substring(1, str.length() - 1).split(","); if (ss.length == 1) { left = fromString(ss[0]); right = left; } else { leftOpen = str.charAt(0) == '('; rightOpen = str.charAt(str.length() - 1) == ')'; left = fromString(ss[0]); right = fromString(ss[1]); // 看看是否需要交换交换... if (null != left && null != right && left.compareTo(right) > 0) { T o = right; right = left; left = o; } } return this; } public String toString(T obj) { return obj.toString(); } public T fromString(String str) { str = Strings.trim(str); if (Strings.isEmpty(str)) return null; return Castors.me().castTo(str, eleType); } public String toString() { if (this.isRegion()) return String.format("%c%s,%s%c", leftOpen ? '(' : '[', toString(left), toString(right), rightOpen ? ')' : ']'); return String.format("%c%s%c", leftOpen ? '(' : '[', toString(left), rightOpen ? ')' : ']'); } }