package org.nutz.ioc.impl;

import org.nutz.castor.Castors;
import org.nutz.lang.*;
import org.nutz.lang.inject.Injecting;
import org.nutz.lang.util.Disks;
import org.nutz.lang.util.FileVisitor;
import org.nutz.lang.util.MultiLineProperties;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mapl.Mapl;
import org.nutz.resource.NutResource;
import org.nutz.resource.Scans;

import java.io.*;
import java.util.*;

/**
 * 代理Properties文件,以便直接在Ioc配置文件中使用
 *
 * @author wendal(wendal1985@gmail.com)
 * @author zozoh(zozohtnt@gmail.com)
 *
 * @since 1.b.37
 */
public class PropertiesProxy extends MultiLineProperties {

    private static final Log log = Logs.get();
    private static final String VM_NUTZ_CONF_PATH = "nutz.conf.path.";
    // 是否为UTF8格式的Properties文件
    private final boolean utf8;
    // 是否忽略无法加载的文件
    private boolean ignoreResourceNotFound = false;

    public PropertiesProxy() {
        this(true);
    }

    private Integer keyIndex;

    public PropertiesProxy(boolean utf8, String... paths) {
        this(utf8);
        this.setPaths(paths);
    }

    public PropertiesProxy(boolean utf8) {
        this.utf8 = utf8;
    }

    public PropertiesProxy(String... paths) {
        this(true);
        this.setPaths(paths);
    }

    public PropertiesProxy(InputStream in) {
        this(true);
        try {
            load(new InputStreamReader(in));
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Streams.safeClose(in);
        }
    }

    /**
     * @param r
     *            文本输入流
     * @since 1.b.50
     */
    public PropertiesProxy(Reader r) {
        this(true);
        try {
            load(r);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Streams.safeClose(r);
        }
    }

    public void setKeyIndex(Integer keyIndex) {
        this.keyIndex = keyIndex;
    }

    /**
     * 加载指定文件/文件夹的Properties文件,合并成一个Properties对象
     *

     * 如果有重复的key,请务必注意加载的顺序!!
     *
     *
     * @param paths
     *            需要加载的Properties文件路径
     */
    public void setPaths(String... paths) {
        clear();
        try {
            List<NutResource> list = getResources(paths);
            if (utf8)
                for (NutResource nr : list) {
                    if (log.isDebugEnabled())
                        log.debug("load properties from " + nr);
                    Reader r = nr.getReader();
                    try {
                        load(nr.getReader(), false);
                    }
                    finally {
                        Streams.safeClose(r);
                    }
                }
            else {
                Properties p = new Properties();
                for (NutResource nr : list) {
                    // 用字符流来读取文件
                    BufferedReader bf = new BufferedReader(new InputStreamReader(nr.getInputStream()));
                    try {
                        p.load(bf);
                    }
                    finally {
                        Streams.safeClose(bf);
                    }
                }
                putAll(p);
            }
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
    }

    public List<String> getKeysWithPrefix(String prefix) {
        List<String> list = new ArrayList<String>();
        for (String key : getKeys()) {
            if (key != null && key.startsWith(prefix)) {
                list.add(key);
            }
        }
        return list;
    }

    /**
     * 加载指定文件/文件夹的Properties文件
     *
     * @param paths
     *            需要加载的Properties文件路径
     * @return 加载到的Properties文件Resource列表
     */
    private List<NutResource> getResources(String... paths) {
        List<NutResource> list = new ArrayList<NutResource>();
        if (null != keyIndex) {
            try {
                String vmJsonStr = "";
                Properties p = System.getProperties();
                for (Object key : p.keySet()) {
                    if (((String) key).startsWith(VM_NUTZ_CONF_PATH + keyIndex))
                        vmJsonStr = p.getProperty((String) key).trim();
                }
                if (Strings.isNotBlank(vmJsonStr)) {
                    paths = vmJsonStr.split("\\,");
                }
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug("-D" + VM_NUTZ_CONF_PATH + keyIndex + " value is invalid:  " + e.getMessage());
                }
            }
        }
        for (String path : paths) {
            try {
                List<NutResource> resources = Scans.me().loadResource("^.+[.]properties$", path);
                list.addAll(resources);
            }
            catch (Exception e) {
                if (ignoreResourceNotFound) {
                    if (log.isWarnEnabled()) {
                        log.warn("Could not load resource from " + path + ": " + e.getMessage());
                    }
                } else {
                    throw Lang.wrapThrow(e);
                }
            }
        }
        return list;
    }

    public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
        this.ignoreResourceNotFound = ignoreResourceNotFound;
    }

    /**
     * @param key
     *            键
     * @return 是否包括这个键
     * @since 1.b.50
     */
    public boolean has(String key) {
        return containsKey(key);
    }

    public PropertiesProxy set(String key, String val) {
        put(key, val);
        return this;
    }

    public String check(String key) {
        String val = get(key);
        if (null == val)
            throw Lang.makeThrow("Ioc.$conf expect property '%s'", key);
        return val;
    }

    public boolean getBoolean(String key) {
        return getBoolean(key, false);
    }

    public boolean getBoolean(String key, boolean dfval) {
        String val = get(key);
        if (Strings.isBlank(val))
            return dfval;
        return Castors.me().castTo(val, Boolean.class);
    }

    public String get(String key, String defaultValue) {
        return Strings.sNull(get(key), defaultValue);
    }

    public List<String> getList(String key) {
        return getList(key,"\n");
    }

    public List<String> getList(String key,String separatorChar) {
        List<String> re = new ArrayList<String>();
        String keyVal = get(key);
        if (Strings.isNotBlank(keyVal)) {
            String[] vlist = Strings.splitIgnoreBlank(keyVal, separatorChar);
            for (String v : vlist) {
                re.add(v);
            }
        }
        return re;
    }

    public String trim(String key) {
        return Strings.trim(get(key));
    }

    public String trim(String key, String defaultValue) {
        return Strings.trim(get(key, defaultValue));
    }

    public int getInt(String key) {
        return getInt(key, -1);
    }

    public int getInt(String key, int defaultValue) {
        try {
            return Integer.parseInt(get(key));
        }
        catch (NumberFormatException e) {
            return defaultValue;
        }
    }

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

    public long getLong(String key, long dfval) {
        try {
            return Long.parseLong(get(key));
        }
        catch (NumberFormatException e) {
            return dfval;
        }
    }

    public String getTrim(String key) {
        return Strings.trim(get(key));
    }

    public String getTrim(String key, String defaultValue) {
        return Strings.trim(get(key, defaultValue));
    }

    public List<String> getKeys() {
        return keys();
    }

    public Collection<String> getValues() {
        return values();
    }

    public Properties toProperties() {
        Properties p = new Properties();
        p.putAll(this);
        return p;
    }

    /**
     * 根据自身的一个键对应的值扩展自身的属性。
     *

     * 本函数假设你可能有下面的键值:
     *
     *

     * ...
     * files:
     * path/to_a.properties
     * path/to_b.properties
     * #End files
     *

     *
     * 那么如果你调用 joinByKey("files");

     * 则会将其值的两个属性文件展开，加入到自身。
     *

     * 属性文件的路径可以是磁盘全路径，或者在 CLASSPATH 里的路径
     *
     * @param key
     *            键
     * @return 自身
     */
    public PropertiesProxy joinByKey(String key) {
        String str = get(key);
        final PropertiesProxy me = this;
        if (!Strings.isBlank(str)) {
            String[] ss = Strings.splitIgnoreBlank(str, "\n");
            for (String s : ss) {
                File f = Files.findFile(s);
                if (null == f) {
                    throw Lang.makeThrow("Fail to found path '%s' in CLASSPATH or File System!", s);
                }
                // 如果是一个包，引用全部 Files
                if (f.isDirectory()) {
                    Disks.visitFile(f, new FileVisitor() {
                        public void visit(File f) {
                            me.joinAndClose(Streams.fileInr(f));
                        }
                    }, new FileFilter() {
                        public boolean accept(File f) {
                            if (f.isDirectory())
                                return !f.isHidden() && !f.getName().startsWith(".");
                            return f.getName().endsWith(".properties");
                        }
                    });
                }
                // 否则引用单个文件
                else if (f.isFile()) {
                    this.joinAndClose(Streams.fileInr(f));
                }
            }
        }
        return this;
    }

    /**
     * 将另外一个 Properties 文本加入本散列表
     *
     * @param r
     *            文本输入流
     * @return 自身
     */
    public PropertiesProxy joinAndClose(Reader r) {
        MultiLineProperties mp = new MultiLineProperties();
        try {
            mp.load(r);
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Streams.safeClose(r);
        }
        this.putAll(mp);
        return this;
    }

    public Map<String, String> toMap() {
        return new LinkedHashMap<String, String>(this);
    }

    public String get(String key) {
        return super.get(key);
    }

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T> T makeDeep(Class<T> klass, String prefix) {
		Map map = this;
		return (T) Mapl.maplistToObj(Lang.filter(map, prefix, null, null, null), klass);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public <T> T make(Class<T> klass, String prefix) {
		Map map = this;
		Mirror<T> mirror = Mirror.me(klass);
		T t = mirror.born();
		map = Lang.filter(map, prefix, null, null, null);
		for (Entry<String, Object> en : ((Map<String, Object>) map).entrySet()) {
			String name = en.getKey();
			Injecting setter = null;
            try {
                setter = mirror.getInjecting(name);
            }
            catch (Exception e) {
                log.debugf("no such field(name=%s) at object class=%s, skip", name, t.getClass().getName());
                continue;
            }
			setter.inject(t, en.getValue());
		}
		return t;
	}
}


package org.nutz.mapl.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.nutz.lang.Lang;
import org.nutz.lang.Nums;
import org.nutz.lang.Strings;

/**
 * 构建新的MapList结构对象, 根据path重建MapList结构
 *
 * @author juqkai(juqkai@gmail.com)
 */
public class MaplRebuild {
    enum Model {
        add, del, cell
    }

