/***
 * 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.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.nutz.repo.org.objectweb.asm.ClassVisitor;
import org.nutz.repo.org.objectweb.asm.Handle;
import org.nutz.repo.org.objectweb.asm.Label;
import org.nutz.repo.org.objectweb.asm.MethodVisitor;
import org.nutz.repo.org.objectweb.asm.Opcodes;
import org.nutz.repo.org.objectweb.asm.Type;

/**
 * A {@link org.objectweb.asm.MethodVisitor} with convenient methods to generate
 * code. For example, using this adapter, the class below
 *
 *

 * public class Example {
 *     public static void main(String[] args) {
 *         System.out.println("Hello world!");
 *     }
 * }
 *

 *
 * can be generated as follows:
 *
 *

 * ClassWriter cw = new ClassWriter(true);
 * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
 *
 * Method m = Method.getMethod("void <init> ()");
 * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
 * mg.loadThis();
 * mg.invokeConstructor(Type.getType(Object.class), m);
 * mg.returnValue();
 * mg.endMethod();
 *
 * m = Method.getMethod("void main (String[])");
 * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
 * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
 * mg.push("Hello world!");
 * mg.invokeVirtual(Type.getType(PrintStream.class),
 *         Method.getMethod("void println (String)"));
 * mg.returnValue();
 * mg.endMethod();
 *
 * cw.visitEnd();
 *

 *
 * @author Juozas Baliuka
 * @author Chris Nokleberg
 * @author Eric Bruneton
 * @author Prashant Deva
 */
public class GeneratorAdapter extends LocalVariablesSorter {

    private static final String CLDESC = "Ljava/lang/Class;";

    private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");

    private static final Type BOOLEAN_TYPE = Type
            .getObjectType("java/lang/Boolean");

    private static final Type SHORT_TYPE = Type
            .getObjectType("java/lang/Short");

    private static final Type CHARACTER_TYPE = Type
            .getObjectType("java/lang/Character");

    private static final Type INTEGER_TYPE = Type
            .getObjectType("java/lang/Integer");

    private static final Type FLOAT_TYPE = Type
            .getObjectType("java/lang/Float");

    private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long");

    private static final Type DOUBLE_TYPE = Type
            .getObjectType("java/lang/Double");

    private static final Type NUMBER_TYPE = Type
            .getObjectType("java/lang/Number");

    private static final Type OBJECT_TYPE = Type
            .getObjectType("java/lang/Object");

    private static final Method BOOLEAN_VALUE = Method
            .getMethod("boolean booleanValue()");

    private static final Method CHAR_VALUE = Method
            .getMethod("char charValue()");

    private static final Method INT_VALUE = Method.getMethod("int intValue()");

    private static final Method FLOAT_VALUE = Method
            .getMethod("float floatValue()");

    private static final Method LONG_VALUE = Method
            .getMethod("long longValue()");

    private static final Method DOUBLE_VALUE = Method
            .getMethod("double doubleValue()");

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int ADD = Opcodes.IADD;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int SUB = Opcodes.ISUB;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int MUL = Opcodes.IMUL;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int DIV = Opcodes.IDIV;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int REM = Opcodes.IREM;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int NEG = Opcodes.INEG;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int SHL = Opcodes.ISHL;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int SHR = Opcodes.ISHR;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int USHR = Opcodes.IUSHR;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int AND = Opcodes.IAND;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int OR = Opcodes.IOR;

    /**
     * Constant for the {@link #math math} method.
     */
    public static final int XOR = Opcodes.IXOR;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public static final int EQ = Opcodes.IFEQ;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public static final int NE = Opcodes.IFNE;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public static final int LT = Opcodes.IFLT;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public static final int GE = Opcodes.IFGE;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public static final int GT = Opcodes.IFGT;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public static final int LE = Opcodes.IFLE;

    /**
     * Access flags of the method visited by this adapter.
     */
    private final int access;

    /**
     * Return type of the method visited by this adapter.
     */
    private final Type returnType;

    /**
     * Argument types of the method visited by this adapter.
     */
    private final Type[] argumentTypes;

    /**
     * Types of the local variables of the method visited by this adapter.
     */
    private final List<Type> localTypes = new ArrayList<Type>();

    /**
     * Creates a new {@link GeneratorAdapter}. Subclasses must not use this
     * constructor. Instead, they must use the
     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
     * version.
     *
     * @param mv
     *            the method visitor to which this adapter delegates calls.
     * @param access
     *            the method's access flags (see {@link Opcodes}).
     * @param name
     *            the method's name.
     * @param desc
     *            the method's descriptor (see {@link Type Type}).
     * @throws IllegalStateException
     *             If a subclass calls this constructor.
     */
    public GeneratorAdapter(final MethodVisitor mv, final int access,
            final String name, final String desc) {
        this(Opcodes.ASM5, mv, access, name, desc);
        if (getClass() != GeneratorAdapter.class) {
            throw new IllegalStateException();
        }
    }

    /**
     * Creates a new {@link GeneratorAdapter}.
     *
     * @param api
     *            the ASM API version implemented by this visitor. Must be one
     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
     * @param mv
     *            the method visitor to which this adapter delegates calls.
     * @param access
     *            the method's access flags (see {@link Opcodes}).
     * @param name
     *            the method's name.
     * @param desc
     *            the method's descriptor (see {@link Type Type}).
     */
    protected GeneratorAdapter(final int api, final MethodVisitor mv,
            final int access, final String name, final String desc) {
        super(api, access, desc, mv);
        this.access = access;
        this.returnType = Type.getReturnType(desc);
        this.argumentTypes = Type.getArgumentTypes(desc);
    }

    /**
     * Creates a new {@link GeneratorAdapter}. Subclasses must not use this
     * constructor. Instead, they must use the
     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
     * version.
     *
     * @param access
     *            access flags of the adapted method.
     * @param method
     *            the adapted method.
     * @param mv
     *            the method visitor to which this adapter delegates calls.
     */
    public GeneratorAdapter(final int access, final Method method,
            final MethodVisitor mv) {
        this(mv, access, null, method.getDescriptor());
    }

    /**
     * Creates a new {@link GeneratorAdapter}. Subclasses must not use this
     * constructor. Instead, they must use the
     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
     * version.
     *
     * @param access
     *            access flags of the adapted method.
     * @param method
     *            the adapted method.
     * @param signature
     *            the signature of the adapted method (may be <tt>null</tt>).
     * @param exceptions
     *            the exceptions thrown by the adapted method (may be
     *            <tt>null</tt>).
     * @param cv
     *            the class visitor to which this adapter delegates calls.
     */
    public GeneratorAdapter(final int access, final Method method,
            final String signature, final Type[] exceptions,
            final ClassVisitor cv) {
        this(access, method, cv
                .visitMethod(access, method.getName(), method.getDescriptor(),
                        signature, getInternalNames(exceptions)));
    }

    /**
     * Returns the internal names of the given types.
     *
     * @param types
     *            a set of types.
     * @return the internal names of the given types.
     */
    private static String[] getInternalNames(final Type[] types) {
        if (types == null) {
            return null;
        }
        String[] names = new String[types.length];
        for (int i = 0; i < names.length; ++i) {
            names[i] = types[i].getInternalName();
        }
        return names;
    }

