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();
			// '*'
			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)) {
				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);
		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;
	}

	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);
		}
	}

}

@@ -566,24 +566,38 @@
  = it.next()
+.toLowerCase()
 ;%0A%09%09%09// '*'%0A
@@ -1359,24 +1359,38 @@
  map.get(key
+.toLowerCase()
 );%0A%09%09if (nul

Fix issue 392