package org.nutz.lang.util;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.Xmls;
import org.nutz.lang.meta.Pair;

/**
 * 简便的 Tag 实现
 *
 * @author zozoh(zozohtnt@gmail.com)
 */
public class Tag extends SimpleNode<HtmlToken> {

    /**
     * 存储一段 HTML 片段，如果这个有值，那么 _join_to_string() 的时候，会直接使用它 TODO zozoh:
     * 我知道这是一个丑陋的实现，但是有什么办法，今天晚上就要用啊。我来不及写个 HTML 的解析器 -_-! 以后有机会，应该写个好点的 HTML
     * 解析类。Jsoup 那玩意稍微有点弱啊~~~
     */
    private String htmlSegment;

    public static Tag tag(String name, String... attrs) {
        return NEW(name).attrs(attrs);
    }

    public static Tag NEW(String name) {
        Tag tag = new Tag();
        tag.set(new HtmlToken().setName(name));
        return tag;
    }

    public static Tag text(String text) {
        Tag tag = new Tag();
        if (null != text) {
            text = Strings.escapeHtml(text);
        }
        tag.set(new HtmlToken().setValue(text));
        return tag;
    }

    public static Tag html(String html) {
        Tag tag = new Tag();
        tag.htmlSegment = html;
        return tag;
    }

    public boolean isBlock() {
        return this.is("^(HEAD|DIV|P|UL|OL|LI|BLOCKQUOTE|PRE|TITLE|H[1-9]|HR|TABLE|TR|TD)$");
    }

    public boolean isInline() {
        return this.is("^(SPAN|B|I|U|EM|DEL|STRONG|SUB|SUP|CODE|FONT)$");
    }

    public boolean isNoChild() {
        return this.is("^(BR|HR|IMG|LINK|META|INPUT)$");
    }

    public boolean isHeading() {
        return this.is("^H[1-9]$");
    }

    /**
     * 标题级别
     *
     * @return 0 表示不是标题， 1-6 分别表示标题级别
     */
    public int getHeadingLevel() {
        if (this.isElement()) {
            Matcher m = Pattern.compile("^H([1-9])$").matcher(tagName());
            if (m.find())
                return Integer.parseInt(m.group(1));
        }
        return 0;
    }

    public boolean isList() {
        return this.is("^[OU]L$");
    }

    public boolean is(String regex) {
        String tagName = this.tagName();
        if (null == tagName)
            return false;
        if (regex.startsWith("^"))
            return tagName.matches(regex.toUpperCase());
        return tagName.equals(regex.toUpperCase());
    }

    public boolean isHtml() {
        return this.is("HTML");
    }

    public boolean isBody() {
        return this.is("BODY");
    }

    public boolean isElement() {
        if (null != htmlSegment)
            return true;
        return this.get().isElement();
    }

    public boolean isTextNode() {
        if (null != htmlSegment)
            return false;
        return this.get().isText();
    }

    public boolean isChildAllInline() {
        if (!get().isElement())
            return false;
        for (Node<HtmlToken> ht : this.getChildren())
            if (((Tag) ht).isBlock())
                return false;
        return true;
    }

    public List<Tag> getChildTags() {
        List<Node<HtmlToken>> list = this.getChildren();
        List<Tag> tags = new ArrayList<Tag>(list.size());
        for (Node<HtmlToken> ele : list) {
            tags.add((Tag) ele);
        }
        return tags;
    }

    public String name() {
        return get().getName();
    }

    public String tagName() {
        if (null != this.htmlSegment) {
            if (this.htmlSegment.startsWith("<")) {
                int pos = this.htmlSegment.indexOf(' ');
                if (pos > 1)
                    return this.htmlSegment.substring(1, pos);
            }
            return null;
        }
        return get().getTagName();
    }

    public Tag attr(String name, String value) {
        get().attr(name, value);
        return this;
    }

    public Tag attr(String name, int value) {
        return attr(name, String.valueOf(value));
    }

    public String attr(String name) {
        return this.get().getAttrVal(name);
    }

    public Tag attrs(String... attrs) {
        if (null != attrs) {
            for (String attr : attrs) {
                if (null != attr && attr.length() > 1) {
                    char c = attr.charAt(0);
                    switch (c) {
                    case '.':
                        this.addClass(attr.substring(1));
                        break;
                    case '#':
                        this.id(attr.substring(1));
                        break;
                    default:
                        Pair<String> p = Pair.create(attr);
                        this.attr(p.getName(), p.getValue());
                    }
                }
            }
        }
        return this;
    }

    public Tag addClass(String name) {
        String cns = get().getAttrVal("class");
        String[] nms = Strings.splitIgnoreBlank(cns, " ");
        if (null == nms) {
            get().attr("class", name);
        } else {
            if (!Lang.contains(nms, name)) {
                get().attr("class", cns + " " + name);
            }
        }
        return this;
    }

    public boolean hasClass(String name) {
        String cns = get().getAttrVal("class");
        if (null == cns || cns.length() < name.length())
            return false;
        return (" " + cns + " ").indexOf(" " + name + " ") != -1;
    }

    public Tag add(String tagName, String... attrs) {
        Tag re = Tag.tag(tagName, attrs);
        this.add(re);
        return re;
    }

    public Tag id(String id) {
        get().attr("id", id);
        return this;
    }

    public String id() {
        return get().getAttrVal("id");
    }

    public String getNodeValue() {
        HtmlToken ht = this.get();
        if (null != ht)
            return ht.getValue();
        return null;
    }

    public String getText() {
        StringBuilder sb = new StringBuilder();
        for (Node<HtmlToken> nd : this.getChildren()) {
            Tag tag = (Tag) nd;
            // 文本
            if (tag.isTextNode()) {
                sb.append(nd.get().getValue());
            }
            // 空格
            else if (tag.isNoChild()) {
                sb.append(' ');
            }
            // 其他
            else {
                sb.append(tag.getText());
            }
        }
        return sb.toString();
    }

    public String getTextContent() {
        String re = this.getText();
        if (Strings.isBlank(re)) {
            re = this.getNodeValue();
        }
        if (Strings.isBlank(re)) {
            re = this.htmlSegment;
        }
        return re;
    }

    public Tag setText(String text) {
        this.add(Tag.text(text));
        return this;
    }

    public List<Tag> childrenTag() {
        List<Node<HtmlToken>> children = this.getChildren();
        List<Tag> list = new ArrayList<Tag>(children.size());
        for (Node<HtmlToken> nd : children) {
            list.add((Tag) nd);
        }
        return list;
    }

    public String toString() {
        return toString(0);
    }

    public String toString(int level) {
        StringBuilder sb = new StringBuilder();
        __join_to_string(sb, this, level, true, null);
        return sb.toString();
    }

    public String toOuterHtml(boolean autoIndent) {
        return toOuterHtml(autoIndent, null);
    }

    public String toOuterHtml(boolean autoIndent, Callback<Tag> tagWatcher) {
        int level = autoIndent ? 0 : -1;
        StringBuilder sb = new StringBuilder();
        __join_to_string(sb, this, level, false, tagWatcher);
        return sb.toString();
    }

    public String toInnerHtml(boolean autoIndent) {
        return toInnerHtml(autoIndent, null);
    }

    public String toInnerHtml(boolean autoIndent, Callback<Tag> tagWatcher) {
        int level = autoIndent ? 0 : -1;
        StringBuilder sb = new StringBuilder();

        for (Node<HtmlToken> child : this.getChildren()) {
            Tag childTag = (Tag) child;

            __join_to_string(sb, childTag, level, false, tagWatcher);

            if (childTag.isBlock() || childTag.isBody())
                sb.append('\n');
        }
        return sb.toString();
    }