    // ------------------------------------------------------------------------
    // Instructions to push constants on the stack
    // ------------------------------------------------------------------------

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value
     *            the value to be pushed on the stack.
     */
    public void push(final boolean value) {
        push(value ? 1 : 0);
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value
     *            the value to be pushed on the stack.
     */
    public void push(final int value) {
        if (value >= -1 && value <= 5) {
            mv.visitInsn(Opcodes.ICONST_0 + value);
        } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
            mv.visitIntInsn(Opcodes.BIPUSH, value);
        } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
            mv.visitIntInsn(Opcodes.SIPUSH, value);
        } else {
            mv.visitLdcInsn(value);
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value
     *            the value to be pushed on the stack.
     */
    public void push(final long value) {
        if (value == 0L || value == 1L) {
            mv.visitInsn(Opcodes.LCONST_0 + (int) value);
        } else {
            mv.visitLdcInsn(value);
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value
     *            the value to be pushed on the stack.
     */
    public void push(final float value) {
        int bits = Float.floatToIntBits(value);
        if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
            mv.visitInsn(Opcodes.FCONST_0 + (int) value);
        } else {
            mv.visitLdcInsn(value);
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value
     *            the value to be pushed on the stack.
     */
    public void push(final double value) {
        long bits = Double.doubleToLongBits(value);
        if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
            mv.visitInsn(Opcodes.DCONST_0 + (int) value);
        } else {
            mv.visitLdcInsn(value);
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value
     *            the value to be pushed on the stack. May be <tt>null</tt>.
     */
    public void push(final String value) {
        if (value == null) {
            mv.visitInsn(Opcodes.ACONST_NULL);
        } else {
            mv.visitLdcInsn(value);
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value
     *            the value to be pushed on the stack.
     */
    public void push(final Type value) {
        if (value == null) {
            mv.visitInsn(Opcodes.ACONST_NULL);
        } else {
            switch (value.getSort()) {
            case Type.BOOLEAN:
                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean",
                        "TYPE", CLDESC);
                break;
            case Type.CHAR:
                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character",
                        "TYPE", CLDESC);
                break;
            case Type.BYTE:
                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE",
                        CLDESC);
                break;
            case Type.SHORT:
                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE",
                        CLDESC);
                break;
            case Type.INT:
                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer",
                        "TYPE", CLDESC);
                break;
            case Type.FLOAT:
                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE",
                        CLDESC);
                break;
            case Type.LONG:
                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE",
                        CLDESC);
                break;
            case Type.DOUBLE:
                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double",
                        "TYPE", CLDESC);
                break;
            default:
                mv.visitLdcInsn(value);
            }
        }
    }

    /**
     * Generates the instruction to push a handle on the stack.
     *
     * @param handle
     *            the handle to be pushed on the stack.
     */
    public void push(final Handle handle) {
        mv.visitLdcInsn(handle);
    }

    // ------------------------------------------------------------------------
    // Instructions to load and store method arguments
    // ------------------------------------------------------------------------

    /**
     * Returns the index of the given method argument in the frame's local
     * variables array.
     *
     * @param arg
     *            the index of a method argument.
     * @return the index of the given method argument in the frame's local
     *         variables array.
     */
    private int getArgIndex(final int arg) {
        int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;
        for (int i = 0; i < arg; i++) {
            index += argumentTypes[i].getSize();
        }
        return index;
    }

    /**
     * Generates the instruction to push a local variable on the stack.
     *
     * @param type
     *            the type of the local variable to be loaded.
     * @param index
     *            an index in the frame's local variables array.
     */
    private void loadInsn(final Type type, final int index) {
        mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
    }

    /**
     * Generates the instruction to store the top stack value in a local
     * variable.
     *
     * @param type
     *            the type of the local variable to be stored.
     * @param index
     *            an index in the frame's local variables array.
     */
    private void storeInsn(final Type type, final int index) {
        mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);
    }

    /**
     * Generates the instruction to load 'this' on the stack.
     */
    public void loadThis() {
        if ((access & Opcodes.ACC_STATIC) != 0) {
            throw new IllegalStateException(
                    "no 'this' pointer within static method");
        }
        mv.visitVarInsn(Opcodes.ALOAD, 0);
    }

    /**
     * Generates the instruction to load the given method argument on the stack.
     *
     * @param arg
     *            the index of a method argument.
     */
    public void loadArg(final int arg) {
        loadInsn(argumentTypes[arg], getArgIndex(arg));
    }

    /**
     * Generates the instructions to load the given method arguments on the
     * stack.
     *
     * @param arg
     *            the index of the first method argument to be loaded.
     * @param count
     *            the number of method arguments to be loaded.
     */
    public void loadArgs(final int arg, final int count) {
        int index = getArgIndex(arg);
        for (int i = 0; i < count; ++i) {
            Type t = argumentTypes[arg + i];
            loadInsn(t, index);
            index += t.getSize();
        }
    }

    /**
     * Generates the instructions to load all the method arguments on the stack.
     */
    public void loadArgs() {
        loadArgs(0, argumentTypes.length);
    }

    /**
     * Generates the instructions to load all the method arguments on the stack,
     * as a single object array.
     */
    public void loadArgArray() {
        push(argumentTypes.length);
        newArray(OBJECT_TYPE);
        for (int i = 0; i < argumentTypes.length; i++) {
            dup();
            push(i);
            loadArg(i);
            box(argumentTypes[i]);
            arrayStore(OBJECT_TYPE);
        }
    }

    /**
     * Generates the instruction to store the top stack value in the given
     * method argument.
     *
     * @param arg
     *            the index of a method argument.
     */
    public void storeArg(final int arg) {
        storeInsn(argumentTypes[arg], getArgIndex(arg));
    }

    // ------------------------------------------------------------------------
    // Instructions to load and store local variables
    // ------------------------------------------------------------------------

    /**
     * Returns the type of the given local variable.
     *
     * @param local
     *            a local variable identifier, as returned by
     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
     * @return the type of the given local variable.
     */
    public Type getLocalType(final int local) {
        return localTypes.get(local - firstLocal);
    }

    @Override
    protected void setLocalType(final int local, final Type type) {
        int index = local - firstLocal;
        while (localTypes.size() < index + 1) {
            localTypes.add(null);
        }
        localTypes.set(index, type);
    }

    /**
     * Generates the instruction to load the given local variable on the stack.
     *
     * @param local
     *            a local variable identifier, as returned by
     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
     */
    public void loadLocal(final int local) {
        loadInsn(getLocalType(local), local);
    }

    /**
     * Generates the instruction to load the given local variable on the stack.
     *
     * @param local
     *            a local variable identifier, as returned by
     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
     * @param type
     *            the type of this local variable.
     */
    public void loadLocal(final int local, final Type type) {
        setLocalType(local, type);
        loadInsn(type, local);
    }

    /**
     * Generates the instruction to store the top stack value in the given local
     * variable.
     *
     * @param local
     *            a local variable identifier, as returned by
     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
     */
    public void storeLocal(final int local) {
        storeInsn(getLocalType(local), local);
    }

    /**
     * Generates the instruction to store the top stack value in the given local
     * variable.
     *
     * @param local
     *            a local variable identifier, as returned by
     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
     * @param type
     *            the type of this local variable.
     */
    public void storeLocal(final int local, final Type type) {
        setLocalType(local, type);
        storeInsn(type, local);
    }

    /**
     * Generates the instruction to load an element from an array.
     *
     * @param type
     *            the type of the array element to be loaded.
     */
    public void arrayLoad(final Type type) {
        mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
    }

    /**
     * Generates the instruction to store an element in an array.
     *
     * @param type
     *            the type of the array element to be stored.
     */
    public void arrayStore(final Type type) {
        mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
    }

    // ------------------------------------------------------------------------
    // Instructions to manage the stack
    // ------------------------------------------------------------------------

    /**
     * Generates a POP instruction.
     */
    public void pop() {
        mv.visitInsn(Opcodes.POP);
    }

    /**
     * Generates a POP2 instruction.
     */
    public void pop2() {
        mv.visitInsn(Opcodes.POP2);
    }

    /**
     * Generates a DUP instruction.
     */
    public void dup() {
        mv.visitInsn(Opcodes.DUP);
    }

    /**
     * Generates a DUP2 instruction.
     */
    public void dup2() {
        mv.visitInsn(Opcodes.DUP2);
    }

    /**
     * Generates a DUP_X1 instruction.
     */
    public void dupX1() {
        mv.visitInsn(Opcodes.DUP_X1);
    }

    /**
     * Generates a DUP_X2 instruction.
     */
    public void dupX2() {
        mv.visitInsn(Opcodes.DUP_X2);
    }

    /**
     * Generates a DUP2_X1 instruction.
     */
    public void dup2X1() {
        mv.visitInsn(Opcodes.DUP2_X1);
    }

    /**
     * Generates a DUP2_X2 instruction.
     */
    public void dup2X2() {
        mv.visitInsn(Opcodes.DUP2_X2);
    }

    /**
     * Generates a SWAP instruction.
     */
    public void swap() {
        mv.visitInsn(Opcodes.SWAP);
    }

    /**
     * Generates the instructions to swap the top two stack values.
     *
     * @param prev
     *            type of the top - 1 stack value.
     * @param type
     *            type of the top stack value.
     */
    public void swap(final Type prev, final Type type) {
        if (type.getSize() == 1) {
            if (prev.getSize() == 1) {
                swap(); // same as dupX1(), pop();
            } else {
                dupX2();
                pop();
            }
        } else {
            if (prev.getSize() == 1) {
                dup2X1();
                pop2();
            } else {
                dup2X2();
                pop2();
            }
        }
    }

    // ------------------------------------------------------------------------
    // Instructions to do mathematical and logical operations
    // ------------------------------------------------------------------------

    /**
     * Generates the instruction to do the specified mathematical or logical
     * operation.
     *
     * @param op
     *            a mathematical or logical operation. Must be one of ADD, SUB,
     *            MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
     * @param type
     *            the type of the operand(s) for this operation.
     */
    public void math(final int op, final Type type) {
        mv.visitInsn(type.getOpcode(op));
    }

    /**
     * Generates the instructions to compute the bitwise negation of the top
     * stack value.
     */
    public void not() {
        mv.visitInsn(Opcodes.ICONST_1);
        mv.visitInsn(Opcodes.IXOR);
    }

    /**
     * Generates the instruction to increment the given local variable.
     *
     * @param local
     *            the local variable to be incremented.
     * @param amount
     *            the amount by which the local variable must be incremented.
     */
    public void iinc(final int local, final int amount) {
        mv.visitIincInsn(local, amount);
    }

    /**
     * Generates the instructions to cast a numerical value from one type to
     * another.
     *
     * @param from
     *            the type of the top stack value
     * @param to
     *            the type into which this value must be cast.
     */
    public void cast(final Type from, final Type to) {
        if (from != to) {
            if (from == Type.DOUBLE_TYPE) {
                if (to == Type.FLOAT_TYPE) {
                    mv.visitInsn(Opcodes.D2F);
                } else if (to == Type.LONG_TYPE) {
                    mv.visitInsn(Opcodes.D2L);
                } else {
                    mv.visitInsn(Opcodes.D2I);
                    cast(Type.INT_TYPE, to);
                }
            } else if (from == Type.FLOAT_TYPE) {
                if (to == Type.DOUBLE_TYPE) {
                    mv.visitInsn(Opcodes.F2D);
                } else if (to == Type.LONG_TYPE) {
                    mv.visitInsn(Opcodes.F2L);
                } else {
                    mv.visitInsn(Opcodes.F2I);
                    cast(Type.INT_TYPE, to);
                }
            } else if (from == Type.LONG_TYPE) {
                if (to == Type.DOUBLE_TYPE) {
                    mv.visitInsn(Opcodes.L2D);
                } else if (to == Type.FLOAT_TYPE) {
                    mv.visitInsn(Opcodes.L2F);
                } else {
                    mv.visitInsn(Opcodes.L2I);
                    cast(Type.INT_TYPE, to);
                }
            } else {
                if (to == Type.BYTE_TYPE) {
                    mv.visitInsn(Opcodes.I2B);
                } else if (to == Type.CHAR_TYPE) {
                    mv.visitInsn(Opcodes.I2C);
                } else if (to == Type.DOUBLE_TYPE) {
                    mv.visitInsn(Opcodes.I2D);
                } else if (to == Type.FLOAT_TYPE) {
                    mv.visitInsn(Opcodes.I2F);
                } else if (to == Type.LONG_TYPE) {
                    mv.visitInsn(Opcodes.I2L);
                } else if (to == Type.SHORT_TYPE) {
                    mv.visitInsn(Opcodes.I2S);
                }
            }
        }
    }

    // ------------------------------------------------------------------------
    // Instructions to do boxing and unboxing operations
    // ------------------------------------------------------------------------

    private static Type getBoxedType(final Type type) {
        switch (type.getSort()) {
        case Type.BYTE:
            return BYTE_TYPE;
        case Type.BOOLEAN:
            return BOOLEAN_TYPE;
        case Type.SHORT:
            return SHORT_TYPE;
        case Type.CHAR:
            return CHARACTER_TYPE;
        case Type.INT:
            return INTEGER_TYPE;
        case Type.FLOAT:
            return FLOAT_TYPE;
        case Type.LONG:
            return LONG_TYPE;
        case Type.DOUBLE:
            return DOUBLE_TYPE;
        }
        return type;
    }

    /**
     * Generates the instructions to box the top stack value. This value is
     * replaced by its boxed equivalent on top of the stack.
     *
     * @param type
     *            the type of the top stack value.
     */
    public void box(final Type type) {
        if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
            return;
        }
        if (type == Type.VOID_TYPE) {
            push((String) null);
        } else {
            Type boxed = getBoxedType(type);
            newInstance(boxed);
            if (type.getSize() == 2) {
                // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
                dupX2();
                dupX2();
                pop();
            } else {
                // p -> po -> opo -> oop -> o
                dupX1();
                swap();
            }
            invokeConstructor(boxed, new Method("<init>", Type.VOID_TYPE,
                    new Type[] { type }));
        }
    }

    /**
     * Generates the instructions to box the top stack value using Java 5's
     * valueOf() method. This value is replaced by its boxed equivalent on top
     * of the stack.
     *
     * @param type
     *            the type of the top stack value.
     */
    public void valueOf(final Type type) {
        if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
            return;
        }
        if (type == Type.VOID_TYPE) {
            push((String) null);
        } else {
            Type boxed = getBoxedType(type);
            invokeStatic(boxed, new Method("valueOf", boxed,
                    new Type[] { type }));
        }
    }

    /**
     * Generates the instructions to unbox the top stack value. This value is
     * replaced by its unboxed equivalent on top of the stack.
     *
     * @param type
     *            the type of the top stack value.
     */
    public void unbox(final Type type) {
        Type t = NUMBER_TYPE;
        Method sig = null;
        switch (type.getSort()) {
        case Type.VOID:
            return;
        case Type.CHAR:
            t = CHARACTER_TYPE;
            sig = CHAR_VALUE;
            break;
        case Type.BOOLEAN:
            t = BOOLEAN_TYPE;
            sig = BOOLEAN_VALUE;
            break;
        case Type.DOUBLE:
            sig = DOUBLE_VALUE;
            break;
        case Type.FLOAT:
            sig = FLOAT_VALUE;
            break;
        case Type.LONG:
            sig = LONG_VALUE;
            break;
        case Type.INT:
        case Type.SHORT:
        case Type.BYTE:
            sig = INT_VALUE;
        }
        if (sig == null) {
            checkCast(type);
        } else {
            checkCast(t);
            invokeVirtual(t, sig);
        }
    }

    // ------------------------------------------------------------------------
    // Instructions to jump to other instructions
    // ------------------------------------------------------------------------

    /**
     * Creates a new {@link Label}.
     *
     * @return a new {@link Label}.
     */
    public Label newLabel() {
        return new Label();
    }

    /**
     * Marks the current code position with the given label.
     *
     * @param label
     *            a label.
     */
    public void mark(final Label label) {
        mv.visitLabel(label);
    }

    /**
     * Marks the current code position with a new label.
     *
     * @return the label that was created to mark the current code position.
     */
    public Label mark() {
        Label label = new Label();
        mv.visitLabel(label);
        return label;
    }

    /**
     * Generates the instructions to jump to a label based on the comparison of
     * the top two stack values.
     *
     * @param type
     *            the type of the top two stack values.
     * @param mode
     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
     *            LE.
     * @param label
     *            where to jump if the comparison result is <tt>true</tt>.
     */
    public void ifCmp(final Type type, final int mode, final Label label) {
        switch (type.getSort()) {
        case Type.LONG:
            mv.visitInsn(Opcodes.LCMP);
            break;
        case Type.DOUBLE:
            mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL
                    : Opcodes.DCMPG);
            break;
        case Type.FLOAT:
            mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL
                    : Opcodes.FCMPG);
            break;
        case Type.ARRAY:
        case Type.OBJECT:
            switch (mode) {
            case EQ:
                mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
                return;
            case NE:
                mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
                return;
            }
            throw new IllegalArgumentException("Bad comparison for type "
                    + type);
        default:
            int intOp = -1;
            switch (mode) {
            case EQ:
                intOp = Opcodes.IF_ICMPEQ;
                break;
            case NE:
                intOp = Opcodes.IF_ICMPNE;
                break;
            case GE:
                intOp = Opcodes.IF_ICMPGE;
                break;
            case LT:
                intOp = Opcodes.IF_ICMPLT;
                break;
            case LE:
                intOp = Opcodes.IF_ICMPLE;
                break;
            case GT:
                intOp = Opcodes.IF_ICMPGT;
                break;
            }
            mv.visitJumpInsn(intOp, label);
            return;
        }
        mv.visitJumpInsn(mode, label);
    }

    /**
     * Generates the instructions to jump to a label based on the comparison of
     * the top two integer stack values.
     *
     * @param mode
     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
     *            LE.
     * @param label
     *            where to jump if the comparison result is <tt>true</tt>.
     */
    public void ifICmp(final int mode, final Label label) {
        ifCmp(Type.INT_TYPE, mode, label);
    }

    /**
     * Generates the instructions to jump to a label based on the comparison of
     * the top integer stack value with zero.
     *
     * @param mode
     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
     *            LE.
     * @param label
     *            where to jump if the comparison result is <tt>true</tt>.
     */
    public void ifZCmp(final int mode, final Label label) {
        mv.visitJumpInsn(mode, label);
    }

    /**
     * Generates the instruction to jump to the given label if the top stack
     * value is null.
     *
     * @param label
     *            where to jump if the condition is <tt>true</tt>.
     */
    public void ifNull(final Label label) {
        mv.visitJumpInsn(Opcodes.IFNULL, label);
    }

    /**
     * Generates the instruction to jump to the given label if the top stack
     * value is not null.
     *
     * @param label
     *            where to jump if the condition is <tt>true</tt>.
     */
    public void ifNonNull(final Label label) {
        mv.visitJumpInsn(Opcodes.IFNONNULL, label);
    }

    /**
     * Generates the instruction to jump to the given label.
     *
     * @param label
     *            where to jump if the condition is <tt>true</tt>.
     */
    public void goTo(final Label label) {
        mv.visitJumpInsn(Opcodes.GOTO, label);
    }

    /**
     * Generates a RET instruction.
     *
     * @param local
     *            a local variable identifier, as returned by
     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
     */
    public void ret(final int local) {
        mv.visitVarInsn(Opcodes.RET, local);
    }

    /**
     * Generates the instructions for a switch statement.
     *
     * @param keys
     *            the switch case keys.
     * @param generator
     *            a generator to generate the code for the switch cases.
     */
    public void tableSwitch(final int[] keys,
            final TableSwitchGenerator generator) {
        float density;
        if (keys.length == 0) {
            density = 0;
        } else {
            density = (float) keys.length
                    / (keys[keys.length - 1] - keys[0] + 1);
        }
        tableSwitch(keys, generator, density >= 0.5f);
    }

    /**
     * Generates the instructions for a switch statement.
     *
     * @param keys
     *            the switch case keys.
     * @param generator
     *            a generator to generate the code for the switch cases.
     * @param useTable
     *            <tt>true</tt> to use a TABLESWITCH instruction, or
     *            <tt>false</tt> to use a LOOKUPSWITCH instruction.
     */
    public void tableSwitch(final int[] keys,
            final TableSwitchGenerator generator, final boolean useTable) {
        for (int i = 1; i < keys.length; ++i) {
            if (keys[i] < keys[i - 1]) {
                throw new IllegalArgumentException(
                        "keys must be sorted ascending");
            }
        }
        Label def = newLabel();
        Label end = newLabel();
        if (keys.length > 0) {
            int len = keys.length;
            int min = keys[0];
            int max = keys[len - 1];
            int range = max - min + 1;
            if (useTable) {
                Label[] labels = new Label[range];
                Arrays.fill(labels, def);
                for (int i = 0; i < len; ++i) {
                    labels[keys[i] - min] = newLabel();
                }
                mv.visitTableSwitchInsn(min, max, def, labels);
                for (int i = 0; i < range; ++i) {
                    Label label = labels[i];
                    if (label != def) {
                        mark(label);
                        generator.generateCase(i + min, end);
                    }
                }
            } else {
                Label[] labels = new Label[len];
                for (int i = 0; i < len; ++i) {
                    labels[i] = newLabel();
                }
                mv.visitLookupSwitchInsn(def, keys, labels);
                for (int i = 0; i < len; ++i) {
                    mark(labels[i]);
                    generator.generateCase(keys[i], end);
                }
            }
        }
        mark(def);
        generator.generateDefault();
        mark(end);
    }

    /**
     * Generates the instruction to return the top stack value to the caller.
     */
    public void returnValue() {
        mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
    }

    // ------------------------------------------------------------------------
    // Instructions to load and store fields
    // ------------------------------------------------------------------------

    /**
     * Generates a get field or set field instruction.
     *
     * @param opcode
     *            the instruction's opcode.
     * @param ownerType
     *            the class in which the field is defined.
     * @param name
     *            the name of the field.
     * @param fieldType
     *            the type of the field.
     */
    private void fieldInsn(final int opcode, final Type ownerType,
            final String name, final Type fieldType) {
        mv.visitFieldInsn(opcode, ownerType.getInternalName(), name,
                fieldType.getDescriptor());
    }

    /**
     * Generates the instruction to push the value of a static field on the
     * stack.
     *
     * @param owner
     *            the class in which the field is defined.
     * @param name
     *            the name of the field.
     * @param type
     *            the type of the field.
     */
    public void getStatic(final Type owner, final String name, final Type type) {
        fieldInsn(Opcodes.GETSTATIC, owner, name, type);
    }

    /**
     * Generates the instruction to store the top stack value in a static field.
     *
     * @param owner
     *            the class in which the field is defined.
     * @param name
     *            the name of the field.
     * @param type
     *            the type of the field.
     */
    public void putStatic(final Type owner, final String name, final Type type) {
        fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
    }

    /**
     * Generates the instruction to push the value of a non static field on the
     * stack.
     *
     * @param owner
     *            the class in which the field is defined.
     * @param name
     *            the name of the field.
     * @param type
     *            the type of the field.
     */
    public void getField(final Type owner, final String name, final Type type) {
        fieldInsn(Opcodes.GETFIELD, owner, name, type);
    }

    /**
     * Generates the instruction to store the top stack value in a non static
     * field.
     *
     * @param owner
     *            the class in which the field is defined.
     * @param name
     *            the name of the field.
     * @param type
     *            the type of the field.
     */
    public void putField(final Type owner, final String name, final Type type) {
        fieldInsn(Opcodes.PUTFIELD, owner, name, type);
    }

    // ------------------------------------------------------------------------
    // Instructions to invoke methods
    // ------------------------------------------------------------------------

    /**
     * Generates an invoke method instruction.
     *
     * @param opcode
     *            the instruction's opcode.
     * @param type
     *            the class in which the method is defined.
     * @param method
     *            the method to be invoked.
     */
    private void invokeInsn(final int opcode, final Type type,
            final Method method, final boolean itf) {
        String owner = type.getSort() == Type.ARRAY ? type.getDescriptor()
                : type.getInternalName();
        mv.visitMethodInsn(opcode, owner, method.getName(),
                method.getDescriptor(), itf);
    }

    /**
     * Generates the instruction to invoke a normal method.
     *
     * @param owner
     *            the class in which the method is defined.
     * @param method
     *            the method to be invoked.
     */
    public void invokeVirtual(final Type owner, final Method method) {
        invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method, false);
    }

    /**
     * Generates the instruction to invoke a constructor.
     *
     * @param type
     *            the class in which the constructor is defined.
     * @param method
     *            the constructor to be invoked.
     */
    public void invokeConstructor(final Type type, final Method method) {
        invokeInsn(Opcodes.INVOKESPECIAL, type, method, false);
    }

    /**
     * Generates the instruction to invoke a static method.
     *
     * @param owner
     *            the class in which the method is defined.
     * @param method
     *            the method to be invoked.
     */
    public void invokeStatic(final Type owner, final Method method) {
        invokeInsn(Opcodes.INVOKESTATIC, owner, method, false);
    }

    /**
     * Generates the instruction to invoke an interface method.
     *
     * @param owner
     *            the class in which the method is defined.
     * @param method
     *            the method to be invoked.
     */
    public void invokeInterface(final Type owner, final Method method) {
        invokeInsn(Opcodes.INVOKEINTERFACE, owner, method, true);
    }

    /**
     * Generates an invokedynamic instruction.
     *
     * @param name
     *            the method's name.
     * @param desc
     *            the method's descriptor (see {@link Type Type}).
     * @param bsm
     *            the bootstrap method.
     * @param bsmArgs
     *            the bootstrap method constant arguments. Each argument must be
     *            an {@link Integer}, {@link Float}, {@link Long},
     *            {@link Double}, {@link String}, {@link Type} or {@link Handle}
     *            value. This method is allowed to modify the content of the
     *            array so a caller should expect that this array may change.
     */
    public void invokeDynamic(String name, String desc, Handle bsm,
            Object... bsmArgs) {
        mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
    }

    // ------------------------------------------------------------------------
    // Instructions to create objects and arrays
    // ------------------------------------------------------------------------

    /**
     * Generates a type dependent instruction.
     *
     * @param opcode
     *            the instruction's opcode.
     * @param type
     *            the instruction's operand.
     */
    private void typeInsn(final int opcode, final Type type) {
        mv.visitTypeInsn(opcode, type.getInternalName());
    }

    /**
     * Generates the instruction to create a new object.
     *
     * @param type
     *            the class of the object to be created.
     */
    public void newInstance(final Type type) {
        typeInsn(Opcodes.NEW, type);
    }

    /**
     * Generates the instruction to create a new array.
     *
     * @param type
     *            the type of the array elements.
     */
    public void newArray(final Type type) {
        int typ;
        switch (type.getSort()) {
        case Type.BOOLEAN:
            typ = Opcodes.T_BOOLEAN;
            break;
        case Type.CHAR:
            typ = Opcodes.T_CHAR;
            break;
        case Type.BYTE:
            typ = Opcodes.T_BYTE;
            break;
        case Type.SHORT:
            typ = Opcodes.T_SHORT;
            break;
        case Type.INT:
            typ = Opcodes.T_INT;
            break;
        case Type.FLOAT:
            typ = Opcodes.T_FLOAT;
            break;
        case Type.LONG:
            typ = Opcodes.T_LONG;
            break;
        case Type.DOUBLE:
            typ = Opcodes.T_DOUBLE;
            break;
        default:
            typeInsn(Opcodes.ANEWARRAY, type);
            return;
        }
        mv.visitIntInsn(Opcodes.NEWARRAY, typ);
    }

    // ------------------------------------------------------------------------
    // Miscelaneous instructions
    // ------------------------------------------------------------------------

    /**
     * Generates the instruction to compute the length of an array.
     */
    public void arrayLength() {
        mv.visitInsn(Opcodes.ARRAYLENGTH);
    }

    /**
     * Generates the instruction to throw an exception.
     */
    public void throwException() {
        mv.visitInsn(Opcodes.ATHROW);
    }

    /**
     * Generates the instructions to create and throw an exception. The
     * exception class must have a constructor with a single String argument.
     *
     * @param type
     *            the class of the exception to be thrown.
     * @param msg
     *            the detailed message of the exception.
     */
    public void throwException(final Type type, final String msg) {
        newInstance(type);
        dup();
        push(msg);
        invokeConstructor(type, Method.getMethod("void <init> (String)"));
        throwException();
    }

    /**
     * Generates the instruction to check that the top stack value is of the
     * given type.
     *
     * @param type
     *            a class or interface type.
     */
    public void checkCast(final Type type) {
        if (!type.equals(OBJECT_TYPE)) {
            typeInsn(Opcodes.CHECKCAST, type);
        }
    }

    /**
     * Generates the instruction to test if the top stack value is of the given
     * type.
     *
     * @param type
     *            a class or interface type.
     */
    public void instanceOf(final Type type) {
        typeInsn(Opcodes.INSTANCEOF, type);
    }

    /**
     * Generates the instruction to get the monitor of the top stack value.
     */
    public void monitorEnter() {
        mv.visitInsn(Opcodes.MONITORENTER);
    }

    /**
     * Generates the instruction to release the monitor of the top stack value.
     */
    public void monitorExit() {
        mv.visitInsn(Opcodes.MONITOREXIT);
    }

    // ------------------------------------------------------------------------
    // Non instructions
    // ------------------------------------------------------------------------

    /**
     * Marks the end of the visited method.
     */
    public void endMethod() {
        if ((access & Opcodes.ACC_ABSTRACT) == 0) {
            mv.visitMaxs(0, 0);
        }
        mv.visitEnd();
    }

    /**
     * Marks the start of an exception handler.
     *
     * @param start
     *            beginning of the exception handler's scope (inclusive).
     * @param end
     *            end of the exception handler's scope (exclusive).
     * @param exception
     *            internal name of the type of exceptions handled by the
     *            handler.
     */
    public void catchException(final Label start, final Label end,
            final Type exception) {
        Label doCatch = new Label();
        if (exception == null) {
            mv.visitTryCatchBlock(start, end, doCatch, null);
        } else {
            mv.visitTryCatchBlock(start, end, doCatch,
                    exception.getInternalName());
        }
        mark(doCatch);
    }
}