    private Model model = Model.add;
    private String[] keys;
    private Object val;
    // 数组索引
    private Integer arrayItem;
    // 数组栈列表
    protected LinkedList<Integer> arrayIndex = new LinkedList<Integer>();
    // 新MapList结构
    private Map<String, Object> newobj = new LinkedHashMap<String, Object>();

    private Object cellObj = null;

    public MaplRebuild() {
        newobj.put("obj", null);
    }

    public MaplRebuild(Object mapl) {
        newobj.put("obj", mapl);
    }

    /**
     * 添加属性
     *
     * @param path
     *            路径
     * @param obj
     *            值
     */
    public void put(String path, Object obj) {
        init(path, obj);
        inject(newobj, 0);
    }

    /**
     * 添加属性
     *
     * @param path
     *            路径
     * @param obj
     *            值
     * @param arrayIndex
     *            索引队列
     */
    public void put(String path, Object obj, LinkedList<Integer> arrayIndex) {
        this.arrayIndex = arrayIndex;
        put(path, obj);
    }

    /**
     * 删除结点
     *
     * @param path
     */
    public void remove(String path) {
        model = Model.del;
        init(path, null);
        inject(newobj, 0);
    }

    /**
     * 访问结点
     *
     * @param path
     *            路径
     */
    public Object cell(String path) {
        model = Model.cell;
        init(path, null);
        inject(newobj, 0);
        return cellObj;
    }

    /**
     * 提取重建后的MapList
     */
    public Object fetchNewobj() {
        return newobj.get("obj");
    }

    private void init(String keys, Object obj) {
        this.keys = Lang.arrayFirst("obj", Strings.split(keys, false, '.'));
        this.val = obj;
        this.arrayItem = 0;
    }

    /**
     * 注入
     *
     * @param obj
     * @param i
     */
    @SuppressWarnings("unchecked")
    private Object inject(Object obj, int i) {
        String key = keys[i];
        // 根数组
        if (key.indexOf('[') == 0) {
            List<Object> list = new ArrayList<Object>();
            if (obj != null) {
                list = (List<Object>) obj;
            }
            injectList(list, i, fetchIndex(key));
            return list;
        }
        // 数组
        // if(key.endsWith("[]")){
        int pos = key.indexOf('[');
        if (pos > 0) {
            Map<String, Object> map = new LinkedHashMap<String, Object>();
            if (obj != null) {
                map = (Map<String, Object>) obj;
            }
            // 有Key的list
            String k = key.substring(0, pos);
            if (!map.containsKey(k)) {
                map.put(k, new ArrayList<Object>());
            }
            int[] index = fetchIndex(key.substring(key.indexOf('['), key.length()));
            injectList((List<Object>) map.get(k), i, index);
            return map;
        }
        // 键值：这里有个特殊考虑，如果当前对象是个列表，那么键值必然是一个下标
        if (obj instanceof List) {
            try {
                int index = Integer.parseInt(keys[i]);
                injectList((List<Object>) obj, i, Nums.array(index));
                return obj;
            }
            catch (Exception e) {
                throw new RuntimeException("路径格式不正确!");
            }
        }
        // 当做普通的 map 好了
        return injectMap(obj, i);
    }

    private int[] fetchIndex(String val) {
        // []格式的路径, 即索引放在arrayIndex里面的.
        if (val.indexOf(']') == 1) {
            if (arrayIndex.size() > arrayItem) {
                return Nums.array(arrayIndex.get(arrayItem++));
            }
            // 默认返回 0
            return Nums.array(0);
        }
        // [1]格式, 路径上自带索引，可以是多个，譬如[1][3][0]
        String[] ss = val.substring(1, val.length() - 1).split("\\]\\[");
        int[] re = new int[ss.length];
        for (int i = 0; i < ss.length; i++)
            re[i] = Integer.parseInt(ss[i]);
        return re;
    }

    /**
     * 注入MAP
     *
     * @param obj
     * @param i
     */
    @SuppressWarnings("unchecked")
    private Object injectMap(Object obj, int i) {
        Map<String, Object> map;
        String key = keys[i];
        if (obj == null) {
            map = new LinkedHashMap<String, Object>();
        } else {
            map = (Map<String, Object>) obj;
        }

        if (model == Model.add) {
            if (i == keys.length - 1) {
                map.put(key, val);
                return map;
            }
            if (!map.containsKey(key) || map.get(key) == null) {
                map.put(key, inject(null, i + 1));
                return map;
            }
        } else if (model == Model.del) {
            if (i == keys.length - 1) {
                map.remove(key);
                return map;
            }
            if (!map.containsKey(key) || map.get(key) == null) {
                return map;
            }
        } else if (model == Model.cell) {
            if (i == keys.length - 1) {
                cellObj = map.get(key);
                return map;
            }
            if (!map.containsKey(key) || map.get(key) == null) {
                return map;
            }
        }

        if (map.containsKey(key) && map.get(key) != null) {
            inject(map.get(key), i + 1);
        }
        return map;
    }

    /**
     * 注入List
     *
     * @param list
     *            列表
     * @param keyIndex
     *            当前 Key 路径的列表
     * @param eleIndexes
     *            注入的元素下标列表
     */
    @SuppressWarnings("unchecked")
    private void injectList(List<Object> list, int keyIndex, int[] eleIndexes) {
        // 下标列表如果是多个，那么预先处理一下列表
        int i_last = eleIndexes.length - 1;
        for (int i = 0; i < i_last; i++) {
            int index = eleIndexes[i];
            Object ele = list.get(index);
            // 是列表？嗯很好很好
            if (ele instanceof List) {
                list = (List<Object>) ele;
            }
            // 不是列表啊，不能忍
            else {
                throw Lang.makeThrow("invalid keyPath '%s' in key:%d eleIndex:%d",
                                     Strings.join(".", keys),
                                     keyIndex,
                                     i);
            }
        }

        // 得到要处理的下标
        int eleIndex = eleIndexes[i_last];

        // 添加模式
        if (model == Model.add) {
            if (keyIndex == keys.length - 1) {
                if (val instanceof Collection) {
                    list.addAll((Collection<? extends Object>) val);
                } else {
                    list.add(eleIndex, val);
                }
                return;
            }
            if (list.size() <= eleIndex) {
                list.add(eleIndex, new LinkedHashMap<String, Object>());
            }
        } else if (model == Model.del) {
            if (keyIndex == keys.length - 1) {
                if (list.size() > eleIndex) {
                    list.remove(eleIndex);
                }
                return;
            }
            if (list.size() <= eleIndex) {
                return;
            }
        } else if (model == Model.cell) {
            if (keyIndex == keys.length - 1) {
                if (list.size() > eleIndex) {
                    cellObj = list.get(eleIndex);
                }
                return;
            }
            if (list.size() <= eleIndex) {
                return;
            }
        }
        inject((Map<String, Object>) list.get(eleIndex), keyIndex + 1);
    }

}


package org.nutz.img;

import java.awt.image.BufferedImage;

/**
 * 根据文字生成图片的小工具，适用于一些生成头像的场景。
 *
 * <pre>
 *
 * 直接生成黑底白字
 * Avatar.createAvatar("王小二");
 *
 * 手动设置图片大小，文字/背景色，字体，字体样式, 字体大小等
 * Avatar.createAvatar("王小二", 128, "rgba(255,0,0,0.8)", "rgba(0,0,0,0.1)", "微软雅黑", 64, Font.BOLD);
 *
 * </pre>
 *
 * @author pw
 * @deprecated 代码已转移到Images类中，请修改引用
 */
@Deprecated
public abstract class Avatar {

    /**
     * 根据名字生成头像，英文采用第一个字母，中文2个字使用2个字，超过2个字采用第一个字
     *
     * @param name
     *            名字
     * @return 头像
     */
    @Deprecated
    public static BufferedImage createAvatar(String name) {
        return Images.createAvatar(name);
    }

    /**
     * 根据名字生成头像，英文采用第一个字母，中文2个字使用2个字，超过2个字采用第一个字
     *
     * @param name
     *            名字
     * @param size
     *            图片大小，默认256
     * @param fontColor
     *            文字颜色 默认白色
     * @param bgColor
     *            背景颜色 默认黑色
     * @param fontName
     *            字体名称 需运行环境中已有该字体名称
     * @param fontSize
     *            字体大小
     * @param fontStyle
     *            字体样式 Font.PLAIN || Font.BOLD || Font.ITALIC
     * @return 头像
     */
    public static BufferedImage createAvatar(String name,
                                             int size,
                                             String fontColor,
                                             String bgColor,
                                             String fontName,
                                             int fontSize,
                                             int fontStyle) {
        return Images.createAvatar(name, size, fontColor, bgColor, fontName, fontSize, fontStyle);
    }

}


/***
 * 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.commons;

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

import org.nutz.repo.org.objectweb.asm.Type;

/**
 * A named method descriptor.
 *
 * @author Juozas Baliuka
 * @author Chris Nokleberg
 * @author Eric Bruneton
 */
public class Method {

    /**
     * The method name.
     */
    private final String name;

    /**
     * The method descriptor.
     */
    private final String desc;

    /**
     * Maps primitive Java type names to their descriptors.
     */
    private static final Map<String, String> DESCRIPTORS;

    static {
        DESCRIPTORS = new HashMap<String, String>();
        DESCRIPTORS.put("void", "V");
        DESCRIPTORS.put("byte", "B");
        DESCRIPTORS.put("char", "C");
        DESCRIPTORS.put("double", "D");
        DESCRIPTORS.put("float", "F");
        DESCRIPTORS.put("int", "I");
        DESCRIPTORS.put("long", "J");
        DESCRIPTORS.put("short", "S");
        DESCRIPTORS.put("boolean", "Z");
    }

    /**
     * Creates a new {@link Method}.
     *
     * @param name
     *            the method's name.
     * @param desc
     *            the method's descriptor.
     */
    public Method(final String name, final String desc) {
        this.name = name;
        this.desc = desc;
    }

