package org.nutz.resource;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.servlet.ServletContext;

import org.nutz.castor.Castors;
import org.nutz.lang.Encoding;
import org.nutz.lang.Lang;
import org.nutz.lang.Stopwatch;
import org.nutz.lang.Streams;
import org.nutz.lang.util.ClassTools;
import org.nutz.lang.util.Disks;
import org.nutz.lang.util.FileVisitor;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.resource.impl.ErrorResourceLocation;
import org.nutz.resource.impl.FileResource;
import org.nutz.resource.impl.FileSystemResourceLocation;
import org.nutz.resource.impl.JarResource;
import org.nutz.resource.impl.JarResourceLocation;
import org.nutz.resource.impl.ResourceLocation;
import org.nutz.resource.impl.SimpleResource;
import org.nutz.resource.impl.WebClassesResourceLocation;

/**
 * 资源扫描的帮助函数集
 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author wendal(wendal1985@gmail.com)
 */
public class Scans {

    private static final String FLT_CLASS = "^.+[.]class$";

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

    private static Scans me = new Scans();

    private Map<String, ResourceLocation> locations = new LinkedHashMap<String, ResourceLocation>();

    // 通过/META-INF/MANIFEST.MF等标记文件,获知所有jar文件的路径
    protected String[] referPaths = new String[]{    "META-INF/MANIFEST.MF",
                                        "log4j.properties",
                                        ".nutz.resource.mark"};

    /**
     * 在Web环境中使用Nutz的任何功能,都应该先调用这个方法,以初始化资源扫描器
     *

     * 调用一次就可以了
     */
	public Scans init(final ServletContext sc) {
	    Stopwatch sw = Stopwatch.begin();
        // 获取classes文件夹的路径, 优先级为125
	    String classesPath = sc.getRealPath("/WEB-INF/classes");
	    if (classesPath == null)
	        addResourceLocation(new WebClassesResourceLocation(sc));
	    else {
	        ResourceLocation rc = ResourceLocation.file(new File(classesPath));
	        if (rc instanceof FileSystemResourceLocation)
	            ((FileSystemResourceLocation)rc).priority = 125;
	        addResourceLocation(rc);
	    }

        // 获取lib文件夹中的全部jar, 优先级是50
        Set<String> jars = sc.getResourcePaths("/WEB-INF/lib/");
        if (jars != null) {// 这个文件夹不一定存在,尤其是Maven的WebApp项目
            for (String path : jars) {
                if (!path.endsWith(".jar"))
                    continue;
                try {
                    addResourceLocation(new JarResourceLocation(sc.getResource(path)));
                }
                catch (Exception e) {
                    log.debug("parse jar fail >> " + e.getMessage());
                }
            }
        }
        sw.stop();
        printLocations(sw);
        return this;
    }

    public List<NutResource> loadResource(String regex, String... paths) {
        List<NutResource> list = new LinkedList<NutResource>();
        for (String path : paths) {
            list.addAll(scan(path, regex));
        }
        // 如果找不到?
        if (list.size() < 1 && paths.length > 0)
            throw Lang.makeThrow(    RuntimeException.class,
                                    "folder or file like '%s' no found in %s",
                                    regex,
                                    Castors.me().castToString(paths));
        return list;
    }

    public void registerLocation(Class<?> klass) {
        if (klass == null)
            return;
        try {
            registerLocation(klass.getProtectionDomain().getCodeSource().getLocation());
        }
        catch (Throwable e) { // Android上会死
            String classFile = klass.getName().replace('.', '/') + ".class";
            URL url = ClassTools.getClassLoader().getResource(classFile);
            if (url != null) { // 基本上不可能为null
                String str = url.toString();
                try {
                    str = URLDecoder.decode(str, Encoding.UTF8);
                }
                catch (UnsupportedEncodingException e1) {
                    throw Lang.impossible();
                }
                str = str.substring(0, str.length() - classFile.length());
                try {
                    registerLocation(new URL(str));
                }
                catch (Throwable e2) {
                    if (log.isInfoEnabled())
                        log.info("Fail to registerLocation --> " + str, e);
                }
            }
        }
    }

    public void registerLocation(URL url) {
        if (url == null)
            return;
        addResourceLocation(makeResourceLocation(url));
    }

    protected ResourceLocation makeResourceLocation(URL url) {
        try {
            String str = url.toString();
            if (str.endsWith(".jar")) {
                return new JarResourceLocation(url);
            } else if (str.contains("jar!")) {
            	if (str.startsWith("jar:file:")) {
            		str = str.substring("jar:file:".length());
            	}
                return ResourceLocation.jar(str.substring(0, str.lastIndexOf("jar!") + 3));
            } else if (str.startsWith("file:")) {
                return ResourceLocation.file(new File(url.getFile()));
            } else {
                if (str.startsWith("jar:file:"))
                    return ResourceLocation.jar(str.substring(str.indexOf('!')));
                if (log.isDebugEnabled())
                    log.debug("Unkown URL " + url);
                //return ResourceLocation.file(new File(url.toURI()));
            }
        }
        catch (Throwable e) {
            if (log.isInfoEnabled())
                log.info("Fail to registerLocation --> " + url, e);
        }
        return ErrorResourceLocation.make(url);
    }

    public List<NutResource> scan(String src) {
        return scan(src, null);
    }

    /**
     * 在磁盘目录或者 CLASSPATH(包括 jar) 中搜索资源
     *

     * 核心方法
     *
     * @param src
     *            起始路径
     * @param regex
     *            资源名需要匹配的正则表达式
     * @return 资源列表
     */
    public List<NutResource> scan(String src, String regex) {
        if (src.isEmpty())
            throw new RuntimeException("emtry src is NOT allow");
        if ("/".equals(src))
            throw new RuntimeException("root path is NOT allow");
        List<NutResource> list = new ArrayList<NutResource>();
        Pattern pattern = regex == null ? null : Pattern.compile(regex);
        // 先看看是不是文件系统上一个具体的文件
        if (src.startsWith("~/"))
            src = Disks.normalize(src);
        File srcFile = new File(src);
        if (srcFile.exists()) {
            if (srcFile.isDirectory()) {
                Disks.visitFile(srcFile,
                                new ResourceFileVisitor(list, src, 250),
                                new ResourceFileFilter(pattern));
            } else {
                list.add(new FileResource(src, srcFile).setPriority(250));
            }
        }
        for (ResourceLocation location : locations.values()) {
            location.scan(src, pattern, list);
        }
        // 如果啥都没找到,那么,用增强扫描
        if (list.isEmpty()) {
            try {
                Enumeration<URL> enu = ClassTools.getClassLoader().getResources(src);
                if (enu != null && enu.hasMoreElements()) {
                    while (enu.hasMoreElements()) {
                        try {
                            URL url = enu.nextElement();
                            ResourceLocation loc = makeResourceLocation(url);
                            if (url.toString().contains("jar!"))
                                loc.scan(src, pattern, list);
                            else
                                loc.scan("", pattern, list);
                        }
                        catch (Throwable e) {
                            if (log.isTraceEnabled())
                                log.trace("", e);
                        }
                    }
                }
            }
            catch (Throwable e) {
                if (log.isDebugEnabled())
                    log.debug("Fail to run deep scan!", e);
            }
            // 依然是空?
            if (list.isEmpty() && !src.endsWith("/")) {
                try {
                    ClassLoader classLoader = getClass().getClassLoader();
                    InputStream tmp = classLoader.getResourceAsStream(src + "/");
                    if (tmp != null) {
                        tmp.close();
                    } else {
                        InputStream ins = classLoader.getResourceAsStream(src);
                        if (ins != null) {
                            list.add(new SimpleResource(src, src, ins));
                        }
                    }
                }
                catch (Exception e) {
                }
            }
        }
        List<NutResource> _list = new ArrayList<NutResource>();
        OUT: for (NutResource nr : list) {
            Iterator<NutResource> it = _list.iterator();
            while (it.hasNext()) {
                NutResource nr2 = it.next();
                if (nr.equals(nr2)) {
                    if (nr.priority > nr2.priority) {
                        it.remove();
                    } else {
                        continue OUT;
                    }
                }
            }
            _list.add(nr);
        }
        list = _list;
        Collections.sort(list);
        if (log.isDebugEnabled())
            log.debugf("Found %s resource by src( %s ) , regex( %s )", list.size(), src, regex);
        return list;
    }

    public List<Class<?>> scanPackage(Class<?> classZ) {
        return scanPackage(classZ.getPackage().getName(), FLT_CLASS);
    }

    public List<Class<?>> scanPackage(Class<?> classZ, String regex) {
        return scanPackage(classZ.getPackage().getName(), regex);
    }

    /**
     * 搜索并返回给定包下所有的类（递归）
     *
     * @param pkg
     *            包名或者包路径
     */
    public List<Class<?>> scanPackage(String pkg) {
        return scanPackage(pkg, FLT_CLASS);
    }

    /**
     * 搜索给定包下所有的类（递归），并返回所有符合正则式描述的类
     *
     * @param pkg
     *            包名或者包路径
     * @param regex
     *            正则表达式，请注意你需要匹配的名称为 'xxxx.class' 而不仅仅是类名，从而保证选出的对象都是类文件
     */
    public List<Class<?>> scanPackage(String pkg, String regex) {
        String packagePath = pkg.replace('.', '/').replace('\\', '/');
        if (!packagePath.endsWith("/"))
            packagePath += "/";
        return rs2class(pkg, scan(packagePath, regex));
    }

    public static boolean isInJar(File file) {
        return isInJar(file.getAbsolutePath());
    }

    public static boolean isInJar(String filePath) {
        return filePath.contains(".jar!");
    }

    public static NutResource makeJarNutResource(File file) {
        return makeJarNutResource(file.getAbsolutePath());
    }