package org.nutz.dao;

import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import org.nutz.dao.entity.MappingField;
import org.nutz.lang.Strings;
import org.nutz.lang.util.Regex;

/**
 * 字段匹配器. 判断顺序 locked--actived-->ignoreNull.
 * 除locked/actived/ignoreNull之外的属性, 当前仅用于Cnd.from和Chain.from方法.

 * ignoreId现在可以用于dao.insert了

 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author wendal
 */
public class FieldMatcher {

    /**
     * 构建一个字段匹配器.
     * @param actived 需要保留的字段,必须是合法的正则表达式,可以为null
     * @param locked  需要忽略的字段,必须是合法的正则表达式,可以为null
     * @param ignoreNull 是否忽略空值
     * @return 字段/属性匹配器
     */
    public static FieldMatcher make(String actived, String locked, boolean ignoreNull) {
        FieldMatcher fm = new FieldMatcher();
        fm.ignoreNull = ignoreNull;
        if (!Strings.isBlank(actived))
            fm.actived = Regex.getPattern(actived);
        if (!Strings.isBlank(locked))
            fm.locked = Regex.getPattern(locked);
        return fm;
    }

    /**
     * 仅配置是否忽略@Id标注的属性,然后生成实例
     * @param ignoreId 是否忽略@Id标注的属性
     * @return 字段匹配器实例
     */
    public static FieldMatcher create(boolean ignoreId) {
        FieldMatcher fm = new FieldMatcher();
        fm.ignoreId = ignoreId;
        return fm;
    }