    /**
     * Creates a new {@link Method}.
     *
     * @param name
     *            the method's name.
     * @param returnType
     *            the method's return type.
     * @param argumentTypes
     *            the method's argument types.
     */
    public Method(final String name, final Type returnType,
            final Type[] argumentTypes) {
        this(name, Type.getMethodDescriptor(returnType, argumentTypes));
    }

    /**
     * Creates a new {@link Method}.
     *
     * @param m
     *            a java.lang.reflect method descriptor
     * @return a {@link Method} corresponding to the given Java method
     *         declaration.
     */
    public static Method getMethod(java.lang.reflect.Method m) {
        return new Method(m.getName(), Type.getMethodDescriptor(m));
    }

    /**
     * Creates a new {@link Method}.
     *
     * @param c
     *            a java.lang.reflect constructor descriptor
     * @return a {@link Method} corresponding to the given Java constructor
     *         declaration.
     */
    public static Method getMethod(java.lang.reflect.Constructor<?> c) {
        return new Method("<init>", Type.getConstructorDescriptor(c));
    }

    /**
     * Returns a {@link Method} corresponding to the given Java method
     * declaration.
     *
     * @param method
     *            a Java method declaration, without argument names, of the form
     *            "returnType name (argumentType1, ... argumentTypeN)", where
     *            the types are in plain Java (e.g. "int", "float",
     *            "java.util.List", ...). Classes of the java.lang package can
     *            be specified by their unqualified name; all other classes
     *            names must be fully qualified.
     * @return a {@link Method} corresponding to the given Java method
     *         declaration.
     * @throws IllegalArgumentException
     *             if method could not get parsed.
     */
    public static Method getMethod(final String method)
            throws IllegalArgumentException {
        return getMethod(method, false);
    }

    /**
     * Returns a {@link Method} corresponding to the given Java method
     * declaration.
     *
     * @param method
     *            a Java method declaration, without argument names, of the form
     *            "returnType name (argumentType1, ... argumentTypeN)", where
     *            the types are in plain Java (e.g. "int", "float",
     *            "java.util.List", ...). Classes of the java.lang package may
     *            be specified by their unqualified name, depending on the
     *            defaultPackage argument; all other classes names must be fully
     *            qualified.
     * @param defaultPackage
     *            true if unqualified class names belong to the default package,
     *            or false if they correspond to java.lang classes. For instance
     *            "Object" means "Object" if this option is true, or
     *            "java.lang.Object" otherwise.
     * @return a {@link Method} corresponding to the given Java method
     *         declaration.
     * @throws IllegalArgumentException
     *             if method could not get parsed.
     */
    public static Method getMethod(final String method,
            final boolean defaultPackage) throws IllegalArgumentException {
        int space = method.indexOf(' ');
        int start = method.indexOf('(', space) + 1;
        int end = method.indexOf(')', start);
        if (space == -1 || start == -1 || end == -1) {
            throw new IllegalArgumentException();
        }
        String returnType = method.substring(0, space);
        String methodName = method.substring(space + 1, start - 1).trim();
        StringBuilder sb = new StringBuilder();
        sb.append('(');
        int p;
        do {
            String s;
            p = method.indexOf(',', start);
            if (p == -1) {
                s = map(method.substring(start, end).trim(), defaultPackage);
            } else {
                s = map(method.substring(start, p).trim(), defaultPackage);
                start = p + 1;
            }
            sb.append(s);
        } while (p != -1);
        sb.append(')');
        sb.append(map(returnType, defaultPackage));
        return new Method(methodName, sb.toString());
    }

    private static String map(final String type, final boolean defaultPackage) {
        if ("".equals(type)) {
            return type;
        }

        StringBuilder sb = new StringBuilder();
        int index = 0;
        while ((index = type.indexOf("[]", index) + 1) > 0) {
            sb.append('[');
        }

        String t = type.substring(0, type.length() - sb.length() * 2);
        String desc = DESCRIPTORS.get(t);
        if (desc != null) {
            sb.append(desc);
        } else {
            sb.append('L');
            if (t.indexOf('.') < 0) {
                if (!defaultPackage) {
                    sb.append("java/lang/");
                }
                sb.append(t);
            } else {
                sb.append(t.replace('.', '/'));
            }
            sb.append(';');
        }
        return sb.toString();
    }

    /**
     * Returns the name of the method described by this object.
     *
     * @return the name of the method described by this object.
     */
    public String getName() {
        return name;
    }

    /**
     * Returns the descriptor of the method described by this object.
     *
     * @return the descriptor of the method described by this object.
     */
    public String getDescriptor() {
        return desc;
    }

    /**
     * Returns the return type of the method described by this object.
     *
     * @return the return type of the method described by this object.
     */
    public Type getReturnType() {
        return Type.getReturnType(desc);
    }

    /**
     * Returns the argument types of the method described by this object.
     *
     * @return the argument types of the method described by this object.
     */
    public Type[] getArgumentTypes() {
        return Type.getArgumentTypes(desc);
    }

    @Override
    public String toString() {
        return name + desc;
    }

    @Override
    public boolean equals(final Object o) {
        if (!(o instanceof Method)) {
            return false;
        }
        Method other = (Method) o;
        return name.equals(other.name) && desc.equals(other.desc);
    }

    @Override
    public int hashCode() {
        return name.hashCode() ^ desc.hashCode();
    }
}


package org.nutz.mapl.impl.convert;

import java.io.Reader;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.Streams;
import org.nutz.mapl.MaplConvert;
import org.nutz.mapl.impl.MaplEach;
import org.nutz.mapl.impl.MaplRebuild;

/**
 * MapList结构转换.
 *
 将一种MapList结构转换成另外一种MapList结构.例:
 *

 *  {
 *      "age":"123",
 *      "name":"juqkai"
 *  }
 *  转换成:
 *  {
 *      "年龄":"123",
 *      "姓名":"juqkai"
 *  }
 *

 *
要进行这样的转换需要预先配置一个对应关系的配置, 具体的配置关系说明如下:
 *

 *
使用原MapList一样的结构
 *
有数组的, 只写第一个元素的结构
 *
原结构中的值, 以字符串或字符串数组做为目标结构的对应关系
 *
对应关系可以为数组
 *
有数组的, 目标结构以key[].abc来代替数组
 *
原结构数组层次强制限定一致, 目标结构中'[]'的索引按原结构中出现先后顺序进行匹配.
 *
如果原结果不存在, 那默认为0
 *
未在模板中申明的不做转换
 *

 *
 例:
 *

 * 例1:
 *  {
 *      "age":"user.年龄",
 *      "name":["user.name", "user.姓名"]
 *  }
 * 例2(原json:[{"name":"nutz"},{"name":"juqkai"}]):
 * [{
 *      "name":"[].姓名"
 * }]
 * 例3:
 * {
 *      users:[
 *          {
 *              "name":["people[].name", "users[].name"],
 *              "age":"users[].name"
 *          }
 *      ]
 * }
 *

 * @author juqkai(juqkai@gmail.com)
 */
public class StructureConvert extends MaplEach implements MaplConvert{
    //关系
    private Map<String, List<String>> relation = new LinkedHashMap<String, List<String>>();

    private MaplRebuild structure = new MaplRebuild();

    /**
     *
     * @param path 模板文件路径
     */
    public StructureConvert(String path){
        Object obj = Json.fromJson(Streams.fileInr(path));
        loadRelation(obj, "");
    }
    /**
     *
     * @param reader 模板流
     */
    public StructureConvert(Reader reader){
        Object obj = Json.fromJson(reader);
        loadRelation(obj, "");
    }
    /**
     *
     * @param obj 模板的Map, List结构
     */
    public StructureConvert(Object obj){
        loadRelation(obj, "");
    }

    /**
     * 转换
     * @param obj 目标对象
     */
    public Object convert(Object obj){
        each(obj);
        return structure.fetchNewobj();
    }

    protected void LRD(String path, Object item) {}

    /**
     * 重建新对象
     */
    protected void DLR(String path, Object object) {
        if(relation.containsKey(path)){
            List<String> dests = relation.get(path);
            for(String dest : dests){
                if(dest.equals("")){
                    structure.put(path, object, arrayIndex);
                    continue;
                }
                structure.put(dest, object, arrayIndex);
            }
        }
    }

    /**
     * 解析配置信息
     * @param obj
     * @param path
     */
    private void loadRelation(Object obj, String path) {
        if(obj instanceof Map){
            loadMapRelation((Map<?, ?>) obj, path);
        } else if(obj instanceof List){
            loadListRelation((List<?>) obj, path);
        } else {
            throw new RuntimeException("无法识别的类型!");
        }
    }
    /**
     * 解析List配置信息
     * @param obj
     * @param path
     */
    @SuppressWarnings("unchecked")
    private void loadListRelation(List<?> obj, String path) {
        if(obj.size() <= 0){
            return;
        }
        if(obj.get(0) instanceof String){
            relation.put(path, (List<String>) obj);
            return;
        }
        loadRelation(obj.get(0), path + "[]");
    }
    /**
     * 解析MAP配置信息
     * @param obj
     * @param path
     */
    private void loadMapRelation(Map<?, ?> obj, String path) {
        for(Object key : obj.keySet()){
            Object val = obj.get(key);
            if(val instanceof String){
                relation.put(path + space(path) + key.toString(), Lang.list(val.toString()));
                continue;
            }
            loadRelation(obj.get(key), path + space(path) + key.toString());
        }
    }

    private static String space(String path){
        return "".equals(path) ? "" : ".";
    }

}


package org.nutz.dao.util;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.sql.DataSource;

