package org.nutz.dao.jdbc;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

import org.nutz.castor.Castors;
import org.nutz.dao.DaoException;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.impl.entity.field.NutMappingField;
import org.nutz.dao.impl.jdbc.BlobValueAdaptor;
import org.nutz.dao.impl.jdbc.ClobValueAdaptor;
import org.nutz.dao.util.Daos;
import org.nutz.filepool.FilePool;
import org.nutz.json.Json;
import org.nutz.lang.Files;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Streams;
import org.nutz.lang.Strings;
import org.nutz.lang.meta.Email;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.trans.Trans;

/**
 * 提供一些与 JDBC 有关的帮助函数
 *
 * @author zozoh(zozohtnt@gmail.com) TODO 合并到NutConfig
 */
public abstract class Jdbcs {

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

    private static final JdbcExpertConfigFile conf;

    public static Map<String, ValueAdaptor> customValueAdaptorMap = new ConcurrentHashMap<String, ValueAdaptor>();

    /*
     * 根据配置文件获取 experts 的列表
     */
    static {
        try {

            // 看看有没有用户自定义的映射文件
            File f = Files.findFile("nutz_jdbc_experts.js");// TODO 不可配置??
            // 如果没有则使用默认的映射文件
            if (null == f) {
                conf = Json.fromJson(JdbcExpertConfigFile.class, Streams.utf8r(Jdbcs.class.getResourceAsStream("nutz_jdbc_experts.js"))).init();
            } else
                conf = Json.fromJson(JdbcExpertConfigFile.class,Streams.fileInr("nutz_jdbc_experts.js")).init();

            for (String key : conf.getExperts().keySet()) {
                // 检查一下正则表达式是否正确
                // 在conf类中自行检查
                // Pattern.compile(key,Pattern.DOTALL &
                // Pattern.CASE_INSENSITIVE);
                // 检查一下是否可以生成 Expert 的实例
                conf.getExpert(key);// TODO 值得商讨
            }
        }
        catch (Exception e) {
            throw Lang.wrapThrow(e);
        }
        if (log.isDebugEnabled())
            log.debug("Jdbcs init complete");
    }

    /**
     * 针对一个数据源，返回其专属的 JdbcExpert
     *
     * @param ds
     *            数据源
     * @return 该数据库的特殊驱动封装类
     *
     * @see org.nutz.dao.jdbc.Jdbcs#getExpert(String, String)
     */
    public static JdbcExpert getExpert(DataSource ds) {
        log.info("Get Connection from DataSource for JdbcExpert, if you lock at here, check your database server and configure");
        Connection conn = null;
        try {
            conn = Trans.getConnectionAuto(ds);
            DatabaseMetaData meta = conn.getMetaData();
            String pnm = meta.getDatabaseProductName();
            String ver = meta.getDatabaseProductVersion();
            return getExpert(pnm, ver);
        }
        catch (Throwable e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Trans.closeConnectionAuto(conn);
        }
    }

    /**
     * 根据数据库的产品名称，获取其专属的 Expert
     *

     * 映射的规则存放在 JSON 文件 "nutz_jdbc_experts.js" 中，你可以通过建立这个文件修改 Nutz 的默认映射规则
     *

     * 比如下面的文件，将支持两种数据库
     *
     *

     * {
     *   experts : {
     *     "postgresql.*" : "org.nutz.dao.impl.jdbc.psql.PostgresqlExpert",
     *     "mysql.*" :  "org.nutz.dao.impl.jdbc.mysql.MysqlExpert"
     *   },
     *   config : {
     *     "temp-home" : "~/.nutz/tmp/dao/",
     *     "temp-max" : 2000
     *   }
     * }
     *

     *
     * 本函数传入的两个参数将会被:
     *
     *

     * String.format("%s::NUTZ_JDBC::%s", productName, version);
     *

     *
     * 并被你声明的正则表达式(expert 段下的键值)依次匹配，如果匹配上了，就会用相应的类当作驱动封装类
     *
     * @param productName
     *            数据库产品名称
     * @param version
     *            数据库版本号
     *
     * @return 该数据库的特殊驱动封装类
     *
     * @see java.sql.Connection#getMetaData()
     * @see java.sql.DatabaseMetaData#getDatabaseProductName()
     */
    public static JdbcExpert getExpert(String productName, String version) {
        String dbName = String.format("%s::NUTZ_JDBC::%s", productName, version).toLowerCase();

        JdbcExpert re = conf.matchExpert(dbName);

        if (null == re) {
        	log.warnf("unknow database type '%s %s', fallback to MySql 5", productName, version);
        	re = conf.matchExpert("mysql 5");
        }

        return re;
    }

    public static ValueAdaptor getAdaptorBy(Object obj) {
        if (null == obj)
            return Adaptor.asNull;
        return getAdaptor(Mirror.me(obj));
    }

    /**
     * 注册一个自定义ValueAdaptor,若adaptor为null,则取消注册
     * @param className 类名
     * @param adaptor 值适配器实例,若为null,则取消注册
     * @return 原有的值适配器
     */
    public static ValueAdaptor register(String className, ValueAdaptor adaptor) {
        if (adaptor == null)
            return customValueAdaptorMap.remove(className);
        return customValueAdaptorMap.put(className, adaptor);
    }

    public static ValueAdaptor getAdaptor(Mirror<?> mirror) {
        ValueAdaptor custom = customValueAdaptorMap.get(mirror.getType().getName());
        if (custom != null)
            return custom;
        // String and char
        if (mirror.isStringLike())
            return Jdbcs.Adaptor.asString;
        // Int
        if (mirror.isInt())
            return Jdbcs.Adaptor.asInteger;
        // Boolean
        if (mirror.isBoolean())
            return Jdbcs.Adaptor.asBoolean;
        // Long
        if (mirror.isLong())
            return Jdbcs.Adaptor.asLong;
        // Enum
        if (mirror.isEnum())
            return Jdbcs.Adaptor.asEnumChar;
        // Char
        if (mirror.isChar())
            return Jdbcs.Adaptor.asChar;
        // Timestamp
        if (mirror.isOf(Timestamp.class))
            return Jdbcs.Adaptor.asTimestamp;
        // Byte
        if (mirror.isByte())
            return Jdbcs.Adaptor.asByte;
        // Short
        if (mirror.isShort())
            return Jdbcs.Adaptor.asShort;
        // Float
        if (mirror.isFloat())
            return Jdbcs.Adaptor.asFloat;
        // Double
        if (mirror.isDouble())
            return Jdbcs.Adaptor.asDouble;
        // BigDecimal
        if (mirror.isOf(BigDecimal.class))
            return Jdbcs.Adaptor.asBigDecimal;
        // java.sql.Date
        if (mirror.isOf(java.sql.Date.class))
            return Jdbcs.Adaptor.asSqlDate;
        // java.sql.Time
        if (mirror.isOf(java.sql.Time.class))
            return Jdbcs.Adaptor.asSqlTime;
        // Calendar
        if (mirror.isOf(Calendar.class))
            return Jdbcs.Adaptor.asCalendar;
        // java.util.Date
        if (mirror.isOf(java.util.Date.class))
            return Jdbcs.Adaptor.asDate;
        // Blob
        if (mirror.isOf(Blob.class))
            return new BlobValueAdaptor(conf.getPool());
        // Clob
        if (mirror.isOf(Clob.class))
            return new ClobValueAdaptor(conf.getPool());
        // byte[]
        if (mirror.getType().isArray() && mirror.getType().getComponentType() == byte.class) {
            return Jdbcs.Adaptor.asBytes;
        }
        // inputstream
        if (mirror.isOf(InputStream.class))
            return Jdbcs.Adaptor.asBinaryStream;
        if (mirror.isOf(Reader.class))
            return Jdbcs.Adaptor.asReader;
        if (mirror.isLocalDateLike())
            return Adaptor.asLocalDate;
        if (mirror.isLocalDateTimeLike())
            return Jdbcs.Adaptor.asLocalDateTime;
        // 默认情况
        return Jdbcs.Adaptor.asString;
    }