    private static void __join_to_string(StringBuilder sb,
                                         Tag tag,
                                         int level,
                                         boolean closeNoChild,
                                         Callback<Tag> tagWatcher) {
        // 预处理 Tag
        if (null != tagWatcher) {
            tagWatcher.invoke(tag);
        }

        // HTML 片段
        if (null != tag.htmlSegment) {
            sb.append(tag.htmlSegment);
            return;
        }

        // 纯文本
        if (tag.get().isText()) {
            sb.append(tag.get().getValue());
            return;
        }

        // 统一的缩进前缀
        String prefix = level >= 0 ? Strings.dup(' ', level * 4) : null;

        // 无子节点的标签
        if (tag.isNoChild()) {
            __join_tag_prefix(sb, tag, prefix);
            sb.append('<').append(tag.name());
            __join_attributes(sb, tag);
            if (closeNoChild)
                sb.append('/');
            sb.append('>');
        }
        // 行内元素
        else if (tag.isInline()) {
            __join_tag_prefix(sb, tag, prefix);
            __join_tag_begin(sb, tag);
            for (Node<HtmlToken> child : tag.getChildren()) {
                __join_to_string(sb, (Tag) child, level, closeNoChild, tagWatcher);
            }
            __join_tag_end(sb, tag);
        }
        // 那么就是块元素咯
        else {
            __join_tag_prefix(sb, tag, prefix);
            __join_tag_begin(sb, tag);

            for (Node<HtmlToken> child : tag.getChildren()) {
                Tag childTag = (Tag) child;

                if (childTag.isBlock() || childTag.isBody())
                    sb.append('\n');

                __join_to_string(sb,
                                 childTag,
                                 level >= 0 ? level + 1 : level,
                                 closeNoChild,
                                 tagWatcher);
            }
            sb.append('\n');
            __join_tag_prefix(sb, tag, prefix);
            __join_tag_end(sb, tag);
        }
    }

    private static void __join_tag_prefix(StringBuilder sb, Tag tag, String prefix) {
        if (null != prefix && prefix.length() > 0)
            sb.append(prefix);
    }

    private static void __join_tag_begin(StringBuilder sb, Tag tag) {
        sb.append('<').append(tag.name());
        __join_attributes(sb, tag);
        sb.append('>');
    }

    private static void __join_tag_end(StringBuilder sb, Tag tag) {
        sb.append("</").append(tag.name()).append('>');
    }

    private static void __join_attributes(StringBuilder sb, Tag tag) {
        for (Pair<String> attr : tag.get().getAttributes()) {
            String name = attr.getName();
            String n2 = name.toLowerCase();
            // 无需 value 节点
            if (n2.matches("^(disabled|checked)$")) {
                sb.append(' ').append(name);
            }
            // 输出值
            else {
                sb.append(' ').append(attr.toString());
            }
        }
    }

    @SuppressWarnings("rawtypes")
    public void toXml(StringBuilder sb, int level) {
        if (level == 0)
            sb.append(Xmls.HEAD);
        if (sb.length() > 2 && sb.charAt(sb.length() - 1) != '\n')
            sb.append("\r\n");
        if (level > 0)
            sb.append(Strings.dup(' ', level * 2));
        __join_tag_begin(sb, this);
        if (hasChild()) {
            boolean flag = true;
            if (getChildren().size() == 1) {
                if (getChildren().get(0).get().getName() == null) {
                    sb.append(getText());
                    flag = false;
                }
            }
            if (flag) {
                for (Node node : getChildren()) {
                    node.toXml(sb, level + 1);
                }
                if (level > 0)
                    sb.append(Strings.dup(' ', level * 2));
            }
        }
        __join_tag_end(sb, this);
        sb.append("\r\n");
    }
}


package org.nutz.lang;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.nutz.lang.util.Callback2;
import org.nutz.lang.util.NutMap;
import org.nutz.lang.util.Regex;
import org.nutz.lang.util.Tag;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * XML 的快捷帮助函数
 *
 * @author zozoh(zozohtnt@gmail.com)
 */
public abstract class Xmls {

    /**
     * 帮你快速获得一个 DocumentBuilder，方便 XML 解析。
     *
     * @return 一个 DocumentBuilder 对象
     * @throws ParserConfigurationException
     */
    public static DocumentBuilder xmls() throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        String FEATURE = null;

        // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented
        // Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl

        FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
        dbf.setFeature(FEATURE, true);

        // If you can't completely disable DTDs, then at least do the following:
        // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities

        // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities

        // JDK7+ - http://xml.org/sax/features/external-general-entities
        FEATURE = "http://xml.org/sax/features/external-general-entities";
        dbf.setFeature(FEATURE, false);

        // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities

        // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities

        // JDK7+ - http://xml.org/sax/features/external-parameter-entities
        FEATURE = "http://xml.org/sax/features/external-parameter-entities";
        dbf.setFeature(FEATURE, false);

        // Disable external DTDs as well
        FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
        dbf.setFeature(FEATURE, false);

        // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
        dbf.setXIncludeAware(false);
        dbf.setExpandEntityReferences(false);
        return dbf.newDocumentBuilder();
    }

    public static Document xml(InputStream ins) {
        return xml(ins, null);
    }

    /**
     * 快捷的解析 XML 文件的帮助方法，它会主动关闭输入流
     *
     * @param ins
     *            XML 文件输入流
     * @return Document 对象
     */
    public static Document xml(InputStream ins, Charset charset) {
        try {
            if (charset == null)
                charset = Encoding.CHARSET_UTF8;
            return xmls().parse(new InputSource(new InputStreamReader(ins, charset)));
        }
        catch (SAXException e) {
            throw Lang.wrapThrow(e);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        catch (ParserConfigurationException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Streams.safeClose(ins);
        }
    }

    public static Document xml(File xmlFile) {
        return xml(xmlFile, null);
    }

    /**
     * 快捷的解析 XML 文件的帮助方法
     *
     * @param xmlFile
     *            XML 文件
     * @return Document 对象
     */
    public static Document xml(File xmlFile, Charset charset) {
        InputStream ins = null;
        try {
            ins = new FileInputStream(xmlFile);
            return xml(ins, charset);
        }
        catch (Exception e) {
            throw Lang.wrapThrow(e);
        }
    }

    /**
     * 获取某元素下某节点的全部文本内容（去掉左右空白）
     *
     * @param ele
     *            元素
     * @param subTagName
     *            子元素名
     * @return 内容，null 表示子元素不存在, 空串表示元素没有对应文本内容
     */
    public static String get(Element ele, String subTagName) {
        Element sub = firstChild(ele, subTagName);
        if (null == sub)
            return null;
        return getText(sub);
    }

    public static String getText(Element ele) {
        StringBuilder sb = new StringBuilder();
        joinText(ele, sb);
        return Strings.trim(sb);
    }

    public static void joinText(Element ele, StringBuilder sb) {
        if (null == ele)
            return;
        NodeList nl = ele.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node nd = nl.item(i);
            switch (nd.getNodeType()) {
            case Node.TEXT_NODE:
                sb.append(nd.getNodeValue());
                break;
            case Node.CDATA_SECTION_NODE:
                sb.append(nd.getNodeValue());
                break;
            case Node.ELEMENT_NODE:
                joinText((Element) nd, sb);
                break;
            default:
                break;
            }
        }
    }

    /**
     * 获取 XML 元素内第一个子元素
     *
     * @param ele
     *            XML 元素
     * @return 子元素，null 表示不存在
     */
    public static Element firstChild(Element ele) {
        final Element[] tag = new Element[1];
        eachChildren(ele, null, new Each<Element>() {
            public void invoke(int index, Element cld, int length) {
                tag[0] = cld;
                Lang.Break();
            }
        });
        return tag[0];
    }

    /**
     * 获取 XML 元素内第一个名字所有符合一个正则表达式的子元素
     *
     * @param ele
     *            XML 元素
     * @param regex
     *            元素名称正则表达式
     * @return 子元素，null 表示不存在
     */
    public static Element firstChild(Element ele, String regex) {
        final Element[] tag = new Element[1];
        eachChildren(ele, regex, new Each<Element>() {
            public void invoke(int index, Element cld, int length) {
                tag[0] = cld;
                Lang.Break();
            }
        });
        return tag[0];
    }

    /**
     * 从一个 XML 元素开始，根据一条 XPath 获取一个元素
     *
     * @param ele
     *            XML 元素
     * @param xpath
     *            要获取的元素的 XPath
     * @return 元素，null 表示不存在
     */
    public static Element getEle(Element ele, String xpath) {
        XPathFactory factory = XPathFactory.newInstance();
        XPath xp = factory.newXPath();
        try {
            XPathExpression expression = xp.compile(xpath);
            return (Element) expression.evaluate(ele, XPathConstants.NODE);
        }
        catch (XPathExpressionException e) {
            throw Lang.wrapThrow(e);
        }

    }

    /**
     * 从某个元素里获取一个指定下标的子元素
     *
     * @param ele
     *            XML 元素
     * @param index
     *            子元素下标（0 base）
     * @return 子元素
     */
    public static Element getChild(Element ele, int index) {
        return getChild(ele, index, null);
    }

    /**
     * 从某个元素里获取一个指定下标且指定名称的子元素
     *
     * @param ele
     *            XML 元素
     * @param index
     *            子元素下标（0 base）
     * @param regex
     *            元素名称的正则表达式
     * @return 子元素
     */
    public static Element getChild(Element ele, final int index, String regex) {
        final int pos = index;
        final Element[] tag = new Element[1];
        eachChildren(ele, null, new Each<Element>() {
            public void invoke(int index, Element cld, int length) {
                if (index >= pos) {
                    tag[0] = cld;
                    Lang.Break();
                }
            }
        });
        return tag[0];
    }

    /**
     * 获取 XML 元素内最后一个子元素
     *
     * @param ele
     *            XML 元素
     * @return 子元素，null 表示不存在
     */
    public static Element lastChild(Element ele) {
        final Element[] tag = new Element[1];
        eachChildren(ele, null, new Each<Element>() {
            public void invoke(int index, Element cld, int length) {
                tag[0] = cld;
                Lang.Break();
            }
        }, -1);
        return tag[0];
    }

    /**
     * 获取 XML 元素内最后一个名字所有符合一个正则表达式的子元素
     *
     * @param ele
     *            XML 元素
     * @param regex
     *            元素名称正则表达式
     * @return 子元素，null 表示不存在
     */
    public static Element lastChild(Element ele, String regex) {
        final Element[] tag = new Element[1];
        eachChildren(ele, regex, new Each<Element>() {
            public void invoke(int index, Element cld, int length) {
                tag[0] = cld;
                Lang.Break();
            }
        }, -1);
        return tag[0];
    }

    /**
     * 获取 XML 元素内所有子元素
     *
     * @param ele
     *            XML 元素
     * @return 一个子元素的列表
     */
    public static List<Element> children(Element ele) {
        return children(ele, null);
    }

    /**
     * 获取 XML 元素内名字符合一个正则表达式的元素
     *
     * @param ele
     *            XML 元素
     * @param regex
     *            元素名称正则表达式
     * @return 一个子元素的列表
     */
    public static List<Element> children(Element ele, String regex) {
        final List<Element> list = new ArrayList<Element>(ele.getChildNodes().getLength());
        eachChildren(ele, regex, new Each<Element>() {
            public void invoke(int index, Element cld, int length) {
                list.add(cld);
            }
        });
        return list;
    }

    /**
     * 迭代 XML 元素内所有子元素
     *
     * @param ele
     *            XML 元素
     * @param callback
     *            回调
     */
    public static void eachChildren(Element ele, Each<Element> callback) {
        eachChildren(ele, null, callback);
    }

    /**
     * 迭代 XML 元素内名字符合一个正则表达式的子元素
     *
     * @param ele
     *            XML 元素
     * @param regex
     *            元素名称正则表达式
     * @param callback
     *            回调
     */
    public static void eachChildren(Element ele, String regex, final Each<Element> callback) {
        Xmls.eachChildren(ele, regex, callback, 0);
    }

    /**
     * 判断某个元素下是否有子元素
     *
     * @param ele
     *            元素
     * @param regex
     *            子元素名称的正则表达式，如果为 null，则元素内如果有任意元素都会返回 false
     * @return 是否有子元素
     */
    public static boolean hasChild(Element ele, String regex) {
        NodeList nl = ele.getChildNodes();
        int len = nl.getLength();
        for (int i = 0; i < len; i++) {
            Node nd = nl.item(i);
            if (nd instanceof Element) {
                if (null == regex)
                    return false;
                if (Regex.match(regex, ((Element) nd).getTagName()))
                    return true;
            }
        }
        return false;
    }

    /**
     * 迭代 XML 元素内名字符合一个正则表达式的子元素
     *
     * @param ele
     *            XML 元素
     * @param regex
     *            元素名称正则表达式
     * @param callback
     *            回调
     * @param off
     *            偏移量。0 表示从第一个迭代。 -1 表示从最后一个迭代。-2表示从倒数第二个迭代
     */
    public static void eachChildren(Element ele,
                                    String regex,
                                    final Each<Element> callback,
                                    int off) {
        if (null == ele || null == callback)
            return;

        // 正则式
        final Pattern p = null == regex ? null : Pattern.compile(regex);

        NodeList nl = ele.getChildNodes();

        // 循环子
        final int len = nl.getLength();

        // 每次循环执行
        Callback2<Integer, Node> eachInvoke = new Callback2<Integer, Node>() {
            public void invoke(Integer index, Node nd) {
                if (nd instanceof Element)
                    try {
                        Element tag = (Element) nd;
                        if (null == p || p.matcher(tag.getTagName()).find())
                            callback.invoke(index, tag, len);
                    }
                    catch (ExitLoop e) {
                        throw Lang.wrapThrow(e);
                    }
                    catch (ContinueLoop e) {}
                    catch (LoopException e) {
                        throw Lang.wrapThrow(e);
                    }
            }
        };

        try {
            // 负向迭代
            if (off < 0) {
                for (int i = len + off; i >= 0; i--) {
                    eachInvoke.invoke(i, nl.item(i));
                }
            }
            // 正向迭代
            else {
                for (int i = off; i < len; i++) {
                    eachInvoke.invoke(i, nl.item(i));
                }
            }
        }
        catch (ExitLoop e) {}
        catch (RuntimeException e) {
            if (e.getCause() instanceof ExitLoop)
                return;
            else
                throw e;
        }
    }

    /**
     * 获取该 XML 元素内所有的属性的值，按照Map的形式返回
     *
     * @param ele
     *            XML 元素
     * @return 所有属性的值
     */
    public static Map<String, String> getAttrs(Element ele) {
        NamedNodeMap nodeMap = ele.getAttributes();
        Map<String, String> attrs = new HashMap<String, String>(nodeMap.getLength());
        for (int i = 0; i < nodeMap.getLength(); i++) {
            attrs.put(nodeMap.item(i).getNodeName(), nodeMap.item(i).getNodeValue());
        }
        return attrs;
    }

    /**
     * 从 XML 元素中得到指定属性的值，如该指定属性不存在，则返回Null
     *
     * @param ele
     *            XML 元素
     * @return 该指定属性的值
     */
    public static String getAttr(Element ele, String attrName) {
        Node node = ele.getAttributes().getNamedItem(attrName);
        return node != null ? node.getNodeValue() : null;
    }

    /**
     * 根据一个 XML 节点，将其变成一个 Map。
     *

     * 注意: 不支持混合节点
     *
     * @param ele
     *            元素
     *
     * @return 一个 Map 对象
     */
    public static NutMap asMap(Element ele) {
        return asMap(ele, false);
    }

    /**
     * 根据一个 XML 节点，将其变成一个 Map。
     *

     * 注意: 不支持混合节点
     *
     * @param ele
     *            元素
     * @param lowFirst
     *            是否把所有key的首字母都小写
     *
     * @return 一个 Map 对象
     */
    public static NutMap asMap(Element ele, final boolean lowFirst) {
        return asMap(ele, lowFirst, false);
    }
    public static NutMap asMap(Element ele, final boolean lowFirst, final boolean dupAsList) {
        return asMap(ele, lowFirst, dupAsList, null);
    }
    public static NutMap asMap(Element ele, final boolean lowerFirst, final boolean dupAsList, final List<String> alwaysAsList) {
        return asMap(ele, new XmlParserOpts(lowerFirst, dupAsList, alwaysAsList, false));
    }
    public static NutMap asMap(Element ele, final XmlParserOpts opts) {
        final NutMap map = new NutMap();
        if (opts.isAttrAsKeyValue()) {
            NamedNodeMap attrs = ele.getAttributes();
            for (int i = 0; i < attrs.getLength(); i++) {
                map.put(attrs.item(i).getNodeName(), attrs.item(i).getNodeValue());
            }
        }
        eachChildren(ele, new Each<Element>() {
            public void invoke(int index, Element _ele, int length)
                    throws ExitLoop, ContinueLoop, LoopException {
                String key = _ele.getNodeName();
                if (opts.lowerFirst)
                    key = Strings.lowerFirst(key);
                Map<String, Object> tmp = asMap(_ele, opts);
                if (!tmp.isEmpty()) {
                    if (opts.alwaysAsList != null && opts.alwaysAsList.contains(key)) {
                        map.addv2(key, tmp);
                    }
                    else if (opts.dupAsList) {
                        map.addv(key, tmp);
                    }
                    else {
                        map.setv(key, tmp);
                    }
                    return;
                }
                String val = getText(_ele);
                if (opts.keeyBlankNode || !Strings.isBlank(val)) {
                    if (opts.alwaysAsList != null && opts.alwaysAsList.contains(key)) {
                        map.addv2(key, val);
                    }
                    else if (opts.dupAsList)
                        map.addv(key, val);
                    else
                        map.setv(key, val);
                }
            }
        });
        return map;
    }

    /**
     * 将一个下面格式的 XML:
     *
     *

     * <xml>
     * <key1>value1</key1>
     * <key2>value2</key2>
     * </xml>
     *

     *
     * 转换成一个 Map
     *
     * @param xml
     *            XML 字符串
     *
     * @return Map
     */
    public static NutMap xmlToMap(String xml) {
        return Xmls.asMap(Xmls.xml(Lang.ins(xml)).getDocumentElement());
    }

    public static NutMap xmlToMap(InputStream ins) {
        return Xmls.asMap(Xmls.xml(ins).getDocumentElement());
    }

    public static NutMap xmlToMap(InputStream ins, final boolean lowerFirst, final boolean dupAsList, final List<String> alwaysAsList) {
        return Xmls.asMap(Xmls.xml(ins).getDocumentElement(), lowerFirst, dupAsList, alwaysAsList);
    }

    /**
     * 将一个 Map 转换成 XML 类似:
     *
     *

     * <xml>
     * <key1>value1</key1>
     * <key2>value2</key2>
     * </xml>
     *

     *
     * @param map
     *            Map
     * @return XML 字符串
     */
    public static String mapToXml(Map<String, Object> map) {
        return mapToXml("xml", map);
    }

    public static String mapToXml(String root, Map<String, Object> map) {
        StringBuilder sb = new StringBuilder();
        map2Tag(root, map).toXml(sb, 0);
        return sb.toString();
    }

    protected static Tag map2Tag(String rootName, Map<String, Object> map) {
        Tag rootTag = Tag.tag(rootName);
        for (Map.Entry<String, Object> en : map.entrySet()) {
            String key = en.getKey();
            Object val = en.getValue();
            List<Tag> children = obj2tag(key, val);
            for (Tag child : children) {
                rootTag.add(child);
            }
        }
        return rootTag;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static List<Tag> obj2tag(String nodeName, Object val) {
        List<Tag> tags = new ArrayList<Tag>();
        if (null == val)
            return tags;
        if (val instanceof Map) {
            tags.add(map2Tag(nodeName, (Map<String, Object>) val));
        } else if (val instanceof Collection) {
            for (Object object : (Collection)val) {
                for (Tag tag : obj2tag(nodeName, object)) {
                    tags.add(tag);
                }
            }
        } else {
            tags.add(Tag.tag(nodeName).setText(val.toString()));
        }
        return tags;
    }

    /**
     * 从一个 XML 元素开始，根据一条 XPath 获取一组元素
     *
     * @param ele
     *            XML 元素
     * @param xpath
     *            要获取的元素的 XPath
     * @return 元素列表
     */
    public static List<Element> getEles(Element ele, String xpath) {
        XPathFactory factory = XPathFactory.newInstance();
        XPath xp = factory.newXPath();
        try {
            XPathExpression expression = xp.compile(xpath);
            NodeList nodes = (NodeList) expression.evaluate(ele, XPathConstants.NODESET);
            List<Element> list = new ArrayList<Element>();
            int len = nodes.getLength();
            for (int i = 0; i < len; i++) {
                Node node = nodes.item(i);
                if (node instanceof Element) {
                    list.add((Element)node);
                }
            }
            return list;
        }
        catch (XPathExpressionException e) {
            throw Lang.wrapThrow(e);
        }
    }

    public static String HEAD = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";

    public static class XmlParserOpts {
        private boolean lowerFirst;
        private boolean dupAsList;
        private List<String> alwaysAsList;
        private boolean keeyBlankNode;
        private boolean attrAsKeyValue;
        public XmlParserOpts() {
        }

        public XmlParserOpts(boolean lowerFirst, boolean dupAsList, List<String> alwaysAsList, boolean keeyBlankNode) {
            super();
            this.lowerFirst = lowerFirst;
            this.dupAsList = dupAsList;
            this.alwaysAsList = alwaysAsList;
            this.keeyBlankNode = keeyBlankNode;
        }

        public boolean isLowerFirst() {
            return lowerFirst;
        }
        public void setLowerFirst(boolean lowerFirst) {
            this.lowerFirst = lowerFirst;
        }
        public boolean isDupAsList() {
            return dupAsList;
        }
        public void setDupAsList(boolean dupAsList) {
            this.dupAsList = dupAsList;
        }
        public List<String> getAlwaysAsList() {
            return alwaysAsList;
        }
        public void setAlwaysAsList(List<String> alwaysAsList) {
            this.alwaysAsList = alwaysAsList;
        }
        public boolean isKeeyBlankNode() {
            return keeyBlankNode;
        }
        public void setKeeyBlankNode(boolean keeyBlankNode) {
            this.keeyBlankNode = keeyBlankNode;
        }

        public boolean isAttrAsKeyValue() {
            return attrAsKeyValue;
        }

        public void setAttrAsKeyValue(boolean attrAsKeyValue) {
            this.attrAsKeyValue = attrAsKeyValue;
        }

    }
}