import org.nutz.dao.Chain;
import org.nutz.dao.Condition;
import org.nutz.dao.ConnCallback;
import org.nutz.dao.Dao;
import org.nutz.dao.DaoException;
import org.nutz.dao.FieldFilter;
import org.nutz.dao.FieldMatcher;
import org.nutz.dao.Sqls;
import org.nutz.dao.TableName;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.entity.EntityIndex;
import org.nutz.dao.entity.MappingField;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.impl.NutDao;
import org.nutz.dao.impl.sql.SqlFormat;
import org.nutz.dao.jdbc.JdbcExpert;
import org.nutz.dao.jdbc.Jdbcs;
import org.nutz.dao.jdbc.ValueAdaptor;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.sql.SqlCallback;
import org.nutz.dao.util.tables.TablesFilter;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Strings;
import org.nutz.lang.random.R;
import org.nutz.lang.util.Callback2;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.resource.Scans;
import org.nutz.trans.Molecule;
import org.nutz.trans.Trans;

/**
 * Dao 的帮助函数
 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author wendal(wendal1985@gmail.com)
 * @author cqyunqin
 * @author rekoe(koukou890@qq.com)
 * @author threefish(306955302@qq.com)
 */
public abstract class Daos {

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

    /**
     * 安全关闭Statement和ResultSet
     *
     * @param stat
     *            Statement实例,可以为null
     * @param rs
     *            ResultSet实例,可以为null
     */
    public static void safeClose(Statement stat, ResultSet rs) {
        safeClose(rs);
        safeClose(stat);
    }

    /**
     * 安全关闭Statement
     *
     * @param stat
     *            Statement实例,可以为null
     */
    public static void safeClose(Statement stat) {
        if (null != stat)
            try {
                stat.close();
            }
            catch (Throwable e) {}
    }

    /**
     * 安全关闭=ResultSet
     *
     * @param rs
     *            ResultSet实例,可以为null
     */
    public static void safeClose(ResultSet rs) {
        if (null != rs)
            try {
                rs.close();
            }
            catch (Throwable e) {}
    }

    /**
     * 获取colName所在的行数
     *
     * @param meta
     *            从连接中取出的ResultSetMetaData
     * @param colName
     *            字段名
     * @return 所在的索引,如果不存在就抛出异常
     * @throws SQLException
     *             指定的colName找不到
     */
    public static int getColumnIndex(ResultSetMetaData meta, String colName) throws SQLException {
        if (meta == null)
            return 0;
        int columnCount = meta.getColumnCount();
        for (int i = 1; i <= columnCount; i++)
            if (meta.getColumnName(i).equalsIgnoreCase(colName))
                return i;
        // TODO 尝试一下meta.getColumnLabel?
        log.debugf("Can not find @Column(%s) in table/view (%s)", colName, meta.getTableName(1));
        throw Lang.makeThrow(SQLException.class, "Can not find @Column(%s)", colName);
    }

    /**
     * 是不是数值字段
     *
     * @param meta
     *            从连接中取出的ResultSetMetaData
     * @param index
     *            字段索引
     * @return 如果是就返回true
     * @throws SQLException
     *             指定的索引不存在
     */
    public static boolean isIntLikeColumn(ResultSetMetaData meta, int index) throws SQLException {
        switch (meta.getColumnType(index)) {
        case Types.BIGINT:
        case Types.INTEGER:
        case Types.SMALLINT:
        case Types.TINYINT:
        case Types.NUMERIC:
            return true;
        }
        return false;
    }

    /**
     * 填充记录总数
     *
     * @param pager
     *            分页对象,如果为null就不进行任何操作
     * @param dao
     *            Dao实例
     * @param entityType
     *            实体类,可以通过dao.getEntity获取
     * @param cnd
     *            查询条件
     * @return 传入的Pager参数
     */
    public static Pager updatePagerCount(Pager pager, Dao dao, Class<?> entityType, Condition cnd) {
        if (null != pager) {
            pager.setRecordCount(dao.count(entityType, cnd));
        }
        return pager;
    }

    /**
     * 填充记录总数
     *
     * @param pager
     *            分页对象,如果为null就不进行任何操作
     * @param dao
     *            Dao实例
     * @param tableName
     *            表名
     * @param cnd
     *            查询条件
     * @return 传入的Pager参数
     */
    public static Pager updatePagerCount(Pager pager, Dao dao, String tableName, Condition cnd) {
        if (null != pager) {
            pager.setRecordCount(dao.count(tableName, cnd));
        }
        return pager;
    }

    /**
     * 根据sql查询特定的记录,并转化为指定的类对象
     *
     * @param dao
     *            Dao实例
     * @param klass
     *            Pojo类
     * @param sql_str
     *            sql语句
     * @return 查询结果
     */
    public static <T> List<T> queryList(Dao dao, Class<T> klass, String sql_str) {
        Sql sql = Sqls.create(sql_str)
                      .setCallback(Sqls.callback.entities())
                      .setEntity(dao.getEntity(klass));
        dao.execute(sql);
        return sql.getList(klass);
    }

    /**
     * 执行sql和callback
     *
     * @param dao
     *            Dao实例
     * @param sql_str
     *            sql语句
     * @param callback
     *            sql回调
     * @return 回调的返回值
     */
    public static Object query(Dao dao, String sql_str, SqlCallback callback) {
        Sql sql = Sqls.create(sql_str).setCallback(callback);
        dao.execute(sql);
        return sql.getResult();
    }

    /**
     * 在同一个事务内查询对象及关联对象
     *
     * @param dao
     *            Dao实例
     * @param classOfT
     *            指定的Pojo类
     * @param cnd
     *            查询条件
     * @param pager
     *            分页语句
     * @param regex
     *            需要查出的关联对象, 可以参阅dao.fetchLinks
     * @return 查询结果
     */
    public static <T> List<T> queryWithLinks(final Dao dao,
                                             final Class<T> classOfT,
                                             final Condition cnd,
                                             final Pager pager,
                                             final String regex) {
        Molecule<List<T>> molecule = new Molecule<List<T>>() {
            public void run() {
                List<T> list = dao.query(classOfT, cnd, pager);
                dao.fetchLinks(list, regex);
                setObj(list);
            }
        };
        return Trans.exec(molecule);
    }

    public static StringBuilder dataDict(DataSource ds, String... packages) {
        return dataDict(new NutDao(ds), packages);
    }

    /** 根据Pojo生成数据字典,zdoc格式 */
    public static StringBuilder dataDict(Dao dao, String... packages) {
        StringBuilder sb = new StringBuilder();
        List<Class<?>> ks = new ArrayList<Class<?>>();
        for (String packageName : packages) {
            ks.addAll(Scans.me().scanPackage(packageName));
        }
        Iterator<Class<?>> it = ks.iterator();
        while (it.hasNext()) {
            Class<?> klass = it.next();
            if (Mirror.me(klass).getAnnotation(Table.class) == null)
                it.remove();
        }
        // log.infof("Found %d table class", ks.size());

        JdbcExpert exp = dao.getJdbcExpert();
        Entity<?> entity = null;
        String line = "-------------------------------------------------------------------\n";
        sb.append("#title:数据字典\n");
        sb.append("#author:wendal\n");
        sb.append("#index:0,1\n").append(line);
        for (Class<?> klass : ks) {
            sb.append(line);
            entity = dao.getEntity(klass);
            sb.append("表名 ").append(entity.getTableName()).append("\n\n");
            if (!Strings.isBlank(entity.getTableComment()))
                sb.append("表注释: ").append(entity.getTableComment());
            sb.append("\t").append("Java类名 ").append(klass.getName()).append("\n\n");
            sb.append("\t||序号||列名||数据类型||主键||非空||默认值||java属性名||java类型||注释||\n");
            int index = 1;
            for (MappingField field : entity.getMappingFields()) {
                String dataType = exp.evalFieldType(field);
                sb.append("\t||")
                  .append(index++)
                  .append("||")
                  .append(field.getColumnName())
                  .append("||")
                  .append(dataType)
                  .append("||")
                  .append(field.isPk())
                  .append("||")
                  .append(field.isNotNull())
                  .append("||")
                  .append(field.getDefaultValue(null) == null ? " " : field.getDefaultValue(null))
                  .append("||")
                  .append(field.getName())
                  .append("||")
                  .append(field.getTypeClass().getName())
                  .append("||")
                  .append(field.getColumnComment() == null ? " " : field.getColumnComment())
                  .append("||\n");
            }
        }
        return sb;
    }

    /**
     * 查询sql并把结果放入传入的class组成的List中
     */
    public static <T> List<T> query(Dao dao,
                                    Class<T> classOfT,
                                    String sql,
                                    Condition cnd,
                                    Pager pager) {
        Sql sql2 = Sqls.queryEntity(sql);
        sql2.setEntity(dao.getEntity(classOfT));
        sql2.setCondition(cnd);
        sql2.setPager(pager);
        dao.execute(sql2);
        return sql2.getList(classOfT);
    }

    /**
     * 查询某sql的结果条数. 请使用Sql接口的版本
     */
    @Deprecated
    public static long queryCount(Dao dao, String sql) {
        String tmpTable = "as _nutz_tmp";
        if (dao.meta().isDB2())
            tmpTable = "as nutz_tmp_" + R.UU32();
        else if (dao.meta().isOracle())
            tmpTable = "";
        else
            tmpTable += "_" + R.UU32();
        Sql sql2 = Sqls.fetchLong("select count(1) from (" + sql + ")" + tmpTable);
        dao.execute(sql2);
        return sql2.getLong();
    }

    /**
     * 查询某sql的结果条数
     * @param dao 用于执行该count方法的dao实例
     * @param sql 原本的Sql对象,将复制其sql语句,变量和参数表.
     */
    public static long queryCount(Dao dao, Sql sql) {
        String tmpTable = "as _nutz_tmp";
        if (dao.meta().isDB2())
            tmpTable = "as nutz_tmp_" + R.UU32();
        else if (dao.meta().isOracle())
            tmpTable = "";
        else
            tmpTable += "_" + R.UU32();
        Sql sql2 = Sqls.fetchLong("select count(1) from (" + sql.getSourceSql() + ")" + tmpTable);
        sql2.setEntity(sql.getEntity());
        for (String key : sql.params().keys()) {
            sql2.setParam(key, sql.params().get(key));
        }
        for (String key : sql.vars().keys()) {
            sql2.setVar(key, sql.vars().get(key));
        }
        return dao.execute(sql2).getLong();
    }