    public static class Adaptor {
        /**
         * 空值适配器
         */
        public static final ValueAdaptor asNull = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                return null;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                stat.setNull(i, Types.NULL);
            }

        };

        /**
         * 字符串适配器
         */
        public static final ValueAdaptor asString = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getString(colName);
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setString(i, null);
                } else {
                    stat.setString(i, Castors.me().castToString(obj));
                }
            }
        };

        /**
         * 字符适配器
         */
        public static final ValueAdaptor asChar = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                String re = Strings.trim(rs.getString(colName));
                if (re == null || re.length() == 0)
                    return null;
                return re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setString(i, null);
                } else {
                    String s;
                    if (obj instanceof Character) {
                        int c = ((Character) obj).charValue();
                        if (c >= 0 && c <= 32)
                            s = " ";
                        else
                            s = String.valueOf((char) c);
                    } else
                        s = obj.toString();
                    stat.setString(i, s);
                }
            }
        };

        /**
         * 整型适配器
         */
        public static final ValueAdaptor asInteger = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                int re = rs.getInt(colName);
                return rs.wasNull() ? null : re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.INTEGER);
                } else {
                    int v;
                    if (obj instanceof Number)
                        v = ((Number) obj).intValue();
                    else
                        v = Castors.me().castTo(obj.toString(), int.class);
                    stat.setInt(i, v);
                }
            }
        };

        /**
         * 大数适配器
         */
        public static final ValueAdaptor asBigDecimal = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getBigDecimal(colName);
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.BIGINT);
                } else {
                    BigDecimal v;
                    if (obj instanceof BigDecimal)
                        v = (BigDecimal) obj;
                    else if (obj instanceof Number)
                        v = BigDecimal.valueOf(((Number) obj).longValue());
                    else
                        v = new BigDecimal(obj.toString());
                    stat.setBigDecimal(i, v);
                }
            }
        };

        /**
         * 布尔适配器
         *

         * 对 Oracle，Types.BOOLEAN 对于 setNull 是不工作的 因此 OracleExpert 会用一个新的
         * Adaptor 处理自己这种特殊情况
         */
        public static final ValueAdaptor asBoolean = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                boolean re = rs.getBoolean(colName);
                return rs.wasNull() ? null : re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.BOOLEAN);
                } else {
                    boolean v;
                    if (obj instanceof Boolean)
                        v = (Boolean) obj;
                    else if (obj instanceof Number)
                        v = ((Number) obj).intValue() > 0;
                    else if (obj instanceof Character)
                        v = Character.toUpperCase((Character) obj) == 'T';
                    else
                        v = Boolean.valueOf(obj.toString());
                    stat.setBoolean(i, v);
                }
            }
        };

        /**
         * 长整适配器
         */
        public static final ValueAdaptor asLong = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                long re = rs.getLong(colName);
                return rs.wasNull() ? null : re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.INTEGER);
                } else {
                    long v;
                    if (obj instanceof Number)
                        v = ((Number) obj).longValue();
                    else
                        v = Castors.me().castTo(obj.toString(), long.class);
                    stat.setLong(i, v);
                }
            }
        };

        /**
         * 字节适配器
         */
        public static final ValueAdaptor asByte = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                byte re = rs.getByte(colName);
                return rs.wasNull() ? null : re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.TINYINT);
                } else {
                    byte v;
                    if (obj instanceof Number)
                        v = ((Number) obj).byteValue();
                    else
                        v = Castors.me().castTo(obj.toString(), byte.class);
                    stat.setByte(i, v);
                }
            }
        };

        /**
         * 短整型适配器
         */
        public static final ValueAdaptor asShort = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                short re = rs.getShort(colName);
                return rs.wasNull() ? null : re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.SMALLINT);
                } else {
                    short v;
                    if (obj instanceof Number)
                        v = ((Number) obj).shortValue();
                    else
                        v = Castors.me().castTo(obj.toString(), short.class);
                    stat.setShort(i, v);
                }
            }
        };

        /**
         * 浮点适配器
         */
        public static final ValueAdaptor asFloat = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                float re = rs.getFloat(colName);
                return rs.wasNull() ? null : re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.FLOAT);
                } else {
                    float v;
                    if (obj instanceof Number)
                        v = ((Number) obj).floatValue();
                    else
                        v = Castors.me().castTo(obj.toString(), float.class);
                    stat.setFloat(i, v);
                }
            }
        };

        /**
         * 双精度浮点适配器
         */
        public static final ValueAdaptor asDouble = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                double re = rs.getDouble(colName);
                return rs.wasNull() ? null : re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.DOUBLE);
                } else {
                    double v;
                    if (obj instanceof Number)
                        v = ((Number) obj).doubleValue();
                    else
                        v = Castors.me().castTo(obj.toString(), double.class);
                    stat.setDouble(i, v);
                }
            }
        };

        /**
         * 日历适配器
         */
        public static final ValueAdaptor asCalendar = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                Timestamp ts = rs.getTimestamp(colName);
                if (null == ts)
                    return null;
                Calendar c = Calendar.getInstance();
                c.setTimeInMillis(ts.getTime());
                return c;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.TIMESTAMP);
                } else {
                    Timestamp v;
                    if (obj instanceof Calendar)
                        v = new Timestamp(((Calendar) obj).getTimeInMillis());
                    else
                        v = Castors.me().castTo(obj, Timestamp.class);
                    stat.setTimestamp(i, v);
                }
            }
        };

        /**
         * 时间戳适配器
         */
        public static final ValueAdaptor asTimestamp = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getTimestamp(colName);
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.TIMESTAMP);
                } else {
                    Timestamp v;
                    if (obj instanceof Timestamp)
                        v = (Timestamp) obj;
                    else
                        v = Castors.me().castTo(obj, Timestamp.class);
                    stat.setTimestamp(i, v);
                }
            }
        };

        /**
         * 日期适配器
         */
        public static final ValueAdaptor asDate = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                Timestamp ts = rs.getTimestamp(colName);
                return null == ts ? null : new java.util.Date(ts.getTime());
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                Timestamp v;
                if (null == obj) {
                    stat.setNull(i, Types.TIMESTAMP);
                } else {
                    if (obj instanceof java.util.Date)
                        v = new Timestamp(((java.util.Date) obj).getTime());
                    else
                        v = Castors.me().castTo(obj, Timestamp.class);
                    stat.setTimestamp(i, v);
                }
            }
        };

        /**
         * Sql 日期适配器
         */
        public static final ValueAdaptor asSqlDate = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getDate(colName);
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.DATE);
                } else {
                    java.sql.Date v;
                    if (obj instanceof java.sql.Date)
                        v = (java.sql.Date) obj;
                    else
                        v = Castors.me().castTo(obj, java.sql.Date.class);
                    stat.setDate(i, v);
                }
            }
        };

        /**
         * Sql 时间适配器
         */
        public static final ValueAdaptor asSqlTime = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getTime(colName);
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                java.sql.Time v;
                if (null == obj) {
                    stat.setNull(i, Types.TIME);
                } else {
                    if (obj instanceof java.sql.Time)
                        v = (java.sql.Time) obj;
                    else
                        v = Castors.me().castTo(obj, java.sql.Time.class);
                    stat.setTime(i, v);
                }
            }
        };

        /**
         * 数字枚举适配器
         */
        public static final ValueAdaptor asEnumInt = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                int re = rs.getInt(colName);
                return rs.wasNull() ? null : re;
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setNull(i, Types.INTEGER);
                } else {
                    stat.setInt(i, Castors.me().castTo(obj, int.class));
                }
            }
        };

        /**
         * 字符枚举适配器
         */
        public static final ValueAdaptor asEnumChar = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getString(colName);
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                if (null == obj) {
                    stat.setString(i, null);
                } else {
                    String v = obj.toString();
                    stat.setString(i, v);
                }
            }
        };

        /**
         * 默认对象适配器
         */
        public static final ValueAdaptor asObject = new ValueAdaptor() {
            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getObject(colName);
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                stat.setObject(i, obj);
            }
        };

        /**
         * 字节数组适配器
         */
        public static final ValueAdaptor asBytes = new ValueAdaptor() {

            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getBytes(colName);
            }

            public void set(PreparedStatement stat, Object obj, int index) throws SQLException {
                if (null == obj) {
                    stat.setNull(index, Types.BINARY);
                } else {
                    stat.setBytes(index, (byte[]) obj);
                }
            }

        };

        public static final ValueAdaptor asBinaryStream = new ValueAdaptor() {

            public Object get(ResultSet rs, String colName) throws SQLException {
            	InputStream in = rs.getBinaryStream(colName);
            	if (in == null) {
            		return in;
            	}
            	try {
					File f = File.createTempFile("nutzdao_blob", ".tmp");
					Files.write(f, in);
					in.close();
					return new ReadOnceInputStream(f);
				}
				catch (IOException e) {
					throw Lang.wrapThrow(e);
				}
            }

            public void set(PreparedStatement stat, Object obj, int index) throws SQLException {
                if (null == obj) {
                    stat.setNull(index, Types.BINARY);
                } else {

                	if (obj instanceof ByteArrayInputStream) {
                		stat.setBinaryStream(index, (InputStream)obj, ((ByteArrayInputStream)obj).available());
                	} else if (obj instanceof InputStream) {
                		if (obj instanceof ReadOnceInputStream) {
                			if (((ReadOnceInputStream)obj).readed) {
                				throw new DaoException("");
                			}
                		}
                        try {
                            File f = Jdbcs.getFilePool().createFile(".dat");
                            Streams.writeAndClose(new FileOutputStream(f), (InputStream)obj);
                            stat.setBinaryStream(index, new FileInputStream(f), f.length());
                        }
                        catch (FileNotFoundException e) {
                        	try {
                                File f = Jdbcs.getFilePool().createFile(".dat");
                                Streams.writeAndClose(new FileOutputStream(f), (InputStream)obj);
                                stat.setBinaryStream(index, new FileInputStream(f), f.length());
                            }
                            catch (FileNotFoundException e2) {
                            	throw Lang.impossible();
                            }
                        }
                    }
                }
            }
        };

        public static final ValueAdaptor asReader = new ValueAdaptor() {

            public Object get(ResultSet rs, String colName) throws SQLException {
                return rs.getCharacterStream(colName);
            }

            public void set(PreparedStatement stat, Object obj, int index) throws SQLException {
                if (null == obj) {
                    stat.setNull(index, Types.BINARY);
                } else {
                    setCharacterStream(index, obj, stat);
                }
            }
        };

        public static final ValueAdaptor asLocalDateTime = new ValueAdaptor() {

            public Object get(ResultSet rs, String colName) throws SQLException {
                Timestamp ts = rs.getTimestamp(colName);
                return null == ts ? null : LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneId.systemDefault());
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                Timestamp v;
                if (null == obj) {
                    stat.setNull(i, Types.TIMESTAMP);
                } else {
                    v = Timestamp.valueOf((LocalDateTime)obj);
                    stat.setTimestamp(i, v);
                }
            }
        };

        public static final ValueAdaptor asLocalDate = new ValueAdaptor() {

            public Object get(ResultSet rs, String colName) throws SQLException {
                Timestamp ts = rs.getTimestamp(colName);
                return null == ts ? null : ts.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            }

            public void set(PreparedStatement stat, Object obj, int i) throws SQLException {
                Timestamp v;
                if (null == obj) {
                    stat.setNull(i, Types.TIMESTAMP);
                } else {
                    v = Timestamp.valueOf(((LocalDate)obj).atStartOfDay(ZoneId.systemDefault()).toLocalDateTime());
                    stat.setTimestamp(i, v);
                }
            }
        };
    }

    /**
     * 根据字段现有的信息，尽可能猜测一下字段的数据库类型
     *
     * @param ef
     *            映射字段
     */
    public static void guessEntityFieldColumnType(NutMappingField ef) {
        Mirror<?> mirror = ef.getTypeMirror();

        // 整型
        if (mirror.isInt()) {
            ef.setColumnType(ColType.INT);
            ef.setWidth(8);
        }
        // 字符串
        else if (mirror.isStringLike() || mirror.is(Email.class)) {
            ef.setColumnType(ColType.VARCHAR);
            ef.setWidth(Daos.DEFAULT_VARCHAR_WIDTH);
        }
        // 长整型
        else if (mirror.isLong()) {
            ef.setColumnType(ColType.INT);
            ef.setWidth(16);
        }
        // 枚举
        else if (mirror.isEnum()) {
            ef.setColumnType(ColType.VARCHAR);
            ef.setWidth(20);
        }
        // 时间戳
        else if (mirror.is(Timestamp.class)) {
            ef.setColumnType(ColType.TIMESTAMP);
        }
        // 布尔
        else if (mirror.isBoolean()) {
            ef.setColumnType(ColType.BOOLEAN);
            ef.setWidth(1);
        }
        // 字符
        else if (mirror.isChar()) {
            ef.setColumnType(ColType.CHAR);
            ef.setWidth(4);
        }
        // 日期
        else if (mirror.is(java.sql.Date.class)) {
            ef.setColumnType(ColType.DATE);
        }
        // 时间
        else if (mirror.is(java.sql.Time.class)) {
            ef.setColumnType(ColType.TIME);
        }
        // 日期时间
        else if (mirror.isOf(Calendar.class) || mirror.is(java.util.Date.class) || mirror.isLocalDateTimeLike()) {
            ef.setColumnType(ColType.DATETIME);
        }
        // 大数
        else if (mirror.is(BigDecimal.class)) {
            ef.setColumnType(ColType.INT);
            ef.setWidth(32);
        }
        // 短整型
        else if (mirror.isShort()) {
            ef.setColumnType(ColType.INT);
            ef.setWidth(4);
        }
        // 字节
        else if (mirror.isByte()) {
            ef.setColumnType(ColType.INT);
            ef.setWidth(2);
        }
        // 浮点
        else if (mirror.isFloat()) {
            ef.setColumnType(ColType.FLOAT);
        }
        // 双精度浮点
        else if (mirror.isDouble()) {
            ef.setColumnType(ColType.FLOAT);
        }
        // 文本流
        else if (mirror.isOf(Reader.class) || mirror.isOf(Clob.class)) {
            ef.setColumnType(ColType.TEXT);
        }
        // 二进制流
        else if (mirror.isOf(InputStream.class)
                 || mirror.is(byte[].class)
                 || mirror.isOf(Blob.class)) {
            ef.setColumnType(ColType.BINARY);
        }
        /*
         * 上面的都不是？ 那就当作字符串好了，反正可以 toString
         */
        else {
            if (log.isDebugEnabled()&& ef.getEntity() != null && ef.getEntity().getType() != null)
                log.debugf("take field '%s(%s)'(%s) as VARCHAR(%d)",
                           ef.getName(),
                           Lang.getTypeClass(ef.getType()).getName(),
                           ef.getEntity().getType().getName(),
                           Daos.DEFAULT_VARCHAR_WIDTH);
            ef.setColumnType(ColType.VARCHAR);
            ef.setWidth(Daos.DEFAULT_VARCHAR_WIDTH);
        }
    }

    public static FilePool getFilePool() {
        return conf.getPool();
    }

    public static void setFilePool(FilePool pool) {
        conf.setPool(pool);
    }

    public static void setCharacterStream(int index, Object obj, PreparedStatement stat) throws SQLException {
        try {
            File f = Jdbcs.getFilePool().createFile(".dat");
            Streams.writeAndClose(new FileWriter(f), (Reader)obj);
            stat.setCharacterStream(index, new FileReader(f), f.length());
        }
        catch (FileNotFoundException e) {
            throw Lang.impossible();
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
    }

    public static JdbcExpertConfigFile getConf() {
        return conf;
    }
}