    public static NutResource makeJarNutResource(String filePath) {
        JarEntryInfo jeInfo = new JarEntryInfo(filePath);
        try {
            ZipInputStream zis = makeZipInputStream(jeInfo.getJarPath());
            ZipEntry ens = null;
            while (null != (ens = zis.getNextEntry())) {
                if (ens.isDirectory())
                    continue;
                if (jeInfo.getEntryName().equals(ens.getName())) {
                    return makeJarNutResource(jeInfo.getJarPath(), ens.getName(), "");
                }
            }
        }
        catch (IOException e) {}
        return null;
    }

    public static NutResource makeJarNutResource(    final String jarPath,
                                                    final String entryName,
                                                    final String base) throws IOException {
        NutResource nutResource = new JarResource(jarPath, entryName);
        if (entryName.equals(base))
            nutResource.setName(entryName);
        else
            nutResource.setName(entryName.substring(base.length()));
        nutResource.setSource(jarPath + ":" + entryName);
        return nutResource;
    }

    public static ZipInputStream makeZipInputStream(String jarPath) throws MalformedURLException,
            IOException {
        ZipInputStream zis = null;
        try {
            zis = new ZipInputStream(new FileInputStream(jarPath));
        }
        catch (IOException e) {
            zis = new ZipInputStream(new URL(jarPath).openStream());
        }
        return zis;
    }

    public static final Scans me() {
        return me;
    }

    /**
     * 将一组 NutResource 转换成 class 对象
     *
     * @param packagePath
     *            包前缀
     * @param list
     *            列表
     * @return 类对象列表
     */
    private static List<Class<?>> rs2class(String pkg, List<NutResource> list) {
        Set<Class<?>> re = new LinkedHashSet<Class<?>>(list.size());
        if (!list.isEmpty()) {
            for (NutResource nr : list) {
                if (!nr.getName().endsWith(".class") || nr.getName().endsWith("package-info.class")) {
                    continue;
                }
                // Class快速载入
                String className = pkg + "." + nr.getName().substring(0, nr.getName().length() - 6).replaceAll("[/\\\\]", ".");
                try {
                	Class<?> klass = Lang.loadClass(className);
                    re.add(klass);
					continue;
				}
				catch (Throwable e) {}
                // 失败了? 尝试终极方法,当然了,慢多了
                InputStream in = null;
                try {
                    in = nr.getInputStream();
                    className = ClassTools.getClassName(in);
                    if (className == null) {
                        if (log.isInfoEnabled())
                            log.infof("Resource can't map to Class, Resource %s", nr);
                        continue;
                    }
                    Class<?> klass = Lang.loadClass(className);
                    re.add(klass);
                }
                catch (Throwable e) {
                    if (log.isInfoEnabled())
                        log.info("Resource can't map to Class, Resource " + nr.getName());
                }
                finally {
                    Streams.safeClose(in);
                }
            }
        }
        return new ArrayList<Class<?>>(re);
    }

    public static class ResourceFileFilter implements FileFilter {
        public boolean accept(File f) {
            if (f.isDirectory()) {
                String fnm = f.getName().toLowerCase();
                // 忽略 SVN 和 CVS 文件,还有Git文件
                if (".svn".equals(fnm) || ".cvs".equals(fnm) || ".git".equals(fnm))
                    return false;
                return true;
            }
            if (f.isHidden())
                return false;
            return pattern == null || pattern.matcher(f.getName()).find();
        }

        private Pattern pattern;

        public ResourceFileFilter(Pattern pattern) {
            super();
            this.pattern = pattern;
        }
    }

    public static class ResourceFileVisitor implements FileVisitor {
        public void visit(File f) {
            list.add(new FileResource(base, f).setPriority(priority));
        }

        String base;
        List<NutResource> list;
        int priority;

        public ResourceFileVisitor(List<NutResource> list, String base, int priority) {
            super();
            this.list = list;
            this.base = base;
            this.priority = priority;
        }
    }

    protected Scans() {
        if (Lang.isAndroid) {
            if (log.isInfoEnabled())
                log.info("Running in Android , so nothing I can scan , just disable myself");
            return;
        }
        Stopwatch sw = Stopwatch.begin();
        // 当前文件夹
        try {
            FileSystemResourceLocation rc = new FileSystemResourceLocation(new File(".").getAbsoluteFile().getCanonicalFile());
            rc.priority = 200;
            addResourceLocation(rc);
        } catch (Throwable e) {
        }
        // 推测一下nutz自身所在的位置
        //registerLocation(Nutz.class);
        ClassLoader cloader = ClassTools.getClassLoader();
        for (String referPath : referPaths) {
            try {
                Enumeration<URL> urls = cloader.getResources(referPath);
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    String url_str = url.toString();
                    if (url_str.contains("jar!")) {
                        String tmp = url_str.substring(0, url_str.lastIndexOf("jar!") + 3);
                        if (tmp.startsWith("jar:"))
                            tmp = tmp.substring("jar:".length());
                        if (tmp.startsWith("file:/"))
                            tmp = tmp.substring("file:/".length());
                        if (tmp.contains("tomcat"))
                            continue;
                        if (tmp.contains("Java"))
                            continue;
                        //jars.add(tmp);
                    }
                    else
                        registerLocation(new URL(url_str.substring(0, url_str.length() - referPath.length())));
                }
            }
            catch (IOException e) {}
        }
        // 把ClassPath也扫描一下
        try {
            String classpath = System.getProperties().getProperty("java.class.path");
            String[] paths = classpath.split(System.getProperties().getProperty("path.separator"));
            for (String pathZ : paths) {
                if (pathZ.endsWith(".jar"))
                    addResourceLocation(ResourceLocation.jar(pathZ));
                else
                    addResourceLocation(ResourceLocation.file(new File(pathZ)));
            }
        }
        catch (Throwable e) {
        }
        sw.stop();
        printLocations(sw);
    }

    public void addResourceLocation(ResourceLocation loc) {
        locations.put(loc.id(), loc);
    }

    protected void printLocations(Stopwatch sw) {
        if (log.isDebugEnabled()) {
            log.debugf("Locations count=%d time use %sms", locations.size(), sw.du());
        }
        if (log.isTraceEnabled()) {
            StringBuilder sb = new StringBuilder();
            for (ResourceLocation rc : locations.values()) {
                sb.append('\t').append(rc.toString()).append("\r\n");
            }
            log.trace("Locations for Scans:\n" + sb);
        }
    }
}


package org.nutz.el.arithmetic;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

import org.nutz.el.Operator;
import org.nutz.el.opt.arithmetic.LBracketOpt;
import org.nutz.el.opt.arithmetic.RBracketOpt;
import org.nutz.el.opt.logic.QuestionOpt;
import org.nutz.el.opt.logic.QuestionSelectOpt;
import org.nutz.el.parse.Converter;

/**
 * Shunting yard算法是一个用于将中缀表达式转换为后缀表达式的经典算法，由艾兹格·迪杰斯特拉引入，因其操作类似于火车编组场而得名。

 * 参考:
 * Shunting yard算法
 *
 * @author juqkai(juqkai@gmail.com)
 *
 */
public class ShuntingYard {
    private LinkedList<Operator> opts;
    private Queue<Object> rpn;

    /**
     * 转换操作符.
     * 根据 ShuntingYard 算法进行操作
     * @param current
     */
    private void parseOperator(Operator current){
        //空,直接添加进操作符队列
        if(opts.isEmpty()){
            opts.addFirst(current);
            return;
        }
        //左括号
        if(current instanceof LBracketOpt){
            opts.addFirst(current);
            return;
        }
        //遇到右括号
        if(current instanceof RBracketOpt){
            while(!opts.isEmpty() && !(opts.peek() instanceof LBracketOpt)){
                rpn.add(opts.poll());
            }
            opts.poll();
            return;
        }

        //符号队列top元素优先级大于当前,则直接添加到
        if(!opts.isEmpty() && opts.peek().fetchPriority() > current.fetchPriority()){
            opts.addFirst(current);
            return;
        }
        //一般情况,即优先级小于栈顶,那么直接弹出来,添加到逆波兰表达式中
        while(!opts.isEmpty() && opts.peek().fetchPriority() <= current.fetchPriority()){
            //三元表达式嵌套的特殊处理
            if(opts.peek() instanceof QuestionOpt && current instanceof QuestionOpt){
                break;
            }
            if(opts.peek() instanceof QuestionOpt && current instanceof QuestionSelectOpt){
                rpn.add(opts.poll());
                break;
            }
            rpn.add(opts.poll());
        }
        opts.addFirst(current);
    }

    /**
     * 转换成 逆波兰表示法（Reverse Polish notation，RPN，或逆波兰记法）
     * @param val
     * @throws IOException
     */
    public Queue<Object> parseToRPN(String val) {
        rpn = new LinkedList<Object>();
        opts = new LinkedList<Operator>();

        Converter converter = new Converter(val);
        converter.initItems();
        while(!converter.isEnd()){
            Object item = converter.fetchItem();
            if(item instanceof Operator){
                parseOperator((Operator) item);
                continue;
            }
            rpn.add(item);
        }
        while(!opts.isEmpty()){
            rpn.add(opts.poll());
        }

        return rpn;
    }
}


package org.nutz.mvc.impl;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.ActionChain;
import org.nutz.mvc.ActionContext;

/**
 * 根据 HTTP 请求的方法 (GET|POST|PUT|DELETE) 来调用响应的动作链
 *
 * @author zozoh(zozohtnt@gmail.com)
 */
public class ActionInvoker {

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

    private ActionChain defaultChain;

    private Map<String, ActionChain> chainMap;

    public ActionInvoker() {
        chainMap = new HashMap<String, ActionChain>();
    }

    /**
     * 增加 ActionChain
     *
     * @param httpMethod
     *            HTTP 的请求方法 (GET|POST|PUT|DELETE),如果为空，则会抛错
     * @param chain
     *            动作链
     */
    public void addChain(String httpMethod, ActionChain chain) {
        if (Strings.isBlank(httpMethod))
            throw Lang.makeThrow("chain need a valid HTTP Method, but is is '%s'", httpMethod);
        ActionChain old = chainMap.put(httpMethod.toUpperCase(), chain);
        if (old != null) {
            log.warnf("Duplicate @At mapping with same HttpMethod");
        }
    }