    /**
     * 执行一个特殊的Chain(事实上普通Chain也能执行,但不建议使用)
     *
     * @see org.nutz.dao.Chain#addSpecial(String, Object)
     */
    @SuppressWarnings({"rawtypes"})
    public static void insertBySpecialChain(Dao dao, Entity en, String tableName, Chain chain) {
        if (en != null)
            tableName = en.getTableName();
        if (tableName == null)
            throw Lang.makeThrow(DaoException.class, "tableName and en is NULL !!");
        final StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append(" (");
        StringBuilder _value_places = new StringBuilder(" VALUES(");
        final List<Object> values = new ArrayList<Object>();
        final List<ValueAdaptor> adaptors = new ArrayList<ValueAdaptor>();
        Chain head = chain.head();
        while (head != null) {
            String colName = head.name();
            MappingField mf = null;
            if (en != null) {
                mf = en.getField(colName);
                if (mf != null)
                    colName = mf.getColumnNameInSql();
            }
            sql.append(colName);

            if (head.special()) {
                _value_places.append(head.value());
            } else {
                if (en != null)
                    mf = en.getField(head.name());
                _value_places.append("?");
                values.add(head.value());
                ValueAdaptor adaptor = head.adaptor();
                if (adaptor == null) {
                    if (mf != null && mf.getAdaptor() != null)
                        adaptor = mf.getAdaptor();
                    else
                    	adaptor = Jdbcs.getAdaptorBy(head.value());
                }
                adaptors.add(adaptor);
            }

            head = head.next();
            if (head != null) {
                sql.append(", ");
                _value_places.append(", ");
            }
        }
        sql.append(")");
        _value_places.append(")");
        sql.append(_value_places);
        if (log.isDebugEnabled())
            log.debug(sql);
        dao.run(new ConnCallback() {
            public void invoke(Connection conn) throws Exception {
                PreparedStatement ps = conn.prepareStatement(sql.toString());
                try {
                    for (int i = 0; i < values.size(); i++)
                        adaptors.get(i).set(ps, values.get(i), i + 1);
                    ps.execute();
                }
                finally {
                    Daos.safeClose(ps);
                }
            }
        });
    }

    /**
     * 为特定package下带@Table注解的类调用dao.create(XXX.class, force),
     * 批量建表,优先建立带@ManyMany的表
     *
     * @param dao
     *            Dao实例
     * @param packageName
     *            package名称,自动包含子类
     * @param force
     *            如果表存在,是否先删后建
     */
    public static void createTablesInPackage(final Dao dao, String packageName, boolean force) {
        List<Class<?>> list = new ArrayList<Class<?>>();
        for(Class<?> klass: Scans.me().scanPackage(packageName)) {
            if (Mirror.me(klass).getAnnotation(Table.class) != null)
                list.add(klass);
        };
        createTables(dao,list,force);
    }

    /**
     * 为特定package下带@Table注解的类调用dao.create(XXX.class, force), 批量建表
     *
     * @param dao
     *            Dao实例
     * @param oneClzInPackage
     *            使用package中某一个class文件, 可以防止写错pkgName
     * @param force
     *            如果表存在,是否先删后建
     */
    public static void createTablesInPackage(Dao dao, Class<?> oneClzInPackage, boolean force) {
        createTablesInPackage(dao, oneClzInPackage.getPackage().getName(), force);
    }

    /**
     * 为特定package下带@Table注解的类调用dao.create(XXX.class, force),
     * 批量建表,优先建立带@ManyMany的表
     *
     * @param dao
     *            Dao实例
     * @param oneClzInPackage
     *            使用package中某一个class文件, 可以防止写错pkgName
     * @param force
     *            如果表存在,是否先删后建
     * @param filter
     *            定义过滤器排除不需要自动创建的表
     */
    public static void createTablesInPackage(final Dao dao,  Class<?> oneClzInPackage, boolean force,TablesFilter filter) {
        createTablesInPackage(dao, oneClzInPackage.getPackage().getName(), force,filter);
    }
    /**
     * 为特定package下带@Table注解的类调用dao.create(XXX.class, force),
     * 批量建表,优先建立带@ManyMany的表
     *
     * @param dao
     *            Dao实例
     * @param packageName
     *            package名称,自动包含子类
     * @param force
     *            如果表存在,是否先删后建
     * @param filter
     *            定义过滤器排除不需要自动创建的表
     */
    public static void createTablesInPackage(final Dao dao, String packageName, boolean force,TablesFilter filter) {
        List<Class<?>> list = new ArrayList<Class<?>>();
        for(Class<?> klass: Scans.me().scanPackage(packageName)) {
            Table table = Mirror.me(klass).getAnnotation(Table.class);
            if (table != null && filter.match(klass,table))
                list.add(klass);
        }
        createTables(dao,list,force);
    }

    /**
     *
     * 批量建表,优先建立带@ManyMany的表
     *
     * @param dao
     *            Dao实例
     * @param list
     *            需要自动创建的表
     * @param force
     *            如果表存在,是否先删后建
     */
    private static void createTables(final Dao dao, List<Class<?>> list, boolean force){
        Collections.sort(list, new Comparator<Class<?>>() {
            public int compare(Class<?> prev, Class<?> next) {
                int links_prev = dao.getEntity(prev).getLinkFields(null).size();
                int links_next = dao.getEntity(next).getLinkFields(null).size();
                if (links_prev == links_next)
                    return 0;
                return links_prev > links_next ? 1 : -1;
            }

        });
        ArrayList<Exception> es = new ArrayList<Exception>();
        for (Class<?> klass : list)
            try {
                dao.create(klass, force);
            }
            catch (Exception e) {
                es.add(new RuntimeException("class=" + klass.getName(), e));
            }
        if (es.size() > 0) {
            for (Exception exception : es) {
                log.debug(exception.getMessage(), exception);
            }
            throw (RuntimeException)es.get(0);
        }
    }

    private static Class<?>[] iz = new Class<?>[]{Dao.class};

    /**
     * 创建一个带FieldFilter的Dao代理实例. 注意,为避免出错,生成的Dao对象不应该传递到其他方法去.
     *
     * @param dao
     *            原始的Dao实例
     * @param filter
     *            字段过滤器
     * @return 带FieldFilter的Dao代理实例
     */
    public static Dao ext(Dao dao, FieldFilter filter) {
        return ext(dao, filter, null);
    }

    /**
     * 创建一个带TableName的Dao代理实例. 注意,为避免出错,生成的Dao对象不应该传递到其他方法去.
     *
     * @param dao
     *            原始的Dao实例
     * @param tableName
     *            动态表名上下文
     * @return 带TableName的Dao代理实例
     */
    public static Dao ext(Dao dao, Object tableName) {
        return ext(dao, null, tableName);
    }

    /**
     * 同时进行字段过滤和动态表名封装
     *
     * @param dao
     *            Dao实例
     * @param filter
     *            字段过滤
     * @param tableName
     *            动态表名参数
     * @return 封装好的Dao实例
     */
    public static Dao ext(Dao dao, FieldFilter filter, Object tableName) {
        if (tableName == null && filter == null)
            return dao;
        ExtDaoInvocationHandler handler = new ExtDaoInvocationHandler(dao, filter, tableName);
        return (Dao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), iz, handler);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static boolean filterFields(Object obj,
                                       FieldMatcher matcher,
                                       Dao dao,
                                       Callback2<MappingField, Object> callback) {
        if (obj == null)
            return false;
        obj = Lang.first(obj);
        if (obj == null) {
            return false;
        }
        if (obj.getClass() == Class.class) {
            throw Lang.impossible();
        }
        if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) {
            throw Lang.impossible();
        }
        Entity en = dao.getEntity(obj.getClass());
        if (en == null) {
            throw Lang.impossible();
        }