    /**
     * 构建一个字段匹配器.
     * @param actived 需要保留的字段,必须是合法的正则表达式,可以为null
     * @param locked  需要忽略的字段,必须是合法的正则表达式,可以为null
     * @param ignoreNull 是否忽略空值
     * @param ignoreZero 是否忽略零值,仅针对数值类型
     * @param ignoreDate 是否忽略java.util.Date及其子类的属性
     * @param ignoreId   是否忽略@Id标注的属性
     * @param ignoreName 是否忽略@Name标注的属性
     * @param ignorePk   是否忽略@Pk引用的属性
     * @return 字段/属性匹配器
     */
    public static FieldMatcher make(String actived, String locked, boolean ignoreNull,
                                    boolean ignoreZero, boolean ignoreDate,
                                    boolean ignoreId,
                                    boolean ignoreName,
                                    boolean ignorePk) {
        FieldMatcher fm = make(actived, locked, ignoreNull);
        fm.ignoreZero = ignoreZero;
        fm.ignoreDate = ignoreDate;
        fm.ignoreId = ignoreId;
        fm.ignoreName = ignoreName;
        fm.ignorePk = ignorePk;
        return fm;
    }

    /**
     * 构建一个字段匹配器.
     * @param actived 需要保留的字段,必须是合法的正则表达式,可以为null
     * @param locked  需要忽略的字段,必须是合法的正则表达式,可以为null
     * @param ignoreNull 是否忽略空值
     * @param ignoreZero 是否忽略零值,仅针对数值类型
     * @param ignoreDate 是否忽略java.util.Date及其子类的属性
     * @param ignoreId   是否忽略@Id标注的属性
     * @param ignoreName 是否忽略@Name标注的属性
     * @param ignorePk   是否忽略@Pk引用的属性
     * @return 字段/属性匹配器
     */
    public static FieldMatcher make(String actived, String locked, boolean ignoreNull,
                                    boolean ignoreZero, boolean ignoreDate,
                                    boolean ignoreId,
                                    boolean ignoreName,
                                    boolean ignorePk,
                                    boolean ignoreBlankStr) {
        FieldMatcher fm = make(actived, locked, ignoreNull);
        fm.ignoreZero = ignoreZero;
        fm.ignoreDate = ignoreDate;
        fm.ignoreId = ignoreId;
        fm.ignoreName = ignoreName;
        fm.ignorePk = ignorePk;
        fm.ignoreBlankStr = ignoreBlankStr;
        return fm;
    }