    public void setDefaultChain(ActionChain defaultChain) {
        this.defaultChain = defaultChain;
    }

    /**
     * 根据动作链上下文对象，调用一个相应的动作链
     *
     * @param ac
     *            动作链上下文
     * @return true- 成功的找到一个动作链并执行。 false- 没有找到动作链
     */
    public boolean invoke(ActionContext ac) {
        ActionChain chain = getActionChain(ac);
        if (chain == null) {
            if (log.isDebugEnabled())
                log.debugf("Not chain for req (path=%s, method=%s)", ac.getPath(), ac.getRequest().getMethod());
            return false;
        }
        chain.doChain(ac);
        return ac.getBoolean(ActionContext.AC_DONE, true);
    }

    public ActionChain getActionChain(ActionContext ac) {
        String httpMethod = "";
        if (!chainMap.isEmpty()) {
            HttpServletRequest req = ac.getRequest();
            httpMethod = Strings.sNull(req.getMethod(), "GET").toUpperCase();
            ActionChain chain = chainMap.get(httpMethod);
            // 找到了特殊HTTP方法的处理动作链
            if (null != chain) {
                return chain;
            }
        }
        // 这个 URL 所有的HTTP方法用统一的动作链处理
        if (null != defaultChain) {
            return defaultChain;
        }
        if (chainMap.size() != 0 && log.isDebugEnabled()) {
            log.debugf("Path=[%s] available methods%s but request [%s], using the wrong http method?", ac.getPath(), chainMap.keySet(), httpMethod);
        }
        // 否则将认为不能处理
        return null;
    }
}


package org.nutz.lang;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PushbackInputStream;
import java.io.Reader;
import java.io.Writer;

import org.nutz.lang.stream.VoidInputStream;
import org.nutz.resource.NutResource;
import org.nutz.resource.Scans;

/**
 * 提供了一组创建 Reader/Writer/InputStream/OutputStream 的便利函数
 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author Wendal(wendal1985@gmail.com)
 * @author bonyfish(mc02cxj@gmail.com)
 */
public abstract class Streams {

    private static final int BUF_SIZE = 8192;

    /**
     * 判断两个输入流是否严格相等
     */
    public static boolean equals(InputStream sA, InputStream sB) throws IOException {
        int dA;
        while ((dA = sA.read()) != -1) {
            int dB = sB.read();
            if (dA != dB)
                return false;
        }
        return sB.read() == -1;
    }

    /**
     * 将一段文本全部写入一个writer。
     *

     * 注意，它并不会关闭输出流
     *
     * @param writer
     *
     * @param cs
     *            文本
     * @throws IOException
     */
    public static void write(Writer writer, CharSequence cs) throws IOException {
        if (null != cs && null != writer) {
            writer.write(cs.toString());
            writer.flush();
        }
    }

    /**
     * 将一段文本全部写入一个writer。
     *

     * 注意，它会关闭输出流
     *
     * @param writer
     *            输出流
     * @param cs
     *            文本
     */
    public static void writeAndClose(Writer writer, CharSequence cs) {
        try {
            write(writer, cs);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            safeClose(writer);
        }
    }

    /**
     * 将输入流写入一个输出流。块大小为 8192
     *

     * 注意，它并不会关闭输入/出流
     *
     * @param ops
     *            输出流
     * @param ins
     *            输入流
     *
     * @return 写入的字节数
     * @throws IOException
     */
    public static long write(OutputStream ops, InputStream ins) throws IOException {
        return write(ops, ins, BUF_SIZE);
    }

    /**
     * 将输入流写入一个输出流。
     *

     * 注意，它并不会关闭输入/出流
     *
     * @param ops
     *            输出流
     * @param ins
     *            输入流
     * @param bufferSize
     *            缓冲块大小
     *
     * @return 写入的字节数
     *
     * @throws IOException
     */
    public static long write(OutputStream ops, InputStream ins, int bufferSize) throws IOException {
        if (null == ops || null == ins)
            return 0;

        byte[] buf = new byte[bufferSize];
        int len;
        long bytesCount = 0;
        while (-1 != (len = ins.read(buf))) {
            bytesCount += len;
            ops.write(buf, 0, len);
        }
        // 啥都没写，强制触发一下写
        // 这是考虑到 walnut 的输出流实现，比如你写一个空文件
        // 那么输入流就是空的，但是 walnut 的包裹输出流并不知道你写过了
        // 它人你就是打开一个输出流，然后再关上，所以自然不会对内容做改动
        // 所以这里触发一个写，它就知道，喔你要写个空喔。
        if (0 == bytesCount) {
            ops.write(buf, 0, 0);
        }
        ops.flush();
        return bytesCount;
    }

    /**
     * 将输入流写入一个输出流。块大小为 8192
     *

     * 注意，它会关闭输入/出流
     *
     * @param ops
     *            输出流
     * @param ins
     *            输入流
     * @return 写入的字节数
     */
    public static long writeAndClose(OutputStream ops, InputStream ins) {
        try {
            return write(ops, ins);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            safeClose(ops);
            safeClose(ins);
        }
    }

    /**
     * 将文本输入流写入一个文本输出流。块大小为 8192
     *

     * 注意，它并不会关闭输入/出流
     *
     * @param writer
     *            输出流
     * @param reader
     *            输入流
     * @throws IOException
     */
    public static long write(Writer writer, Reader reader) throws IOException {
        if (null == writer || null == reader)
            return 0;

        char[] cbuf = new char[BUF_SIZE];
        int len, count = 0;
        while (true) {
            len = reader.read(cbuf);
            if (len == -1)
                break;
            writer.write(cbuf, 0, len);
            count += len;
        }
        return count;
    }

    /**
     * 将文本输入流写入一个文本输出流。块大小为 8192
     *

     * 注意，它会关闭输入/出流
     *
     * @param writer
     *            输出流
     * @param reader
     *            输入流
     */
    public static long writeAndClose(Writer writer, Reader reader) {
        try {
            return write(writer, reader);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            safeClose(writer);
            safeClose(reader);
        }
    }

    /**
     * 将一个字节数组写入一个输出流。
     *

     * 注意，它并不会关闭输出流
     *
     * @param ops
     *            输出流
     * @param bytes
     *            字节数组
     * @throws IOException
     */
    public static void write(OutputStream ops, byte[] bytes) throws IOException {
        if (null == ops || null == bytes || bytes.length == 0)
            return;
        ops.write(bytes);
    }

    /**
     * 将一个字节数组写入一个输出流。
     *

     * 注意，它会关闭输出流
     *
     * @param ops
     *            输出流
     * @param bytes
     *            字节数组
     */
    public static void writeAndClose(OutputStream ops, byte[] bytes) {
        try {
            write(ops, bytes);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            safeClose(ops);
        }
    }

    /**
     * 从一个文本流中读取全部内容并返回
     *

     * 注意，它并不会关闭输出流
     *
     * @param reader
     *            文本输出流
     * @return 文本内容
     * @throws IOException
     */
    public static StringBuilder read(Reader reader) throws IOException {
        StringBuilder sb = new StringBuilder();
        read(reader, sb);
        return sb;
    }

    /**
     * 从一个文本流中读取全部内容并返回
     *

     * 注意，它会关闭输入流
     *
     * @param reader
     *            文本输入流
     * @return 文本内容
     * @throws IOException
     */
    public static String readAndClose(Reader reader) {
        try {
            return read(reader).toString();
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            safeClose(reader);
        }
    }

    /**
     * 从一个文本流中读取全部内容并写入缓冲
     *

     * 注意，它并不会关闭输出流
     *
     * @param reader
     *            文本输出流
     * @param sb
     *            输出的文本缓冲
     * @return 读取的字符数量
     * @throws IOException
     */
    public static int read(Reader reader, StringBuilder sb) throws IOException {
        char[] cbuf = new char[BUF_SIZE];
        int count = 0;
        int len;
        while (-1 != (len = reader.read(cbuf))) {
            sb.append(cbuf, 0, len);
            count += len;
        }
        return count;
    }

    /**
     * 从一个文本流中读取全部内容并写入缓冲
     *

     * 注意，它会关闭输出流
     *
     * @param reader
     *            文本输出流
     * @param sb
     *            输出的文本缓冲
     * @return 读取的字符数量
     */
    public static int readAndClose(InputStreamReader reader, StringBuilder sb) {
        try {
            return read(reader, sb);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            safeClose(reader);
        }
    }