package org.nutz.el.arithmetic;

import java.util.LinkedList;
import java.util.Queue;

import org.nutz.el.ElCache;
import org.nutz.el.Operator;
import org.nutz.el.obj.Elobj;
import org.nutz.lang.util.Context;

/**
 * 逆波兰表示法（Reverse Polish notation，RPN，或逆波兰记法），是一种是由波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式，在逆波兰记法中，所有操作符置于操作数的后面，因此也被称为后缀表示法。

 * 参考:逆波兰表达式
 *
 * @author juqkai(juqkai@gmail.com)
 *
 */
public class RPN {
    //存放context
    private final ElCache ec = new ElCache();
    //预编译后的对象
    private LinkedList<Object> el;

    public RPN() {}

    /**
     * 进行EL的预编译
     */
    public RPN(Queue<Object> rpn) {
        compile(rpn);
    }

    /**
     * 执行已经预编译的EL
     */
    public Object calculate(Context context){
        ec.setContext(context);
        return calculate(el);
    }
    /**
     * 根据逆波兰表达式进行计算
     */
    public Object calculate(Context context, Queue<Object> rpn) {
        ec.setContext(context);
        LinkedList<Object> operand = OperatorTree(rpn);
        return calculate(operand);
    }

    /**
     * 计算
     */
    private Object calculate(LinkedList<Object> el2){
        if(el2.peek() instanceof Operator){
            Operator obj = (Operator) el2.peek();
            return obj.calculate();
        }
        if(el2.peek() instanceof Elobj){
            return ((Elobj) el2.peek()).fetchVal();
        }
        return el2.peek();
    }

    /**
     * 预先编译
     */
    public void compile(Queue<Object> rpn){
        el = OperatorTree(rpn);
    }

    /**
     * 转换成操作树
     */
    private LinkedList<Object> OperatorTree(Queue<Object> rpn){
        LinkedList<Object> operand = new LinkedList<Object>();
        while(!rpn.isEmpty()){
            if(rpn.peek() instanceof Operator){
                Operator opt = (Operator) rpn.poll();
                opt.wrap(operand);
                operand.addFirst(opt);
                continue;
            }
            if(rpn.peek() instanceof Elobj){
                ((Elobj) rpn.peek()).setEc(ec);
            }
            operand.addFirst(rpn.poll());
        }
        return operand;
    }
}


package org.nutz.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.nutz.castor.Castors;
import org.nutz.dao.pager.Pager;

/**
 * 封装了一个分页查询的结果集合，包括本页数据列表以及分页信息
 * @author zozoh(zozohtnt@gmail.com)
 * @author wendal(wendal1985@gmail.com)
 *
 * @see org.nutz.dao.pager.Pager
 */
public class QueryResult implements Serializable {

    private static final long serialVersionUID = 5104522523949248573L;
    private List<?> list;
    private Pager pager;

    /**
     * 新建一个分页查询的结果集合
     */
    public QueryResult() {}

    /**
     * 一个分页查询的结果集合
     * @param list 查询结果
     * @param pager 分页对象
     */
    public QueryResult(List<?> list, Pager pager) {
        this.list = list;
        this.pager = pager;
    }

    /**
     * 获取结果集
     * @return 结果集
     */
    public List<?> getList() {
        return list;
    }

    /**
     * 按特定泛型获取结果集,属于直接强转,不带转换
     * @param eleType 泛型
     * @return 结果集
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> getList(Class<T> eleType) {
        return (List<T>) list;
    }

    /**
     * 转换为特定类型的结果集
     * @param eleType 新的结果集
     * @return 特定类型的结果集
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> convertList(Class<T> eleType) {
        if (null == list || list.isEmpty())
            return (List<T>) list;

        List<T> re = new ArrayList<T>(list.size());
        Castors castors = Castors.me();
        for (Object obj : list)
            re.add(castors.castTo(obj, eleType));

        return re;
    }

    /**
     * 设置结果集
     * @param list 结果集
     * @return 当前对象,用于链式调用
     */
    public QueryResult setList(List<?> list) {
        this.list = list;
        return this;
    }