class ReadOnceInputStream extends FilterInputStream implements Serializable {

	private static final long serialVersionUID = -2601685798106193691L;

	private File f;

	public boolean readed;

	protected ReadOnceInputStream(File f) throws FileNotFoundException {
		super(new FileInputStream(f));
		this.f = f;
	}

	public int read() throws IOException {
		readed = true;
		return super.read();
	}

	public int read(byte[] b) throws IOException {
		readed = true;
		return super.read(b);
	}

	public int read(byte[] b, int off, int len) throws IOException {
		readed = true;
		return super.read(b, off, len);
	}

	public void close() throws IOException {
		super.close();
		f.delete();
	}

	protected void finalize() throws Throwable {
		f.delete();
		super.finalize();
	}

    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
        Streams.writeAndClose(out, new FileInputStream(f));
    }
    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException{
        f = Jdbcs.getFilePool().createFile(".dat");
        Files.write(f, in);
    }
}


package org.nutz.mvc;

import java.io.InputStream;
import java.util.Arrays;
import java.util.Enumeration;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.nutz.ioc.Ioc;
import org.nutz.ioc.impl.NutIoc;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.combo.ComboIocLoader;
import org.nutz.lang.Mirror;
import org.nutz.lang.Streams;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.resource.Scans;

/**
 *

 * 定义全局Ioc容器,并实现IocProvider接口,以便与@IocBy注解的融合.
 *

 *

 * 本实现使用ComboIocLoader作为加载器,所以允许加载其他人员IocLoader定义
 *

 *

 * 基本配置流程
 *

 *

 * 1. 在web.xml声明本监听器
 *

 *

 * 2. 在web.xml中声明一个param-name叫nutz-iocby,内容是传统ComboIocLoader的参数,用逗号分隔
 *

 *

 * 2. 或者在/WEB-INF下放一个nutz.properties, 内容是nutz-iocby=XXXXXXXX
 *

 *

 * 3. 在MainModule中声明@IocBy(type=NutMvcListener.class)
 *

 *
 * @author wendal(wendal1985@gmail.com)
 *
 */