    /**
     * 读取一个输入流中所有的字节
     *
     * @param ins
     *            输入流，必须支持 available()
     * @return 一个字节数组
     * @throws IOException
     */
    public static byte[] readBytes(InputStream ins) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        write(out, ins);
        return out.toByteArray();
    }

    /**
     * 读取一个输入流中所有的字节，并关闭输入流
     *
     * @param ins
     *            输入流，必须支持 available()
     * @return 一个字节数组
     * @throws IOException
     */
    public static byte[] readBytesAndClose(InputStream ins) {
        byte[] bytes = null;
        try {
            bytes = readBytes(ins);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Streams.safeClose(ins);
        }
        return bytes;
    }

    /**
     * 关闭一个可关闭对象，可以接受 null。如果成功关闭，返回 true，发生异常 返回 false
     *
     * @param cb
     *            可关闭对象
     * @return 是否成功关闭
     */
    public static boolean safeClose(Closeable cb) {
        if (null != cb)
            try {
                cb.close();
            }
            catch (IOException e) {
                return false;
            }
        return true;
    }

    /**
     * 安全刷新一个可刷新的对象，可接受 null
     *
     * @param fa
     *            可刷新对象
     */
    public static void safeFlush(Flushable fa) {
        if (null != fa)
            try {
                fa.flush();
            }
            catch (IOException e) {}
    }

    /**
     * 为一个输入流包裹一个缓冲流。如果这个输入流本身就是缓冲流，则直接返回
     *
     * @param ins
     *            输入流。
     * @return 缓冲输入流
     */
    public static BufferedInputStream buff(InputStream ins) {
        if (ins == null)
            throw new NullPointerException("ins is null!");
        if (ins instanceof BufferedInputStream)
            return (BufferedInputStream) ins;
        // BufferedInputStream的构造方法,竟然是允许null参数的!! 我&$#^$&%
        return new BufferedInputStream(ins);
    }

    /**
     * 为一个输出流包裹一个缓冲流。如果这个输出流本身就是缓冲流，则直接返回
     *
     * @param ops
     *            输出流。
     * @return 缓冲输出流
     */
    public static BufferedOutputStream buff(OutputStream ops) {
        if (ops == null)
            throw new NullPointerException("ops is null!");
        if (ops instanceof BufferedOutputStream)
            return (BufferedOutputStream) ops;
        return new BufferedOutputStream(ops);
    }

    /**
     * 为一个文本输入流包裹一个缓冲流。如果这个输入流本身就是缓冲流，则直接返回
     *
     * @param reader
     *            文本输入流。
     * @return 缓冲文本输入流
     */
    public static BufferedReader buffr(Reader reader) {
        if (reader instanceof BufferedReader)
            return (BufferedReader) reader;
        return new BufferedReader(reader);
    }

    /**
     * 为一个文本输出流包裹一个缓冲流。如果这个文本输出流本身就是缓冲流，则直接返回
     *
     * @param ops
     *            文本输出流。
     * @return 缓冲文本输出流
     */
    public static BufferedWriter buffw(Writer ops) {
        if (ops instanceof BufferedWriter)
            return (BufferedWriter) ops;
        return new BufferedWriter(ops);
    }

    /**
     * 根据一个文件路径建立一个输入流
     *
     * @param path
     *            文件路径
     * @return 输入流
     */
    public static InputStream fileIn(String path) {
        InputStream ins = Files.findFileAsStream(path);
        if (null == ins) {
            File f = Files.findFile(path);
            if (null != f)
                try {
                    ins = Streams._input(f);
                }
                catch (IOException e) {}
        }
        if (null == ins) {
            // TODO 考虑一下,应该抛异常呢?还是返回null呢?

            throw new RuntimeException(new FileNotFoundException(path));
            // return null;
        }
        return buff(ins);
    }

    /**
     * 根据一个文件路径建立一个输入流
     *
     * @param file
     *            文件
     * @return 输入流
     */
    public static InputStream fileIn(File file) {
        try {
            return buff(Streams._input(file));
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
    }

    /**
     * 根据一个文件路径建立一个 UTF-8文本输入流 警告!! 本方法会预先读取3个字节以判断该文件是否存在BOM头
     *

     * 警告!! 如果存在BOM头,则自动跳过
     *

     *
     * @param path
     *            文件路径
     * @return 文本输入流
     */
    public static Reader fileInr(String path) {
        return utf8r(fileIn(path));
    }

    /**
     * 根据一个文件路径建立一个 UTF-8 文本输入流 警告!! 本方法会预先读取3个字节以判断该文件是否存在BOM头
     *

     * 警告!! 如果存在BOM头,则自动跳过
     *

     *
     * @param file
     *            文件
     * @return 文本输入流
     */
    public static Reader fileInr(File file) {
        return utf8r(fileIn(file));
    }

    private static final byte[] UTF_BOM = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};

    /**
     * 判断并移除UTF-8的BOM头
     */
    public static InputStream utf8filte(InputStream in) {
        try {
            if (in.available() == -1)
                return in;
            PushbackInputStream pis = new PushbackInputStream(in, 3);
            byte[] header = new byte[3];
            int len = pis.read(header, 0, 3);
            if (len < 1)
                return in;
            if (header[0] != UTF_BOM[0] || header[1] != UTF_BOM[1] || header[2] != UTF_BOM[2]) {
                pis.unread(header, 0, len);
            }
            return pis;
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
    }

    /**
     * 根据一个文件路径建立一个输出流
     *
     * @param path
     *            文件路径
     * @return 输出流
     */
    public static OutputStream fileOut(String path) {
        return fileOut(Files.findFile(path));
    }

    /**
     * 根据一个文件建立一个输出流
     *
     * @param file
     *            文件
     * @return 输出流
     */
    public static OutputStream fileOut(File file) {
        try {
            return buff(new FileOutputStream(file));
        }
        catch (FileNotFoundException e) {
            throw Lang.wrapThrow(e);
        }
    }

    /**
     * 根据一个文件路径建立一个 UTF-8 文本输出流
     *
     * @param path
     *            文件路径
     * @return 文本输出流
     */
    public static Writer fileOutw(String path) {
        return fileOutw(Files.findFile(path));
    }

    /**
     * 根据一个文件建立一个 UTF-8 文本输出流
     *
     * @param file
     *            文件
     * @return 输出流
     */
    public static Writer fileOutw(File file) {
        return utf8w(fileOut(file));
    }

    public static Reader utf8r(InputStream is) {
        return new InputStreamReader(utf8filte(is), Encoding.CHARSET_UTF8);
    }

    public static Writer utf8w(OutputStream os) {
        return new OutputStreamWriter(os, Encoding.CHARSET_UTF8);
    }

    public static InputStream nullInputStream() {
        return new VoidInputStream();
    }

    public static InputStream wrap(byte[] bytes) {
        return new ByteArrayInputStream(bytes);
    }

    /**
     * 对一个文本输入流迭代每一行，并将其关闭
     *
     * @param r
     *            文本输入流
     * @param callback
     *            回调
     * @return 迭代的行数
     */
    public static int eachLine(Reader r, Each<String> callback) {
        if (null == callback || null == r)
            return 0;
        BufferedReader br = null;
        try {
            br = Streams.buffr(r);
            String line;
            int index = 0;
            while (null != (line = br.readLine())) {
                try {
                    callback.invoke(index++, line, -1);
                }
                catch (ExitLoop e) {
                    break;
                }
                catch (ContinueLoop e) {
                    continue;
                }
            }
            return index;
        }
        catch (IOException e2) {
            throw Lang.wrapThrow(e2);
        }
        finally {
            Streams.safeClose(br);
        }
    }

    /**
     * 获取File对象输入流,即使在Jar文件中一样工作良好!! 强烈推荐
     *
     */
    protected static InputStream _input(File file) throws IOException {
        if (file.exists())
            return new FileInputStream(file);
        if (Scans.isInJar(file)) {
            NutResource nutResource = Scans.makeJarNutResource(file);
            if (nutResource != null)
                return nutResource.getInputStream();
        }
        throw new FileNotFoundException(file.toString());
    }

    public static void appendWriteAndClose(File f, String text) {
        FileWriter fw = null;
        try {
            fw = new FileWriter(f, true);
            fw.write(text);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            safeClose(fw);
        }

    }

    public static String nextLineTrim(BufferedReader br) throws IOException {
        String line = null;
        while (br.ready()) {
            line = br.readLine();
            if (line == null)
                break;
            if (Strings.isBlank(line))
                continue;
            return line.trim();
        }
        return line;
    }

    public static long writeAndClose(OutputStream ops, InputStream ins, int buf) {
        try {
            return write(ops, ins, buf);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            safeClose(ops);
            safeClose(ins);
        }
    }
}


package org.nutz.dao.impl.sql.run;

import static java.lang.String.format;

import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map.Entry;

import org.nutz.dao.DaoException;
import org.nutz.dao.DatabaseMeta;
import org.nutz.dao.entity.Record;
import org.nutz.dao.impl.DaoExecutor;
import org.nutz.dao.jdbc.JdbcExpert;
import org.nutz.dao.jdbc.ValueAdaptor;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.DaoStatement;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.sql.SqlType;
import org.nutz.dao.sql.VarIndex;
import org.nutz.dao.sql.VarSet;
import org.nutz.dao.util.Daos;
import org.nutz.lang.Configurable;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;

public class NutDaoExecutor implements DaoExecutor, Configurable {

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

    protected int defaultQueryTimeout;

    protected int defaultFetchSize;

    public void exec(Connection conn, DaoStatement st) {
        // 这个变量声明，后面两 case 要用到
        Object[][] paramMatrix;

        // 在这个块里执行语句
        try {
            /*
             * 语句执行前的预操作
             */
            st.onBefore(conn);
            /*
             * 开始执行语句
             */
            switch (st.getSqlType()) {
            // 查询
            case SELECT:
                _runSelect(conn, st);
                break;
            // 创建 & 删除 & 修改 & 清空
            case ALTER:
            case TRUNCATE:
            case CREATE:
            case DROP:
                _runStatement(conn, st);
                break;
            // 仅仅是运行回调
            case RUN:
                st.onAfter(conn, null, null);
                break;
            case CALL:
            case EXEC:
            	_runExec(conn, st);
            	break;
            // 插入 & 删除 & 更新
            // case DELETE:
            // case UPDATE:
            // case INSERT:
            // 见鬼了，未知类型，也当作普通 SQL 运行吧，见 Issue#13
            default:
            	if (st.isForceExecQuery()) {
            		// run as select
            		_runSelect(conn, st);
                    break;
            	}
                if (st.getSqlType() == SqlType.OTHER && log.isInfoEnabled())
                    log.info("Can't identify SQL type :   " + st);
                paramMatrix = st.getParamMatrix();
                // 木有参数，直接运行
                if (null == paramMatrix || paramMatrix.length == 0) {
                    _runStatement(conn, st);
                }
                // 有参数，用缓冲语句
                else {
                    _runPreparedStatement(conn, st, paramMatrix);
                }
            }
        }
        // If any SQLException happened, throw out the SQL string
        catch (SQLException e) {
            if (log.isDebugEnabled()) {
            	log.debug("SQLException", e);
            	SQLException nextException = e.getNextException();
                if (nextException != null)
                	log.debug("SQL NextException", nextException);
            }
            throw new DaoException(format(    "!Nutz SQL Error: '%s'\nPreparedStatement: \n'%s'",
                                            st.toString(),
                                            st.toPreparedStatement()) + "\nCaseMessage=" + e.getMessage(), e);
        }

    }