    /**
     * 获取分页对象
     * @return 分页对象
     */
    public Pager getPager() {
        return pager;
    }

    /**
     * 设置分页对象
     * @param pager 分页对象
     * @return 当前对象,用于链式调用
     */
    public QueryResult setPager(Pager pager) {
        this.pager = pager;
        return this;
    }

}


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;

/**
 * 对象路径节点转换.

 * 将URL中的字符串参数名转换成对结构, 然后通过 {@link org.nutz.mapl.Mapl}转换成实体对象

 * URL规则:
 *

 *
对象与属性之间使用"."做为连接符
 *
数组,Collection对象, 使用"[]"或":"做为索引引用符. 索引只是一个参考字段, 不会根据其值设置索引
 *
Map使用"()"或"."分割key值
 *

 * 例:

 *
 * Object:  node.str = str

 * list:    node.list[1].str = abc;

 *          node.list:2.str = 2

 * set:     node.set[2].str = bbb

 *          node.set:jk.str = jk

 * Map:     node.map(key).str = bb;

 *          node.map.key.name = map

 *
 *
 * @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;
    }
}


package org.nutz.lang.util;

import java.io.File;
import java.io.FileFilter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.LinkedList;

import org.nutz.lang.Encoding;
import org.nutz.lang.Files;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;

/**
 * 磁盘操作的帮助函数集合
 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author bonyfish(mc02cxj@gmail.com)
 */
public abstract class Disks {

    /**
     * 一个 Vistor 模式的目录深层遍历
     *
     * @param f
     *            要遍历的目录或者文件，如果是目录，深层遍历，否则，只访问一次文件
     * @param fv
     *            对文件要进行的操作
     * @param filter
     *            遍历目录时，哪些文件应该被忽略
     * @return 遍历的文件个数
     */
    public static int visitFile(File f, FileVisitor fv, FileFilter filter) {
        int re = 0;
        if (f.isFile()) {
            fv.visit(f);
            re++;
        } else if (f.isDirectory()) {
            File[] fs = null == filter ? f.listFiles() : f.listFiles(filter);
            if (fs != null)
                for (File theFile : fs)
                    re += visitFile(theFile, fv, filter);
        }
        return re;
    }

    /**
     * 一个 Vistor 模式的目录深层遍历, 包含目录也会返回
     *
     * @param f
     *            要遍历的目录或者文件，如果是目录，深层遍历，否则，只访问一次文件
     * @param fv
     *            对文件要进行的操作
     * @param filter
     *            遍历目录时，哪些文件应该被忽略
     * @return 遍历的文件（目录）个数
     */
    public static int visitFileWithDir(File f, FileVisitor fv, FileFilter filter) {
        int re = 0;
        fv.visit(f);
        re++;
        if (f.isDirectory()) {
            File[] fs = null == filter ? f.listFiles() : f.listFiles(filter);
            if (fs != null)
                for (File theFile : fs)
                    re += visitFileWithDir(theFile, fv, filter);
        }
        return re;
    }

    /**
     * 将两个文件对象比较，得出相对路径
     *
     * @param base
     *            基础文件对象
     * @param file
     *            相对文件对象
     * @return 相对于基础文件对象的相对路径
     */
    public static String getRelativePath(File base, File file) {
        String pathBase = base.getAbsolutePath();
        if (base.isDirectory())
            pathBase += "/";

        String pathFile = file.getAbsolutePath();
        if (file.isDirectory())
            pathFile += "/";

        return getRelativePath(pathBase, pathFile);
    }

    /**
     * 将两个路径比较，得出相对路径
     *
     * @param base
     *            基础路径，以 '/' 结束，表示目录
     * @param path
     *            相对文件路径，以 '/' 结束，表示目录
     * @return 相对于基础路径对象的相对路径
     */
    public static String getRelativePath(String base, String path) {
        return getRelativePath(base, path, "./");
    }

    /**
     * 将两个路径比较，得出相对路径
     *
     * @param base
     *            基础路径，以 '/' 结束，表示目录
     * @param path
     *            相对文件路径，以 '/' 结束，表示目录
     * @param equalPath
     *            如果两个路径相等，返回什么，通常为 "./"。 你也可以用 "" 或者 "." 或者随便什么字符串来表示
     *
     * @return 相对于基础路径对象的相对路径
     */
    public static String getRelativePath(String base, String path, String equalPath) {
        // 如果两个路径相等
        if (base.equals(path) || "./".equals(path) || ".".equals(path)) {
            return equalPath;
        }

        // 开始判断
        String[] bb = Strings.splitIgnoreBlank(getCanonicalPath(base), "[\\\\/]");
        String[] ff = Strings.splitIgnoreBlank(getCanonicalPath(path), "[\\\\/]");
        int len = Math.min(bb.length, ff.length);
        int pos = 0;
        for (; pos < len; pos++)
            if (!bb[pos].equals(ff[pos]))
                break;

        // 证明路径是相等的
        if (len == pos && bb.length == ff.length) {
            return equalPath;
        }

        // 开始查找不同
        int dir = 1;
        if (base.endsWith("/"))
            dir = 0;

        StringBuilder sb = new StringBuilder(Strings.dup("../", bb.length - pos - dir));
        sb.append(Lang.concat(pos, ff.length - pos, '/', ff));
        if (path.endsWith("/"))
            sb.append('/');
        return sb.toString();
    }

    /**
     * 获取两个路径从头部开始计算的交集
     *
     * @param ph0
     *            路径1
     * @param ph1
     *            路径2
     * @param dft
     *            如果两个路径完全没有相交，那么返回什么
     * @return 两个路径的交集
     */
    public static String getIntersectPath(String ph0, String ph1, String dft) {
        // 木可能有交集
        if (null == ph0 || null == ph1)
            return dft;

        String[] ss0 = Strings.splitIgnoreBlank(ph0, "[\\\\/]");
        String[] ss1 = Strings.splitIgnoreBlank(ph1, "[\\\\/]");

        int pos = 0;
        int len = Math.min(ss0.length, ss1.length);
        for (; pos < len; pos++) {
            if (!ss0[pos].equals(ss1[pos]))
                break;
        }

        // 木有交集
        if (pos == 0)
            return dft;

        // 得到
        String re = Lang.concat(0, pos, "/", ss0).toString();

        // 需要补全后面的 "/" 吗
        if (ph0.endsWith("/") && ph1.endsWith("/"))
            return re + "/";

        return re;
    }

    /**
     * 整理路径。 将会合并路径中的 ".."
     *
     * @param path
     *            路径
     * @return 整理后的路径
     */
    public static String getCanonicalPath(String path) {
        if (Strings.isBlank(path))
            return path;
        String[] pa = Strings.splitIgnoreBlank(path, "[\\\\/]");
        LinkedList<String> paths = new LinkedList<String>();
        for (String s : pa) {
            if ("..".equals(s)) {
                if (paths.size() > 0)
                    paths.removeLast();
                continue;
            }
            if (".".equals(s)) {
                // pass
            } else {
                paths.add(s);
            }
        }

        StringBuilder sb = Lang.concat("/", paths);
        if (path.startsWith("/"))
            sb.insert(0, '/');
        if (path.endsWith("/"))
            sb.append('/');
        return sb.toString();
    }

    /**
     * @return 当前账户的主目录全路径
     */
    public static String home() {
        return System.getProperty("user.home");
    }

    /**
     * @param path
     *            相对用户主目录的路径
     * @return 相对用户主目录的全路径
     */
    public static String home(String path) {
        return home() + path;
    }

    /**
     * 获取一个路径的绝对路径。如果该路径不存在，则返回null
     *
     * @param path
     *            路径
     * @return 绝对路径
     */
    public static String absolute(String path) {
        return absolute(path, ClassTools.getClassLoader(), Encoding.defaultEncoding());
    }