    /**
     * 哪些字段可用
     */
    private Pattern actived;
    /**
     * 哪些字段不可用
     */
    private Pattern locked;
    /**
     * 是否忽略空值
     */
    private Boolean ignoreNull = true;
    /**
     * 是否忽略空白字符串
     */
    private Boolean ignoreBlankStr;
    /**
     * 是否忽略零值
     */
    private Boolean ignoreZero;
    /**
     * 是否忽略日期
     */
    private Boolean ignoreDate;
    /**
     * 是否忽略@Id标注的属性
     */
    private Boolean ignoreId = true;
    /**
     * 是否忽略@Name标注的属性
     */
    private Boolean ignoreName;
    /**
     * 是否忽略@Pk标注引用的属性
     */
    private Boolean ignorePk;

    private Boolean ignoreFalse;

    /**
     * 匹配顺序 locked -- actived-- ignoreNull
     * @param str 需要匹配的字段名称
     * @return true,如果可用
     */
    public boolean match(String str) {
        if (null != locked && locked.matcher(str).find()) {
            return false;
        }
        if (null != actived && !actived.matcher(str).find()) {
            return false;
        }
        return true;
    }

    public boolean match(MappingField mf, Object obj) {
        String fieldName = mf.getName();
        if (null != locked && locked.matcher(fieldName).find()) {
            return false;
        }
        if (null != actived && !actived.matcher(fieldName).find()) {
            return false;
        }
        if (ignoreId != null && ignoreId && mf.isId())
            return false;
        if (ignoreName != null && ignoreName && mf.isName())
            return false;
        if (ignorePk != null && ignorePk && mf.isCompositePk())
            return false;
        Object val = mf.getValue(obj);
        if (val == null) {
            if (ignoreNull != null && ignoreNull)
                return false;
        } else {
            if (ignoreZero != null && ignoreZero
                && val instanceof Number
                && ((Number) val).doubleValue() == 0.0) {
                return false;
            }
            if (ignoreDate != null && ignoreDate && val instanceof Date) {
                return false;
            }
            if (ignoreBlankStr != null && ignoreBlankStr
                && val instanceof CharSequence
                && Strings.isBlank((CharSequence) val)) {
                return false;
            }
            if (val instanceof Boolean && ignoreFalse != null && ignoreFalse && !((Boolean)val))
                return false;
        }
        return true;
    }

    /**
     * 是否忽略控制
     * @return true,如果忽略控制
     */
    public boolean isIgnoreNull() {
        return ignoreNull;
    }

    /**
     * 设置是否忽略空值
     * @param ignoreNull 是否忽略空值属性
     * @return 原对象,用于链式调用
     */
    public FieldMatcher setIgnoreNull(boolean ignoreNull) {
        this.ignoreNull = ignoreNull;
        return this;
    }

    /**
     * 获取可用字段/属性的正则表达式
     * @return 正则表达式,可能为null
     */
    public Pattern getActived() {
        return actived;
    }

    /**
     * 获取不可用字段/属性的正则表达式
     * @return 正则表达式,可能为null
     */
    public Pattern getLocked() {
        return locked;
    }

    /**
     * 设置过滤可用字段/属性的正则表达式
     * @param actived 正则表达式
     * @return 原对象,用于链式调用
     */
    public FieldMatcher setActived(String actived) {
        if (actived != null)
            this.actived = Regex.getPattern(actived);
        else
            this.actived = null;
        return this;
    }

    /**
     * 设置过滤不可用字段/属性的正则表达式
     * @param locked 正则表达式
     * @return 原对象,用于链式调用
     */
    public FieldMatcher setLocked(String locked) {
        if (locked != null)
            this.locked = Regex.getPattern(locked);
        else
            this.locked = null;
        return this;
    }

    /**
     * 是否忽略零值
     * @return true,如果是的话,默认为false
     */
    public boolean isIgnoreZero() {
        return ignoreZero;
    }

    /**
     * 设置是否忽略零值
     * @param ignoreZero 是否忽略零值
     * @return 原对象,用于链式调用
     */
    public FieldMatcher setIgnoreZero(boolean ignoreZero) {
        this.ignoreZero = ignoreZero;
        return this;
    }

    /**
     * 是否忽略Date及其子类的属性值
     * @return true,如果需要忽略的话
     */
    public boolean isIgnoreDate() {
        return ignoreDate;
    }

    /**
     * 设置是否忽略Date及其子类的属性值
     * @param ignoreDate 是否忽略
     * @return 原对象,用于链式调用
     */
    public FieldMatcher setIgnoreDate(boolean ignoreDate) {
        this.ignoreDate = ignoreDate;
        return this;
    }

    /**
     * 是否忽略@Id标注的属性,默认忽略
     * @return true,如果忽略
     */
    public boolean isIgnoreId() {
        return ignoreId;
    }

    /**
     * 设置是否忽略@Id标注的属性
     * @param ignoreId 是否忽略
     * @return 原对象,用于链式调用
     */
    public FieldMatcher setIgnoreId(boolean ignoreId) {
        this.ignoreId = ignoreId;
        return this;
    }

    /**
     * 是否忽略@Name标注的属性,默认是false
     * @return true,如果忽略的话
     */
    public boolean isIgnoreName() {
        return ignoreName;
    }

    /**
     * 设置是否忽略@Name标注的属性
     * @param ignoreName 是否忽略
     * @return 原对象,用于链式调用
     */
    public FieldMatcher setIgnoreName(boolean ignoreName) {
        this.ignoreName = ignoreName;
        return this;
    }

    /**
     * 是否忽略@Pk所引用的属性,默认是false
     * @return true,如果忽略的话
     */
    public boolean isIgnorePk() {
        return ignorePk;
    }

    /**
     * 设置是否忽略@Pk所引用的属性
     * @param ignorePk 是否忽略
     * @return 原对象,用于链式调用
     */
    public FieldMatcher setIgnorePk(boolean ignorePk) {
        this.ignorePk = ignorePk;
        return this;
    }

    public boolean isIgnoreBlankStr() {
        return ignoreBlankStr != null && ignoreBlankStr;
    }

    public FieldMatcher setIgnoreBlankStr(boolean ignoreBlankStr) {
        this.ignoreBlankStr = ignoreBlankStr;
        return this;
    }

    public void setIgnoreFalse(Boolean ignoreFalse) {
        this.ignoreFalse = ignoreFalse;
    }

    public static FieldMatcher simple(String ...fields) {
        final Set<String> m = new HashSet<String>(Arrays.asList(fields));
        return new FieldMatcher() {
            public boolean match(String str) {
                return m.contains(str);
            }
            public boolean match(MappingField mf, Object obj) {
                return this.match(mf.getName());
            }
        };
    }
}


package org.nutz.lang.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Streams;
import org.nutz.lang.born.Borning;
import org.nutz.lang.util.Context;
import org.nutz.log.Log;
import org.nutz.log.Logs;

public abstract class Sockets {

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

    public static void send(String host, int port, InputStream ins, OutputStream ops) {
    	send(host, port, ins, ops, 0);
    }

    /**
     * 向某主机发送一些字节内容，并将返回写入输出流
     *
     * @param host
     *            主机
     * @param port
     *            端口
     * @param ins
     *            发送的内容
     * @param ops
     *            主机返回的输入流
     */
    public static void send(String host, int port, InputStream ins, OutputStream ops, int timeout) {
        Socket socket = null;
        try {
            socket = new Socket();
            socket.connect(new InetSocketAddress(host, port), timeout);
            // 发送关闭命令
            OutputStream sOut = socket.getOutputStream();
            Streams.write(sOut, ins);
            sOut.flush();

            // 接收服务器的反馈
            if (!socket.isClosed()) {
                InputStream sReturn = socket.getInputStream();
                Streams.write(ops, sReturn);
            }
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Streams.safeClose(ins);
            Streams.safeClose(ops);
            safeClose(socket);
        }
    }

    /**
     * 向某主机发送一段文本，并将主机的返回作为文本返回
     *
     * @param host
     *            主机
     * @param port
     *            端口
     * @param text
     *            发送的内容
     * @return 主机返回的文本
     */
    public static String sendText(String host, int port, String text) {
        StringBuilder sb = new StringBuilder();
        send(host, port, Lang.ins(text), Lang.ops(sb));
        return sb.toString();
    }

    /**
     * 监听本地某一个端口，仅仅收到某一个特殊命令时，才会开始一个动作。
     *

     * 并且原生的，它支持输入 "close|stop|bye|exit" 来结束本地监听
     *
     * @param port
     *            要监听的端口
     * @param line
     *            命令名称
     * @param action
     *            动作执行类
     */
    public static void localListenOneAndStop(int port, String line, SocketAction action) {
        Map<String, SocketAction> actions = createActions();
        actions.put(line, action);
        actions.put("$:^(close|stop|bye|exit)$", doClose());
        localListenByLine(port, actions);
    }

    /**
     * 监听本地某一个端口，仅仅收到某一个特殊命令时，才会开始一个动作。
     *
     * @param port
     *            要监听的端口
     * @param line
     *            命令名称
     * @param action
     *            动作执行类
     */
    public static void localListenOne(int port, String line, SocketAction action) {
        Map<String, SocketAction> actions = createActions();
        actions.put(line, action);
        localListenByLine(port, actions);
    }

    /**
     * 对于一个 CPU 默认起的处理线程数
     */
    private static final int DEFAULT_POOL_SIZE = 10;

    /**
     * 简化了一个参数，采用默认线程数
     *
     * @see org.nutz.lang.socket.Sockets#localListenByLine(int, Map, int)
     */
    public static void localListenByLine(int port, Map<String, SocketAction> actions) {
        Sockets.localListenByLine(port, actions, DEFAULT_POOL_SIZE);
    }