public class NutMvcListener implements ServletContextListener, IocProvider {

    public static String PROP_LOCATION = "nutz-properties-location";
    public static String IOCBY = "iocby";
    protected ServletContext sc;
    protected PropertiesProxy pp;

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

    protected static Ioc ioc;

    /**
     * 返回全局Ioc对象,如果未经初始化, 这里就会抛出异常
     */
    public static Ioc ioc() {
        if (ioc == null)
            throw new IllegalArgumentException("NutMvcListener NOT init!!! check your web.xml!!");
        return ioc;
    }

    public void contextInitialized(ServletContextEvent event) {
        sc = event.getServletContext();
        Scans.me().init(sc);
        findConfig();
        initIoc();

        // TODO 其他可配置项.
    }

    /**
     * 首先,载入需要的配置信息, 分别从nutz.properties和ServletContext的上下文获取.
     *

     * 子类可以覆盖这个方法实现从任意方式加载配置
     */
    protected void findConfig() {
        String propLocation = sc.getInitParameter(PROP_LOCATION);
        if (Strings.isBlank(propLocation)) {
            propLocation = "nutz.properties";
        }
        PropertiesProxy pp = new PropertiesProxy();
        Enumeration<String> params = sc.getInitParameterNames();
        while (params.hasMoreElements()) {
            String name = (String) params.nextElement();
            if (name.startsWith("nutz-")) {
                pp.put(name, sc.getInitParameter(name).trim());
            }
        }
        // 先找找classpath
        InputStream in = getClass().getClassLoader().getResourceAsStream("/" + propLocation);
        if (in == null) {
            in = sc.getResourceAsStream("/WEB-INF/" + propLocation);
        }
        if (in == null) {
            log.debug(propLocation + " not found");
        } else {
            pp = new PropertiesProxy(in);
            Streams.safeClose(in);
        }
        this.pp = pp;
    }

    /**
     * 初始化Ioc容器,使用ComboIocLoader作为配置方式
     */
    protected void initIoc() {
        String key = "nutz-" + IOCBY;
        String iocby = pp.get(key);
        if (Strings.isBlank(iocby)) {
            throw new RuntimeException(key + " not found nutz.ini or context-param !!");
        }
        String[] args = Strings.splitIgnoreBlank(iocby);
        for (int i = 0; i < args.length; i++) {
            args[i] = args[i].trim();
        }
        log.info("init Ioc by args=" + Arrays.toString(args));
        try {
            ioc = new NutIoc(new ComboIocLoader(args));
        }
        catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 容器销毁时,检查Ioc是否已经关闭,没有的话就关闭之.
     */
    public void contextDestroyed(ServletContextEvent event) {
        if (ioc() != null) {
            Ioc ioc = ioc();
            if (ioc instanceof NutIoc) {
                boolean deposed = (Boolean) Mirror.me(ioc).getValue(ioc, "deposed");
                if (!deposed)
                    ioc.depose();
            }
        }
    }

    /**
     * 这里与IocBy结合起来. 注意,这个实现会忽略IocBy的args参数.
     */
    public Ioc create(NutConfig config, String[] args) {
        if (args != null && args.length > 0) {
            if (log != null)
                log.warn("args ignore : " + Arrays.toString(args));
        }
        return ioc();
    }

}


package org.nutz.ioc.impl;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.nutz.ioc.Ioc;
import org.nutz.ioc.Ioc2;
import org.nutz.ioc.IocContext;
import org.nutz.ioc.IocEventListener;
import org.nutz.ioc.IocException;
import org.nutz.ioc.IocLoader;
import org.nutz.ioc.IocLoading;
import org.nutz.ioc.IocMaking;
import org.nutz.ioc.ObjectLoadException;
import org.nutz.ioc.ObjectMaker;
import org.nutz.ioc.ObjectProxy;
import org.nutz.ioc.ValueProxyMaker;
import org.nutz.ioc.annotation.InjectName;
import org.nutz.ioc.aop.MirrorFactory;
import org.nutz.ioc.aop.impl.DefaultMirrorFactory;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.loader.combo.ComboIocLoader;
import org.nutz.ioc.meta.IocObject;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.nutz.lang.util.LifeCycle;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.repo.LevenshteinDistance;

/**
 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author wendal(wendal1985@gmail.com)
 */
public class NutIoc implements Ioc2 {

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

    private Object lock_get = new Object();

    private static final String DEF_SCOPE = "app";

    protected Date createTime;

    /**
     * 读取配置文件的 Loader
     */
    private ComboIocLoader loader;
    /**
     * 缓存对象上下文环境
     */
    private IocContext context;
    /**
     * 装配对象的逻辑
     */
    private ObjectMaker maker;
    /**
     * 可扩展的"字段值"生成器
     */
    private List<ValueProxyMaker> vpms;
    /**
     * 反射工厂，封装 AOP 的逻辑
     */
    private MirrorFactory mirrors;
    /**
     * 对象默认生命周期范围名
     */
    private String defaultScope;
    /**
     *

     *
缓存支持的对象值类型
     *
如果 addValueProxyMaker() 被调用，这个缓存会被清空
     *
createLoading() 将会检查这个缓存
     *

     */
    private Set<String> supportedTypes;

    protected List<IocEventListener> listeners;

    protected ThreadLocal<Object> listenerH = new ThreadLocal<Object>();

    public NutIoc(IocLoader loader) {
        this(loader, new ScopeContext(DEF_SCOPE), DEF_SCOPE);
    }

    public NutIoc(IocLoader loader, IocContext context, String defaultScope) {
        this(new ObjectMakerImpl(), loader, context, defaultScope);
    }

    protected NutIoc(ObjectMaker maker, IocLoader loader, IocContext context, String defaultScope) {
        this(maker, loader, context, defaultScope, null);
    }