    /**
     * 获取一个路径的绝对路径。如果该路径不存在，则返回null
     *
     * @param path
     *            路径
     * @param klassLoader
     *            参考 ClassLoader
     * @param enc
     *            路径编码方式
     * @return 绝对路径
     */
    public static String absolute(String path, ClassLoader klassLoader, String enc) {
        path = normalize(path, enc);
        if (Strings.isEmpty(path))
            return null;

        File f = new File(path);
        if (!f.exists()) {
            URL url = null;
            try {
                url = klassLoader.getResource(path);
                if (null == url)
                    url = Thread.currentThread().getContextClassLoader().getResource(path);
                if (null == url)
                    url = ClassLoader.getSystemResource(path);
            }
            catch (Throwable e) {}
            if (null != url)
                return normalize(url.getPath(), Encoding.UTF8);// 通过URL获取String,一律使用UTF-8编码进行解码
            return null;
        }
        return path;
    }

    /**
     * 让路径变成正常路径，将 ~ 替换成用户主目录
     *
     * @param path
     *            路径
     * @return 正常化后的路径
     */
    public static String normalize(String path) {
        return normalize(path, Encoding.defaultEncoding());
    }

    /**
     * 让路径变成正常路径，将 ~ 替换成用户主目录
     *
     * @param path
     *            路径
     * @param enc
     *            路径编码方式
     * @return 正常化后的路径
     */
    public static String normalize(String path, String enc) {
        if (Strings.isEmpty(path))
            return null;
        if (path.charAt(0) == '~')
            path = Disks.home() + path.substring(1);
        try {
            return URLDecoder.decode(path, enc);
        }
        catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    /**
     * 遍历文件夹下以特定后缀结尾的文件(不包括文件夹,不包括.开头的文件)
     *
     * @param path
     *            根路径
     * @param regex
     *            文件名的正则表达式
     * @param deep
     *            是否深层遍历
     * @param fv
     *            你所提供的访问器,当然就是你自己的逻辑咯
     */
    public static final void visitFile(String path,
                                       final String regex,
                                       final boolean deep,
                                       final FileVisitor fv) {
        File d = Files.findFile(path);
        if (null == d)
            return;
        visitFile(d, new FileVisitor() {
            @Override
            public void visit(File f) {
                if (f.isDirectory())
                    return;
                fv.visit(f);
            }
        }, new FileFilter() {
            @Override
            public boolean accept(File f) {
                if (f.isDirectory())
                    return deep;
                if (f.isHidden())
                    return false;
                if (Strings.isEmpty(regex))
                    return true;
                return Regex.match(regex, f.getName());
            }
        });
    }

    /**
     * 遍历文件夹下以特定后缀结尾的文件与文件夹 不包括.开头的文件
     *
     * @param path
     *            根路径
     * @param regex
     *            文件名的正则表达式
     * @param deep
     *            是否深层遍历
     * @param fv
     *            你所提供的访问器,当然就是你自己的逻辑咯
     */
    public static final void visitFileWithDir(String path,
                                              final String regex,
                                              final boolean deep,
                                              final FileVisitor fv) {
        File d = Files.findFile(path);
        if (null == d)
            return;
        visitFileWithDir(d, new FileVisitor() {
            @Override
            public void visit(File f) {
                fv.visit(f);
            }
        }, new FileFilter() {
            @Override
            public boolean accept(File f) {
                if (f.isDirectory())
                    return deep;
                if (f.isHidden())
                    return false;
                if (Strings.isEmpty(regex))
                    return true;
                return f.getName().matches(regex);
            }
        });
    }

    /**
     * 将多个路径拼合成一个路径，他会自动去除重复的 "/"
     *
     * <pre>
     * appendPath("a","b")  => "a/b"
     * appendPath("/a","b/c")  => "/a/b/c"
     * appendPath("/a/","/b/c")  => "/a/b/c"
     * </pre>
     *
     * @param phs
     *            路径数组
     * @return 拼合后的路径
     */
    public static String appendPath(String... phs) {
        String[] paths = Lang.without(phs, null);
        if (null != paths && paths.length > 0) {
            // zozoh: 嗯下面的逻辑木有必要了吧
            // if (null == paths[0])
            // paths[0] = "/";
            String str = Lang.concat("/", paths).toString();
            String[] ss = Strings.splitIgnoreBlank(str, "/");
            str = Lang.concat("/", ss).toString();
            if (paths[0].startsWith("/")) {
                return "/" + str;
            }
            return str;
        }
        return null;
    }
}


package org.nutz.dao;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

import org.nutz.dao.impl.DaoExecutor;
import org.nutz.dao.sql.DaoStatement;
import org.nutz.dao.sql.SqlContext;
import org.nutz.lang.random.R;
import org.nutz.log.Log;
import org.nutz.log.Logs;

/**
 * Dao执行拦截器链.
 *
 * @author wendal
 * @see org.nutz.dao.impl.interceptor.DaoLogInterceptor
 * @see org.nutz.dao.impl.interceptor.DaoTimeInterceptor
 */
public class DaoInterceptorChain implements ConnCallback {

    private static final Log log = Logs.get();

    protected int autoTransLevel;

    protected Connection connection;

    protected int current = 0;

    protected DaoStatement daoStatement;

    protected DaoExecutor executor;

    protected List<DaoInterceptor> interceptors = new ArrayList<DaoInterceptor>();

    protected int updateCount;

    protected DaoStatement[] sts;

    protected String id;

    /**
     * 新建一个DaoInterceptorChain.
     *
     * @param sts
     *            将要进行的Dao操作(不一定是SQL操作,有可能是EL)
     */
    public DaoInterceptorChain(DaoStatement... sts) {
        this.sts = sts;
        id = R.UU32();
    }

    /**
     * 继续下一个拦截器,如果已经是最后一个拦截器,那么执行executor.exec
     *
     * @return 本对象,用于链式操作
     * @throws Exception
     */
    public DaoInterceptorChain doChain() throws DaoException {
        if (hasNext()) {
            DaoInterceptor interceptor = next();
            current++;
            interceptor.filter(this);
        } else {
            executor.exec(getConnection(), getDaoStatement());
            updateCount += getDaoStatement().getUpdateCount();
        }
        return this;
    }

    /**
     * 获取当前自动事务级别,DaoRunner中使用强制事务时会使用之.拦截器不能修改,即使修改也不会生效
     *
     * @return 当前自动(强制)事务级别
     */
    public int getAutoTransLevel() {
        return autoTransLevel;
    }

    /**
     * 当前执行的DaoStatement
     *
     * @return 当前执行的DaoStatement
     */
    public DaoStatement getDaoStatement() {
        return daoStatement;
    }

    /**
     * 全部DaoStatement,可能不止一条
     *
     * @return 全部DaoStatement
     */
    public DaoStatement[] getDaoStatements() {
        return sts;
    }

    /**
     * 拦截器列表(暂不开放修改)
     *
     * @return 全体拦截器列表
     */
    public List<DaoInterceptor> getInterceptors() {
        return interceptors;
    }

    /**
     * 更新总数,用于DaoSupport(NutDao)获取更新总数.
     *
     * @return 更新记录总数
     */
    public int getUpdateCount() {
        return updateCount;
    }

    /**
     * 是否还有下一个拦截器
     *
     * @return true,如果还有拦截器要执行
     */
    public boolean hasNext() {
        return current < interceptors.size();
    }

    /**
     * 这是DaoExecutor会执行的方法,拦截器内不要执行这个方法!! 这里也是拦截器开始生效的地方.
     */
    public void invoke(Connection conn) throws Exception {
        for (DaoStatement st : sts) {
            if (st == null) {
                if (log.isInfoEnabled())
                    log.info("Found a null DaoStatement(SQL), ingore it ~~");
                continue;
            }
            current = 0;
            daoStatement = st;
            this.connection = conn;
            doChain();
        }
    }

    /**
     * 获取下一个拦截器. 调用前必须先调用hasNext进行判断
     *
     * @return 下一个拦截器
     */
    public DaoInterceptor next() {
        return interceptors.get(current);
    }

    /**
     * 设置强制事务的级别,对拦截器来说无意义.
     *
     * @param autoTransLevel
     *            与DaoSupport(NutDao)内的值一致
     */
    public void setAutoTransLevel(int autoTransLevel) {
        this.autoTransLevel = autoTransLevel;
    }

    /**
     * 设置当前拦截器索引. 若设置值大于拦截器列表的大小,那么效果就等同于跳过剩余拦截器,直接执行DaoStatement
     *
     * @param current
     */
    public void setCurrent(int current) {
        this.current = current;
    }

    /**
     * 设置DaoExecutor. 典型应用是在拦截器中替换成daocache提供的DaoExecutor
     *
     * @param executor
     *            新的DaoExecutor,不可以是null
     */
    public void setExecutor(DaoExecutor executor) {
        this.executor = executor;
    }

    /**
     * 设置新的拦截器列表.
     *
     * @param interceptors
     *            新的拦截器列表
     */
    public void setInterceptors(List<DaoInterceptor> interceptors) {
        this.interceptors = interceptors;
    }