        List<MappingField> mfs = new ArrayList<MappingField>(en.getMappingFields());
        if (matcher != null) {
            Iterator<MappingField> it = mfs.iterator();
            while (it.hasNext()) {
                MappingField mf = it.next();
                if (!matcher.match(mf.getName()))
                    it.remove();
            }
        }
        boolean flag = false;
        for (MappingField mf : mfs) {
            if (matcher == null) {
                Object val = mf.getValue(obj);
                callback.invoke(mf, val);
                flag = true;
                continue;
            }
            if (!matcher.match(mf, obj))
                continue;
            callback.invoke(mf, mf.getValue(obj));
            flag = true;
        }
        return flag;
    }

    /**
     * 为数据表自动增减字段
     *
     * @param dao
     *            Dao实例
     * @param klass
     *            映射Pojo
     * @param add
     *            是否允许添加字段
     * @param del
     *            是否允许删除字段
     * @param checkIndex
     *            是否检查索引
     */
    public static void migration(Dao dao,
                                 final Class<?> klass,
                                 final boolean add,
                                 final boolean del,
                                 boolean checkIndex) {
        migration(dao, klass, add, del, checkIndex, null);
    }

    /**
     * 为数据表自动增减字段
     *
     * @param dao
     *            Dao实例
     * @param klass
     *            映射Pojo
     * @param add
     *            是否允许添加字段
     * @param del
     *            是否允许删除字段
     */
    public static void migration(Dao dao,
                                 final Class<?> klass,
                                 final boolean add,
                                 final boolean del) {
        migration(dao, klass, add, del, false, null);
    }

    /**
     * 为数据表自动增减字段
     *
     * @param dao
     *            Dao实例
     * @param klass
     *            映射Pojo
     * @param add
     *            是否允许添加字段
     * @param del
     *            是否允许删除字段
     * @param tableName
     *            动态表名上下文
     */
    public static void migration(Dao dao,
                                 final Class<?> klass,
                                 final boolean add,
                                 final boolean del,
                                 Object tableName) {
        migration(dao, klass, add, del, false, tableName);
    }

    /**
     * 为数据表自动增减字段
     *
     * @param dao
     *            Dao实例
     * @param klass
     *            映射Pojo
     * @param add
     *            是否允许添加字段
     * @param del
     *            是否允许删除字段
     * @param checkIndex
     *            是否检查索引
     * @param tableName
     *            动态表名上下文
     */
    public static void migration(Dao dao,
                                 final Class<?> klass,
                                 final boolean add,
                                 final boolean del,
                                 final boolean checkIndex,
                                 final Object tableName) {
        migration(dao, dao.getEntity(klass), add, del, checkIndex, tableName);
    }
    public static void migration(Dao dao,
                                 final Entity<?> en,
                                 final boolean add,
                                 final boolean del,
                                 final boolean checkIndex,
                                 final Object tableName) {
        final JdbcExpert expert = dao.getJdbcExpert();
        if (tableName != null && Strings.isNotBlank(tableName.toString())) {
            dao = ext(dao, tableName);
        }
        final List<Sql> sqls = new ArrayList<Sql>();
        final Set<String> _indexs = new HashSet<String>();
        dao.run(new ConnCallback() {
            public void invoke(Connection conn) throws Exception {
                expert.setupEntityField(conn, en);
                Statement stat = null;
                ResultSet rs = null;
                ResultSetMetaData meta = null;
                try {
                    // 获取数据库元信息
                    stat = conn.createStatement();
                    rs = stat.executeQuery("select * from " + en.getTableName() + " where 1 != 1");
                    meta = rs.getMetaData();

                    Set<String> columnNames = new HashSet<String>();
                    int columnCount = meta.getColumnCount();
                    for (int i = 1; i <= columnCount; i++) {
                        columnNames.add(meta.getColumnName(i).toLowerCase());
                    }
                    for (MappingField mf : en.getMappingFields()) {
                        if (mf.isReadonly())
                            continue;
                        String colName = mf.getColumnName();
                        if (columnNames.contains(colName.toLowerCase())) {
                            columnNames.remove(colName.toLowerCase());
                            continue;
                        }
                        if (add) {
                            log.infof("add column[%s] to table[%s]",
                                      mf.getColumnName(),
                                      en.getTableName());
                            sqls.add(expert.createAddColumnSql(en, mf));
                        }
                    }
                    if (del) {
                        for (String colName : columnNames) {
                            log.infof("del column[%s] from table[%s]", colName, en.getTableName());
                            Sql sql = Sqls.create("ALTER table $table DROP column $name");
                            sql.vars().set("table", en.getTableName());
                            sql.vars().set("name", colName);
                            sqls.add(sql);
                        }
                    }
                    // show index from mytable;
                    if (checkIndex)
                        _indexs.addAll(expert.getIndexNames(en, conn));
                }
                catch (SQLException e) {
                    if (log.isDebugEnabled())
                        log.debugf("migration Table '%s' fail!", en.getTableName(), e);
                }
                // Close ResultSet and Statement
                finally {
                    Daos.safeClose(stat, rs);
                }
            }
        });
        // 创建索引
        UpdateIndexSql indexSqls = createIndexs(dao, en, _indexs, tableName);
        if (checkIndex) {
            // 因为已删除的字段的索引是没办法删除的 所以要先处理索引 再处理字段
            Sql[] delIndexSqls = indexSqls.getSqlsDel();
            if (!Lang.isEmptyArray(delIndexSqls)) {
                dao.execute(delIndexSqls);
            }
        }
        for (Sql sql : sqls) {
            dao.execute(sql);
        }
        if (checkIndex) {
            Sql[] addIndexSqls = indexSqls.getSqlsAdd();
            if (!Lang.isEmptyArray(addIndexSqls)) {
                dao.execute(addIndexSqls);
            }
        }
        // 创建关联表
        dao.getJdbcExpert().createRelation(dao, en);
    }

    static class UpdateIndexSql {
        private Sql[] sqlsAdd;
        private Sql[] sqlsDel;

        public Sql[] getSqlsAdd() {
            return sqlsAdd;
        }

        public void setSqlsAdd(Sql[] sqlsAdd) {
            this.sqlsAdd = sqlsAdd;
        }

        public Sql[] getSqlsDel() {
            return sqlsDel;
        }

        public void setSqlsDel(Sql[] sqlsDel) {
            this.sqlsDel = sqlsDel;
        }

    }

    private static UpdateIndexSql createIndexs(Dao dao,
                                               Entity<?> en,
                                               Set<String> indexsHis,
                                               Object t) {
        UpdateIndexSql uis = new UpdateIndexSql();
        List<Sql> sqls = new ArrayList<Sql>();
        List<String> delIndexs = new ArrayList<String>();
        List<EntityIndex> indexs = en.getIndexes();
        for (EntityIndex index : indexs) {
            String indexName = index.getName(en);
            // 索引存在, 不要动
            if (indexsHis.contains(indexName)) {
                indexsHis.remove(indexName);
            }
            // 不存在,则新增
            else {
                sqls.add(dao.getJdbcExpert().createIndexSql(en, index));
            }
        }
        uis.setSqlsAdd(sqls.toArray(new Sql[sqls.size()]));
        // 剩余的,就是要删除的
        Iterator<String> iterator = indexsHis.iterator();
        List<Sql> delSqls = new ArrayList<Sql>();
        while (iterator.hasNext()) {
            String indexName = iterator.next();
            if (delIndexs.contains(indexName) || Lang.equals("PRIMARY", indexName)) {
                continue;
            }
            MappingField mf = en.getColumn(indexName);
            if (mf != null) {
                if (mf.isName())
                    continue;
            }
            if (dao.meta().isSqlServer()) {
                delSqls.add(Sqls.createf("DROP INDEX %s.%s",
                                         getTableName(dao, en, t),
                                         indexName));
            } else {
                delSqls.add(Sqls.createf("ALTER TABLE %s DROP INDEX %s",
                                         getTableName(dao, en, t),
                                         indexName));
            }
        }
        uis.setSqlsDel(delSqls.toArray(new Sql[delSqls.size()]));
        return uis;
    }

    /**
     * 为指定package及旗下package中带@Table注解的Pojo执行migration
     *
     * @param dao
     *            Dao实例
     * @param packageName
     *            指定的package名称
     * @param add
     *            是否允许添加字段
     * @param del
     *            是否允许删除字段
     * @param checkIndex
     *            是否检查索引
     * @param nameTable
     *            动态表名上下文
     */
    public static void migration(Dao dao,
                                 String packageName,
                                 boolean add,
                                 boolean del,
                                 boolean checkIndex,
                                 Object nameTable) {
        for (Class<?> klass : Scans.me().scanPackage(packageName)) {
            if (Mirror.me(klass).getAnnotation(Table.class) != null) {
                migration(dao, klass, add, del, checkIndex, nameTable);
            }
        }
    }

    /**
     * 为指定package及旗下package中带@Table注解的Pojo执行migration
     *
     * @param dao
     *            Dao实例
     * @param packageName
     *            指定的package名称
     * @param add
     *            是否允许添加字段
     * @param del
     *            是否允许删除字段
     * @param nameTable
     *            动态表名上下文
     */
    public static void migration(Dao dao,
                                 String packageName,
                                 boolean add,
                                 boolean del,
                                 Object nameTable) {
        migration(dao, packageName, add, del, true, nameTable);
    }

    /**
     * 为指定package及旗下package中带@Table注解的Pojo执行migration
     *
     * @param dao
     *            Dao实例
     * @param packageName
     *            指定的package名称
     * @param add
     *            是否允许添加字段
     * @param del
     *            是否允许删除字段
     * @param checkIndex
     *            是否检查索引
     */
    public static void migration(Dao dao,
                                 String packageName,
                                 boolean add,
                                 boolean del,
                                 boolean checkIndex) {
        for (Class<?> klass : Scans.me().scanPackage(packageName)) {
            if (Mirror.me(klass).getAnnotation(Table.class) != null) {
                migration(dao, klass, add, del, checkIndex, null);
            }
        }
    }

    /**
     * 为指定package及旗下package中带@Table注解的Pojo执行migration
     *
     * @param dao
     *            Dao实例
     * @param packageName
     *            指定的package名称
     * @param add
     *            是否允许添加字段
     * @param del
     *            是否允许删除字段
     */
    public static void migration(Dao dao, String packageName, boolean add, boolean del) {
        migration(dao, packageName, add, del, true);
    }

    /**
     * 检查分表中是否有字段变化 提示
     *
     * @param dao
     *            Dao实例
     * @param tableName
     *            动态表名上下文
     * @param clsType
     *            映射Pojo
     */
    public static void checkTableColumn(Dao dao, Object tableName, final Class<?> clsType) {
        final NutDao d = (NutDao) dao;
        final JdbcExpert expert = d.getJdbcExpert();
        ext(d, tableName).run(new ConnCallback() {
            public void invoke(Connection conn) throws Exception {
                Entity<?> en = d.getEntity(clsType);
                expert.setupEntityField(conn, en);
            }
        });
    }

    /**
     * 获取动态表的表名
     *
     * @param dao
     *            Dao实例
     * @param klass
     *            映射Pojo
     * @param target
     *            参考对象
     */
    public static String getTableName(Dao dao, Class<?> klass, Object target) {
        return getTableName(dao, dao.getEntity(klass), target);
    }

    /**
     * 获取动态表的表名
     *
     * @param dao
     *            Dao实例
     * @param en
     *            Pojo的数据库实体
     * @param target
     *            参考对象
     */
    public static String getTableName(Dao dao, final Entity<?> en, Object target) {
        if (target == null)
            return en.getTableName();
        final String[] name = new String[1];
        TableName.run(target, new Runnable() {
            public void run() {
                name[0] = en.getTableName();
            }
        });
        return name[0];
    }

    private static SqlFormat sqlFormat = SqlFormat.full;

    /** 获取SQL打印格式 */
    public static SqlFormat getSqlFormat() {
        return sqlFormat;
    }

    /**
     * 设置SQL打印格式
     *
     * @param sqlFormat
     *            SQL打印格式
     */
    public static void setSqlFormat(SqlFormat sqlFormat) {
        Daos.sqlFormat = sqlFormat;
    }

    /** 获取SQL2003关键字 */
    public static Set<String> sql2003Keywords() {
        Set<String> keywords = new HashSet<String>();
        String k = "ADD,ALL,ALLOCATE,ALTER,AND,ANY,ARE,ARRAY,AS,ASENSITIVE,ASYMMETRIC,AT,ATOMIC,AUTHORIZATION,BEGIN,BETWEEN,BIGINT,BINARY,BLOB,BOOLEAN,BOTH,BY,CALL,CALLED,CASCADED,CASE,CAST,CHAR,CHARACTER,CHECK,CLOB,CLOSE,COLLATE,COLUMN,COMMIT,CONDITION,CONNECT,CONSTRAINT,CONTINUE,CORRESPONDING,CREATE,CROSS,CUBE,CURRENT,CURRENT_DATE,CURRENT_DEFAULT_TRANSFORM_GROUP,CURRENT_PATH,CURRENT_ROLE,CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_TRANSFORM_GROUP_FOR_TYPE,CURRENT_USER,CURSOR,CYCLE,DATE,DAY,DEALLOCATE,DEC,DECIMAL,DECLARE,DEFAULT,DELETE,DEREF,DESCRIBE,DETERMINISTIC,DISCONNECT,DISTINCT,DO,DOUBLE,DROP,DYNAMIC,EACH,ELEMENT,ELSE,ELSEIF,END,ESCAPE,EXCEPT,EXEC,EXECUTE,EXISTS,EXIT,EXTERNAL,FALSE,FETCH,FILTER,FLOAT,FOR,FOREIGN,FREE,FROM,FULL,FUNCTION,GET,GLOBAL,GRANT,GROUP,GROUPING,HANDLER,HAVING,HOLD,HOUR,IDENTITY,IF,IMMEDIATE,IN,INDICATOR,INNER,INOUT,INPUT,INSENSITIVE,INSERT,INT,INTEGER,INTERSECT,INTERVAL,INTO,IS,ITERATE,JOIN,LANGUAGE,LARGE,LATERAL,LEADING,LEAVE,LEFT,LIKE,LOCAL,LOCALTIME,LOCALTIMESTAMP,LOOP,MATCH,MEMBER,MERGE,METHOD,MINUTE,MODIFIES,MODULE,MONTH,MULTISET,NATIONAL,NATURAL,NCHAR,NCLOB,NEW,NO,NONE,NOT,NULL,NUMERIC,OF,OLD,ON,ONLY,OPEN,OR,ORDER,OUT,OUTER,OUTPUT,OVER,OVERLAPS,PARAMETER,PARTITION,PRECISION,PREPARE,PROCEDURE,RANGE,READS,REAL,RECURSIVE,REF,REFERENCES,REFERENCING,RELEASE,REPEAT,RESIGNAL,RESULT,RETURN,RETURNS,REVOKE,RIGHT,ROLLBACK,ROLLUP,ROW,ROWS,SAVEPOINT,SCOPE,SCROLL,SEARCH,SECOND,SELECT,SENSITIVE,SESSION_USER,SET,SIGNAL,SIMILAR,SMALLINT,SOME,SPECIFIC,SPECIFICTYPE,SQL,SQLEXCEPTION,SQLSTATE,SQLWARNING,START,STATIC,SUBMULTISET,SYMMETRIC,SYSTEM,SYSTEM_USER,TABLE,TABLESAMPLE,THEN,TIME,TIMESTAMP,TIMEZONE_HOUR,TIMEZONE_MINUTE,TO,TRAILING,TRANSLATION,TREAT,TRIGGER,TRUE,UNDO,UNION,UNIQUE,UNKNOWN,UNNEST,UNTIL,UPDATE,USER,USING,VALUE,VALUES,VARCHAR,VARYING,WHEN,WHENEVER,WHERE,WHILE,WINDOW,WITH,WITHIN,WITHOUT,YEAR";
        for (String keyword : k.split(",")) {
            keywords.add(keyword);
        }
        keywords.remove("VALUE");
        keywords.remove("SQL");
        keywords.remove("YEAR");
        return keywords;
    }

    /** 是否检查字段为数据库的关键字 */
    public static boolean CHECK_COLUMN_NAME_KEYWORD = false;

    /** 是否把字段名用字符包裹来进行关键字逃逸 */
    public static boolean FORCE_WRAP_COLUMN_NAME = false;

    /** 是否把字段名给变成大写 */
    public static boolean FORCE_UPPER_COLUMN_NAME = false;

    public static boolean FORCE_HUMP_COLUMN_NAME = false;

    /** varchar 字段的默认字段长度 */
    public static int DEFAULT_VARCHAR_WIDTH = 128;

    /** Table&View名称生成器 */
    public static interface NameMaker {
        String make(Class<?> klass);
    }
    /** 默认的Table名称生成器 */
    private static NameMaker tableNameMaker = new NameMaker() {
        @Override
        public String make(Class<?> klass) {
            return Strings.lowerWord(klass.getSimpleName(), '_');
        }
    };
    /** 默认的View名称生成器 */
    private static NameMaker viewNameMaker = new NameMaker() {
        @Override
        public String make(Class<?> klass) {
            return Strings.lowerWord(klass.getSimpleName(), '_');
        }
    };

    public static NameMaker getTableNameMaker() {
        return tableNameMaker;
    }

    public static void setTableNameMaker(NameMaker tableNameMaker) {
        Daos.tableNameMaker = tableNameMaker;
    }

    public static NameMaker getViewNameMaker() {
        return viewNameMaker;
    }

    public static void setViewNameMaker(NameMaker viewNameMaker) {
        Daos.viewNameMaker = viewNameMaker;
    }
}

