package org.nutz.mvc.adaptor.injector; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 对象路径节点转换.<br/> * 将URL中的字符串参数名转换成对结构, 然后通过 {@link org.nutz.mapl.Mapl}转换成实体对象<br/> * URL规则: * <ul> * <li>对象与属性之间使用"."做为连接符 * <li>数组,Collection对象, 使用"[]"或":"做为索引引用符. <b style='color:red'>索引只是一个参考字段, 不会根据其值设置索引</b> * <li>Map使用"()"或"."分割key值 * </ul> * 例:<br> * <code> * Object: node.str = str<br> * list: node.list[1].str = abc;<br> * node.list:2.str = 2<br> * set: node.set[2].str = bbb<br> * node.set:jk.str = jk<br> * Map: node.map(key).str = bb;<br> * node.map.key.name = map<br> * * </code> * @author juqkai(juqkai@gmail.com) * */ public class ObjectNaviNode { private static final char separator = '.'; private static final char LIST_SEPARATOR = ':'; private static final int TYPE_NONE = 0; private static final int TYPE_LIST = 1; //节点名 private String name; //叶子节点的值 private String[] value; //是否是叶子节点 private boolean leaf = true; //子节点 private Map<String, ObjectNaviNode> child = new HashMap<String, ObjectNaviNode>(); //类型 private int type = TYPE_NONE; /** * 初始化当前结点 * */ public void put(String path, String[] value) { StringBuilder sb = new StringBuilder(); char[] chars = path.toCharArray(); OUT: for (int i = 0; i < chars.length; i++) { char c = chars[i]; switch (c) { case '[': case '(': i++; StringBuilder sb2 = new StringBuilder(); boolean isNumber = true; for (; i < chars.length; i++) { char c2 = chars[i]; switch (c2) { case ']': case ')': if ((c == '[' && c2 == ']') || (c == '(' && c2 == ')')) { if (isNumber && !(c == '(')) { sb.append(':').append(sb2); } else { sb.append('.').append(sb2); } continue OUT; } } isNumber = isNumber && Character.isDigit(c2); sb2.append(c2); } break; default: sb.append(c); break; } } path = sb.toString(); putPath(path, value); } private void putPath(String path, String[] value){ init(path); String subPath = fetchSubPath(path); if ("".equals(subPath) || path.equals(subPath)) { this.value = value; return; } leaf = false; addChild(subPath, value); } /** * 添加子结点 * */ private void addChild(String path, String[] value) { String subname = fetchName(path); ObjectNaviNode onn = child.get(subname); if (onn == null) { onn = new ObjectNaviNode(); } onn.putPath(path, value); child.put(subname, onn); } /** * 初始化name, type信息 * @param path */ private void init(String path){ String key = fetchNode(path); if(isList(key)){ type = TYPE_LIST; name = key.substring(0, key.indexOf(LIST_SEPARATOR)); return; } name = key; } /** * 提取子路径 * @param path */ private String fetchSubPath(String path){ if(isList(fetchNode(path))){ return path.substring(path.indexOf(LIST_SEPARATOR) + 1); } return path.substring(path.indexOf(separator) + 1); } /** * 取得节点名 * */ private static String fetchNode(String path) { if (path.indexOf(separator) <= 0) { return path; } return path.substring(0, path.indexOf(separator)); } /** * 取得节点的name信息 */ private String fetchName(String path){ String key = fetchNode(path); if(isList(key)){ return key.substring(0, key.indexOf(LIST_SEPARATOR)); } return key; } /** * 提取出list,map结构 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Object get(){ if(isLeaf()){ return value == null ? null : value.length == 1 ? value[0] : value; } if(type == TYPE_LIST){ // fix issue #1109, 列表的key需要重排 List list = new ArrayList(); List<Integer> keys = new ArrayList<Integer>(); List<String> keys2 = new ArrayList<String>(); for (String key : child.keySet()) { try { keys.add(Integer.parseInt(key)); } catch (NumberFormatException e) { keys2.add(key); } } Collections.sort(keys); for(Integer index : keys){ list.add(child.get(index.toString()).get()); } for(String key2 : keys2){ list.add(child.get(key2).get()); } return list; } Map map = new HashMap(); for(String o : child.keySet()){ map.put(o, child.get(o).get()); } return map; } /** * 是否是list节点 * @param key */ private static boolean isList(String key){ return key.indexOf(LIST_SEPARATOR) > 0; } public String getName() { return name; } public String[] getValue() { return value; } public boolean isLeaf() { return leaf; } }