    /**
     * 设置当前使用的数据库连接
     *
     * @param connection
     *            新的数据库连接,不可以是null
     */
    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    /**
     * 获取当前数据库连接
     *
     * @return 当前数据库连接
     */
    public Connection getConnection() {
        return connection;
    }

    /**
     * 获取当前DaoStatement的上下文,注意,一个拦截器链可能包含多个DaoStatement
     *
     * @return 当前DaoStatement的上下文
     */
    public SqlContext getSqlContext() {
        return getDaoStatement().getContext();
    }

    /**
     * 拦截器链的id, 为一个uu32识别符.
     *
     * @return 本拦截器链的id
     */
    public String getId() {
        return id;
    }

    /**
     * 替换当前执行的DaoStatement
     * @param daoStatement
     */
    public void setDaoStatement(DaoStatement daoStatement) {
        this.daoStatement = daoStatement;
    }
}


package org.nutz.dao.entity;

import org.nutz.castor.Castors;
import org.nutz.dao.Chain;
import org.nutz.dao.DaoException;
import org.nutz.dao.impl.jdbc.BlobValueAdaptor;
import org.nutz.dao.jdbc.Jdbcs;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;

import java.sql.*;
import java.util.*;
import java.util.concurrent.Callable;

/**
 * 记录对象
 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author wendal(wendal1985@gmail.com)
 * @author wizzercn(wizzer.cn@gmail.com)
 */
public class Record implements Map<String, Object>, java.io.Serializable, Cloneable, Comparable<Record> {

    private static final long serialVersionUID = -7753504263747912181L;

    protected static Callable<Record> factory;

    /**
     * 根据 ResultSet 创建一个记录对象
     *
     * @param rs ResultSet 对象
     * @return 记录对象
     */
    public static Record create(ResultSet rs) {
        Record re = create();
        create(re, rs, null);
        return re;
    }

    public static void create(Map<String, Object> re, ResultSet rs, ResultSetMetaData meta) {
        String name = null;
        int i = 0;
        try {
            if (meta == null)
                meta = rs.getMetaData();
            int count = meta.getColumnCount();
            for (i = 1; i <= count; i++) {
                name = meta.getColumnLabel(i);
                switch (meta.getColumnType(i)) {
                    case Types.TIMESTAMP: {
                        re.put(name, rs.getTimestamp(i));
                        break;
                    }
                    case Types.DATE: {// ORACLE的DATE类型包含时间,如果用默认的只有日期没有时间 from
                        // cqyunqin
                        re.put(name, rs.getTimestamp(i));
                        break;
                    }
                    case Types.CLOB: {
                        re.put(name, rs.getString(i));
                        break;
                    }
                    case Types.BLOB: {
                        re.put(name, new BlobValueAdaptor(Jdbcs.getFilePool()).get(rs, name));
                        break;
                    }
                    default:
                        re.put(name, rs.getObject(i));
                        break;
                }
            }
        } catch (SQLException e) {
            if (name != null) {
                throw new DaoException(String.format("Column Name=%s, index=%d", name, i), e);
            }
            throw new DaoException(e);
        }
    }

    private Map<String, Object> map;
    private List<String> keys;

    public Record() {
        map = new LinkedHashMap<String, Object>();
        keys = new ArrayList<String>();
    }

    /**
     * 设置值
     *
     * @param name  字段名
     * @param value 字段值
     * @return 记录本身
     */
    public Record set(String name, Object value) {
        map.put(name.toLowerCase(), value);
        keys.add(name);
        return this;
    }

    /**
     * 移除一个字段
     *
     * @param name 字段名
     * @return 移除的字段值
     */
    public Object remove(String name) {
        keys.remove(name);
        return map.remove(name.toLowerCase());
    }

    /**
     * 返回记录中已有的字段的数量
     *
     * @return 记录中已有的字段的数量
     */
    public int getColumnCount() {
        return map.size();
    }

    /**
     * 返回记录中所有的字段名
     *
     * @return 记录中所有的字段名
     */
    public Set<String> getColumnNames() {
        return map.keySet();
    }

    /**
     * 返回指定字段的 int 值
     *

     * 如果该字段在记录中不存在，返回 -1；如果该字段的值不是 int 类型，返回 -1
     *
     * @param name 字段名
     * @return 指定字段名的 int 值。如果该字段在记录中不存在，返回 -1；如果该字段的值不是 int 类型，返回 -1
     */
    public int getInt(String name) {
        return getInt(name, -1);
    }

    public int getInt(String name, int dft) {
        try {
            Object val = get(name);
            if (null == val)
                return dft;
            return Castors.me().castTo(val, int.class);
        } catch (Exception e) {
        }
        return dft;
    }

    public long getLong(String name) {
        return getLong(name, -1);
    }

    public long getLong(String name, long dft) {
        try {
            Object val = get(name);
            if (null == val)
                return dft;
            return Castors.me().castTo(val, long.class);
        } catch (Exception e) {
        }
        return dft;
    }

    public double getDouble(String name) {
        return getDouble(name, -1);
    }

    public double getDouble(String name, double dft) {
        try {
            Object val = get(name);
            if (null == val)
                return dft;
            return Castors.me().castTo(val, double.class);
        } catch (Exception e) {
        }
        return dft;
    }

    /**
     * 返回指定字段的 String 值
     *

     * 如果该字段在记录中不存在，返回 null
     *
     * @param name 字段名
     * @return 指定字段的 String 值。如果该字段在记录中不存在，返回 null
     */
    public String getString(String name) {
        Object val = get(name);
        if (null == val)
            return null;
        return Castors.me().castToString(val);
    }

    /**
     * 返回指定字段的 Blob 值
     *

     * 如果该字段在记录中不存在，返回 null
     *
     * @param name 字段名
     * @return 指定字段的 Blob 值。如果该字段在记录中不存在，返回 null
     */
    public Blob getBlob(String name) {
        Object val = get(name);
        if (null == val)
            return null;
        return Castors.me().castTo(val, Blob.class);
    }

    /**
     * 返回指定字段的 Timestamp 值
     *

     * 如果该字段在记录中不存在，返回 null
     *
     * @param name 字段名
     * @return 指定字段的 Timestamp 值。如果该字段在记录中不存在，返回 null
     */
    public Timestamp getTimestamp(String name) {
        Object val = get(name);
        if (null == val)
            return null;
        return Castors.me().castTo(val, Timestamp.class);
    }

    /**
     * 返回该记录的 JSON 字符串，并且可以设定 JSON 字符串的格式化方式
     *
     * @param format JSON 字符串格式化方式 ，若 format 为 null ，则以 JsonFormat.nice() 格式输出
     * @return JSON 字符串
     */
    public String toJson(JsonFormat format) {
        return Json.toJson(map, format);
    }

    /**
     * 返回该记录 JSON 格式的字符串表示
     *
     * @return 该记录 JSON 格式的字符串表示
     */
    public String toString() {
        return Json.toJson(map, JsonFormat.full());
    }

    /**
     * 根据指定的类的类型，把该记录转换成该类型的对象
     *
     * @param type 指定的类的类型
     * @return 指定的类型的对象
     */
    public <T> T toPojo(Class<T> type) {
        return Lang.map2Object(this, type);
    }

    public <T> T toEntity(Entity<T> en) {
        return en.getObject(this);
    }

    public <T> T toEntity(Entity<T> en, String prefix) {
        return en.getObject(this, prefix);
    }

    /**
     * 从记录中移除所有字段与值的对应关系
     */
    public void clear() {
        map.clear();
        keys.clear();
    }

    /**
     * 如果该字段在记录中存在，则返回 true
     *
     * @param key 字段名
     * @return true 该字段在记录中存在
     */
    public boolean containsKey(Object key) {
        return map.containsKey(key.toString().toLowerCase());
    }

    /**
     * 如果该字段值在记录中存在，则返回 true
     *
     * @param value 字段值
     * @return true 该字段值在记录中存在
     */
    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }

    public Set<Entry<String, Object>> entrySet() {
        return map.entrySet();
    }

    public boolean equals(Object out) {
        return map.equals(out);
    }

    /**
     * 返回指定字段的值
     *

     * 如果该字段在记录中不存在，返回 null
     *
     * @param name 字段名
     * @return 指定字段的值。如果该字段在记录中不存在，返回 null
     */
    public Object get(Object name) {
        if (null == name)
            return null;
        return map.get(name.toString().toLowerCase());
    }

    /**
     * 返回该记录的哈希码值
     */
    public int hashCode() {
        return map.hashCode();
    }