    protected NutIoc(ObjectMaker maker,
                     IocLoader loader,
                     IocContext context,
                     String defaultScope,
                     MirrorFactory mirrors) {
        this.createTime = new Date();
        this.maker = maker;
        this.defaultScope = defaultScope;
        this.context = context;
        if (loader instanceof ComboIocLoader)
            this.loader = (ComboIocLoader) loader;
        else
            this.loader = new ComboIocLoader(loader);
        vpms = new ArrayList<ValueProxyMaker>(5); // 预留五个位置，足够了吧
        addValueProxyMaker(new DefaultValueProxyMaker());

        // 初始化类工厂， 这是同 AOP 的连接点
        if (mirrors == null)
            this.mirrors = new DefaultMirrorFactory(this);
        else
            this.mirrors = mirrors;
        try {
            this.loader.init();
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
        log.info("... NutIoc init complete");
    }

    /**
     * @return 一个新创建的 IocLoading 对象
     */
    protected IocLoading createLoading() {
        if (null == supportedTypes) {
            synchronized (this) {
                if (null == supportedTypes) {
                    // TODO 看看可不可以改成更快的 Set
                    supportedTypes = new HashSet<String>();
                    for (ValueProxyMaker maker : vpms) {
                        String[] ss = maker.supportedTypes();
                        if (ss != null)
                            for (String s : ss)
                                supportedTypes.add(s);
                    }
                }
            }
        }
        return new IocLoading(supportedTypes);
    }

    public <T> T get(Class<T> type) throws IocException {
        InjectName inm = type.getAnnotation(InjectName.class);
        if (null != inm && (!Strings.isBlank(inm.value())))
            return get(type, inm.value());
        IocBean iocBean = type.getAnnotation(IocBean.class);
        if (iocBean != null && (!Strings.isBlank(iocBean.name())))
            return get(type, iocBean.name());
        return get(type, Strings.lowerFirst(type.getSimpleName()));
    }

    public <T> T get(Class<T> type, String name, IocContext context) throws IocException {
        if (log.isDebugEnabled())
            log.debugf("Get '%s'<%s>", name, type == null ? "" : type);
        try {
            if (this.mirrors instanceof LifeCycle)
                ((LifeCycle) this.mirrors).init();
        }
        catch (Exception e) {
            throw new IocException("_mirror_factory_init", e, "Mirror Factory init fail");
        }

        // 创建对象创建时
        IocMaking ing = makeIocMaking(context, name);
        IocContext cntx = ing.getContext();

        // 从上下文缓存中获取对象代理
        ObjectProxy op = cntx.fetch(name);

        // 如果未发现对象
        if (null == op) {
            // 线程同步
            synchronized (lock_get) {
                // 再次读取
                op = cntx.fetch(name);
                // 如果未发现对象
                if (null == op) {
                    try {
                        if (log.isDebugEnabled())
                            log.debug("\t >> Load definition name=" + name);

                        // 读取对象定义
                        IocObject iobj = loader.load(createLoading(), name);
                        if (null == iobj) {
                            for (String iocBeanName : loader.getName()) {
                                // 相似性少于3 --> 大小写错误,1-2个字符调换顺序或写错
                                // 感觉没必要..没有就没有呗
                                if (3 > LevenshteinDistance.computeLevenshteinDistance(name.toLowerCase(),
                                                                                iocBeanName.toLowerCase())) {
                                    throw new IocException(name,
                                                           "Undefined object '%s' but found similar name '%s'",
                                                           name,
                                                           iocBeanName);
                                }
                            }
                            throw new IocException(name, "Undefined object '%s'", name);
                        }

                        // 修正对象类型
                        if (null == iobj.getType())
                            if (null == type && Strings.isBlank(iobj.getFactory()))
                                throw new IocException(name, "NULL TYPE object '%s'", name);
                            else
                                iobj.setType(type);
                        // 检查对象级别
                        if (Strings.isBlank(iobj.getScope()))
                            iobj.setScope(defaultScope);

                        // 根据对象定义，创建对象，maker 会自动的缓存对象到 context 中
                        if (log.isDebugEnabled())
                            log.debugf("\t >> Make...'%s'<%s>", name, type == null ? "" : type);
                        if (iobj.getType() != null && IocEventListener.class.isAssignableFrom(iobj.getType())) {
                            if (listenerH.get() != null) {
                                op = maker.make(ing, iobj);
                            }
                            else {
                                try {
                                    listenerH.set(Boolean.TRUE);
                                    op = maker.make(ing, iobj);
                                }
                                finally {
                                    listenerH.remove();
                                }
                            }
                        }
                        else {
                            _checkIocEventListeners();
                            ing.setListeners(listeners);
                            op = maker.make(ing, iobj);
                        }
                    }
                    // 处理异常
                    catch (IocException e) {
                        ((IocException) e).addBeanNames(name);
                        throw e;
                    }
                    catch (Throwable e) {
                        throw new IocException(name,
                                               e,
                                               "For object [%s] - type:[%s]",
                                               name,
                                               type == null ? "" : type);
                    }
                }
            }
        }

        synchronized (lock_get) {
            T re = op.get(type, ing);

            if (!name.startsWith("$") && re instanceof IocLoader) {
                loader.addLoader((IocLoader) re);
            }
            return re;
        }
    }

    public <T> T get(Class<T> type, String name) {
        return this.get(type, name, null);
    }

    public boolean has(String name) {
        return loader.has(name) || context.fetch(name) != null;
    }

    private boolean deposed = false;

    public void depose() {
        if (deposed) {
            if (log.isInfoEnabled())
                log.info("You can't depose a Ioc twice!");
            return;
        }
        if (log.isInfoEnabled())
            log.infof("%s@%s is closing. startup date [%s]",
                      getClass().getName(),
                      hashCode(),
                      Times.sDTms2(this.createTime));
        try {
            this.loader.depose();
        }
        catch (Exception e) {
            log.warn("something happen when depose IocLoader", e);
        }
        context.depose();
        loader.clear();
        deposed = true;
        if (log.isInfoEnabled())
            log.infof("%s@%s is deposed. startup date [%s]",
                      getClass().getName(),
                      hashCode(),
                      Times.sDTms2(this.createTime));
    }

    public void reset() {
        context.clear();
    }

    public String[] getNames() {
        LinkedHashSet<String> list = new LinkedHashSet<String>();
        list.addAll(Arrays.asList(loader.getName()));
        if (context != null)
            list.addAll(context.names());
        return list.toArray(new String[list.size()]);
    }

    public void addValueProxyMaker(ValueProxyMaker vpm) {
        vpms.add(0, vpm);// 优先使用最后加入的ValueProxyMaker
        supportedTypes = null;
        loader.clear();
    }

    public IocContext getIocContext() {
        return context;
    }

    public void setMaker(ObjectMaker maker) {
        this.maker = maker;
    }

    public void setMirrorFactory(MirrorFactory mirrors) {
        this.mirrors = mirrors;
    }

    public void setDefaultScope(String defaultScope) {
        this.defaultScope = defaultScope;
    }

    /**
     * 暴露IocMaking的创建过程
     */
    public IocMaking makeIocMaking(IocContext context, String name) {
        // 连接上下文
        IocContext cntx;
        if (null == context || context == this.context)
            cntx = this.context;
        else {
            if (log.isTraceEnabled())
                log.trace("Link contexts");
            cntx = new ComboContext(context, this.context);
        }
        return new IocMaking(this, mirrors, cntx, maker, vpms, name);
    }

    @Override
    public String toString() {
        return "/*NutIoc count=" + loader.getName().length + "*/";
    }

    @Override
    protected void finalize() throws Throwable {
        if (!deposed) {
            log.error("Ioc depose tigger by GC!!!\n"
                      + "Common Reason for that is YOUR code call 'new NutIoc(...)',"
                      + " and then get some beans(most is Dao) from it and abandon it!!!\n"
                      + "If using nutz.mvc, call Mvcs.ctx().getDefaultIoc() to get ioc container.\n"
                      + "Not nutz.mvc? use like this:     public static Ioc ioc;");
            depose();
        }
        super.finalize();
    }

    public String[] getNamesByType(Class<?> klass) {
        return this.getNamesByType(klass, null);
    }

    public String[] getNamesByType(Class<?> klass, IocContext context) {
        List<String> names = new ArrayList<String>(loader.getNamesByTypes(createLoading(), klass));
        IocContext cntx;
        if (null == context || context == this.context)
            cntx = this.context;
        else
            cntx = new ComboContext(context, this.context);
        for (String name : cntx.names()) {
            ObjectProxy op = cntx.fetch(name);
            if (op.getObj() != null && klass.isAssignableFrom(op.getObj().getClass()))
                names.add(name);
        }
        LinkedHashSet<String> re = new LinkedHashSet<String>();
        for (String name : names) {
            if (Strings.isBlank(name) || "null".equals(name))
                continue;
            re.add(name);
        }
        return re.toArray(new String[re.size()]);
    }

    public String[] getNamesByAnnotation(Class<? extends Annotation> klass) {
        return this.getNamesByAnnotation(klass, null);
    }

    public String[] getNamesByAnnotation(Class<? extends Annotation> klass, IocContext context) {
        List<String> names = new ArrayList<String>(loader.getNamesByAnnotation(createLoading(), klass));
        IocContext cntx;
        if (null == context || context == this.context)
            cntx = this.context;
        else
            cntx = new ComboContext(context, this.context);
        for (String name : cntx.names()) {
            ObjectProxy op = cntx.fetch(name);
            if (op.getObj() != null && klass.getAnnotation(klass) != null)
                names.add(name);
        }
        LinkedHashSet<String> re = new LinkedHashSet<String>();
        for (String name : names) {
            if (Strings.isBlank(name) || "null".equals(name))
                continue;
            re.add(name);
        }
        return re.toArray(new String[re.size()]);
    }

    public <K> K getByType(Class<K> klass) {
        return this.getByType(klass, null);
    }

    public <K> K getByType(Class<K> klass, IocContext context) {
        String _name = null;
        IocContext cntx;
        if (null == context || context == this.context)
            cntx = this.context;
        else
            cntx = new ComboContext(context, this.context);
        for (String name : cntx.names()) {
            ObjectProxy op = cntx.fetch(name);
            if (op.getObj() != null && klass.isAssignableFrom(op.getObj().getClass())) {
                _name = name;
                break;
            }
        }
        if (_name != null)
            return get(klass, _name, context);
        for (String name : getNames()) {
            try {
                IocObject iobj = loader.load(createLoading(), name);
                if (iobj != null
                    && iobj.getType() != null
                    && klass.isAssignableFrom(iobj.getType()))
                    _name = name;
            }
            catch (Exception e) {
                continue;
            }
            if (_name != null)
                return get(klass, name, context);
        }
        throw new IocException("class:"
                               + klass.getName(),
                               "none ioc bean match class=" + klass.getName());
    }

    protected void _checkIocEventListeners() {
        if (listeners != null)
            return;
        List<IocEventListener> listeners = new ArrayList<IocEventListener>();
        for (String beanName : this.loader.getNamesByTypes(createLoading(), IocEventListener.class)) {
            listeners.add(get(IocEventListener.class, beanName));
        }
        if (listeners.size() > 0) {
            Collections.sort(listeners, new Comparator<IocEventListener>() {
                public int compare(IocEventListener prev, IocEventListener next) {
                    if (prev.getOrder() == next.getOrder())
                        return 0;
                    return prev.getOrder() > next.getOrder() ? -1 : 1;
                }
            });
        }
        this.listeners = listeners;
    }

    public Ioc addBean(String name, Object obj) {
        if (obj == null)
            throw new RuntimeException("can't add bean=null!!");
        if (Strings.isBlank(name))
            throw new RuntimeException("can't add bean name is blank!!");
        if (obj instanceof ObjectProxy)
            getIocContext().save("app", name, (ObjectProxy)obj);
        else
            getIocContext().save("app", name, new ObjectProxy(obj));
        return this;
    }

    public Class<?> getType(String beanName) throws ObjectLoadException {
        return getType(beanName, null);
    }

    public Class<?> getType(String beanName, IocContext context) throws ObjectLoadException {
        IocContext cntx;
        if (null == context || context == this.context)
            cntx = this.context;
        else
            cntx = new ComboContext(context, this.context);
        ObjectProxy op = cntx.fetch(beanName);
        if (op != null && op.getObj() != null)
            return op.getObj().getClass();
        return loader.getType(createLoading(), beanName);
    }
}


package org.nutz.lang.util;

import org.nutz.castor.Castors;
import org.nutz.lang.Mirror;
import org.nutz.lang.Strings;

/**
 * 描述了一个区间
 *

 *
T 对象必须实现有意义的 toString，并且字符串中不能包含半角逗号
 *
T 对象必须可比较
 *
T 对象必须可以被 Castors 正确的从字符串转换
 *
T 对象的 toString() 和 Castors 的转换必须可逆
 *

 *
 * 任何区间的字符串描述都符合:
 *
 *

 * 全封闭的区间 : [T0, T1]
 * 左开右闭区间 : (T0, T1]
 * 左闭右开区间 : [T0, T1)
 * 左开右闭区间 : (T0, T1]
 * 全开放的区间 : (T0, T1)
 * 精确等于某值 : (T0] 或 [T0) 或 [T0]
 * 精确不等于某值 : (T0)
 *

 *
 * 比如对于数字:
 *
 *

 * [4,10]   // >=4 && <=10
 * (6,54]   // >=6 && <54
 * (,78)    // <78
 * [50]     // == 50
 * (99)     // !=99
 *

 *
 * 对于日期
 *
 *

 * [2012-09-10 12:33:24, 2013-08-14]   // 会自动交换大小值，可以是日期或者时间
 *

 *
 * @author zozoh(zozohtnt@gmail.com)
 */
public abstract class Region<T extends Comparable<T>> {