    // 执行存储过程,简单实现
    protected void _runExec(Connection conn, DaoStatement st) throws SQLException {
		if (st.getContext().getPager() != null) {
			throw Lang.makeThrow(DaoException.class, "NOT Pageable : " + st);
		}

		// 打印调试信息
		String sql = st.toPreparedStatement();
        if (log.isDebugEnabled())
            log.debug(sql);

		Object[][] paramMatrix = st.getParamMatrix();

		CallableStatement stmt = null;
		ResultSet rs = null;
		try {
            stmt = conn.prepareCall(sql);
            ValueAdaptor[] adaptors = st.getAdaptors();
            HashMap<Integer, OutParam> outParams = new HashMap<Integer, OutParam>();
            if (st instanceof Sql) {
                VarIndex varIndex = ((Sql) st).paramIndex();
                VarSet varSet = ((Sql) st).params();
                for (int i = 0; i < varIndex.size(); i++) {
                    String name = varIndex.getOrderName(i);
                    if (name.startsWith("OUT") && varSet.get(name).getClass() == Integer.class) {
                        Integer t = (Integer) varSet.get(name);
                        outParams.put(i, new OutParam(name, t));
                    }
                }
            }
			// 创建语句并设置参数
            if (paramMatrix != null && paramMatrix.length > 0) {
                PreparedStatement pst = (PreparedStatement) stmt;
                Object[] pm = paramMatrix[0];
                for (int i = 0; i < pm.length; i++) {
                    OutParam outParam = outParams.get(i);
                    if (outParam == null)
                        adaptors[i].set(pst, pm[i], i + 1);
                    else
                        stmt.registerOutParameter(i + 1, outParam.jdbcType);
                }
            }

			stmt.execute();

            if (outParams.size() > 0) {
                Record r = Record.create();
                for (Entry<Integer, OutParam> en : outParams.entrySet()) {
                    OutParam outParam = en.getValue();
                    int jdbcIndex = en.getKey() + 1;
                    Object value;
                    switch (outParam.jdbcType) {
                    case Types.INTEGER:
                        value = stmt.getInt(jdbcIndex);
                        break;
                    case Types.TIMESTAMP:
                        value = stmt.getTimestamp(jdbcIndex);
                        break;
                    case Types.CLOB:
                        value = stmt.getString(jdbcIndex);
                        break;
                    case Types.DATE:
                        value = stmt.getDate(jdbcIndex);
                        break;
                    default:
                        value = stmt.getObject(jdbcIndex);
                        break;
                    }
                    r.set(outParam.name.substring(3), value);
                }
                st.getContext().attr("OUT", r);
            }
			//先尝试读取第一个,并调用一次回调
			rs = stmt.getResultSet();
			try {
				st.onAfter(conn, rs, null);
			}
			finally {
				if (rs != null)
					rs.close();
			}

			while (true) {
				if (stmt.getMoreResults()) {
					rs = stmt.getResultSet();
					try {
						if (rs != null)
							st.onAfter(conn, rs, null);
					}
					finally {
						if (rs != null)
							rs.close();
					}
				}
				break;
			}
		}
		finally {
			if (stmt != null)
				stmt.close();
		}
	}

	private void _runSelect(Connection conn, DaoStatement st)
            throws SQLException {

        Object[][] paramMatrix = st.getParamMatrix();
        // -------------------------------------------------
        // 以下代码,就为了该死的游标分页!!
        // -------------------------------------------------
        int startRow = -1;
        int lastRow = -1;
        if (st.getContext().getResultSetType() == ResultSet.TYPE_SCROLL_INSENSITIVE) {
            Pager pager = st.getContext().getPager();
            if (pager != null) {
                startRow = pager.getOffset();
                lastRow = pager.getOffset() + pager.getPageSize();
            }
        }
        // -------------------------------------------------
        // 生成 Sql 语句
        String sql = st.toPreparedStatement();
        // 打印调试信息

        ResultSet rs = null;
        Statement stat = null;
        try {
            // 木有参数，直接运行
            if (null == paramMatrix || paramMatrix.length == 0
                    || paramMatrix[0].length == 0) {
                stat = conn.createStatement(st.getContext()
                        .getResultSetType(), ResultSet.CONCUR_READ_ONLY);
                if (lastRow > 0)
                    stat.setMaxRows(lastRow); // 游标分页,现在总行数
                afterCreateStatement(stat, st);
                rs = stat.executeQuery(sql);
            }
            // 有参数，用缓冲语句
            else {

                // 打印调试信息
                if (paramMatrix.length > 1) {
                    if (log.isWarnEnabled())
                        log.warnf("Drop last %d rows parameters for:\n%s",
                                paramMatrix.length - 1, st);
                }

                // 准备运行语句
                ValueAdaptor[] adaptors = st.getAdaptors();
                // 创建语句并设置参数
                stat = conn.prepareStatement(sql, st
                        .getContext().getResultSetType(),
                        ResultSet.CONCUR_READ_ONLY);
                if (lastRow > 0)
                    stat.setMaxRows(lastRow);
                afterCreateStatement(stat, st);
                for (int i = 0; i < paramMatrix[0].length; i++) {
                    adaptors[i].set((PreparedStatement) stat,
                            paramMatrix[0][i], i + 1);
                }
                rs = ((PreparedStatement) stat).executeQuery();
            }
            if (startRow > 0)
                rs.absolute(startRow);
            // 执行回调
            st.onAfter(conn, rs, stat);
        } finally {
            Daos.safeClose(stat, rs);
        }
        // 打印更详细的调试信息
        if (log.isTraceEnabled())
            log.trace("...DONE");
    }

    private void _runPreparedStatement(Connection conn, DaoStatement st, Object[][] paramMatrix)
            throws SQLException {
        ValueAdaptor[] adaptors = st.getAdaptors();
        if (adaptors.length != paramMatrix[0].length)
            throw Lang.makeThrow("DaoStatement adaptor MUST same width with param matrix.");

        boolean statIsClosed = false;
        String sql = st.toPreparedStatement();
        PreparedStatement pstat = null;

        try {
            // 创建 SQL 语句
        	if (st.getContext().attr("RETURN_GENERATED_KEYS") == null)
        		pstat = conn.prepareStatement(sql);
        	else
        		pstat = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

            // 就一条记录，不要批了吧
            if (paramMatrix.length == 1) {
                for (int i = 0; i < paramMatrix[0].length; i++) {
                    adaptors[i].set(pstat, paramMatrix[0][i], i + 1);
                }
                pstat.execute();
                st.getContext().setUpdateCount(pstat.getUpdateCount());
                st.onAfter(conn, null, pstat);
                pstat.close();
                statIsClosed = true;
            }
            // 恩，批
            else {
                for (Object[] params : paramMatrix) {
                    for (int i = 0; i < params.length; i++) {
                        adaptors[i].set(pstat, params[i], i + 1);
                    }
                    pstat.addBatch();// 需要配置一下batchSize,嘻嘻,不然分分钟爆内存!!
                }
                int[] counts = pstat.executeBatch();

                // 计算总共影响的行数
                int sum = 0;
                for (int i : counts)
                    if (i > 0)
                        sum += i;

                if (sum == 0)
                    sum = pstat.getUpdateCount();

                st.onAfter(conn, null, pstat);
                pstat.close();
                statIsClosed = true;

                st.getContext().setUpdateCount(sum);
            }
        }
        finally {
            if (!statIsClosed)
                Daos.safeClose(pstat);
        }

        // 打印更详细的调试信息
        if (log.isTraceEnabled())
            log.trace("...DONE");
    }

    private void _runStatement(Connection conn, DaoStatement st) throws SQLException {
        boolean statIsClosed = false;
        Statement stat = null;
        String sql = st.toPreparedStatement();

        try {
            stat = conn.createStatement();
            stat.execute(sql);
            st.getContext().setUpdateCount(stat.getUpdateCount());
            st.onAfter(conn, null, stat);
            stat.close();
            statIsClosed = true;
        }
        finally {
            if (!statIsClosed)
                Daos.safeClose(stat);
        }
        // 打印更详细的调试信息
        if (log.isTraceEnabled())
            log.trace("...DONE");
    }

    protected DatabaseMeta meta;

    protected JdbcExpert expert;

    public void setMeta(DatabaseMeta meta) {
		this.meta = meta;
	}

    public void setExpert(JdbcExpert expert) {
		this.expert = expert;
	}

    // 写在这里完全是为了兼容老版本的log4j配置
    public static void printSQL(DaoStatement sql) {
        // 打印调试信息
        if (log.isDebugEnabled())
            log.debug(sql.forPrint());
    }

    static class OutParam implements Serializable {
        private static final long serialVersionUID = 1L;
        String name;
        int jdbcType;
        public OutParam() {}
        public OutParam(String name, int jdbcType) {
            super();
            this.name = name;
            this.jdbcType = jdbcType;
        }
    }

    protected void afterCreateStatement(Statement stat, DaoStatement st) throws SQLException {
        if (st.getContext().getFetchSize() != 0)
            stat.setFetchSize(st.getContext().getFetchSize());
        else if (defaultFetchSize > 0) {
            stat.setFetchSize(defaultFetchSize);
        }
        if (st.getContext().getQueryTimeout() > 0)
            stat.setQueryTimeout(st.getContext().getQueryTimeout());
        else if (defaultQueryTimeout > 0) {
            stat.setQueryTimeout(defaultQueryTimeout);
        }
    }

    public int getDefaultQueryTimeout() {
        return defaultQueryTimeout;
    }

    public void setDefaultQueryTimeout(int defaultQueryTimeout) {
        this.defaultQueryTimeout = defaultQueryTimeout;
    }

    public int getDefaultFetchSize() {
        return defaultFetchSize;
    }

    public void setDefaultFetchSize(int defaultFetchSize) {
        this.defaultFetchSize = defaultFetchSize;
    }