    /**
     * 如果记录中不存在字段与值的对应关系，则返回 true
     *
     * @return true 记录中不存在字段与值的对应关系
     */
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * 返回记录中所有的字段名
     *
     * @return 记录中所有的字段名
     */
    public Set<String> keySet() {
        return map.keySet();
    }

    /**
     * 将字段与其对应的值放入该记录中
     *
     * @param name  字段名
     * @param value 字段值
     * @return 该字段之前所对应的值；如果之前该字段在该记录中不存在，则返回 null
     */
    public Object put(String name, Object value) {
        keys.add(name);
        return map.put(name.toLowerCase(), value);
    }

    public void putAll(Map<? extends String, ? extends Object> out) {
        for (Entry<? extends String, ? extends Object> entry : out.entrySet())
            put(entry.getKey(), entry.getValue());
    }

    /**
     * 将字段从记录中删除
     *
     * @param key 字段名
     * @return 该字段所对应的值；如果该字段在该记录中不存在，则返回 null
     */
    public Object remove(Object key) {
        return map.remove(key.toString().toLowerCase());
    }

    /**
     * 返回记录的记录数
     *
     * @return 记录的记录数
     */
    public int size() {
        return map.size();
    }

    /**
     * 返回记录中所有的字段的值
     *
     * @return 记录中所有的字段的值
     */
    public Collection<Object> values() {
        return map.values();
    }

    /**
     * 返回该记录对应的 Chain 对象
     *
     * @return 该记录对应的 Chain 对象
     */
    public Chain toChain() {
        return Chain.from(map);
    }

    public Record clone() {
        Record re = create();
        re.putAll(this);
        return re;
    }

    public Map<String, Object> sensitive() {
        NutMap map = new NutMap();
        for (String key : keys) {
            map.put(key, get(key));
        }
        return map;
    }

    public int compareTo(Record re) {
        if (re == null)
            return 1;
        if (re.size() == this.size())
            return 0;
        return re.size() > this.size() ? -1 : 1;
    }

    public static void setFactory(Callable<Record> factory) {
        Record.factory = factory;
    }

    public static Record create() {
        if (factory != null)
            try {
                return factory.call();
            } catch (Exception e) {
                throw Lang.wrapThrow(e);
            }
        return new Record();
    }
}


package org.nutz.ioc.java;

import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.LinkedArray;
import org.nutz.lang.util.LinkedCharArray;
import org.nutz.lang.util.Regex;

public class ChainParsing {

    private char[] cs;
    private int i;
    private ChainNode first;
    private ChainNode last;
    /**
     * 如果不为 null，那么 addNode 将全加到这里
     */
    private LinkedArray<LinkedArray<ChainNode>> argss;
    /**
     * 控制 parse 函数，合适退出
     */
    private LinkedCharArray ends;
    /**
     * parse 函数所使用的字符缓冲区
     */
    private StringBuilder sb;

    public ChainParsing(String s) {
        ends = new LinkedCharArray(10);
        sb = new StringBuilder();
        argss = new LinkedArray<LinkedArray<ChainNode>>(5);
        cs = s.toCharArray();
        parse();
    }

    //记录递归层数
    private int recursiveCount = 0;

    private void parse() {
        recursiveCount++;
        for (; i < cs.length; i++) {
            char c = cs[i];
            if (c == ',') {
                checkIfNeedAddNode();
            }
            // 属性
            else if (null != first && c == '.') {
                String fn = clearStringBuffer();
                if (!Strings.isBlank(fn)) {
                    addNode(new FieldNode(fn));
                }
            }
            // String
            else if (c == '\'' || c == '"') {
                clearStringBuffer();
                for (i++; i < cs.length; i++) {
                    char n = cs[i];
                    if (n == c)
                        break;
                    sb.append(n);
                }
                addNode(new StringNode(clearStringBuffer()));
            }
            // @Ioc | @Context | @Name
            else if (c == '@') {
                if (recursiveCount > 1 && hasFieldOrFunction()) {//正在解析参数 并且 参数是对象获取属性或调用方法
                    String strNodes = "@" + readToComma();
                    addNode(new ChainParsing(strNodes).getNode());
                } else {
                    String name = readToDot().toUpperCase();
                    if ("IOC".equals(name)) {
                        addNode(new IocSelfNode());
                    } else if ("CONTEXT".equals(name)) {
                        addNode(new IocContextNode());
                    } else if ("NAME".equals(name)) {
                        addNode(new IocObjectNameNode());
                    }
                }
                continue;
            }
            // $xxx
            else if (c == '$') {
                if (recursiveCount > 1 && hasFieldOrFunction()) {//正在解析参数 并且 参数是对象获取属性或调用方法
                    String strNodes = "$" + readToComma();
                    addNode(new ChainParsing(strNodes).getNode());
                } else {
                    String name = readToDot();
                    addNode(new IocObjectNode(name));
                }
                continue;
            }
            // (...) in func
            else if (c == '(') {
                String funcName = Strings.trim(clearStringBuffer());
                argss.push(new LinkedArray<ChainNode>(ChainNode.class, 5));
                ends.push(')');
                i++;
                parse();
                ends.popLast();
                ChainNode[] args = argss.popLast().toArray();
                int pos = funcName.lastIndexOf('.');
                // 嗯... 看来是静态方法调用 ...
                if (pos > 0) {
                    String className = funcName.substring(0, pos);
                    funcName = funcName.substring(pos + 1);
                    addNode(new StaticFunctionNode(className, funcName, args));
                }
                // 前面节点如果为 (...)，那么 . 会被计入
                else if (pos == 0) {
                    addNode(new ObjectFunctionNode(funcName.substring(1), args));
                }
                // 简直就是一个方法调用
                else {
                    addNode(new ObjectFunctionNode(funcName, args));
                }
                clearStringBuffer();
            }
            // If funcs, it will will quit when encounter the ')'
            else if (ends.size() > 0 && c == ends.last()) {
                checkIfNeedAddNode();
                return;
            }
            //
            else if(c==')'&&i==cs.length-1){
                return;
            }
            // xxx.xxx.xxx
            else {
                sb.append(c);
            }
        }
        recursiveCount--;
        checkIfNeedAddNode();
    }

    //对象在参数中，是否后续获取属性或调用方法
    private boolean hasFieldOrFunction() {
        int dot = 0, comma = 0;
        for (int currentIndex = i; currentIndex < cs.length; currentIndex++) {
            char c = cs[currentIndex];
            if (c == '.')
                dot = currentIndex;
            if (c == ',')
                comma = currentIndex;
        }
        return dot < comma || (dot != 0 && comma == 0);//点号在逗号前边或后边有点号没有逗号
    }

    private void checkIfNeedAddNode() {
        // Finished Parsing, check the buffer
        if (!Strings.isBlank(sb)) {
            String s = Strings.trim(clearStringBuffer());
            // null
            if (s.equalsIgnoreCase("null")) {
                addNode(new NullNode());
            }
            // boolean
            else if (Regex.match("^(true|false)$", s)) {
                addNode(new BooleanNode(s));
            }
            // number
            else if (Regex.match("^([-]?[0-9]+)?([.][0-9]+)?([fL]?)$", s)) {
                addNode(new NumberNode(s));
            }
            // the chain is empty
            else if (null == last) {
                int pos = s.lastIndexOf('.');
                if (pos < 0)
                    throw Lang.makeThrow("Don't know how to invoke '%s'", s);
                String className = s.substring(0, pos);
                String funcName = s.substring(pos + 1);
                addNode(new StaticFunctionNode(className,
                        funcName,
                        new ChainNode[0]));
            }
            // some node had been built
            else {
                addNode(new FieldNode(s));
            }
        }
    }

    private String readToDot() {
        for (i++; i < cs.length; i++) {
            char c = cs[i];
            if (c == '.' || c == ',')
                break;
            sb.append(c);
        }
        return clearStringBuffer();
    }

    private String readToComma() {
        for (i++; i < cs.length; i++) {
            char c = cs[i];
            if (c == ',' || c == ')')
                break;
            sb.append(c);
        }
        return clearStringBuffer();
    }

    private String clearStringBuffer() {
        String re = Strings.trim(sb);
        sb = new StringBuilder();
        return re;
    }

    private void addNode(ChainNode node) {
        // For arguments
        if (argss.size() > 0) {
            argss.last().push(node);
        }
        // First time
        else if (last == null) {
            first = node;
            last = node;
        }
        // Normal ...
        else {
            last.setNext(node);
            last = node;
        }
    }

    public ChainNode getNode() {
        return first;
    }

}