    public static IntRegion Int(String str) {
        return new IntRegion(str);
    }

    public static IntRegion Intf(String fmt, Object... args) {
        return new IntRegion(String.format(fmt, args));
    }

    public static LongRegion Long(String str) {
        return new LongRegion(str);
    }

    public static LongRegion Longf(String fmt, Object... args) {
        return new LongRegion(String.format(fmt, args));
    }

    public static FloatRegion Float(String str) {
        return new FloatRegion(str);
    }

    public static FloatRegion Floatf(String fmt, Object... args) {
        return new FloatRegion(String.format(fmt, args));
    }

    public static DoubleRegion Double(String str) {
        return new DoubleRegion(str);
    }

    public static DoubleRegion Doublef(String fmt, Object... args) {
        return new DoubleRegion(String.format(fmt, args));
    }

    public static DateRegion Date(String str) {
        return new DateRegion(str);
    }

    public static DateRegion Datef(String fmt, Object... args) {
        return new DateRegion(String.format(fmt, args));
    }

    public static TimeRegion Time(String str) {
        return new TimeRegion(str);
    }

    public static TimeRegion Timef(String fmt, Object... args) {
        return new TimeRegion(String.format(fmt, args));
    }

    protected Class<T> eleType;

    protected T left;

    protected T right;

    protected boolean leftOpen;

    protected boolean rightOpen;

    public T left() {
        return left;
    }

    public Region<T> left(T left) {
        this.left = left;
        return this;
    }

    public Region<T> leftOpen(boolean open) {
        this.leftOpen = open;
        return this;
    }

    public T right() {
        return right;
    }

    public Region<T> right(T right) {
        this.right = right;
        return this;
    }

    public Region<T> rightOpen(boolean open) {
        this.rightOpen = open;
        return this;
    }

    public boolean isLeftOpen() {
        return leftOpen;
    }

    public boolean isRightOpen() {
        return rightOpen;
    }

    /**
     * @return 是区间还是一个精确匹配的值
     */
    public boolean isRegion() {
        return left != right && !isNull();
    }

    /**
     * @return 当前区间是否为空
     */
    public boolean isNull() {
        return null == left && null == right;
    }