    /**
     * 监听本地某一个端口，根据用户输入的命令的不同，执行不同的操作
     *

     * 当然，你如果想让一个过程处理多种命令，请给的 key 前用 "REGEX:" 作为前缀，后面用一个正则表达式 来表示你的你要的匹配的命令

     * "REGEX:!" 开头的，表示后面的正则表达式是一个命令过滤，所有没有匹配上的命令都会被处理
     *
     * @param port
     *            要监听的端口
     * @param actions
     *            动作执行类映射表
     * @param poolSize
     *            针对一个 CPU 你打算启动几个处理线程
     *
     * @see org.nutz.lang.socket.Sockets#localListenByLine(int, Map,
     *      ExecutorService)
     */
    public static void localListenByLine(int port, Map<String, SocketAction> actions, int poolSize) {
        Sockets.localListenByLine(    port,
                                    actions,
                                    Executors.newFixedThreadPool(Runtime.getRuntime()
                                                                        .availableProcessors()
                                                                    * poolSize));
    }

    /**
     * 监听本地某一个端口，根据用户输入的命令的不同，执行不同的操作
     *

     * 当然，你如果想让一个过程处理多种命令，请给的 key 前用 "REGEX:" 作为前缀，后面用一个正则表达式 来表示你的你要的匹配的命令

     * "REGEX:!" 开头的，表示后面的正则表达式是一个命令过滤，所有没有匹配上的命令都会被处理
     *
     * @param port
     *            要监听的端口
     * @param actions
     *            动作执行类映射表
     * @param service
     *            线程池的实现类
     */
    public static void localListenByLine(    int port,
                                            Map<String, SocketAction> actions,
                                            ExecutorService service) {
        localListen(port, actions, service, SocketAtom.class);
    }

    /**
     * 监听本地某一个端口，根据用户输入的命令的不同，执行不同的操作
     *

     * 当然，你如果想让一个过程处理多种命令，请给的 key 前用 "REGEX:" 作为前缀，后面用一个正则表达式 来表示你的你要的匹配的命令

     * "REGEX:!" 开头的，表示后面的正则表达式是一个命令过滤，所有没有匹配上的命令都会被处理
     *
     * @param port
     *            要监听的端口
     * @param actions
     *            动作执行类映射表
     * @param service
     *            线程池的实现类
     */
    @SuppressWarnings("rawtypes")
    public static void localListen(    int port,
                                    Map<String, SocketAction> actions,
                                    ExecutorService service,
                                    Class<? extends SocketAtom> klass) {
        try {
            // 建立动作映射表
            SocketActionTable saTable = new SocketActionTable(actions);

            // 初始化 socket 接口
            final ServerSocket server;
            try {
                server = new ServerSocket(port);
            }
            catch (IOException e1) {
                throw Lang.wrapThrow(e1);
            }

            if (log.isInfoEnabled())
                log.infof("Local socket is up at :%d with %d action ready", port, actions.size());

            final Context context = Lang.context();
            context.set("stop", false);
            /*
             * 启动一个守护线程，判断是否该关闭 socket 服务
             */
            (new Thread() {
                @Override
                public void run() {
                    setName("Nutz.Sockets monitor thread");
                    while (true) {
                        try {
                            Thread.sleep(1000);
                            if (context.getBoolean("stop")) {
                                try {
                                    server.close();
                                }
                                catch (Throwable e) {}
                                return;
                            }
                        }
                        catch (Throwable e) {}
                    }
                }
            }).start();
            /*
             * 准备 SocketAtom 的生成器
             */
            Borning borning = Mirror.me(klass).getBorningByArgTypes(Context.class,
                                                                    Socket.class,
                                                                    SocketActionTable.class);
            if (borning == null) {
                log.error("boring == null !!!!");
                return;
            }
            /*
             * 进入监听循环
             */
            while (!context.getBoolean("stop")) {
                try {
                    if (log.isDebugEnabled())
                        log.debug("Waiting for new socket");
                    Socket socket = server.accept();
                    if (context.getBoolean("stop")) {
                        Sockets.safeClose(socket);
                        break;// 监护线程也许还是睡觉,还没来得及关掉哦,所以自己检查一下
                    }
                    if (log.isDebugEnabled())
                        log.debug("accept a new socket, create new SocketAtom to handle it ...");
                    Runnable runnable = (Runnable) borning.born(new Object[]{    context,
                                                                                socket,
                                                                                saTable});
                    service.execute(runnable);
                }
                catch (Throwable e) {
                    log.info("Throwable catched!! maybe ask to exit", e);
                }
            }

            if (!server.isClosed()) {
                try {
                    server.close();
                }
                catch (Throwable e) {}
            }

            log.info("Seem stop signal was got, wait 15 for all running thread");

            try {
                service.shutdown();
                service.awaitTermination(15, TimeUnit.SECONDS);
            }
            catch (InterruptedException e) {}
            try {
                service.shutdownNow();
            }
            catch (Throwable e2) {}
        }
        catch (RuntimeException e) {
            throw e;
        }
        finally {
            if (log.isInfoEnabled())
                log.info("Stop services ...");
            service.shutdown();
        }

        if (log.isInfoEnabled())
            log.infof("Local socket is down for :%d", port);

    }

    /**
     * 安全关闭套接层，容忍 null
     *
     * @param socket
     *            套接层
     * @return 一定会返回 null
     */
    public static Socket safeClose(Socket socket) {
        if (null != socket)
            try {
                socket.close();
                socket = null;
            }
            catch (IOException e) {
                throw Lang.wrapThrow(e);
            }
        return null;
    }

    /**
     * 创建一个停止监听的动作对象
     *
     * @return 动作对象
     */
    public static SocketAction doClose() {
        return new SocketAction() {
            public void run(SocketContext context) {
                throw new CloseSocketException();
            }
        };
    }

    /**
     * 这个函数可以在你的 SocketAction 实现类里被调用，用来关闭当前的监听星闻
     */
    public static void close() {
        throw new CloseSocketException();
    }

    /**
     * 快捷创建动作映射表的方法
     *
     * @return 动作映射表
     */
    public static Map<String, SocketAction> createActions() {
        return new HashMap<String, SocketAction>();
    }

}


package org.nutz.dao.impl;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.sql.DataSource;

import org.nutz.dao.ConnCallback;
import org.nutz.dao.DaoInterceptor;
import org.nutz.dao.DaoInterceptorChain;
import org.nutz.dao.DatabaseMeta;
import org.nutz.dao.SqlManager;
import org.nutz.dao.entity.EntityMaker;
import org.nutz.dao.impl.entity.AnnotationEntityMaker;
import org.nutz.dao.impl.interceptor.DaoLogInterceptor;
import org.nutz.dao.impl.interceptor.DaoTimeInterceptor;
import org.nutz.dao.impl.sql.NutPojoMaker;
import org.nutz.dao.impl.sql.run.NutDaoExecutor;
import org.nutz.dao.impl.sql.run.NutDaoRunner;
import org.nutz.dao.jdbc.JdbcExpert;
import org.nutz.dao.jdbc.Jdbcs;
import org.nutz.dao.sql.DaoStatement;
import org.nutz.dao.sql.PojoMaker;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.sql.SqlContext;
import org.nutz.dao.util.Daos;
import org.nutz.lang.Configurable;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;

/**
 * Dao 接口实现类的一些基础环境
 *
 * @author zozoh(zozohtnt@gmail.com)
 */
public class DaoSupport implements Configurable {

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

    /**
     * 给子类使用的 Dao 的运行器，用来封装事务
     */
    protected DaoRunner runner;

    /**
     * 给子类使用的 Dao 语句执行器，用来具体运行某一条语句
     */
    protected DaoExecutor executor;

    /**
     * 给子类使用数据源
     */
    protected DataSource dataSource;

    /**
     * 给子类使用的数据特殊性的封装
     */
    protected JdbcExpert expert;

    /**
     * 给子类使用的 PojoStatementMaker 接口
     */
    protected PojoMaker pojoMaker;

    /**
     * 给子类使用的 Entity 获取对象
     */
    protected EntityHolder holder;

    /**
     * 数据库的描述
     */
    protected DatabaseMeta meta;

    /**
     * SQL 管理接口实现类
     */
    protected SqlManager sqlManager;

    protected int autoTransLevel = Connection.TRANSACTION_READ_COMMITTED;

    protected List<DaoInterceptor> _interceptors;

    public DaoSupport() {
        this.runner = new NutDaoRunner();
        this.executor = new NutDaoExecutor();
        this.setInterceptors(Lang.list((Object)"log"));
    }

    /**
     * @return Sql 管理接口的实例
     */
    public SqlManager sqls() {
        return sqlManager;
    }

    /**
     * @return 当前连接的数据库的一些描述数据
     */
    public DatabaseMeta meta() {
        return meta;
    }

    /**
     * 设置一个新的 Sql 管理接口实例
     *
     * @param sqls
     *            Sql 管理接口实例
     */
    public void setSqlManager(SqlManager sqls) {
        this.sqlManager = sqls;
        if (sqls != null) {
            int count = sqls.count();
            log.debug("SqlManager Sql count=" + count);
        }
    }

    /**
     * 设置一个新的 Dao 运行器
     *
     * @param runner
     *            运行器对象
     */
    public void setRunner(DaoRunner runner) {
        this.runner = runner;
        if (runner instanceof NutDaoRunner) {
        	((NutDaoRunner)runner).setMeta(meta);
        }
    }

    /**
     * 设置一个新的 Dao 语句执行器
     *
     * @param executor
     *            Dao 语句执行器对象
     */
    public void setExecutor(DaoExecutor executor) {
        this.executor = executor;
        if (executor instanceof NutDaoExecutor) {
        	((NutDaoExecutor)executor).setMeta(meta);
        	((NutDaoExecutor)executor).setExpert(expert);
        }
    }

    /**
     * 设置一个新的 Pojo 语句创建器
     *
     * @param pojoMaker
     *            Pojo 语句创建器
     */
    public void setPojoMaker(PojoMaker pojoMaker) {
        this.pojoMaker = pojoMaker;
    }

    /**
     * @return 当前的 JDBC 专家类
     */
    public JdbcExpert getJdbcExpert() {
        return expert;
    }

