package org.nutz.lang.util;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.nutz.castor.Castors;
import org.nutz.lang.Each;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Strings;
import org.nutz.lang.born.Borning;
/**
* 对于 LinkedHashMap 的一个友好封装
* <p>
* 同 TreeMap 不同的是,如果 get(null),它不会抛错,就是返回 null 或默认值
*
* @author zozoh(zozohtnt@gmail.com)
*/
@SuppressWarnings("serial")
public class NutMap extends LinkedHashMap<String, Object> implements NutBean {
public static NutMap WRAP(Map<String, Object> map) {
if (null == map)
return null;
if (map instanceof NutMap)
return (NutMap) map;
return new NutMap(map);
}
public NutMap() {
super();
}
public NutMap(Map<String, Object> map) {
super();
this.putAll(map);
}
public NutMap(String json) {
super();
this.putAll(Lang.map(json));
}
public NutMap(String key, Object value) {
super();
put(key, value);
}
/**
* 设置一个字段,如果值为 null 则表示移除
*
* @param key
* 键
* @param v
* 值
*/
public void setOrRemove(String key, Object v) {
if (null == v) {
this.remove(key);
} else {
this.put(key, v);
}
}
public static NutMap NEW() {
return new NutMap();
}
public static NutMap WRAP(String json) {
return new NutMap(json);
}
public boolean has(String key) {
return null != get(key);
}
public boolean is(String key, Object val) {
Object obj = this.get(key);
if (null == obj && null == val)
return true;
if (null == obj || null == val)
return false;
return obj.equals(val);
}
public NutMap duplicate() {
NutMap map = new NutMap();
map.putAll(this);
return map;
}
/**
* 从 Map 里挑选一些键生成一个新的 Map
*
* @param keys
* 键
* @return 新 Map
*/
public NutMap pick(String... keys) {
if (keys.length == 0)
return new NutMap();
NutMap re = new NutMap();
for (Map.Entry<String, Object> en : this.entrySet()) {
String key = en.getKey();
if (Lang.contains(keys, key)) {
re.put(key, en.getValue());
}
}
return re;
}
/**
* 从 Map 里挑选一些键生成一个新的 Map,自己同时删除这些键
*
* @param keys
* 键
* @return 新 Map
*/
public NutMap pickAndRemove(String... keys) {
if (keys.length == 0)
return new NutMap();
NutMap re = new NutMap();
for (String key : keys) {
Object val = this.remove(key);
re.put(key, val);
}
return re;
}
/**
* 从 Map 里挑选一些键生成一个新的 Map
*
* @param regex
* 匹配键的正则表达式,"!" 开头,表示取反
* @return 新 Map
*/
public NutMap pickBy(String regex) {
if (Strings.isBlank(regex))
return this.duplicate();
boolean isNot = regex.startsWith("!");
Pattern p = Pattern.compile(isNot ? regex.substring(1) : regex);
return pickBy(p, isNot);
}
/**
* 从 Map 里挑选一些键生成一个新的 Map
*
* @param p
* 匹配键的正则表达式,null 不会匹配任何一个键
* @param isNot
* true 表示被匹配上的会被忽略,false 表示被匹配上的才加入到返回的集合里
* @return 新 Map
*/
public NutMap pickBy(Pattern p, boolean isNot) {
// 一定不匹配
if (null == p) {
return isNot ? this.duplicate() : new NutMap();
}
// 挑选
NutMap re = new NutMap();
for (Map.Entry<String, Object> en : this.entrySet()) {
String key = en.getKey();
boolean matched = p.matcher(key).find();
if (matched) {
if (!isNot) {
re.put(key, en.getValue());
}
} else if (isNot) {
re.put(key, en.getValue());
}
}
// 返回
return re;
}
/**
* 就是 pickAndRemoveBy 的一个便利写法
*
* @param regex
* 正则表达式,! 开头表示取反
* @return 新 Map
*
* @see #pickAndRemoveBy(Pattern, boolean)
*/
public NutMap pickAndRemoveBy(String regex) {
if (Strings.isBlank(regex))
return new NutMap();
boolean isNot = regex.startsWith("!");
Pattern p = Pattern.compile(isNot ? regex.substring(1) : regex);
return pickAndRemoveBy(p, isNot);
}
/**
* 从 Map 里挑选一些键生成一个新的 Map,自己同时删除这些键
*
* @param p
* 匹配键的正则表达式,null 不会匹配任何一个键
* @param isNot
* true 表示被匹配上的会被忽略,false 表示被匹配上的才加入到返回的集合里
* @return 新 Map
*/
public NutMap pickAndRemoveBy(Pattern p, boolean isNot) {
// 一定不匹配
if (null == p) {
if (isNot) {
NutMap re = this.duplicate();
this.clear();
return re;
} else {
return new NutMap();
}
}
// 挑选
NutMap re = new NutMap();
List<String> delKeys = new ArrayList<String>(this.size());
for (Map.Entry<String, Object> en : this.entrySet()) {
String key = en.getKey();
boolean matched = p.matcher(key).find();
if (matched) {
if (!isNot) {
delKeys.add(key);
re.put(key, en.getValue());
}
} else if (isNot) {
delKeys.add(key);
re.put(key, en.getValue());
}
}
// 删除 Key
for (String key : delKeys)
this.remove(key);
// 返回
return re;
}
/**
* 从 Map 里将指定的键过滤,生成一个新的 Map
*
* @param keys
* 键
* @return 新 Map
*/
public NutMap omit(String... keys) {
NutMap re = new NutMap();
for (Map.Entry<String, Object> en : this.entrySet()) {
String key = en.getKey();
if (!Lang.contains(keys, key)) {
re.put(key, en.getValue());
}
}
return re;
}
/**
* 如果一个键的值无效(has(key) 返回 false),那么为其设置默认值
*
* @param key
* 键
* @param dft
* 值
* @return 自身以便链式赋值
*/
public NutMap putDefault(String key, Object dft) {
if (!this.has(key)) {
this.put(key, dft);
}
return this;
}
@Override
public boolean containsValue(Object value) {
if (null == _map)
return super.containsValue(value);
return super.containsValue(value) || _map.containsValue(value);
}
@Override
public boolean containsKey(Object key) {
if (null == _map)
return super.containsKey(key);
return super.containsKey(key) || _map.containsKey(key);
}
public Set<String> keySet() {
if (null == _map)
return super.keySet();
HashSet<String> keys = new HashSet<String>();
keys.addAll(super.keySet());
keys.addAll(_map.keySet());
return keys;
}
public Collection<Object> values() {
if (null == _map)
return super.values();
List<Object> vals = new LinkedList<Object>();
vals.addAll(super.values());
vals.addAll(_map.values());
return vals;
}
public Set<Entry<String, Object>> entrySet() {
if (null == _map)
return super.entrySet();
HashSet<Entry<String, Object>> vals = new HashSet<Entry<String, Object>>();
vals.addAll(_map.entrySet());
vals.addAll(super.entrySet());
return vals;
}
public void clear() {
super.clear();
if (null != _map)
_map.clear();
}
private NutMap _map;
public NutMap attach(NutMap map) {
_map = map;
return this;
}
public NutMap detach() {
NutMap re = _map;
_map = null;
return re;
}
@Override
public Object get(Object key) {
if (_map == null)
return super.get(key);
if (super.containsKey(key)) {
return super.get(key);
}
return _map.get(key);
}
public Object get(String key, Object dft) {
Object v = get(key);
return null == v ? dft : v;
}
public int getInt(String key) {
return getInt(key, -1);
}
public int getInt(String key, int dft) {
Object v = get(key);
return null == v ? dft : Castors.me().castTo(v, int.class);
}
public float getFloat(String key) {
return getFloat(key, Float.NaN);
}
public float getFloat(String key, float dft) {
Object v = get(key);
return null == v ? dft : Castors.me().castTo(v, float.class);
}
public long getLong(String key) {
return getLong(key, -1);
}
public long getLong(String key, long dft) {
Object v = get(key);
return null == v ? dft : Castors.me().castTo(v, long.class);
}
public double getDouble(String key) {
return getDouble(key, 0.0);
}
public double getDouble(String key, double dft) {
Object v = get(key);
return null == v ? dft : Castors.me().castTo(v, double.class);
}
public boolean getBoolean(String key) {
return getBoolean(key, false);
}
public boolean getBoolean(String key, boolean dft) {
Object v = get(key);
return null == v ? dft : Castors.me().castTo(v, boolean.class);
}
public String getString(String key) {
return getString(key, null);
}
@SuppressWarnings("rawtypes")
public String getString(String key, String dft) {
Object v = get(key);
if (v == null)
return dft;
if (v instanceof CharSequence)
return v.toString();
if (v instanceof List) {
v = ((List) v).iterator().next();
}
// by wendal : 这还有必要castTo么?
// zozoh: 当然有啦,比如日期对象,要变成字符串的话 ...
return Castors.me().castTo(v, String.class);
}
public Date getTime(String key) {
return getTime(key, null);
}
public Date getTime(String key, Date dft) {
Object v = get(key);
return null == v ? dft : Castors.me().castTo(v, Date.class);
}
public <T extends Enum<T>> T getEnum(String key, Class<T> classOfEnum) {
String s = getString(key);
if (Strings.isBlank(s))
return null;
return Enum.valueOf(classOfEnum, s);
}
@SuppressWarnings("unchecked")
public boolean isEnum(String key, Enum<?>... eus) {
if (null == eus || eus.length == 0)
return false;
try {
Enum<?> v = getEnum(key, eus[0].getClass());
for (Enum<?> eu : eus)
if (!v.equals(eu))
return false;
return true;
}
catch (Exception e) {
return false;
}
}
public <T> T getAs(String key, Class<T> classOfT) {
return getAs(key, classOfT, null);
}
public <T> T getAs(String key, Class<T> classOfT, T dft) {
Object v = get(key);
return null == v ? dft : Castors.me().castTo(v, classOfT);
}
public <T> List<T> getList(String key, final Class<T> eleType) {
return getList(key, eleType, new ArrayList<T>());
}
@SuppressWarnings("unchecked")
public <T> List<T> getList(String key, final Class<T> eleType, List<T> dft) {
Object v = get(key);
if (null == v)
return dft;
if (v instanceof CharSequence) {
return Lang.list(Castors.me().castTo(v, eleType));
}
int len = Lang.length(v);
final List<T> list = new ArrayList<T>(len);
Lang.each(v, new Each<Object>() {
public void invoke(int index, Object ele, int length) {
list.add(Castors.me().castTo(ele, eleType));
}
});
return list;
}
@SuppressWarnings("unchecked")
@Override
public <T> T[] getArray(String key, Class<T> eleType) {
return getArray(key, eleType, (T[]) Array.newInstance(eleType, 0));
}
@SuppressWarnings("unchecked")
public <T> T[] getArray(String key, final Class<T> eleType, T[] dft) {
Object v = get(key);
if (null == v)
return dft;
if (v instanceof CharSequence) {
return Lang.array(Castors.me().castTo(v, eleType));
}
int len = Lang.length(v);
final Object arr = Array.newInstance(eleType, len);
final int[] i = new int[]{0};
Lang.each(v, new Each<Object>() {
public void invoke(int index, Object ele, int length) {
Array.set(arr, i[0]++, Castors.me().castTo(ele, eleType));
}
});
return (T[]) arr;
}
/**
* 为 Map 增加一个名值对。如果同名已经有值了,那么会将两个值合并成一个列表
*
* @param key
* @param value
*/
@SuppressWarnings("unchecked")
public NutMap addv(String key, Object value) {
Object obj = get(key);
if (null == obj) {
put(key, value);
} else if (obj instanceof List<?>)
((List<Object>) obj).add(value);
else {
List<Object> list = new LinkedList<Object>();
list.add(obj);
list.add(value);
put(key, list);
}
return this;
}
/**
* @deprecated 本函数意义容易发生混淆,已经改名成 addv,下个版将被删除
* @since 1.b.51
*/
public NutMap putv(String key, Object value) {
return addv(key, value);
}
public NutMap setv(String key, Object value) {
this.put(key, value);
return this;
}
public void unset(String key) {
this.remove(key);
}
public NutBean setAll(Map<String, Object> map) {
this.putAll(map);
return this;
}
public NutMap setMap(Map<?, ?> map, boolean ignoreNullValue) {
for (Map.Entry<?, ?> en : map.entrySet()) {
Object key = en.getKey();
Object val = en.getValue();
if (null == key)
continue;
if (null == val && ignoreNullValue)
continue;
this.put(key.toString(), val);
}
return this;
}
/**
* 相当于 mergeWith(map, false)
*
* @see #mergeWith(Map, boolean)
*/
public NutMap mergeWith(Map<String, Object> map) {
return this.mergeWith(map, false);
}
/**
* 与一个给定的 Map 融合,如果有子 Map 递归
*
* @param map
* 要合并进来的 Map
* @param onlyAbsent
* true 表示只有没有 key 才设置值
* @return 自身以便链式赋值
*/
@SuppressWarnings("unchecked")
public NutMap mergeWith(Map<String, Object> map, boolean onlyAbsent) {
for (Map.Entry<String, Object> en : map.entrySet()) {
String key = en.getKey();
Object val = en.getValue();
if (null == key || null == val)
continue;
Object myVal = this.get(key);
// 如果两边都是 Map ,则融合
if (null != myVal && myVal instanceof Map && val instanceof Map) {
Map<String, Object> m0 = (Map<String, Object>) myVal;
Map<String, Object> m1 = (Map<String, Object>) val;
NutMap m2 = NutMap.WRAP(m0).mergeWith(m1, onlyAbsent);
// 搞出了新 Map,设置一下
if (m2 != m0)
this.put(key, m2);
}
// 只有没有的时候才设置
else if (onlyAbsent) {
this.setnx(key, val);
}
// 否则直接替换
else {
this.put(key, val);
}
}
return this;
}
/**
* 与JDK8+的 putIfAbsent(key, val)一致, 当且仅当值不存在时设置进去,但与putIfAbsent返回值有不一样
*
* @param key
* 键
* @param val
* 值
* @return 当前的NutMap实例
*/
public NutMap setnx(String key, Object val) {
if (!containsKey(key))
setv(key, val);
return this;
}
/**
* 将一个集合与自己补充(相当于针对每个 key 调用 setnx)
*
* @param map
* 集合
* @return 自身
*
* @see #setnx(String, Object)
*/
public NutMap setnxAll(Map<String, Object> map) {
if (null != map && map.size() > 0) {
for (Map.Entry<String, Object> en : map.entrySet()) {
this.setnx(en.getKey(), en.getValue());
}
}
return this;
}
/**
* 获取对应的值,若不存在,用factory创建一个,然后设置进去,返回之
*
* @param key
* 键
* @param factory
* 若不存在的话用于生成实例
* @return 已存在的值或新的值
*/
@SuppressWarnings("unchecked")
public <T> T getOrBorn(String key, Borning<T> factory) {
T t = (T) get(key);
if (t == null) {
t = factory.born(key);
put(key, t);
}
return t;
}
/**
* 将自身作为一个条件,看看给定的 Map 是否全部满足这个条件
* <p>
* 注意,字符串型的值有下列意义
* <ul>
* <li>"^xxxxx" : 正则表达式
* <li>"" : 相当于 Blank
* </ul>
*
* @param map
* 给定的 Map
* @return 是否匹配
*/
public boolean match(Map<String, Object> map) {
// 空 map 一定是不匹配的
if (null == map)
return false;
// 本 Map 如果没值,表示全匹配
if (this.size() == 0)
return true;
// 逐个匹配键
for (Map.Entry<String, Object> en : this.entrySet()) {
String key = en.getKey();
Object mtc = en.getValue();
// null 表示对方不能包括这个键
if (null == mtc) {
if (map.containsKey(key))
return false;
}
// 其他的值,匹配一下
else {
Object val = map.get(key);
if (!__match_val(mtc, val)) {
return false;
}
}
}
// 都检查过了 ...
return true;
}
private boolean __match_val(final Object mtc, Object val) {
Mirror<?> mi = Mirror.me(mtc);
// 如果为 null,则只有空串能匹配
if (null == val) {
return mi.isStringLike() && Strings.isEmpty(mtc.toString());
}
// 字符串的话
Pattern regex = mi.is(Pattern.class) ? (Pattern) mtc : null;
if (mi.isStringLike()) {
final String s = mtc.toString();
if (s.startsWith("^")) {
regex = Pattern.compile(s);
}
// 不是正则表达式,那么精确匹配字符串
else {
final boolean[] re = new boolean[1];
Lang.each(val, new Each<Object>() {
public void invoke(int index, Object ele, int length) {
if (null != ele && ele.equals(s)) {
re[0] = true;
Lang.Break();
}
}
});
return re[0];
}
}
// 正则表达式
if (null != regex) {
final boolean[] re = new boolean[1];
final Pattern REG = regex;
Lang.each(val, new Each<Object>() {
public void invoke(int index, Object ele, int length) {
if (null != ele && REG.matcher(ele.toString()).matches()) {
re[0] = true;
Lang.Break();
}
}
});
return re[0];
}
// 简单类型的比较
if (mi.isSimple()) {
final boolean[] re = new boolean[1];
Lang.each(val, new Each<Object>() {
public void invoke(int index, Object ele, int length) {
if (null != ele && ele.equals(mtc)) {
re[0] = true;
Lang.Break();
}
}
});
return re[0];
}
// 范围的话...
else if (mi.is(Region.class)) {
throw Lang.noImplement();
}
// 其他的统统为不匹配
return false;
}
}