class ExtDaoInvocationHandler implements InvocationHandler {

    protected ExtDaoInvocationHandler(Dao dao, FieldFilter filter, Object tableName) {
        this.dao = dao;
        this.filter = filter;
        this.tableName = tableName;
    }

    public Dao dao;
    public FieldFilter filter;
    public Object tableName;

    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {

        final Molecule<Object> m = new Molecule<Object>() {
            public void run() {
                try {
                    setObj(method.invoke(dao, args));
                }
                catch (Exception e) {
                    throw Lang.wrapThrow(e);
                }
            }
        };
        if (filter != null && tableName != null) {
            TableName.run(tableName, new Runnable() {
                public void run() {
                    filter.run(m);
                }
            });
            return m.getObj();
        }
        if (filter != null)
            filter.run(m);
        else
            TableName.run(tableName, m);
        return m.getObj();
    }
}


package org.nutz.lang.socket;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class SocketActionTable {

    private boolean[] nots;

    private SocketAction[] aary;

    private Pattern[] ptns;

    private Map<String, SocketAction> map;

    SocketActionTable(Map<String, SocketAction> actions) {
        List<String> rl = new ArrayList<String>(actions.size());
        List<SocketAction> al = new ArrayList<SocketAction>(actions.size());
        map = new HashMap<String, SocketAction>();
        // 建立正则式列表
        for (String key : actions.keySet()) {
            // key 为空 ，相当于全部匹配
            if (key == null) {
                rl.add("$:.*");
                al.add(actions.get(key));
            }
            // 通过一个正则表达式匹配动作
            else if (key.startsWith("$:")) {
                rl.add(key.substring(2));
                al.add(actions.get(key));
            }
            // 精确匹配动作
            else {
                map.put(key, actions.get(key));
            }
        }

        // 处理正则式列表中的 "!"
        nots = new boolean[rl.size()];
        Arrays.fill(nots, false);
        aary = new SocketAction[nots.length];
        ptns = new Pattern[nots.length];
        for (int i = 0; i < nots.length; i++) {
            aary[i] = al.get(i);
            if (rl.get(i).startsWith("!")) {
                nots[i] = true;
                ptns[i] = Pattern.compile(rl.get(i).substring(1));
            } else {
                ptns[i] = Pattern.compile(rl.get(i));
            }
        }
    }

    /**
     * 根据输入的行，得到一个动作执行对象
     *
     * @param line
     *            输入的行
     * @return 动作执对象，null 表示没有动作可以匹配这一行
     */
    public SocketAction get(String line) {
        // 是否有精确匹配
        SocketAction sa = map.get(line);
        if (null != sa)
            return sa;

        // 用正则式匹配
        for (int i = 0; i < nots.length; i++) {
            if (ptns[i].matcher(line).find() && !nots[i]) {
                return aary[i];
            } else if (nots[i]) {
                return aary[i];
            }
        }

        // 啥都没有 -_-!
        return null;

    }

}


package org.nutz.mvc.view;

import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.nutz.img.Images;
import org.nutz.lang.Encoding;
import org.nutz.lang.Lang;
import org.nutz.lang.Streams;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.View;

