package org.nutz.mvc.impl;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.mvc.ActionContext;
public class MappingNode<T> {
private T obj;
private T asterisk;
private MappingNode<T> quesmark;
private Map<String, MappingNode<T>> map;
public MappingNode() {
map = new HashMap<String, MappingNode<T>>();
}
private void add(Iterator<String> it, T obj) {
// 还有路径
if (it.hasNext()) {
String key = it.next().toLowerCase();
// '*'
if ("*".equals(key)) {
if (it.hasNext()) {
throw Lang.makeThrow( "char '*' should be the last item in a Path '../*/%s/..'",
it.next());
}
asterisk = obj;
}
// '?'
else if ("?".equals(key)) {
if (quesmark == null) //也许这个节点之前就已经有值呢
quesmark = new MappingNode<T>();
quesmark.add(it, obj);
}
// 其它节点,加入 map
else {
MappingNode<T> node = map.get(key);
if (null == node) {
node = new MappingNode<T>();
map.put(key, node);
}
node.add(it, obj);
}
}
// 没有路径了
else {
this.obj = obj;
}
}
private T get(ActionContext ac, Iterator<String> it) {
// 路径已经没有内容了,看看本节点是否有一个对象
if (!it.hasNext()) {
return obj == null ? asterisk : obj;
}
String key = it.next();
// 先在 map 里寻找,
MappingNode<T> node = map.get(key.toLowerCase());
if (null != node)
return node.get(ac, it);
// 如果没有看看是否有 '?' 的匹配
if (quesmark != null) {
ac.getPathArgs().add(key);
return quesmark.get(ac, it);
}
// 还没有则看看是否有 '*' 的匹配
if (null != asterisk) {
List<String> pathArgs = ac.getPathArgs();
pathArgs.add(key);
while (it.hasNext())
pathArgs.add(it.next());
return asterisk;
}
return null;
}
/**
* 增加一个映射,将 obj 映射到 path 上,或 path 上的[?,*]
*/
public void add(String path, T obj) {
try {
add(Lang.list(Strings.splitIgnoreBlank(path, "/")).iterator(), obj);
}
catch (Exception e) {
throw Lang.wrapThrow(e, "Wrong Url path format '%s'", path);
}
}
public T get(ActionContext ac, String path) {
ac.setPath(path);
ac.setPathArgs(new LinkedList<String>());
return get(ac, Lang.list(Strings.splitIgnoreBlank(path, "/")).iterator());
}
public String toString() {
StringBuilder sb = new StringBuilder();
appendTo(sb, 0);
return sb.toString();
}
private void appendTo(StringBuilder sb, int indent) {
String prefix = Strings.dup(" ", indent);
sb.append(prefix).append('<').append(Strings.sNull(obj, "null")).append('>');
prefix = "\n " + prefix;
if (null != asterisk) {
sb.append(prefix).append(" * : ").append(asterisk.toString());
}
if (null != quesmark) {
sb.append(prefix).append(" ? : ");
quesmark.appendTo(sb, indent + 1);
}
for (String key : map.keySet()) {
sb.append(prefix).append(" '" + key + "' : ");
map.get(key).appendTo(sb, indent + 1);
}
}
}