    /**
     * 根据左边开闭区间的情况返回一个正确的左边比较的运算符
     *
     * @param gt
     *            大于的运算符，开区间时采用
     * @param gte
     *            大于等于的运算符，闭区间时采用
     * @return 运算符
     */
    public String leftOpt(String gt, String gte) {
        if (null == left)
            return null;
        return leftOpen ? gt : gte;
    }

    /**
     * 根据右边开闭区间的情况返回一个正确的右边比较的运算符
     *
     * @param lt
     *            小于的运算符，开区间时采用
     * @param lte
     *            小于等于的运算符，闭区间时采用
     * @return 运算符
     */
    public String rightOpt(String lt, String lte) {
        if (null == right)
            return null;
        return rightOpen ? lt : lte;
    }

    /**
     * @param obj
     *            对象
     * @return 对象是否在这个区间
     */
    public boolean match(T obj) {
        if (null == obj)
            return false;
        if (!isRegion()) {
            // 左右都是开区间，表示不等于
            if (this.leftOpen && this.rightOpen) {
                return left.compareTo(obj) != 0;
            }
            return left.compareTo(obj) == 0;
        }
        if (null != left) {
            int c = obj.compareTo(left);
            if (c < 0 || c == 0 && leftOpen) {
                return false;
            }
        }
        if (null != right) {
            int c = obj.compareTo(right);
            if (c > 0 || c == 0 && rightOpen) {
                return false;
            }
        }
        return true;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public Region() {
        eleType = (Class<T>) (Class) Mirror.getTypeParam(getClass(), 0);
    }

    /**
     * 解析
     *
     * @param str
     *            字符串
     * @return 自身
     */
    public Region<T> valueOf(String str) {
        String s2 = Strings.trim(str.substring(1, str.length() - 1));
        leftOpen = str.charAt(0) == '(';
        rightOpen = str.charAt(str.length() - 1) == ')';

        // 只有左值
        if (s2.endsWith(",")) {
            left = fromString(Strings.trim(s2.substring(0, s2.length() - 1)));
            right = null;
        }
        // 只有右值
        else if (s2.startsWith(",")) {
            left = null;
            right = fromString(Strings.trim(s2.substring(1)));
        }
        // 两侧都有值
        else {
            String[] ss = Strings.splitIgnoreBlank(s2, ",");
            // 精确的值
            if (1 == ss.length) {
                left = fromString(ss[0]);
                right = left;
            }
            // 一个区间
            else {
                left = fromString(ss[0]);
                right = fromString(ss[1]);
                // 看看是否需要交换交换...
                if (null != left && null != right && left.compareTo(right) > 0) {
                    T o = right;
                    right = left;
                    left = o;
                }
            }
        }
        return this;
    }

    public String toString(T obj) {
        return null == obj ? "" : obj.toString();
    }

    public T fromString(String str) {
        str = Strings.trim(str);
        if (Strings.isEmpty(str))
            return null;
        return Castors.me().castTo(str, eleType);
    }

    public String toString() {
        if (this.isRegion())
            return String.format("%c%s,%s%c",
                                 leftOpen ? '(' : '[',
                                 toString(left),
                                 toString(right),
                                 rightOpen ? ')' : ']');

        return String.format("%c%s%c", leftOpen ? '(' : '[', toString(left), rightOpen ? ')' : ']');
    }

}


package org.nutz.mapl.impl;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 递归MapList结构, 将路径与相应的值传递给子类进行处理.
 * @author juqkai(juqkai@gmail.com)
 */
public abstract class MaplEach {
    //路径
    protected LinkedList<String> paths = new LinkedList<String>();
    protected LinkedList<Integer> arrayIndex = new LinkedList<Integer>();
    /**
     * 转换对象
     * @param obj
     */
    protected void each(Object obj) {
        if(obj instanceof Map){
            convertMap((Map<?, ?>) obj);
        } else if(obj instanceof List){
            convertList((List<?>) obj);
        }
    }
    /**
     * 转换map
     * @param obj
     */
    private void convertMap(Map<?, ?> obj) {
        for(Object key : obj.keySet()){
            paths.addLast(key.toString());
            DLR(fetchPath(), obj.get(key));
            each(obj.get(key));
            LRD(fetchPath(), obj.get(key));
            paths.removeLast();
        }
    }
    /**
     * 提取路径
     * @return
     */
    private String fetchPath(){
        StringBuffer sb = new StringBuffer();
        boolean first = true;
        for(String path : paths){
            if(!first){
                sb.append(".");
            }
            sb.append(path);
            first = false;
        }
        return sb.toString();
    }
    /**
     * 转换LIST
     * @param val
     */
    private void convertList(List<?> val){
        if(paths.size() <= 0){
            paths.add("[]");
        }else{
            paths.addLast(paths.removeLast() + "[]");
        }
        for(int i = 0; i < val.size(); i++){
            arrayIndex.addLast(i);
            each(val.get(i));
            arrayIndex.removeLast();
        }
    }

    /**
     * 前序
     * @param path
     * @param item
     */
    protected abstract void DLR(String path, Object item);
    /**
     * 后序
     * @param path
     * @param item
     */
    protected abstract void LRD(String path, Object item);
}


package org.nutz.dao.impl;

import java.io.Closeable;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;

import javax.sql.DataSource;

import org.nutz.dao.ConnCallback;
import org.nutz.dao.Dao;
import org.nutz.dao.DaoException;
import org.nutz.dao.impl.sql.run.NutDaoRunner;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;

/**
 * 独立于Trans事务的Dao实例. 本实例不是线程安全的,不可以在不同线程中同时操作, 但可以重复使用.
 *
 * @author wendal
 *
 */
public class NutTxDao extends NutDao implements Closeable {

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

    protected Connection conn;

    protected String id;

    protected boolean debug;

    /**
     * 是否恢复conn原本的auto commit设置
     */
    protected boolean _autoCommit;

    protected NutMap sps;

    /**
     * 通过NutDao实例构建一个独立事务的Dao实例
     *
     * @param _dao
     *            参数类型是Dao,但必须是NutDao实例
     * @throws DaoException
     *             获取数据连接失败时会抛出
     */
    public NutTxDao(Dao _dao) throws DaoException {
        NutDao dao = (NutDao) _dao;
        // ---------------------------------
        // 下面两个属性在1.r.59及之前的版本没有开放,但仍可以通过Mirror.set/get的方法操作
        this.meta = dao.meta;
        this.sqlManager = dao.sqlManager;
        // ---------------------------------
        this.dataSource = dao.dataSource;
        this.executor = dao.executor;
        this.dataSource = dao.dataSource;
        this.expert = dao.expert;
        this.pojoMaker = dao.pojoMaker;
        this.holder = dao.holder;
        this.autoTransLevel = dao.autoTransLevel;
        this._interceptors = dao._interceptors;
        this.setRunner(new NutDaoRunner() {
            public void _run(DataSource dataSource, ConnCallback callback) {
                try {
                    runCallback(getConnection(), callback);
                }
                catch (Exception e) {
                    if (e instanceof RuntimeException)
                        throw (RuntimeException) e;
                    throw new DaoException(e);
                }
            }
        });
        sps = new NutMap();
    }

    /**
     * 开启事务,级别为READ_COMMITTED
     *
     * @return 原对象
     */
    public NutTxDao beginRC() {
        return begin(Connection.TRANSACTION_READ_COMMITTED);
    }

    /**
     * 开启事务,级别为SERIALIZABLE
     *
     * @return 原对象
     */
    public NutTxDao beginSE() {
        return begin(Connection.TRANSACTION_SERIALIZABLE);
    }

    /**
     * 开启事务
     *
     * @param transLevel
     *            事务级别
     * @see java.sql.Connection
     * @return 原对象
     * @throws DaoException
     *             如果已经开启过事务
     */
    public NutTxDao begin(int transLevel) throws DaoException {
        if (this.conn != null)
            throw new DaoException("NutTxDao has been begined!!");
        id = R.UU32();
        if (debug)
            log.debugf("begin level=%d id=%s", transLevel, id);
        try {
            this.conn = dataSource.getConnection();
            this.conn.setTransactionIsolation(transLevel);
            if (this.conn.getAutoCommit() == true) {
                this.conn.setAutoCommit(false);
                _autoCommit = true;
            }
            setSavepoint(id);
        }
        catch (SQLException e) {
            throw new DaoException(e);
        }
        return this;
    }