/**
 * 将数据对象直接写入 HTTP 响应
 *

 *
数据对象可以是如下类型:

 *

 *
null - 什么都不做
 *
File - 文件,以下载方法返回,文件名将自动设置
 *
byte[] - 按二进制方式写入HTTP响应流
 *
InputStream - 按二进制方式写入响应流，并关闭 InputStream
 *
char[] - 按文本方式写入HTTP响应流
 *
Reader - 按文本方式写入HTTP响应流，并关闭 Reader
 *
BufferedImage - 按图片方式写入HTTP响应流，并关闭 Reader
 *
默认的 - 直接将对象 toString() 后按文本方式写入HTTP响应流
 *

 *

 *
ContentType 支持几种缩写:

 *

 *
xml - 表示 text/xml
 *
html - 表示 text/html
 *
htm - 表示 text/html
 *
stream - 表示 application/octet-stream
 *
js - 表示 application/javascript
 *
json - 表示 application/json
 *
pdf -- 表示application/pdf
 *
jpeg - 表示 image/jpeg 返回值是BufferedImage对象时自动转二进制流,质量为0.8f
 *
jpg - 表示 image/jpeg 返回值是BufferedImage对象时自动转二进制流,质量为0.8f
 *
png - 表示 image/png 返回值是BufferedImage对象时自动转二进制流
 *
webp - 表示 application/webp 返回值是BufferedImage对象时自动转二进制流
 *
默认的(即 '@Ok("raw")' ) - 将采用 ContentType=text/plain
 *

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

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

    private static final int big4G = Integer.MAX_VALUE;

    public static final boolean DISABLE_RANGE_DOWNLOAD = false; // 禁用断点续传

    protected String contentType;

    protected RawView() {}

    public RawView(String contentType) {
        if (Strings.isBlank(contentType))
            contentType = "text/plain";
        this.contentType = Strings.sNull(contentTypeMap.get(contentType.toLowerCase()), contentType);
    }

    public void render(HttpServletRequest req, HttpServletResponse resp, Object obj)
            throws Throwable {
        // 如果用户自行设置了,那就不要再设置了!
        if (resp.getContentType() == null) {
            if (!Lang.isAndroid
                && obj != null
                && obj instanceof BufferedImage
                && "text/plain".equals(contentType)) {
                contentType = contentTypeMap.get("png");
            }
            resp.setContentType(contentType);
        }
        if (obj == null)
            return;
        // 图片?难道是验证码?
        if (!Lang.isAndroid && obj instanceof BufferedImage) {
            OutputStream out = resp.getOutputStream();
            if (contentType.contains("png"))
                ImageIO.write((BufferedImage) obj, "png", out);
            // @see
            // https://code.google.com/p/webm/source/browse/java/src/main/java/com/google/imageio/?repo=libwebp&name=sandbox%2Fpepijnve%2Fwebp-imageio#imageio%2Fwebp
            else if (contentType.contains("webp"))
                ImageIO.write((BufferedImage) obj, "webp", out);
            else
                Images.writeJpeg((BufferedImage) obj, out, 0.8f);
            return;
        }
        // 文件
        else if (obj instanceof File) {
            File file = (File) obj;
            long fileSz = file.length();
            if (log.isDebugEnabled())
                log.debug("File downloading ... " + file.getAbsolutePath());
            if (!file.exists() || file.isDirectory()) {
                log.debug("File downloading ... Not Exist : " + file.getAbsolutePath());
                resp.sendError(404);
                return;
            }
            if (!resp.containsHeader("Content-Disposition")) {
                String filename = URLEncoder.encode(file.getName(), Encoding.UTF8);
                resp.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
            }

            String rangeStr = req.getHeader("Range");
            OutputStream out = resp.getOutputStream();
            if (DISABLE_RANGE_DOWNLOAD
                || fileSz == 0
                || (rangeStr == null || !rangeStr.startsWith("bytes=") || rangeStr.length() < "bytes=1".length())) {
                resp.setHeader("Content-Length", "" + fileSz);
                Streams.writeAndClose(out, Streams.fileIn(file));
            } else {
                // log.debug("Range Download : " + req.getHeader("Range"));
                List<RangeRange> rs = new ArrayList<RawView.RangeRange>();
                if (!parseRange(rangeStr, rs, fileSz)) {
                    resp.setStatus(416);
                    return;
                }
                // 暂时只实现了单range
                if (rs.size() != 1) {
                    // TODO 完成多range的下载
                    //log.info("multipart/byteranges is NOT support yet");
                    //resp.setStatus(416);
                    rs = rs.subList(0, 1);
                }
                long totolSize = 0;
                for (RangeRange rangeRange : rs) {
                    totolSize += (rangeRange.end - rangeRange.start);
                }
                resp.setStatus(206);
                resp.setHeader("Content-Length", "" + totolSize);
                resp.setHeader("Accept-Ranges", "bytes");

                // 暂时只有单range,so,简单起见吧
                RangeRange rangeRange = rs.get(0);
                resp.setHeader("Content-Range", String.format("bytes %d-%d/%d",
                                                              rangeRange.start,
                                                              rangeRange.end - 1,
                                                              fileSz));
                writeFileRange(file, out, rangeRange);
            }
        }
        // 字节数组
        else if (obj instanceof byte[]) {
            resp.setHeader("Content-Length", "" + ((byte[]) obj).length);
            OutputStream out = resp.getOutputStream();
            Streams.writeAndClose(out, (byte[]) obj);
        }
        // 字符数组
        else if (obj instanceof char[]) {
            Writer writer = resp.getWriter();
            writer.write((char[]) obj);
            writer.flush();
        }
        // 文本流
        else if (obj instanceof Reader) {
            Streams.writeAndClose(resp.getWriter(), (Reader) obj);
        }
        // 二进制流
        else if (obj instanceof InputStream) {
            OutputStream out = resp.getOutputStream();
            Streams.writeAndClose(out, (InputStream) obj);
        }
        // 普通对象
        else {
            byte[] data = String.valueOf(obj).getBytes(Encoding.UTF8);
            resp.setHeader("Content-Length", "" + data.length);
            OutputStream out = resp.getOutputStream();
            Streams.writeAndClose(out, data);
        }
    }

    protected static final Map<String, String> contentTypeMap = new HashMap<String, String>();

    static {
        contentTypeMap.put("xml", "application/xml");
        contentTypeMap.put("html", "text/html");
        contentTypeMap.put("htm", "text/html");
        contentTypeMap.put("stream", "application/octet-stream");
        contentTypeMap.put("js", "application/javascript");
        contentTypeMap.put("json", "application/json");
        contentTypeMap.put("jpg", "image/jpeg");
        contentTypeMap.put("jpeg", "image/jpeg");
        contentTypeMap.put("png", "image/png");
        contentTypeMap.put("webp", "image/webp");
    }

    public static class RangeRange implements Comparable<RangeRange> {
        public RangeRange(long start, long end) {
            this.start = start;
            this.end = end;
        }

        public long start;
        public long end = -1;

        public String toString(int maxLen) {
            return String.format("bytes %d-%d/%d", start, end - 1, maxLen);
        }

        public boolean equals(Object obj) {
            if (obj == null || !(obj instanceof RangeRange))
                return false;
            return this.start == ((RangeRange)obj).start && this.end == ((RangeRange)obj).end;
        }

        public int compareTo(RangeRange other) {
            if (this.start > other.start)
                return 1;
            if (this.start < other.start)
                return -1;
            return 0;
        }
    }

    public static final boolean parseRange(String rangeStr, List<RangeRange> rs, long maxSize) {
        rangeStr = rangeStr.substring("bytes=".length());
        String[] ranges = rangeStr.split(",");
        for (String range : ranges) {
            if (range == null || Strings.isBlank(range)) {
                log.debug("Bad Range -->    " + rangeStr);
                return false;
            }
            range = range.trim();
            try {
                // 首先是从后往前算的 bytes=-100 取最后100个字节
                if (range.startsWith("-")) {

                    // 注意,这里是负数
                    long end = Long.parseLong(range);
                    long start = maxSize + end;
                    if (start < 0) {
                        log.debug("Bad Range -->    " + rangeStr);
                        return false;
                    }
                    rs.add(new RangeRange(start, maxSize));
                    continue;
                }

                // 然后就是从开头到最后 bytes=1024-
                if (range.endsWith("-")) {
                    // 注意,这里是负数
                    long start = Long.parseLong(range.substring(0, range.length() - 1));
                    if (start < 0) {
                        log.debug("Bad Range -->    " + rangeStr);
                        return false;
                    }
                    rs.add(new RangeRange(start, maxSize));
                    continue;
                }

                // 哦也,是最标准的有头有尾?
                if (range.contains("-")) {
                    String[] tmp = range.split("-");
                    long start = Long.parseLong(tmp[0]);
                    long end = Long.parseLong(tmp[1]);
                    if (start > end) {
                        log.debug("Bad Range -->    " + rangeStr);
                        return false;
                    }
                    rs.add(new RangeRange(start, end + 1)); // 这里需要调查一下
                } else {
                    // 操!! 单个字节?!!
                    long start = Long.parseLong(range);
                    rs.add(new RangeRange(start, start + 1));
                }
            }
            catch (Throwable e) {
                log.debug("Bad Range -->    " + rangeStr, e);
                return false;
            }
        }
        if (rs.size() > 1)
            Collections.sort(rs);
        return !rs.isEmpty();
    }

    public static void writeDownloadRange(DataInputStream in,
                                          OutputStream out,
                                          RangeRange rangeRange) {
        writeDownloadRange(in, out, rangeRange, null);
    }

    public static void writeDownloadRange(DataInputStream in,
                                          OutputStream out,
                                          RangeRange rangeRange,
                                          RangeRange preRangeRange) {
        try {
            if (rangeRange.start > 0) {
                long start = rangeRange.start;
                if (preRangeRange != null)
                    start -= preRangeRange.end;
                while (start > 0) {
                    if (start > big4G) {
                        start -= big4G;
                        in.skipBytes(big4G);
                    } else {
                        in.skipBytes((int) start);
                        break;
                    }
                }
            }
            byte[] buf = new byte[8192];
            long pos = rangeRange.start;
            int len = 0;
            while (pos < rangeRange.end) {
                if (rangeRange.end - pos > 8192) {
                    len = in.read(buf);
                } else {
                    len = in.read(buf, 0, (int) (rangeRange.end - pos));
                }
                if (len == -1) {// 有时候,非常巧合的,文件已经读取完,就悲剧开始了...
                    break;
                }
                if (len > 0) {
                    out.write(buf, 0, len);
                    pos += len;
                }
            }
            out.flush();
        }
        catch (Throwable e) {
            throw Lang.wrapThrow(e);
        }
    }

    public static void writeFileRange(File file, OutputStream out, RangeRange rangeRange) {
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(file);
            DataInputStream in = new DataInputStream(fin);
            writeDownloadRange(in, out, rangeRange);
        }
        catch (Throwable e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Streams.safeClose(fin);
        }
    }
}