    /**
     * 设置新的数据源。
     * <p>
     * 如果有老的数据源需要你在外部手动关闭
     *
     * @param ds
     *            数据源
     */
    public void setDataSource(DataSource ds) {
        setDataSource(ds,false);
    }

    public void setDataSource(DataSource ds,boolean isLazy) {
        if (null != dataSource)
            if (log.isWarnEnabled())
                log.warn("Replaced a running dataSource!");
        dataSource = ds;
        if (expert == null)
            expert = Jdbcs.getExpert(ds);
        log.debug("select expert : " + expert.getClass().getName());
        pojoMaker = new NutPojoMaker(expert);

        meta = new DatabaseMeta();
        final Set<String> keywords = new HashSet<String>(Daos.sql2003Keywords());
        run(new ConnCallback() {
            public void invoke(Connection conn) throws Exception {
                try {
                    DatabaseMetaData dmd = conn.getMetaData();
                    meta.setProductName(dmd.getDatabaseProductName());
                    meta.setVersion(dmd.getDatabaseProductVersion());
                    log.debug("JDBC Driver --> " + dmd.getDriverVersion());
                    log.debug("JDBC Name   --> " + dmd.getDriverName());
                    if (!Strings.isBlank(dmd.getURL()))
                        log.debug("JDBC URL    --> " + dmd.getURL());
                    if (dmd.getDriverName().contains("mariadb") || dmd.getDriverName().contains("sqlite")) {
                        log.warn("Auto-select fetch size to Integer.MIN_VALUE, enable for ResultSet Streaming");
                        SqlContext.DEFAULT_FETCH_SIZE = Integer.MIN_VALUE;
                    }
                    String tmp = dmd.getSQLKeywords();
                    if (tmp != null) {
                        for (String keyword : tmp.split(",")) {
                            keywords.add(keyword.toUpperCase());
                        }
                    }
                    expert.checkDataSource(conn);
                }
                catch (Exception e) {
                    log.info("something wrong when checking DataSource", e);
                }
            }
        });
        if (log.isDebugEnabled())
            log.debug("Database info --> " + meta);
        expert.setKeywords(keywords);

        if(!isLazy)
        {
            holder = new EntityHolder(this.expert, dataSource);
            holder.maker = createEntityMaker();
        }
        setRunner(runner);
        setExecutor(executor);
    }

    public void execute(final Sql... sqls) {
        for (Sql sql : sqls)
            expert.formatQuery(sql);
        _exec(sqls);
    }

    public void run(ConnCallback callback) {
        runner.run(dataSource, callback);
    }

    protected int _exec(final DaoStatement... sts) {
        if (sts != null)
            for (DaoStatement ds : sts) {
                ds.setExpert(expert);
            }
        final DaoInterceptorChain callback = new DaoInterceptorChain(sts);
        callback.setExecutor(executor);
        callback.setAutoTransLevel(autoTransLevel);
        callback.setInterceptors(Collections.unmodifiableList(this._interceptors));
        run(callback);
        // 搞定，返回结果 ^_^
        return callback.getUpdateCount();
    }

    /**
     * 子类可以重写这个类，用来扩展成其他的实体配置方式
     *
     * @return 实体工厂
     */
    protected EntityMaker createEntityMaker() {
        return new AnnotationEntityMaker(dataSource, expert, holder);
    }

    public PojoMaker pojoMaker() {
		return pojoMaker;
	}

    public void setAutoTransLevel(int autoTransLevel) {
        this.autoTransLevel = autoTransLevel;
    }

    public void setInterceptors(List<Object> interceptors) {
        List<DaoInterceptor> list = new LinkedList<DaoInterceptor>();
        for (Object it : interceptors) {
            DaoInterceptor d = makeInterceptor(it);
            if (d != null)
                list.add(d);
        }
        this._interceptors = list;
    }

    public void addInterceptor(Object it) {
        DaoInterceptor d = makeInterceptor(it);
        if (d != null) {
            List<DaoInterceptor> list = new LinkedList<DaoInterceptor>(this._interceptors);
            list.add(d);
            this._interceptors = list;
        }
    }

    public DaoInterceptor makeInterceptor(Object it) {
        if (it == null)
            return null;
        if (it instanceof String) {
            String itName = it.toString().trim();
            if ("log".equals(itName)) {
                return new DaoLogInterceptor();
            }
            else if ("time".equals(itName)) {
                return new DaoTimeInterceptor();
            }
            else if (itName.contains(".")) {
                Class<?> klass = Lang.loadClassQuite(itName);
                if (klass == null) {
                    log.warn("no such interceptor name="+itName);
                } else {
                    return (DaoInterceptor) Mirror.me(klass).born();
                }
            } else {
                log.info("unkown interceptor name="+itName);
            }
        }
        else if (it instanceof DaoInterceptor) {
            return (DaoInterceptor) it;
        } else {
            log.info("unkown interceptor -> "+it);
        }
        return null;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setupProperties(NutMap conf) {
        if (expert instanceof Configurable)
            ((Configurable)expert).setupProperties(conf);
        if (executor instanceof Configurable)
            ((Configurable)executor).setupProperties(conf);
        if (runner instanceof Configurable)
            ((Configurable)runner).setupProperties(conf);
    }
}


package org.nutz.dao.impl;

import static java.lang.String.format;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.nutz.dao.SqlManager;
import org.nutz.dao.SqlNotFoundException;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.lang.Lang;
import org.nutz.lang.Streams;
import org.nutz.lang.Strings;
import org.nutz.lang.util.LinkedCharArray;

/**
 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author wendal(wendal1985@gmail.com)
 * @author mawenming at 2010-4-10 上午10:04:17
 *
 */
public abstract class AbstractSqlManager implements SqlManager {

    protected Map<String, String> _sql_map;
    private List<String> _sql_keys;

    private boolean allowDuplicate = true;

    private Map<String, String> map() {
        if (null == _sql_map)
            this.refresh();
        return _sql_map;
    }

    private List<String> keylist() {
        if (null == _sql_keys)
            this.refresh();
        return _sql_keys;
    }

    public boolean contains(String key) {
        return map().containsKey(key);
    }

    public void saveAs(File f) throws IOException {
        Writer w = Streams.fileOutw(f);
        for (String key : keylist()) {
            w.append("/*").append(Strings.dup('-', 60)).append("*/\n");
            String sql = map().get(key);
            w.append(format("/*%s*/\n", key));
            w.append(sql).append('\n');
        }
        w.flush();
        w.close();
    }

    public String get(String key) {
        String sql = map().get(key);
        if (null == sql)
            throw new SqlNotFoundException(key);
        return sql;
    }

    public Sql create(String key) throws SqlNotFoundException {
        return Sqls.create(get(key));
    }

    public List<Sql> createCombo(String... keys) {
        if (null == keys || keys.length == 0)
            keys = this.keys();
        List<Sql> list = new ArrayList<Sql>(keys.length);
        for (String key : keys) {
            Sql sql = create(key);
            list.add(sql);
        }
        return list;
    }

    public int count() {
        return map().size();
    }

    public String[] keys() {
        return keylist().toArray(new String[keylist().size()]);
    }

    public void addSql(String key, String value) {
        if (map().containsKey(key) && !allowDuplicate)
            throw Lang.makeThrow("duplicate key '%s'", key);
        key = Strings.trim(key);
        map().put(key, value);
        keylist().add(key);
    }

    static final Pattern ptn = Pattern.compile("(?<=^\n/[*])(.*)(?=[*]/)");

    static class InnerStack {

        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
        LinkedCharArray list = new LinkedCharArray();
        LinkedCharArray cmts = new LinkedCharArray();
        String key = null;
        boolean inNormalComment;

        void eat(int c) {
            if (inNormalComment) {
                if (cmts.push(c).endsWith("*/")) {
                    cmts.clear();
                    inNormalComment = false;
                }
            } else if (key != null) {
                if (list.push(c).endsWith("\n/*")) {
                    list.popLast(3);
                    addOne();
                    list.push("\n/*");
                } else if (list.endsWith("/*")) {
                    list.popLast(2);
                    inNormalComment = true;
                }
            } else {
                if (list.size() < 3) {
                    if (!"\n/*".startsWith(list.push(c).toString())) {
                        list.clear();
                    }
                } else {
                    if (list.push(c).endsWith("*/")) {
                        Matcher matcher = ptn.matcher(list.popAll());
                        if (matcher.find()) {
                            key = Strings.trim(matcher.group());
                        }
                    }
                }
            }
        }

        void addOne() {
            String value = Strings.trim(list.popAll());
            if (!Strings.isBlank(value))
                map.put(key, value);
            key = null;
        }

    }

    static class SqlFileBuilder {
        LinkedHashMap<String, String> map;

        SqlFileBuilder(BufferedReader reader) throws IOException {
            InnerStack stack = new InnerStack();
            int c;
            stack.eat('\n');
            while (-1 != (c = reader.read())) {
                stack.eat(c);
            }
            if (stack.key != null)
                stack.addOne();
            map = stack.map;
            Streams.safeClose(reader);
        }

        Set<String> keys() {
            return map.keySet();
        }

        String get(String key) {
            return map.get(key);
        }

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

    public void remove(String key) {
        this.keylist().remove(key);
        this.map().remove(key);
    }

    /**
     * 执行根据字符流来加载sql内容的操作
     *
     * @param reader
     * @throws IOException
     */
    protected void loadSQL(Reader reader) throws IOException {
        BufferedReader bufferedReader = null;
        try {
            if(reader instanceof BufferedReader)
                bufferedReader = (BufferedReader)reader;
            else
                bufferedReader = new BufferedReader(reader);
            SqlFileBuilder p = new SqlFileBuilder(bufferedReader);
            _sql_keys = new ArrayList<String>(p.map.size());
            for (Entry<String, String> en : p.entrySet()) {
            	addSql(en.getKey(), Strings.trim(en.getValue()));
			}
        }
        finally {
            Streams.safeClose(bufferedReader);
        }

    }