    public void setupProperties(NutMap conf) {
        defaultQueryTimeout = conf.getInt("nutz.dao.query.timeout", 0);
        defaultFetchSize = conf.getInt("nutz.dao.query.fetchSize", 0);
    }

}


/***
 * ASM: a very small and fast Java bytecode manipulation framework
 * Copyright (c) 2000-2011 INRIA, France Telecom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.nutz.repo.org.objectweb.asm;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * A Java field or method type. This class can be used to make it easier to
 * manipulate type and method descriptors.
 *
 * @author Eric Bruneton
 * @author Chris Nokleberg
 */
public class Type {

    /**
     * The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
     */
    public static final int VOID = 0;

    /**
     * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
     */
    public static final int BOOLEAN = 1;

    /**
     * The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
     */
    public static final int CHAR = 2;

    /**
     * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
     */
    public static final int BYTE = 3;

    /**
     * The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
     */
    public static final int SHORT = 4;

    /**
     * The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
     */
    public static final int INT = 5;

    /**
     * The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
     */
    public static final int FLOAT = 6;

    /**
     * The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
     */
    public static final int LONG = 7;

    /**
     * The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
     */
    public static final int DOUBLE = 8;

    /**
     * The sort of array reference types. See {@link #getSort getSort}.
     */
    public static final int ARRAY = 9;

    /**
     * The sort of object reference types. See {@link #getSort getSort}.
     */
    public static final int OBJECT = 10;

    /**
     * The sort of method types. See {@link #getSort getSort}.
     */
    public static final int METHOD = 11;