    /**
     * 提交事务
     *
     * @return 原对象
     */
    public NutTxDao commit() {
        if (debug)
            log.debugf("commit id=%s", id);
        try {
            conn.commit();
        }
        catch (SQLException e) {
            throw new DaoException(e);
        }
        return this;
    }

    /**
     * 回滚事务
     *
     * @return 原对象
     */
    public NutTxDao rollback() {
        return rollback(id);
    }

    /**
     * 回滚事务
     *
     * @param id
     *            回滚点
     * @return 原对象
     */
    public NutTxDao rollback(String id) {
        if (debug)
            log.debugf("rollback id=%s", id);
        try {
            Savepoint sp = sps.getAs(id, Savepoint.class);
            if (sp != null)
                conn.rollback(sp);
            else
                log.debug("Null Savepoint found, skip, id=" + id);
        }
        catch (Throwable e) {
        }
        return this;
    }

    public NutTxDao setSavepoint(String spId) {
        try {
            sps.put(spId, conn.setSavepoint());
        }
        catch (SQLException e) {
            throw new DaoException(e);
        }
        return this;
    }

    /**
     * 关闭事务及连接
     */
    public void close() {
        if (debug)
            log.debugf("close id=%s", id);
        try {
            if (conn == null)
                return;
            if (_autoCommit)
                conn.setAutoCommit(true);
            conn.close();
            conn = null;
        }
        catch (Throwable e) {
        }
    }

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

    /**
     * 本事务的id
     *
     * @return 一个字符串
     */
    public String getId() {
        return id;
    }

    /**
     * 设置debug默认
     *
     * @param debug
     *            是否开启debug日志
     */
    public NutTxDao setDebug(boolean debug) {
        this.debug = debug;
        return this;
    }

    protected void finalize() throws Throwable {
        close();
        super.finalize();
    }
}


package org.nutz.dao.impl.entity.field;

import org.nutz.dao.Cnd;
import org.nutz.dao.Condition;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.entity.LinkType;
import org.nutz.dao.entity.PkType;
import org.nutz.dao.impl.EntityHolder;
import org.nutz.dao.impl.entity.EntityName;
import org.nutz.dao.impl.entity.info.LinkInfo;
import org.nutz.dao.util.cri.SimpleCriteria;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;

public class ManyManyLinkField extends AbstractLinkField {

    private EntityName relationTableName;

    private String fromColumnName;

    private String toColumnName;

    public ManyManyLinkField(Entity<?> host, EntityHolder holder, LinkInfo info, Class<?> klass, String from, String to, String relation, String key) {
    	super(host, holder, info);
    	this.targetType = klass;
        this.mapKey = key;
        this.relationTableName = EntityName.create(relation);

        String[] ss = Strings.splitIgnoreBlank(from, ":");
        this.fromColumnName = ss[0];
        String fromField = ss.length > 1 ? ss[1] : null;

        ss = Strings.splitIgnoreBlank(to, ":");
        this.toColumnName = ss[0];
        String toField = ss.length > 1 ? ss[1] : null;
        _make(host, fromField, toField);
	}

    public ManyManyLinkField(Entity<?> host, EntityHolder holder, LinkInfo info) {
        super(host, holder, info);
        this.targetType = guessTargetClass(info, info.manymany.target());
        this.mapKey = info.manymany.key();
        this.relationTableName = EntityName.create(info.manymany.relation());

        String[] ss = Strings.splitIgnoreBlank(info.manymany.from(), ":");
        this.fromColumnName = ss[0];
        String fromField = ss.length > 1 ? ss[1] : null;

        ss = Strings.splitIgnoreBlank(info.manymany.to(), ":");
        this.toColumnName = ss[0];
        String toField = ss.length > 1 ? ss[1] : null;
        _make(host, fromField, toField);
    }
    protected void _make(Entity<?> host, String fromField, String toField) {
        /*
         * 开始分析两个实体的链接字段
         */
        Entity<?> ta = this.getLinkedEntity();

        // 用户指定了 "from" 的 Java 字段名
        if (fromField != null) {
            hostField = host.getField(fromField);
            if (hostField == null) {
                // 指定了from的字段名,但找不到?!!!
                throw Lang.makeThrow(    "@ManyMany(from='%s') is invalid, no such field!! Host class=%s",
                                         fromField,
                                         host.getType().getName());
            }
        }
        // 用户指定了 "to" 的 Java 字段名
        if (null != toField) {
            linkedField = ta.getField(toField);
            if (linkedField == null) {
                // 指定了from的字段名,但找不到?!!!
                throw Lang.makeThrow(    "@ManyMany(to='%s') is invalid, no such field!! Host class=%s",
                                         toField,
                                         host.getType().getName());
            }
        }

        // 用户仅仅指定了 "from" 的 Java 字段
        if (null != hostField && linkedField == null) {
            linkedField = ta.getPkType() == PkType.ID ? ta.getIdField() : ta.getNameField();
        }
        // 用户仅仅指定了 "to" 的 Java 字段
        else if (null == hostField && linkedField != null) {
            hostField = host.getPkType() == PkType.ID ? host.getIdField() : host.getNameField();
        }
        // 都没指定，优先使用 Id 的链接主键
        else {
            // 都有 ID
            if (null != host.getIdField() && null != ta.getIdField()) {
                hostField = host.getIdField();
                linkedField = ta.getIdField();
            }
            // 宿主ID，链 Name
            else if (null != host.getIdField() && null != ta.getNameField()) {
                hostField = host.getIdField();
                linkedField = ta.getNameField();
            }
            // 宿主Name 链 ID
            else if (null != host.getNameField() && null != ta.getIdField()) {
                hostField = host.getNameField();
                linkedField = ta.getIdField();
            }
            // 都有 Name
            else if (null != host.getNameField() && null != ta.getNameField()) {
                hostField = host.getNameField();
                linkedField = ta.getNameField();
            }

        }
        // 最后再检查一下 ...
        if (null == hostField) {
            throw Lang.makeThrow(    "@ManyMany at [%s#%s] is Invalid: lack @Id or @Name at class=%s",
                                     host.getType().getName(),
                                     getName(),
                                     host.getType().getName());
        }
        if (null == linkedField) {
            throw Lang.makeThrow(    "@ManyMany at [%s#%s] is Invalid: lack @Id or @Name at class=%s",
                                     host.getType().getName(),
                                     getName(),
                                     target.getType().getName());
        }

    }

    public Condition createCondition(Object host) {
        SimpleCriteria cri = Cnd.cri();
        cri.where().andInBySql(    linkedField.getColumnName(),
                                "SELECT %s FROM %s WHERE %s=%s",
                                toColumnName,
                                this.getRelationName(),
                                fromColumnName,
                                Sqls.formatFieldValue(hostField.getValue(host)));
        return cri;
    }

    public void updateLinkedField(Object obj, Object linked) {}

    public void saveLinkedField(Object obj, Object linked) {}

    public LinkType getLinkType() {
        return LinkType.MANYMANY;
    }

    public String getRelationName() {
        return this.relationTableName.value();
    }

    public String getFromColumnName() {
        return fromColumnName;
    }

    public String getToColumnName() {
        return toColumnName;
    }

    /**
     * 返回关联两个实体的主键 Java 字段名数组
     * <p>
     * 数组的第一个元素是宿主主键的字段名，第二个元素是映射实体的主键字段名
     *
     * @return 关联两个实体的主键 Java 字段名数组
     */
    public String[] getLinkedPkNames() {
        String[] re = new String[2];
        re[0] = hostField.getName();
        re[1] = linkedField.getName();
        return re;
    }

}