    public void setAllowDuplicate(boolean allowDuplicate) {
        this.allowDuplicate = allowDuplicate;
    }
}


package org.nutz.service;

import java.sql.ResultSet;
import java.util.List;

import org.nutz.dao.Chain;
import org.nutz.dao.Condition;
import org.nutz.dao.Dao;
import org.nutz.dao.FieldFilter;
import org.nutz.dao.FieldMatcher;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.pager.Pager;
import org.nutz.lang.Each;
import org.nutz.lang.Mirror;
import org.nutz.log.Log;
import org.nutz.log.Logs;

/**
 * 实体Service抽象类. 属于辅助类. 任何方法被调用前,必须确保Dao实例已经传入
 *
 * @author wendal(wendal1985@gmail.com)
 *
 * @param <T>
 *            实体类型
 */
public abstract class EntityService<T> extends Service {

    private Mirror<T> mirror;

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

    /**
     * 本抽象类能提供一些帮助方法,减少重复写实体类型的麻烦
     */
    @SuppressWarnings("unchecked")
    public EntityService() {
        try {
            Class<T> entryClass = (Class<T>) Mirror.getTypeParam(getClass(), 0);
            mirror = Mirror.me(entryClass);
            if (log.isDebugEnabled())
                log.debugf("Get TypeParams for self : %s", entryClass.getName());
        }
        catch (Throwable e) {
            if (log.isWarnEnabled())
                log.warn("!!!Fail to get TypeParams for self!", e);
        }
    }

    /**
     * 新建并传入Dao实例
     *
     * @param dao
     *            Dao实例
     */
    public EntityService(Dao dao) {
        this();
        this.setDao(dao);
    }

    /**
     * 新建并传入Dao实例,同时指定实体类型
     *
     * @param dao
     *            Dao实例
     * @param entityType
     *            实体类型
     */
    public EntityService(Dao dao, Class<T> entityType) {
        setEntityType(entityType);
        setDao(dao);
    }

    /**
     * 获取实体类型的反射封装类实例
     *
     * @return 反射封装类实例
     */
    public Mirror<T> mirror() {
        return mirror;
    }

    /**
     * 设置新的实体类型, 极少调用
     *
     * @param classOfT
     */
    @SuppressWarnings("unchecked")
    public <C extends T> void setEntityType(Class<C> classOfT) {
        mirror = (Mirror<T>) Mirror.me(classOfT);
    }

    /**
     * 获取实体的Entity
     *
     * @return 实体的Entity
     */
    public Entity<T> getEntity() {
        return dao().getEntity(mirror.getType());
    }

    /**
     * 获取实体类型
     *
     * @return 实体类型
     */
    public Class<T> getEntityClass() {
        return mirror.getType();
    }

    /**
     * 批量删除
     *
     * @param cnd
     *            条件
     * @return 删除的条数
     */
    public int clear(Condition cnd) {
        return dao().clear(getEntityClass(), cnd);
    }

    /**
     * 全表删除
     *
     * @return 删除的条数
     */
    public int clear() {
        return dao().clear(getEntityClass(), null);
    }

    /**
     * 根据条件分页查询
     *
     * @param cnd
     *            查询条件
     * @param pager
     *            分页
     * @return 查询结果
     */
    public List<T> query(Condition cnd, Pager pager) {
        return (List<T>) dao().query(getEntityClass(), cnd, pager);
    }

    /**
     * 遍历条件分页查询结果
     *
     * @param cnd
     *            查询条件
     * @param pager
     *            分页
     * @param callback
     *            遍历回调
     * @return 遍历的总条数
     */
    public int each(Condition cnd, Pager pager, Each<T> callback) {
        return dao().each(getEntityClass(), cnd, pager, callback);
    }

    /**
     * 根据条件统计符合条件的记录数
     *
     * @param cnd
     *            查询条件
     * @return 记录数
     */
    public int count(Condition cnd) {
        return dao().count(getEntityClass(), cnd);
    }

    /**
     * 全表的总记录数
     *
     * @return 总记录数
     */
    public int count() {
        return dao().count(getEntityClass());
    }

    /**
     * 查出符合条件的第一条记录
     *
     * @param cnd
     *            查询条件
     * @return 实体,如不存在则为null
     */
    public T fetch(Condition cnd) {
        return dao().fetch(getEntityClass(), cnd);
    }

    /**
     * 复合主键专用
     *
     * @param pks
     *            键值
     * @return 对象 T
     */
    public T fetchx(Object... pks) {
        return dao().fetchx(getEntityClass(), pks);
    }

    /**
     * 复合主键专用
     *
     * @param pks
     *            键值
     * @return 对象 T
     */
    public boolean exists(Object... pks) {
        return null != fetchx(pks);
    }

    /**
     * 批量更新
     *
     * @param chain
     *            设置值的键值对
     * @param cnd
     *            需要更新的条件语句
     */
    public int update(Chain chain, Condition cnd) {
        return dao().update(getEntityClass(), chain, cnd);
    }

    /**
     * 更新@ManyMany关联表中的数据
     *
     * @param regex
     *            关联字段的匹配正则表达式,如果为null则代表全部
     * @param chain
     *            键值对
     * @param cnd
     *            条件语句
     */
    public int updateRelation(String regex, Chain chain, Condition cnd) {
        return dao().updateRelation(getEntityClass(), regex, chain, cnd);
    }

    /**
     * 根据复合主键删除记录
     *
     * @param pks
     *            复合主键,必须按@Pk的声明顺序传入
     * @return 删除的记录数
     */
    public int deletex(Object... pks) {
        return dao().deletex(getEntityClass(), pks);
    }

    /**
     * 根据一个实体的配置信息为其创建一张表
     *
     * @param dropIfExists
     *            如果表存在是否强制移除
     * @return 实体对象
     */
    public Entity<T> create(boolean dropIfExists) {
        return dao().create(getEntityClass(), dropIfExists);
    }

    /**
     * 与 insert(String tableName, Chain chain) 一样，不过，数据表名，将取自 POJO 的数据表声明，请参看
     * '@Table' 注解的详细说明
     *
     * @param chain
     *            数据名值链
     */
    public void insert(Chain chain) {
        dao().insert(getEntityClass(), chain);
    }

    /**
     * 查询一组对象。你可以为这次查询设定条件
     *
     * @param cnd
     *            WHERE 条件。如果为 null，将获取全部数据，顺序为数据库原生顺序

     *            只有在调用这个函数的时候， cnd.limit 才会生效
     * @return 对象列表
     */
    public List<T> query(Condition cnd) {
        return dao().query(getEntityClass(), cnd);
    }

    /**
     * 对一组对象进行迭代，这个接口函数非常适用于很大的数据量的集合，因为你不可能把他们都读到内存里
     *
     * @param cnd
     *            WHERE 条件。如果为 null，将获取全部数据，顺序为数据库原生顺序
     * @param callback
     *            处理回调
     * @return 一共迭代的数量
     */
    public int each(Condition cnd, Each<T> callback) {
        return dao().each(getEntityClass(), cnd, callback);
    }

    /**
     * 对某一个对象字段，进行计算。
     *
     * @param funcName
     *            计算函数名，请确保你的数据库是支持这个函数的
     * @param fieldName
     *            对象 java 字段名
     * @return 计算结果
     */
    public int func(String funcName, String fieldName) {
        return dao().func(getEntityClass(), funcName, fieldName);
    }

    /**
     * 对某一个对象字段，进行计算。
     *
     * @param funcName
     *            计算函数名，请确保你的数据库是支持这个函数的
     * @param fieldName
     *            对象 java 字段名
     * @param cnd
     *            过滤条件
     * @return 计算结果
     */
    public int func(String funcName, String fieldName, Condition cnd) {
        return dao().func(getEntityClass(), funcName, fieldName, cnd);
    }

    /**
     * 从一个 ResultSet 中获取一个对象。
     *

     * 因为 Dao 接口可以知道一个 POJO 的映射细节，这个函数可以帮你节省一点体力。
     *
     * @param rs
     *            结果集
     * @param fm
     *            字段过滤器
     * @return 对象
     */
    public T getObject(ResultSet rs, FieldMatcher fm) {
        return dao().getObject(getEntityClass(), rs, fm);
    }

    public T getObject(ResultSet rs, FieldMatcher fm, String prefix) {
        return dao().getObject(getEntityClass(), rs, fm, prefix);
    }

    public List<T> _query(final Condition cnd, final Pager pager, FieldMatcher matcher) {
        return dao().query(getEntityClass(), cnd, pager, matcher);
    }

    public List<T> _query(final Condition cnd, final Pager pager, String regex) {
        return dao().query(getEntityClass(), cnd, pager, regex);
    }

    public T _insert(T obj){
        return dao().insert(obj);
    }

    public T _fastInsert(T obj){
        return dao().fastInsert(obj);
    }

    public T _insert(T obj, FieldFilter filter){
        return dao().insert(obj,filter);
    }

    public T _insert(T t, boolean ignoreNull, boolean ignoreZero, boolean ignoreBlankStr){
        return dao().insert(t,ignoreNull,ignoreZero,ignoreBlankStr);
    }

    public T _insertWith(T obj, String regex){
        return dao().insertWith(obj,regex);
    }

    public T _insertLinks(T obj, String regex){
        return dao().insertLinks(obj,regex);
    }

    public T _insertRelation(T obj, String regex){
        return dao().insertRelation(obj,regex);
    }

    public int _update(T obj){
        return dao().update(obj);
    }

    public int _update(T obj, String regex){
        return dao().update(obj,regex);
    }

    public int _updateIgnoreNull(Object obj){
        return dao().updateIgnoreNull(obj);
    }

    public T _updateWith(T obj, String regex){
        return dao().updateWith(obj,regex);
    }

    public T _updateLinks(T obj, String regex){
        return dao().updateLinks(obj,regex);
    }

    public int _delete(T obj){
        return dao().delete(obj);
    }

    public int _deleteWith(T obj, String regex){
        return dao().deleteWith(obj,regex);
    }

    public int _deleteLinks(T obj, String regex){
        return dao().deleteLinks(obj,regex);
    }

    public T _fetch(T obj){
        return dao().fetch(obj);
    }

    public T _fetchLinks(T obj, String regex){
        return dao().fetchLinks(obj,regex);
    }

    public T _fetchLinks(T obj, String regex, Condition cnd){
        return dao().fetchLinks(obj,regex,cnd);
    }

    public T _clearLinks(T obj, String regex){
        return dao().clearLinks(obj,regex);
    }

    public void setExpert(T obj) throws Exception{
         dao().setExpert(obj);
    }

    public List<T> query() {
        return dao().query(getEntityClass(),null);
    }
}