    /**
     * The <tt>void</tt> type.
     */
    public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
            | (5 << 16) | (0 << 8) | 0, 1);

    /**
     * The <tt>boolean</tt> type.
     */
    public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
            | (0 << 16) | (5 << 8) | 1, 1);

    /**
     * The <tt>char</tt> type.
     */
    public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
            | (0 << 16) | (6 << 8) | 1, 1);

    /**
     * The <tt>byte</tt> type.
     */
    public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
            | (0 << 16) | (5 << 8) | 1, 1);

    /**
     * The <tt>short</tt> type.
     */
    public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
            | (0 << 16) | (7 << 8) | 1, 1);

    /**
     * The <tt>int</tt> type.
     */
    public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
            | (0 << 16) | (0 << 8) | 1, 1);

    /**
     * The <tt>float</tt> type.
     */
    public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
            | (2 << 16) | (2 << 8) | 1, 1);

    /**
     * The <tt>long</tt> type.
     */
    public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
            | (1 << 16) | (1 << 8) | 2, 1);

    /**
     * The <tt>double</tt> type.
     */
    public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
            | (3 << 16) | (3 << 8) | 2, 1);

    // ------------------------------------------------------------------------
    // Fields
    // ------------------------------------------------------------------------

    /**
     * The sort of this Java type.
     */
    private final int sort;

    /**
     * A buffer containing the internal name of this Java type. This field is
     * only used for reference types.
     */
    private final char[] buf;

    /**
     * The offset of the internal name of this Java type in {@link #buf buf} or,
     * for primitive types, the size, descriptor and getOpcode offsets for this
     * type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset
     * for IALOAD or IASTORE, byte 3 the offset for all other instructions).
     */
    private final int off;

    /**
     * The length of the internal name of this Java type.
     */
    private final int len;

    // ------------------------------------------------------------------------
    // Constructors
    // ------------------------------------------------------------------------

    /**
     * Constructs a reference type.
     *
     * @param sort
     *            the sort of the reference type to be constructed.
     * @param buf
     *            a buffer containing the descriptor of the previous type.
     * @param off
     *            the offset of this descriptor in the previous buffer.
     * @param len
     *            the length of this descriptor.
     */
    private Type(final int sort, final char[] buf, final int off, final int len) {
        this.sort = sort;
        this.buf = buf;
        this.off = off;
        this.len = len;
    }

    /**
     * Returns the Java type corresponding to the given type descriptor.
     *
     * @param typeDescriptor
     *            a field or method type descriptor.
     * @return the Java type corresponding to the given type descriptor.
     */
    public static Type getType(final String typeDescriptor) {
        return getType(typeDescriptor.toCharArray(), 0);
    }

    /**
     * Returns the Java type corresponding to the given internal name.
     *
     * @param internalName
     *            an internal name.
     * @return the Java type corresponding to the given internal name.
     */
    public static Type getObjectType(final String internalName) {
        char[] buf = internalName.toCharArray();
        return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length);
    }

    /**
     * Returns the Java type corresponding to the given method descriptor.
     * Equivalent to Type.getType(methodDescriptor).
     *
     * @param methodDescriptor
     *            a method descriptor.
     * @return the Java type corresponding to the given method descriptor.
     */
    public static Type getMethodType(final String methodDescriptor) {
        return getType(methodDescriptor.toCharArray(), 0);
    }

    /**
     * Returns the Java method type corresponding to the given argument and
     * return types.
     *
     * @param returnType
     *            the return type of the method.
     * @param argumentTypes
     *            the argument types of the method.
     * @return the Java type corresponding to the given argument and return
     *         types.
     */
    public static Type getMethodType(final Type returnType,
            final Type... argumentTypes) {
        return getType(getMethodDescriptor(returnType, argumentTypes));
    }

    /**
     * Returns the Java type corresponding to the given class.
     *
     * @param c
     *            a class.
     * @return the Java type corresponding to the given class.
     */
    public static Type getType(final Class<?> c) {
        if (c.isPrimitive()) {
            if (c == Integer.TYPE) {
                return INT_TYPE;
            } else if (c == Void.TYPE) {
                return VOID_TYPE;
            } else if (c == Boolean.TYPE) {
                return BOOLEAN_TYPE;
            } else if (c == Byte.TYPE) {
                return BYTE_TYPE;
            } else if (c == Character.TYPE) {
                return CHAR_TYPE;
            } else if (c == Short.TYPE) {
                return SHORT_TYPE;
            } else if (c == Double.TYPE) {
                return DOUBLE_TYPE;
            } else if (c == Float.TYPE) {
                return FLOAT_TYPE;
            } else /* if (c == Long.TYPE) */{
                return LONG_TYPE;
            }
        } else {
            return getType(getDescriptor(c));
        }
    }

    /**
     * Returns the Java method type corresponding to the given constructor.
     *
     * @param c
     *            a {@link Constructor Constructor} object.
     * @return the Java method type corresponding to the given constructor.
     */
    public static Type getType(final Constructor<?> c) {
        return getType(getConstructorDescriptor(c));
    }

    /**
     * Returns the Java method type corresponding to the given method.
     *
     * @param m
     *            a {@link Method Method} object.
     * @return the Java method type corresponding to the given method.
     */
    public static Type getType(final Method m) {
        return getType(getMethodDescriptor(m));
    }

    /**
     * Returns the Java types corresponding to the argument types of the given
     * method descriptor.
     *
     * @param methodDescriptor
     *            a method descriptor.
     * @return the Java types corresponding to the argument types of the given
     *         method descriptor.
     */
    public static Type[] getArgumentTypes(final String methodDescriptor) {
        char[] buf = methodDescriptor.toCharArray();
        int off = 1;
        int size = 0;
        while (true) {
            char car = buf[off++];
            if (car == ')') {
                break;
            } else if (car == 'L') {
                while (buf[off++] != ';') {
                }
                ++size;
            } else if (car != '[') {
                ++size;
            }
        }
        Type[] args = new Type[size];
        off = 1;
        size = 0;
        while (buf[off] != ')') {
            args[size] = getType(buf, off);
            off += args[size].len + (args[size].sort == OBJECT ? 2 : 0);
            size += 1;
        }
        return args;
    }

    /**
     * Returns the Java types corresponding to the argument types of the given
     * method.
     *
     * @param method
     *            a method.
     * @return the Java types corresponding to the argument types of the given
     *         method.
     */
    public static Type[] getArgumentTypes(final Method method) {
        Class<?>[] classes = method.getParameterTypes();
        Type[] types = new Type[classes.length];
        for (int i = classes.length - 1; i >= 0; --i) {
            types[i] = getType(classes[i]);
        }
        return types;
    }

    /**
     * Returns the Java type corresponding to the return type of the given
     * method descriptor.
     *
     * @param methodDescriptor
     *            a method descriptor.
     * @return the Java type corresponding to the return type of the given
     *         method descriptor.
     */
    public static Type getReturnType(final String methodDescriptor) {
        char[] buf = methodDescriptor.toCharArray();
        int off = 1;
        while (true) {
            char car = buf[off++];
            if (car == ')') {
                return getType(buf, off);
            } else if (car == 'L') {
                while (buf[off++] != ';') {
                }
            }
        }
    }

    /**
     * Returns the Java type corresponding to the return type of the given
     * method.
     *
     * @param method
     *            a method.
     * @return the Java type corresponding to the return type of the given
     *         method.
     */
    public static Type getReturnType(final Method method) {
        return getType(method.getReturnType());
    }

    /**
     * Computes the size of the arguments and of the return value of a method.
     *
     * @param desc
     *            the descriptor of a method.
     * @return the size of the arguments of the method (plus one for the
     *         implicit this argument), argSize, and the size of its return
     *         value, retSize, packed into a single int i =
     *         <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to
     *         <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
     */
    public static int getArgumentsAndReturnSizes(final String desc) {
        int n = 1;
        int c = 1;
        while (true) {
            char car = desc.charAt(c++);
            if (car == ')') {
                car = desc.charAt(c);
                return n << 2
                        | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1));
            } else if (car == 'L') {
                while (desc.charAt(c++) != ';') {
                }
                n += 1;
            } else if (car == '[') {
                while ((car = desc.charAt(c)) == '[') {
                    ++c;
                }
                if (car == 'D' || car == 'J') {
                    n -= 1;
                }
            } else if (car == 'D' || car == 'J') {
                n += 2;
            } else {
                n += 1;
            }
        }
    }

    /**
     * Returns the Java type corresponding to the given type descriptor. For
     * method descriptors, buf is supposed to contain nothing more than the
     * descriptor itself.
     *
     * @param buf
     *            a buffer containing a type descriptor.
     * @param off
     *            the offset of this descriptor in the previous buffer.
     * @return the Java type corresponding to the given type descriptor.
     */
    private static Type getType(final char[] buf, final int off) {
        int len;
        switch (buf[off]) {
        case 'V':
            return VOID_TYPE;
        case 'Z':
            return BOOLEAN_TYPE;
        case 'C':
            return CHAR_TYPE;
        case 'B':
            return BYTE_TYPE;
        case 'S':
            return SHORT_TYPE;
        case 'I':
            return INT_TYPE;
        case 'F':
            return FLOAT_TYPE;
        case 'J':
            return LONG_TYPE;
        case 'D':
            return DOUBLE_TYPE;
        case '[':
            len = 1;
            while (buf[off + len] == '[') {
                ++len;
            }
            if (buf[off + len] == 'L') {
                ++len;
                while (buf[off + len] != ';') {
                    ++len;
                }
            }
            return new Type(ARRAY, buf, off, len + 1);
        case 'L':
            len = 1;
            while (buf[off + len] != ';') {
                ++len;
            }
            return new Type(OBJECT, buf, off + 1, len - 1);
            // case '(':
        default:
            return new Type(METHOD, buf, off, buf.length - off);
        }
    }

    // ------------------------------------------------------------------------
    // Accessors
    // ------------------------------------------------------------------------

    /**
     * Returns the sort of this Java type.
     *
     * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR},
     *         {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT},
     *         {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE},
     *         {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD
     *         METHOD}.
     */
    public int getSort() {
        return sort;
    }

    /**
     * Returns the number of dimensions of this array type. This method should
     * only be used for an array type.
     *
     * @return the number of dimensions of this array type.
     */
    public int getDimensions() {
        int i = 1;
        while (buf[off + i] == '[') {
            ++i;
        }
        return i;
    }

    /**
     * Returns the type of the elements of this array type. This method should
     * only be used for an array type.
     *
     * @return Returns the type of the elements of this array type.
     */
    public Type getElementType() {
        return getType(buf, off + getDimensions());
    }

    /**
     * Returns the binary name of the class corresponding to this type. This
     * method must not be used on method types.
     *
     * @return the binary name of the class corresponding to this type.
     */
    public String getClassName() {
        switch (sort) {
        case VOID:
            return "void";
        case BOOLEAN:
            return "boolean";
        case CHAR:
            return "char";
        case BYTE:
            return "byte";
        case SHORT:
            return "short";
        case INT:
            return "int";
        case FLOAT:
            return "float";
        case LONG:
            return "long";
        case DOUBLE:
            return "double";
        case ARRAY:
            StringBuilder sb = new StringBuilder(getElementType().getClassName());
            for (int i = getDimensions(); i > 0; --i) {
                sb.append("[]");
            }
            return sb.toString();
        case OBJECT:
            return new String(buf, off, len).replace('/', '.');
        default:
            return null;
        }
    }

    /**
     * Returns the internal name of the class corresponding to this object or
     * array type. The internal name of a class is its fully qualified name (as
     * returned by Class.getName(), where '.' are replaced by '/'. This method
     * should only be used for an object or array type.
     *
     * @return the internal name of the class corresponding to this object type.
     */
    public String getInternalName() {
        return new String(buf, off, len);
    }

    /**
     * Returns the argument types of methods of this type. This method should
     * only be used for method types.
     *
     * @return the argument types of methods of this type.
     */
    public Type[] getArgumentTypes() {
        return getArgumentTypes(getDescriptor());
    }

    /**
     * Returns the return type of methods of this type. This method should only
     * be used for method types.
     *
     * @return the return type of methods of this type.
     */
    public Type getReturnType() {
        return getReturnType(getDescriptor());
    }

    /**
     * Returns the size of the arguments and of the return value of methods of
     * this type. This method should only be used for method types.
     *
     * @return the size of the arguments (plus one for the implicit this
     *         argument), argSize, and the size of the return value, retSize,
     *         packed into a single
     *         int i = <tt>(argSize << 2) | retSize</tt>
     *         (argSize is therefore equal to <tt>i >> 2</tt>,
     *         and retSize to <tt>i & 0x03</tt>).
     */
    public int getArgumentsAndReturnSizes() {
        return getArgumentsAndReturnSizes(getDescriptor());
    }

    // ------------------------------------------------------------------------
    // Conversion to type descriptors
    // ------------------------------------------------------------------------

    /**
     * Returns the descriptor corresponding to this Java type.
     *
     * @return the descriptor corresponding to this Java type.
     */
    public String getDescriptor() {
        StringBuilder buf = new StringBuilder();
        getDescriptor(buf);
        return buf.toString();
    }

    /**
     * Returns the descriptor corresponding to the given argument and return
     * types.
     *
     * @param returnType
     *            the return type of the method.
     * @param argumentTypes
     *            the argument types of the method.
     * @return the descriptor corresponding to the given argument and return
     *         types.
     */
    public static String getMethodDescriptor(final Type returnType,
            final Type... argumentTypes) {
        StringBuilder buf = new StringBuilder();
        buf.append('(');
        for (int i = 0; i < argumentTypes.length; ++i) {
            argumentTypes[i].getDescriptor(buf);
        }
        buf.append(')');
        returnType.getDescriptor(buf);
        return buf.toString();
    }

    /**
     * Appends the descriptor corresponding to this Java type to the given
     * string buffer.
     *
     * @param buf
     *            the string buffer to which the descriptor must be appended.
     */
    private void getDescriptor(final StringBuilder buf) {
        if (this.buf == null) {
            // descriptor is in byte 3 of 'off' for primitive types (buf ==
            // null)
            buf.append((char) ((off & 0xFF000000) >>> 24));
        } else if (sort == OBJECT) {
            buf.append('L');
            buf.append(this.buf, off, len);
            buf.append(';');
        } else { // sort == ARRAY || sort == METHOD
            buf.append(this.buf, off, len);
        }
    }

    // ------------------------------------------------------------------------
    // Direct conversion from classes to type descriptors,
    // without intermediate Type objects
    // ------------------------------------------------------------------------

    /**
     * Returns the internal name of the given class. The internal name of a
     * class is its fully qualified name, as returned by Class.getName(), where
     * '.' are replaced by '/'.
     *
     * @param c
     *            an object or array class.
     * @return the internal name of the given class.
     */
    public static String getInternalName(final Class<?> c) {
        return c.getName().replace('.', '/');
    }

    /**
     * Returns the descriptor corresponding to the given Java type.
     *
     * @param c
     *            an object class, a primitive class or an array class.
     * @return the descriptor corresponding to the given class.
     */
    public static String getDescriptor(final Class<?> c) {
        StringBuilder buf = new StringBuilder();
        getDescriptor(buf, c);
        return buf.toString();
    }

    /**
     * Returns the descriptor corresponding to the given constructor.
     *
     * @param c
     *            a {@link Constructor Constructor} object.
     * @return the descriptor of the given constructor.
     */
    public static String getConstructorDescriptor(final Constructor<?> c) {
        Class<?>[] parameters = c.getParameterTypes();
        StringBuilder buf = new StringBuilder();
        buf.append('(');
        for (int i = 0; i < parameters.length; ++i) {
            getDescriptor(buf, parameters[i]);
        }
        return buf.append(")V").toString();
    }

    /**
     * Returns the descriptor corresponding to the given method.
     *
     * @param m
     *            a {@link Method Method} object.
     * @return the descriptor of the given method.
     */
    public static String getMethodDescriptor(final Method m) {
        Class<?>[] parameters = m.getParameterTypes();
        StringBuilder buf = new StringBuilder();
        buf.append('(');
        for (int i = 0; i < parameters.length; ++i) {
            getDescriptor(buf, parameters[i]);
        }
        buf.append(')');
        getDescriptor(buf, m.getReturnType());
        return buf.toString();
    }

    /**
     * Appends the descriptor of the given class to the given string buffer.
     *
     * @param buf
     *            the string buffer to which the descriptor must be appended.
     * @param c
     *            the class whose descriptor must be computed.
     */
    private static void getDescriptor(final StringBuilder buf, final Class<?> c) {
        Class<?> d = c;
        while (true) {
            if (d.isPrimitive()) {
                char car;
                if (d == Integer.TYPE) {
                    car = 'I';
                } else if (d == Void.TYPE) {
                    car = 'V';
                } else if (d == Boolean.TYPE) {
                    car = 'Z';
                } else if (d == Byte.TYPE) {
                    car = 'B';
                } else if (d == Character.TYPE) {
                    car = 'C';
                } else if (d == Short.TYPE) {
                    car = 'S';
                } else if (d == Double.TYPE) {
                    car = 'D';
                } else if (d == Float.TYPE) {
                    car = 'F';
                } else /* if (d == Long.TYPE) */{
                    car = 'J';
                }
                buf.append(car);
                return;
            } else if (d.isArray()) {
                buf.append('[');
                d = d.getComponentType();
            } else {
                buf.append('L');
                String name = d.getName();
                int len = name.length();
                for (int i = 0; i < len; ++i) {
                    char car = name.charAt(i);
                    buf.append(car == '.' ? '/' : car);
                }
                buf.append(';');
                return;
            }
        }
    }

    // ------------------------------------------------------------------------
    // Corresponding size and opcodes
    // ------------------------------------------------------------------------

    /**
     * Returns the size of values of this type. This method must not be used for
     * method types.
     *
     * @return the size of values of this type, i.e., 2 for <tt>long</tt> and
     *         <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise.
     */
    public int getSize() {
        // the size is in byte 0 of 'off' for primitive types (buf == null)
        return buf == null ? (off & 0xFF) : 1;
    }

    /**
     * Returns a JVM instruction opcode adapted to this Java type. This method
     * must not be used for method types.
     *
     * @param opcode
     *            a JVM instruction opcode. This opcode must be one of ILOAD,
     *            ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
     *            ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
     * @return an opcode that is similar to the given opcode, but adapted to
     *         this Java type. For example, if this type is <tt>float</tt> and
     *         <tt>opcode</tt> is IRETURN, this method returns FRETURN.
     */
    public int getOpcode(final int opcode) {
        if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
            // the offset for IALOAD or IASTORE is in byte 1 of 'off' for
            // primitive types (buf == null)
            return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4);
        } else {
            // the offset for other instructions is in byte 2 of 'off' for
            // primitive types (buf == null)
            return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4);
        }
    }

    // ------------------------------------------------------------------------
    // Equals, hashCode and toString
    // ------------------------------------------------------------------------

    /**
     * Tests if the given object is equal to this type.
     *
     * @param o
     *            the object to be compared to this type.
     * @return <tt>true</tt> if the given object is equal to this type.
     */
    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Type)) {
            return false;
        }
        Type t = (Type) o;
        if (sort != t.sort) {
            return false;
        }
        if (sort >= ARRAY) {
            if (len != t.len) {
                return false;
            }
            for (int i = off, j = t.off, end = i + len; i < end; i++, j++) {
                if (buf[i] != t.buf[j]) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Returns a hash code value for this type.
     *
     * @return a hash code value for this type.
     */
    @Override
    public int hashCode() {
        int hc = 13 * sort;
        if (sort >= ARRAY) {
            for (int i = off, end = i + len; i < end; i++) {
                hc = 17 * (hc + buf[i]);
            }
        }
        return hc;
    }

    /**
     * Returns a string representation of this type.
     *
     * @return the descriptor of this type.
     */
    @Override
    public String toString() {
        return getDescriptor();
    }
}


package org.nutz.lang.born;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.nutz.castor.Castors;
import org.nutz.lang.Lang;
import org.nutz.lang.MatchType;
import org.nutz.lang.Mirror;

/**
 * 关于创建对象的一些帮助方法
 *
 * @author zozoh(zozohtnt@gmail.com)
 */
public abstract class Borns {

	/**
	 * 根据参数类型数组获取一个对象的构建信息
	 *
	 * @param <T>
	 *            对象类型信息
	 * @param type
	 *            对象类型
	 * @param argTypes
	 *            构造参数类型数组
	 * @return 构建信息对象
	 */
	public static <T> BornContext<T> evalByArgTypes(Class<T> type, Class<?>... argTypes) {
		BornContext<T> re;
		if (argTypes.length == 0) {
			re = evalWithoutArgs(type);
		} else {
			re = evalWithArgTypes(true, type, argTypes, null);
		}
		return re;
	}

	/**
	 * 根据参数类型数组获取一个对象的构建信息
	 *
	 * @param <T>
	 *            对象类型信息
	 * @param type
	 *            对象类型
	 * @param args
	 *            构造参数数组
	 * @return 构建信息对象
	 * @throws NullPointerException
	 *             when args is null
	 */
	public static <T> BornContext<T> eval(Class<T> type, Object... args) {
		BornContext<T> re;
		if (args.length == 0) {
			re = evalWithoutArgs(type);
		} else {
			re = evalWithArgs(type, args);
		}
		return re;
	}

	/**
	 * 根据一个调用参数数组，获取一个对象的构建信息
	 *
	 * @param <T>
	 *            对象类型信息
	 * @param type
	 *            对象类型
	 * @param args
	 *            参考构建参数
	 * @return 构建信息对象
	 */
	private static <T> BornContext<T> evalWithArgs(Class<T> type, Object[] args) {
		// 准备变参数组
		Object dynaArg = Mirror.evalArgToSameTypeRealArray(args);

		// 准备好参数类型
		Class<?>[] argTypes = Mirror.evalToTypes(args);

		BornContext<T> re = evalWithArgTypes(false, type, argTypes, dynaArg);

		if (null == re)
			return null;

		if (MatchType.LACK == re.getMatchType()) {
			re.setArgs(Lang.arrayLast(args, re.getLackArg()));
		} else {
			re.setArgs(args);
		}

		switch (re.getMatchType()) {
		case LACK:
			re.setArgs(Lang.arrayLast(args, re.getLackArg()));
			break;
		case NEED_CAST:
			re.setArgs(Lang.array2ObjectArray(args, re.getCastType()));
			break;
		default:
			re.setArgs(args);
		}

		return re;
	}

	/**
	 * 根据一个调用参数类型数组，获取一个对象的构建信息
	 *
	 * @param <T>
	 *            对象类型信息
	 * @param accurate
	 *            是否需要精确匹配
	 * @param type
	 *            对象类型
	 * @param argTypes
	 *            参考参数类型数组
	 * @param dynaAry
	 *            参考参数类型信息是否是一个变参数组
	 * @return 构建信息对象
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	private static <T> BornContext<T> evalWithArgTypes(boolean accurate,
													   Class<T> type,
													   Class<?>[] argTypes,
													   Object dynaArg) {
		// 准备好返回对象
		BornContext<T> re = new BornContext<T>();

		// 先看看有没对应的构造函数
		Mirror<T> mirror = Mirror.me(type);
		for (Constructor<?> cc : type.getConstructors()) {
			Class<?>[] pts = cc.getParameterTypes();
			MatchType mt = Mirror.matchParamTypes(pts, argTypes);
			re.setMatchType(mt);
			// 正好合适
			if (MatchType.YES == mt) {
				return re.setBorning(new ConstructorBorning(cc));
			}
			// 差一个参数，说明这个构造函数有变参数组
			else if (MatchType.LACK == mt) {
				re.setLackArg(Mirror.blankArrayArg(pts));
				return re.setBorning(new ConstructorBorning(cc));
			}
			// 看看整个输入的参数是不是变参
			else if (null != dynaArg && pts.length == 1 && pts[0] == dynaArg.getClass()) {
				return re.setBorning(new DynamicConstructorBorning(cc));
			}
		}

		// 看看有没有对应静态工厂函数
		Method[] sms = mirror.getStaticMethods();
		for (Method m : sms) {
			Class<?>[] pts = m.getParameterTypes();
			MatchType mt = Mirror.matchParamTypes(pts, argTypes);
			re.setMatchType(mt);
			if (MatchType.YES == mt) {
				return re.setBorning(new MethodBorning<T>(m));
			} else if (MatchType.LACK == mt) {
				re.setLackArg(Mirror.blankArrayArg(pts));
				return re.setBorning(new MethodBorning<T>(m));
			} else if (null != dynaArg && pts.length == 1) {
				if (pts[0] == dynaArg.getClass()) {
					return re.setBorning(new DynaMethodBorning<T>(m));
				}
			}
		}
		// 如果不是要精确查找的话
		if (!accurate) {
			// 找到一个长度合适的构造函数，准备转换
			try {
				for (Constructor<?> cc : type.getConstructors()) {
					Class<?>[] pts = cc.getParameterTypes();
					if (canBeCasted(argTypes, pts)) {
						re.setMatchType(MatchType.NEED_CAST);
						re.setCastType(pts);
						return re.setBorning(new ConstructorCastingBorning(cc));
					}
				}
			}
			catch (RuntimeException e) {}
			// 有没有变参的静态构造方法
			try {
				for (Method m : sms) {
					Class<?>[] pts = m.getParameterTypes();
					if (canBeCasted(argTypes, pts)) {
						re.setMatchType(MatchType.NEED_CAST);
						re.setCastType(pts);
						return re.setBorning(new MethodCastingBorning<T>(m));
					}
				}
			}
			catch (Exception e) {}
		}

		return null;
	}

	private static boolean canBeCasted(Class<?>[] argTypes, Class<?>[] pts) {
		if (pts.length != argTypes.length)
			return false;
		for (int i = 0; i < pts.length; i++) {
			if (!Castors.me().canCast(argTypes[i], pts[i]))
				return false;
		}

		return true;
	}

	/**
	 * 为一个给定类，寻找一个不需要参数的构造方法
	 *
	 * @param <T>
	 *            类
	 * @param type
	 *            类实例
	 * @return 构造信息
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	private static <T> BornContext<T> evalWithoutArgs(Class<T> type) {
		// 准备好返回对象
		BornContext<T> re = new BornContext<T>();
		Mirror<T> mirror = Mirror.me(type);
		boolean isAbstract = Modifier.isAbstract(type.getModifiers());

		// 先看看有没有默认构造函数
		try {
			if (!isAbstract) {
				re.setBorning(new EmptyArgsConstructorBorning<T>(type.getConstructor()));
				return re.setArgs(new Object[0]);
			}
		}
		// 如果没有默认构造函数 ...
		catch (Exception e) {}
		// 看看有没有默认静态工厂函数
		Method[] stMethods = mirror.getStaticMethods();
		for (Method m : stMethods) {
			if (m.getReturnType().equals(type) && m.getParameterTypes().length == 0) {
				return re.setBorning(new EmptyArgsMethodBorning<T>(m)).setArgs(new Object[0]);
			}
		}
		// 看看有没有带一个动态参数的构造函数
		if (!isAbstract) {
			for (Constructor<?> cons : type.getConstructors()) {
				Class<?>[] pts = cons.getParameterTypes();
				if (pts.length == 1 && pts[0].isArray()) {
					Object[] args = new Object[1];
					args[0] = Mirror.blankArrayArg(pts);
					return re.setBorning(new ConstructorBorning(cons)).setArgs(args);
				}
			}
		}
		// 看看有没有带一个动态参数的静态工厂函数
		for (Method m : stMethods) {
			Class<?>[] pts = m.getParameterTypes();
			if (m.getReturnType() == type && m.getParameterTypes().length == 1 && pts[0].isArray()) {
				Object[] args = new Object[1];
				args[0] = Mirror.blankArrayArg(pts);
				return re.setBorning(new MethodBorning<T>(m)).setArgs(args);
			}
		}

		return null;
	}

}