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

/**
 * Information about the input and output stack map frames of a basic block.
 *
 * @author Eric Bruneton
 */
class Frame {

    /*
     * Frames are computed in a two steps process: during the visit of each
     * instruction, the state of the frame at the end of current basic block is
     * updated by simulating the action of the instruction on the previous state
     * of this so called "output frame". In visitMaxs, a fix point algorithm is
     * used to compute the "input frame" of each basic block, i.e. the stack map
     * frame at the beginning of the basic block, starting from the input frame
     * of the first basic block (which is computed from the method descriptor),
     * and by using the previously computed output frames to compute the input
     * state of the other blocks.
     *
     * All output and input frames are stored as arrays of integers. Reference
     * and array types are represented by an index into a type table (which is
     * not the same as the constant pool of the class, in order to avoid adding
     * unnecessary constants in the pool - not all computed frames will end up
     * being stored in the stack map table). This allows very fast type
     * comparisons.
     *
     * Output stack map frames are computed relatively to the input frame of the
     * basic block, which is not yet known when output frames are computed. It
     * is therefore necessary to be able to represent abstract types such as
     * "the type at position x in the input frame locals" or "the type at
     * position x from the top of the input frame stack" or even "the type at
     * position x in the input frame, with y more (or less) array dimensions".
     * This explains the rather complicated type format used in output frames.
     *
     * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a
     * signed number of array dimensions (from -8 to 7). KIND is either BASE,
     * LOCAL or STACK. BASE is used for types that are not relative to the input
     * frame. LOCAL is used for types that are relative to the input local
     * variable types. STACK is used for types that are relative to the input
     * stack types. VALUE depends on KIND. For LOCAL types, it is an index in
     * the input local variable types. For STACK types, it is a position
     * relatively to the top of input frame stack. For BASE types, it is either
     * one of the constants defined below, or for OBJECT and UNINITIALIZED
     * types, a tag and an index in the type table.
     *
     * Output frames can contain types of any kind and with a positive or
     * negative dimension (and even unassigned types, represented by 0 - which
     * does not correspond to any valid type value). Input frames can only
     * contain BASE types of positive or null dimension. In all cases the type
     * table contains only internal type names (array type descriptors are
     * forbidden - dimensions must be represented through the DIM field).
     *
     * The LONG and DOUBLE types are always represented by using two slots (LONG
     * + TOP or DOUBLE + TOP), for local variable types as well as in the
     * operand stack. This is necessary to be able to simulate DUPx_y
     * instructions, whose effect would be dependent on the actual type values
     * if types were always represented by a single slot in the stack (and this
     * is not possible, since actual type values are not always known - cf LOCAL
     * and STACK type kinds).
     */

    /**
     * Mask to get the dimension of a frame type. This dimension is a signed
     * integer between -8 and 7.
     */
    static final int DIM = 0xF0000000;

    /**
     * Constant to be added to a type to get a type with one more dimension.
     */
    static final int ARRAY_OF = 0x10000000;

    /**
     * Constant to be added to a type to get a type with one less dimension.
     */
    static final int ELEMENT_OF = 0xF0000000;

    /**
     * Mask to get the kind of a frame type.
     *
     * @see #BASE
     * @see #LOCAL
     * @see #STACK
     */
    static final int KIND = 0xF000000;

    /**
     * Flag used for LOCAL and STACK types. Indicates that if this type happens
     * to be a long or double type (during the computations of input frames),
     * then it must be set to TOP because the second word of this value has been
     * reused to store other data in the basic block. Hence the first word no
     * longer stores a valid long or double value.
     */
    static final int TOP_IF_LONG_OR_DOUBLE = 0x800000;

    /**
     * Mask to get the value of a frame type.
     */
    static final int VALUE = 0x7FFFFF;

    /**
     * Mask to get the kind of base types.
     */
    static final int BASE_KIND = 0xFF00000;

    /**
     * Mask to get the value of base types.
     */
    static final int BASE_VALUE = 0xFFFFF;

    /**
     * Kind of the types that are not relative to an input stack map frame.
     */
    static final int BASE = 0x1000000;

    /**
     * Base kind of the base reference types. The BASE_VALUE of such types is an
     * index into the type table.
     */
    static final int OBJECT = BASE | 0x700000;

    /**
     * Base kind of the uninitialized base types. The BASE_VALUE of such types
     * in an index into the type table (the Item at that index contains both an
     * instruction offset and an internal class name).
     */
    static final int UNINITIALIZED = BASE | 0x800000;

    /**
     * Kind of the types that are relative to the local variable types of an
     * input stack map frame. The value of such types is a local variable index.
     */
    private static final int LOCAL = 0x2000000;

    /**
     * Kind of the the types that are relative to the stack of an input stack
     * map frame. The value of such types is a position relatively to the top of
     * this stack.
     */
    private static final int STACK = 0x3000000;

    /**
     * The TOP type. This is a BASE type.
     */
    static final int TOP = BASE | 0;

    /**
     * The BOOLEAN type. This is a BASE type mainly used for array types.
     */
    static final int BOOLEAN = BASE | 9;

    /**
     * The BYTE type. This is a BASE type mainly used for array types.
     */
    static final int BYTE = BASE | 10;

    /**
     * The CHAR type. This is a BASE type mainly used for array types.
     */
    static final int CHAR = BASE | 11;

    /**
     * The SHORT type. This is a BASE type mainly used for array types.
     */
    static final int SHORT = BASE | 12;

    /**
     * The INTEGER type. This is a BASE type.
     */
    static final int INTEGER = BASE | 1;

    /**
     * The FLOAT type. This is a BASE type.
     */
    static final int FLOAT = BASE | 2;

    /**
     * The DOUBLE type. This is a BASE type.
     */
    static final int DOUBLE = BASE | 3;

    /**
     * The LONG type. This is a BASE type.
     */
    static final int LONG = BASE | 4;

    /**
     * The NULL type. This is a BASE type.
     */
    static final int NULL = BASE | 5;

    /**
     * The UNINITIALIZED_THIS type. This is a BASE type.
     */
    static final int UNINITIALIZED_THIS = BASE | 6;

    /**
     * The stack size variation corresponding to each JVM instruction. This
     * stack variation is equal to the size of the values produced by an
     * instruction, minus the size of the values consumed by this instruction.
     */
    static final int[] SIZE;

    /**
     * Computes the stack size variation corresponding to each JVM instruction.
     */
    static {
        int i;
        int[] b = new int[202];
        String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD"
                + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD"
                + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED"
                + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
        for (i = 0; i < b.length; ++i) {
            b[i] = s.charAt(i) - 'E';
        }
        SIZE = b;

        // code to generate the above string
        //
        // int NA = 0; // not applicable (unused opcode or variable size opcode)
        //
        // b = new int[] {
        // 0, //NOP, // visitInsn
        // 1, //ACONST_NULL, // -
        // 1, //ICONST_M1, // -
        // 1, //ICONST_0, // -
        // 1, //ICONST_1, // -
        // 1, //ICONST_2, // -
        // 1, //ICONST_3, // -
        // 1, //ICONST_4, // -
        // 1, //ICONST_5, // -
        // 2, //LCONST_0, // -
        // 2, //LCONST_1, // -
        // 1, //FCONST_0, // -
        // 1, //FCONST_1, // -
        // 1, //FCONST_2, // -
        // 2, //DCONST_0, // -
        // 2, //DCONST_1, // -
        // 1, //BIPUSH, // visitIntInsn
        // 1, //SIPUSH, // -
        // 1, //LDC, // visitLdcInsn
        // NA, //LDC_W, // -
        // NA, //LDC2_W, // -
        // 1, //ILOAD, // visitVarInsn
        // 2, //LLOAD, // -
        // 1, //FLOAD, // -
        // 2, //DLOAD, // -
        // 1, //ALOAD, // -
        // NA, //ILOAD_0, // -
        // NA, //ILOAD_1, // -
        // NA, //ILOAD_2, // -
        // NA, //ILOAD_3, // -
        // NA, //LLOAD_0, // -
        // NA, //LLOAD_1, // -
        // NA, //LLOAD_2, // -
        // NA, //LLOAD_3, // -
        // NA, //FLOAD_0, // -
        // NA, //FLOAD_1, // -
        // NA, //FLOAD_2, // -
        // NA, //FLOAD_3, // -
        // NA, //DLOAD_0, // -
        // NA, //DLOAD_1, // -
        // NA, //DLOAD_2, // -
        // NA, //DLOAD_3, // -
        // NA, //ALOAD_0, // -
        // NA, //ALOAD_1, // -
        // NA, //ALOAD_2, // -
        // NA, //ALOAD_3, // -
        // -1, //IALOAD, // visitInsn
        // 0, //LALOAD, // -
        // -1, //FALOAD, // -
        // 0, //DALOAD, // -
        // -1, //AALOAD, // -
        // -1, //BALOAD, // -
        // -1, //CALOAD, // -
        // -1, //SALOAD, // -
        // -1, //ISTORE, // visitVarInsn
        // -2, //LSTORE, // -
        // -1, //FSTORE, // -
        // -2, //DSTORE, // -
        // -1, //ASTORE, // -
        // NA, //ISTORE_0, // -
        // NA, //ISTORE_1, // -
        // NA, //ISTORE_2, // -
        // NA, //ISTORE_3, // -
        // NA, //LSTORE_0, // -
        // NA, //LSTORE_1, // -
        // NA, //LSTORE_2, // -
        // NA, //LSTORE_3, // -
        // NA, //FSTORE_0, // -
        // NA, //FSTORE_1, // -
        // NA, //FSTORE_2, // -
        // NA, //FSTORE_3, // -
        // NA, //DSTORE_0, // -
        // NA, //DSTORE_1, // -
        // NA, //DSTORE_2, // -
        // NA, //DSTORE_3, // -
        // NA, //ASTORE_0, // -
        // NA, //ASTORE_1, // -
        // NA, //ASTORE_2, // -
        // NA, //ASTORE_3, // -
        // -3, //IASTORE, // visitInsn
        // -4, //LASTORE, // -
        // -3, //FASTORE, // -
        // -4, //DASTORE, // -
        // -3, //AASTORE, // -
        // -3, //BASTORE, // -
        // -3, //CASTORE, // -
        // -3, //SASTORE, // -
        // -1, //POP, // -
        // -2, //POP2, // -
        // 1, //DUP, // -
        // 1, //DUP_X1, // -
        // 1, //DUP_X2, // -
        // 2, //DUP2, // -
        // 2, //DUP2_X1, // -
        // 2, //DUP2_X2, // -
        // 0, //SWAP, // -
        // -1, //IADD, // -
        // -2, //LADD, // -
        // -1, //FADD, // -
        // -2, //DADD, // -
        // -1, //ISUB, // -
        // -2, //LSUB, // -
        // -1, //FSUB, // -
        // -2, //DSUB, // -
        // -1, //IMUL, // -
        // -2, //LMUL, // -
        // -1, //FMUL, // -
        // -2, //DMUL, // -
        // -1, //IDIV, // -
        // -2, //LDIV, // -
        // -1, //FDIV, // -
        // -2, //DDIV, // -
        // -1, //IREM, // -
        // -2, //LREM, // -
        // -1, //FREM, // -
        // -2, //DREM, // -
        // 0, //INEG, // -
        // 0, //LNEG, // -
        // 0, //FNEG, // -
        // 0, //DNEG, // -
        // -1, //ISHL, // -
        // -1, //LSHL, // -
        // -1, //ISHR, // -
        // -1, //LSHR, // -
        // -1, //IUSHR, // -
        // -1, //LUSHR, // -
        // -1, //IAND, // -
        // -2, //LAND, // -
        // -1, //IOR, // -
        // -2, //LOR, // -
        // -1, //IXOR, // -
        // -2, //LXOR, // -
        // 0, //IINC, // visitIincInsn
        // 1, //I2L, // visitInsn
        // 0, //I2F, // -
        // 1, //I2D, // -
        // -1, //L2I, // -
        // -1, //L2F, // -
        // 0, //L2D, // -
        // 0, //F2I, // -
        // 1, //F2L, // -
        // 1, //F2D, // -
        // -1, //D2I, // -
        // 0, //D2L, // -
        // -1, //D2F, // -
        // 0, //I2B, // -
        // 0, //I2C, // -
        // 0, //I2S, // -
        // -3, //LCMP, // -
        // -1, //FCMPL, // -
        // -1, //FCMPG, // -
        // -3, //DCMPL, // -
        // -3, //DCMPG, // -
        // -1, //IFEQ, // visitJumpInsn
        // -1, //IFNE, // -
        // -1, //IFLT, // -
        // -1, //IFGE, // -
        // -1, //IFGT, // -
        // -1, //IFLE, // -
        // -2, //IF_ICMPEQ, // -
        // -2, //IF_ICMPNE, // -
        // -2, //IF_ICMPLT, // -
        // -2, //IF_ICMPGE, // -
        // -2, //IF_ICMPGT, // -
        // -2, //IF_ICMPLE, // -
        // -2, //IF_ACMPEQ, // -
        // -2, //IF_ACMPNE, // -
        // 0, //GOTO, // -
        // 1, //JSR, // -
        // 0, //RET, // visitVarInsn
        // -1, //TABLESWITCH, // visiTableSwitchInsn
        // -1, //LOOKUPSWITCH, // visitLookupSwitch
        // -1, //IRETURN, // visitInsn
        // -2, //LRETURN, // -
        // -1, //FRETURN, // -
        // -2, //DRETURN, // -
        // -1, //ARETURN, // -
        // 0, //RETURN, // -
        // NA, //GETSTATIC, // visitFieldInsn
        // NA, //PUTSTATIC, // -
        // NA, //GETFIELD, // -
        // NA, //PUTFIELD, // -
        // NA, //INVOKEVIRTUAL, // visitMethodInsn
        // NA, //INVOKESPECIAL, // -
        // NA, //INVOKESTATIC, // -
        // NA, //INVOKEINTERFACE, // -
        // NA, //INVOKEDYNAMIC, // visitInvokeDynamicInsn
        // 1, //NEW, // visitTypeInsn
        // 0, //NEWARRAY, // visitIntInsn
        // 0, //ANEWARRAY, // visitTypeInsn
        // 0, //ARRAYLENGTH, // visitInsn
        // NA, //ATHROW, // -
        // 0, //CHECKCAST, // visitTypeInsn
        // 0, //INSTANCEOF, // -
        // -1, //MONITORENTER, // visitInsn
        // -1, //MONITOREXIT, // -
        // NA, //WIDE, // NOT VISITED
        // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn
        // -1, //IFNULL, // visitJumpInsn
        // -1, //IFNONNULL, // -
        // NA, //GOTO_W, // -
        // NA, //JSR_W, // -
        // };
        // for (i = 0; i < b.length; ++i) {
        // System.err.print((char)('E' + b[i]));
        // }
        // System.err.println();
    }

    /**
     * The label (i.e. basic block) to which these input and output stack map
     * frames correspond.
     */
    Label owner;

    /**
     * The input stack map frame locals.
     */
    int[] inputLocals;

    /**
     * The input stack map frame stack.
     */
    int[] inputStack;

    /**
     * The output stack map frame locals.
     */
    private int[] outputLocals;

    /**
     * The output stack map frame stack.
     */
    private int[] outputStack;

    /**
     * Relative size of the output stack. The exact semantics of this field
     * depends on the algorithm that is used.
     *
     * When only the maximum stack size is computed, this field is the size of
     * the output stack relatively to the top of the input stack.
     *
     * When the stack map frames are completely computed, this field is the
     * actual number of types in {@link #outputStack}.
     */
    int outputStackTop;

    /**
     * Number of types that are initialized in the basic block.
     *
     * @see #initializations
     */
    private int initializationCount;

    /**
     * The types that are initialized in the basic block. A constructor
     * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace
     * <i>every occurence</i> of this type in the local variables and in the
     * operand stack. This cannot be done during the first phase of the
     * algorithm since, during this phase, the local variables and the operand
     * stack are not completely computed. It is therefore necessary to store the
     * types on which constructors are invoked in the basic block, in order to
     * do this replacement during the second phase of the algorithm, where the
     * frames are fully computed. Note that this array can contain types that
     * are relative to input locals or to the input stack (see below for the
     * description of the algorithm).
     */
    private int[] initializations;

    /**
     * Sets this frame to the given value.
     *
     * @param cw
     *            the ClassWriter to which this label belongs.
     * @param nLocal
     *            the number of local variables.
     * @param local
     *            the local variable types. Primitive types are represented by
     *            {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
     *            {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
     *            {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
     *            {@link Opcodes#UNINITIALIZED_THIS} (long and double are
     *            represented by a single element). Reference types are
     *            represented by String objects (representing internal names),
     *            and uninitialized types by Label objects (this label
     *            designates the NEW instruction that created this uninitialized
     *            value).
     * @param nStack
     *            the number of operand stack elements.
     * @param stack
     *            the operand stack types (same format as the "local" array).
     */
    final void set(ClassWriter cw, final int nLocal, final Object[] local,
            final int nStack, final Object[] stack) {
        int i = convert(cw, nLocal, local, inputLocals);
        while (i < local.length) {
            inputLocals[i++] = TOP;
        }
        int nStackTop = 0;
        for (int j = 0; j < nStack; ++j) {
            if (stack[j] == Opcodes.LONG || stack[j] == Opcodes.DOUBLE) {
                ++nStackTop;
            }
        }
        inputStack = new int[nStack + nStackTop];
        convert(cw, nStack, stack, inputStack);
        outputStackTop = 0;
        initializationCount = 0;
    }

    /**
     * Converts types from the MethodWriter.visitFrame() format to the Frame
     * format.
     *
     * @param cw
     *            the ClassWriter to which this label belongs.
     * @param nInput
     *            the number of types to convert.
     * @param input
     *            the types to convert. Primitive types are represented by
     *            {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
     *            {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
     *            {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
     *            {@link Opcodes#UNINITIALIZED_THIS} (long and double are
     *            represented by a single element). Reference types are
     *            represented by String objects (representing internal names),
     *            and uninitialized types by Label objects (this label
     *            designates the NEW instruction that created this uninitialized
     *            value).
     * @param output
     *            where to store the converted types.
     * @return the number of output elements.
     */
    private static int convert(ClassWriter cw, int nInput, Object[] input,
            int[] output) {
        int i = 0;
        for (int j = 0; j < nInput; ++j) {
            if (input[j] instanceof Integer) {
                output[i++] = BASE | ((Integer) input[j]).intValue();
                if (input[j] == Opcodes.LONG || input[j] == Opcodes.DOUBLE) {
                    output[i++] = TOP;
                }
            } else if (input[j] instanceof String) {
                output[i++] = type(cw, Type.getObjectType((String) input[j])
                        .getDescriptor());
            } else {
                output[i++] = UNINITIALIZED
                        | cw.addUninitializedType("",
                                ((Label) input[j]).position);
            }
        }
        return i;
    }

    /**
     * Sets this frame to the value of the given frame. WARNING: after this
     * method is called the two frames share the same data structures. It is
     * recommended to discard the given frame f to avoid unexpected side
     * effects.
     *
     * @param f
     *            The new frame value.
     */
    final void set(final Frame f) {
        inputLocals = f.inputLocals;
        inputStack = f.inputStack;
        outputLocals = f.outputLocals;
        outputStack = f.outputStack;
        outputStackTop = f.outputStackTop;
        initializationCount = f.initializationCount;
        initializations = f.initializations;
    }

    /**
     * Returns the output frame local variable type at the given index.
     *
     * @param local
     *            the index of the local that must be returned.
     * @return the output frame local variable type at the given index.
     */
    private int get(final int local) {
        if (outputLocals == null || local >= outputLocals.length) {
            // this local has never been assigned in this basic block,
            // so it is still equal to its value in the input frame
            return LOCAL | local;
        } else {
            int type = outputLocals[local];
            if (type == 0) {
                // this local has never been assigned in this basic block,
                // so it is still equal to its value in the input frame
                type = outputLocals[local] = LOCAL | local;
            }
            return type;
        }
    }

    /**
     * Sets the output frame local variable type at the given index.
     *
     * @param local
     *            the index of the local that must be set.
     * @param type
     *            the value of the local that must be set.
     */
    private void set(final int local, final int type) {
        // creates and/or resizes the output local variables array if necessary
        if (outputLocals == null) {
            outputLocals = new int[10];
        }
        int n = outputLocals.length;
        if (local >= n) {
            int[] t = new int[Math.max(local + 1, 2 * n)];
            System.arraycopy(outputLocals, 0, t, 0, n);
            outputLocals = t;
        }
        // sets the local variable
        outputLocals[local] = type;
    }

    /**
     * Pushes a new type onto the output frame stack.
     *
     * @param type
     *            the type that must be pushed.
     */
    private void push(final int type) {
        // creates and/or resizes the output stack array if necessary
        if (outputStack == null) {
            outputStack = new int[10];
        }
        int n = outputStack.length;
        if (outputStackTop >= n) {
            int[] t = new int[Math.max(outputStackTop + 1, 2 * n)];
            System.arraycopy(outputStack, 0, t, 0, n);
            outputStack = t;
        }
        // pushes the type on the output stack
        outputStack[outputStackTop++] = type;
        // updates the maximum height reached by the output stack, if needed
        int top = owner.inputStackTop + outputStackTop;
        if (top > owner.outputStackMax) {
            owner.outputStackMax = top;
        }
    }

    /**
     * Pushes a new type onto the output frame stack.
     *
     * @param cw
     *            the ClassWriter to which this label belongs.
     * @param desc
     *            the descriptor of the type to be pushed. Can also be a method
     *            descriptor (in this case this method pushes its return type
     *            onto the output frame stack).
     */
    private void push(final ClassWriter cw, final String desc) {
        int type = type(cw, desc);
        if (type != 0) {
            push(type);
            if (type == LONG || type == DOUBLE) {
                push(TOP);
            }
        }
    }

    /**
     * Returns the int encoding of the given type.
     *
     * @param cw
     *            the ClassWriter to which this label belongs.
     * @param desc
     *            a type descriptor.
     * @return the int encoding of the given type.
     */
    private static int type(final ClassWriter cw, final String desc) {
        String t;
        int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
        switch (desc.charAt(index)) {
        case 'V':
            return 0;
        case 'Z':
        case 'C':
        case 'B':
        case 'S':
        case 'I':
            return INTEGER;
        case 'F':
            return FLOAT;
        case 'J':
            return LONG;
        case 'D':
            return DOUBLE;
        case 'L':
            // stores the internal name, not the descriptor!
            t = desc.substring(index + 1, desc.length() - 1);
            return OBJECT | cw.addType(t);
            // case '[':
        default:
            // extracts the dimensions and the element type
            int data;
            int dims = index + 1;
            while (desc.charAt(dims) == '[') {
                ++dims;
            }
            switch (desc.charAt(dims)) {
            case 'Z':
                data = BOOLEAN;
                break;
            case 'C':
                data = CHAR;
                break;
            case 'B':
                data = BYTE;
                break;
            case 'S':
                data = SHORT;
                break;
            case 'I':
                data = INTEGER;
                break;
            case 'F':
                data = FLOAT;
                break;
            case 'J':
                data = LONG;
                break;
            case 'D':
                data = DOUBLE;
                break;
            // case 'L':
            default:
                // stores the internal name, not the descriptor
                t = desc.substring(dims + 1, desc.length() - 1);
                data = OBJECT | cw.addType(t);
            }
            return (dims - index) << 28 | data;
        }
    }

    /**
     * Pops a type from the output frame stack and returns its value.
     *
     * @return the type that has been popped from the output frame stack.
     */
    private int pop() {
        if (outputStackTop > 0) {
            return outputStack[--outputStackTop];
        } else {
            // if the output frame stack is empty, pops from the input stack
            return STACK | -(--owner.inputStackTop);
        }
    }

    /**
     * Pops the given number of types from the output frame stack.
     *
     * @param elements
     *            the number of types that must be popped.
     */
    private void pop(final int elements) {
        if (outputStackTop >= elements) {
            outputStackTop -= elements;
        } else {
            // if the number of elements to be popped is greater than the number
            // of elements in the output stack, clear it, and pops the remaining
            // elements from the input stack.
            owner.inputStackTop -= elements - outputStackTop;
            outputStackTop = 0;
        }
    }

    /**
     * Pops a type from the output frame stack.
     *
     * @param desc
     *            the descriptor of the type to be popped. Can also be a method
     *            descriptor (in this case this method pops the types
     *            corresponding to the method arguments).
     */
    private void pop(final String desc) {
        char c = desc.charAt(0);
        if (c == '(') {
            pop((Type.getArgumentsAndReturnSizes(desc) >> 2) - 1);
        } else if (c == 'J' || c == 'D') {
            pop(2);
        } else {
            pop(1);
        }
    }

    /**
     * Adds a new type to the list of types on which a constructor is invoked in
     * the basic block.
     *
     * @param var
     *            a type on a which a constructor is invoked.
     */
    private void init(final int var) {
        // creates and/or resizes the initializations array if necessary
        if (initializations == null) {
            initializations = new int[2];
        }
        int n = initializations.length;
        if (initializationCount >= n) {
            int[] t = new int[Math.max(initializationCount + 1, 2 * n)];
            System.arraycopy(initializations, 0, t, 0, n);
            initializations = t;
        }
        // stores the type to be initialized
        initializations[initializationCount++] = var;
    }

    /**
     * Replaces the given type with the appropriate type if it is one of the
     * types on which a constructor is invoked in the basic block.
     *
     * @param cw
     *            the ClassWriter to which this label belongs.
     * @param t
     *            a type
     * @return t or, if t is one of the types on which a constructor is invoked
     *         in the basic block, the type corresponding to this constructor.
     */
    private int init(final ClassWriter cw, final int t) {
        int s;
        if (t == UNINITIALIZED_THIS) {
            s = OBJECT | cw.addType(cw.thisName);
        } else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) {
            String type = cw.typeTable[t & BASE_VALUE].strVal1;
            s = OBJECT | cw.addType(type);
        } else {
            return t;
        }
        for (int j = 0; j < initializationCount; ++j) {
            int u = initializations[j];
            int dim = u & DIM;
            int kind = u & KIND;
            if (kind == LOCAL) {
                u = dim + inputLocals[u & VALUE];
            } else if (kind == STACK) {
                u = dim + inputStack[inputStack.length - (u & VALUE)];
            }
            if (t == u) {
                return s;
            }
        }
        return t;
    }

    /**
     * Initializes the input frame of the first basic block from the method
     * descriptor.
     *
     * @param cw
     *            the ClassWriter to which this label belongs.
     * @param access
     *            the access flags of the method to which this label belongs.
     * @param args
     *            the formal parameter types of this method.
     * @param maxLocals
     *            the maximum number of local variables of this method.
     */
    final void initInputFrame(final ClassWriter cw, final int access,
            final Type[] args, final int maxLocals) {
        inputLocals = new int[maxLocals];
        inputStack = new int[0];
        int i = 0;
        if ((access & Opcodes.ACC_STATIC) == 0) {
            if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) {
                inputLocals[i++] = OBJECT | cw.addType(cw.thisName);
            } else {
                inputLocals[i++] = UNINITIALIZED_THIS;
            }
        }
        for (int j = 0; j < args.length; ++j) {
            int t = type(cw, args[j].getDescriptor());
            inputLocals[i++] = t;
            if (t == LONG || t == DOUBLE) {
                inputLocals[i++] = TOP;
            }
        }
        while (i < maxLocals) {
            inputLocals[i++] = TOP;
        }
    }

    /**
     * Simulates the action of the given instruction on the output stack frame.
     *
     * @param opcode
     *            the opcode of the instruction.
     * @param arg
     *            the operand of the instruction, if any.
     * @param cw
     *            the class writer to which this label belongs.
     * @param item
     *            the operand of the instructions, if any.
     */
    void execute(final int opcode, final int arg, final ClassWriter cw,
            final Item item) {
        int t1, t2, t3, t4;
        switch (opcode) {
        case Opcodes.NOP:
        case Opcodes.INEG:
        case Opcodes.LNEG:
        case Opcodes.FNEG:
        case Opcodes.DNEG:
        case Opcodes.I2B:
        case Opcodes.I2C:
        case Opcodes.I2S:
        case Opcodes.GOTO:
        case Opcodes.RETURN:
            break;
        case Opcodes.ACONST_NULL:
            push(NULL);
            break;
        case Opcodes.ICONST_M1:
        case Opcodes.ICONST_0:
        case Opcodes.ICONST_1:
        case Opcodes.ICONST_2:
        case Opcodes.ICONST_3:
        case Opcodes.ICONST_4:
        case Opcodes.ICONST_5:
        case Opcodes.BIPUSH:
        case Opcodes.SIPUSH:
        case Opcodes.ILOAD:
            push(INTEGER);
            break;
        case Opcodes.LCONST_0:
        case Opcodes.LCONST_1:
        case Opcodes.LLOAD:
            push(LONG);
            push(TOP);
            break;
        case Opcodes.FCONST_0:
        case Opcodes.FCONST_1:
        case Opcodes.FCONST_2:
        case Opcodes.FLOAD:
            push(FLOAT);
            break;
        case Opcodes.DCONST_0:
        case Opcodes.DCONST_1:
        case Opcodes.DLOAD:
            push(DOUBLE);
            push(TOP);
            break;
        case Opcodes.LDC:
            switch (item.type) {
            case ClassWriter.INT:
                push(INTEGER);
                break;
            case ClassWriter.LONG:
                push(LONG);
                push(TOP);
                break;
            case ClassWriter.FLOAT:
                push(FLOAT);
                break;
            case ClassWriter.DOUBLE:
                push(DOUBLE);
                push(TOP);
                break;
            case ClassWriter.CLASS:
                push(OBJECT | cw.addType("java/lang/Class"));
                break;
            case ClassWriter.STR:
                push(OBJECT | cw.addType("java/lang/String"));
                break;
            case ClassWriter.MTYPE:
                push(OBJECT | cw.addType("java/lang/invoke/MethodType"));
                break;
            // case ClassWriter.HANDLE_BASE + [1..9]:
            default:
                push(OBJECT | cw.addType("java/lang/invoke/MethodHandle"));
            }
            break;
        case Opcodes.ALOAD:
            push(get(arg));
            break;
        case Opcodes.IALOAD:
        case Opcodes.BALOAD:
        case Opcodes.CALOAD:
        case Opcodes.SALOAD:
            pop(2);
            push(INTEGER);
            break;
        case Opcodes.LALOAD:
        case Opcodes.D2L:
            pop(2);
            push(LONG);
            push(TOP);
            break;
        case Opcodes.FALOAD:
            pop(2);
            push(FLOAT);
            break;
        case Opcodes.DALOAD:
        case Opcodes.L2D:
            pop(2);
            push(DOUBLE);
            push(TOP);
            break;
        case Opcodes.AALOAD:
            pop(1);
            t1 = pop();
            push(ELEMENT_OF + t1);
            break;
        case Opcodes.ISTORE:
        case Opcodes.FSTORE:
        case Opcodes.ASTORE:
            t1 = pop();
            set(arg, t1);
            if (arg > 0) {
                t2 = get(arg - 1);
                // if t2 is of kind STACK or LOCAL we cannot know its size!
                if (t2 == LONG || t2 == DOUBLE) {
                    set(arg - 1, TOP);
                } else if ((t2 & KIND) != BASE) {
                    set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE);
                }
            }
            break;
        case Opcodes.LSTORE:
        case Opcodes.DSTORE:
            pop(1);
            t1 = pop();
            set(arg, t1);
            set(arg + 1, TOP);
            if (arg > 0) {
                t2 = get(arg - 1);
                // if t2 is of kind STACK or LOCAL we cannot know its size!
                if (t2 == LONG || t2 == DOUBLE) {
                    set(arg - 1, TOP);
                } else if ((t2 & KIND) != BASE) {
                    set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE);
                }
            }
            break;
        case Opcodes.IASTORE:
        case Opcodes.BASTORE:
        case Opcodes.CASTORE:
        case Opcodes.SASTORE:
        case Opcodes.FASTORE:
        case Opcodes.AASTORE:
            pop(3);
            break;
        case Opcodes.LASTORE:
        case Opcodes.DASTORE:
            pop(4);
            break;
        case Opcodes.POP:
        case Opcodes.IFEQ:
        case Opcodes.IFNE:
        case Opcodes.IFLT:
        case Opcodes.IFGE:
        case Opcodes.IFGT:
        case Opcodes.IFLE:
        case Opcodes.IRETURN:
        case Opcodes.FRETURN:
        case Opcodes.ARETURN:
        case Opcodes.TABLESWITCH:
        case Opcodes.LOOKUPSWITCH:
        case Opcodes.ATHROW:
        case Opcodes.MONITORENTER:
        case Opcodes.MONITOREXIT:
        case Opcodes.IFNULL:
        case Opcodes.IFNONNULL:
            pop(1);
            break;
        case Opcodes.POP2:
        case Opcodes.IF_ICMPEQ:
        case Opcodes.IF_ICMPNE:
        case Opcodes.IF_ICMPLT:
        case Opcodes.IF_ICMPGE:
        case Opcodes.IF_ICMPGT:
        case Opcodes.IF_ICMPLE:
        case Opcodes.IF_ACMPEQ:
        case Opcodes.IF_ACMPNE:
        case Opcodes.LRETURN:
        case Opcodes.DRETURN:
            pop(2);
            break;
        case Opcodes.DUP:
            t1 = pop();
            push(t1);
            push(t1);
            break;
        case Opcodes.DUP_X1:
            t1 = pop();
            t2 = pop();
            push(t1);
            push(t2);
            push(t1);
            break;
        case Opcodes.DUP_X2:
            t1 = pop();
            t2 = pop();
            t3 = pop();
            push(t1);
            push(t3);
            push(t2);
            push(t1);
            break;
        case Opcodes.DUP2:
            t1 = pop();
            t2 = pop();
            push(t2);
            push(t1);
            push(t2);
            push(t1);
            break;
        case Opcodes.DUP2_X1:
            t1 = pop();
            t2 = pop();
            t3 = pop();
            push(t2);
            push(t1);
            push(t3);
            push(t2);
            push(t1);
            break;
        case Opcodes.DUP2_X2:
            t1 = pop();
            t2 = pop();
            t3 = pop();
            t4 = pop();
            push(t2);
            push(t1);
            push(t4);
            push(t3);
            push(t2);
            push(t1);
            break;
        case Opcodes.SWAP:
            t1 = pop();
            t2 = pop();
            push(t1);
            push(t2);
            break;
        case Opcodes.IADD:
        case Opcodes.ISUB:
        case Opcodes.IMUL:
        case Opcodes.IDIV:
        case Opcodes.IREM:
        case Opcodes.IAND:
        case Opcodes.IOR:
        case Opcodes.IXOR:
        case Opcodes.ISHL:
        case Opcodes.ISHR:
        case Opcodes.IUSHR:
        case Opcodes.L2I:
        case Opcodes.D2I:
        case Opcodes.FCMPL:
        case Opcodes.FCMPG:
            pop(2);
            push(INTEGER);
            break;
        case Opcodes.LADD:
        case Opcodes.LSUB:
        case Opcodes.LMUL:
        case Opcodes.LDIV:
        case Opcodes.LREM:
        case Opcodes.LAND:
        case Opcodes.LOR:
        case Opcodes.LXOR:
            pop(4);
            push(LONG);
            push(TOP);
            break;
        case Opcodes.FADD:
        case Opcodes.FSUB:
        case Opcodes.FMUL:
        case Opcodes.FDIV:
        case Opcodes.FREM:
        case Opcodes.L2F:
        case Opcodes.D2F:
            pop(2);
            push(FLOAT);
            break;
        case Opcodes.DADD:
        case Opcodes.DSUB:
        case Opcodes.DMUL:
        case Opcodes.DDIV:
        case Opcodes.DREM:
            pop(4);
            push(DOUBLE);
            push(TOP);
            break;
        case Opcodes.LSHL:
        case Opcodes.LSHR:
        case Opcodes.LUSHR:
            pop(3);
            push(LONG);
            push(TOP);
            break;
        case Opcodes.IINC:
            set(arg, INTEGER);
            break;
        case Opcodes.I2L:
        case Opcodes.F2L:
            pop(1);
            push(LONG);
            push(TOP);
            break;
        case Opcodes.I2F:
            pop(1);
            push(FLOAT);
            break;
        case Opcodes.I2D:
        case Opcodes.F2D:
            pop(1);
            push(DOUBLE);
            push(TOP);
            break;
        case Opcodes.F2I:
        case Opcodes.ARRAYLENGTH:
        case Opcodes.INSTANCEOF:
            pop(1);
            push(INTEGER);
            break;
        case Opcodes.LCMP:
        case Opcodes.DCMPL:
        case Opcodes.DCMPG:
            pop(4);
            push(INTEGER);
            break;
        case Opcodes.JSR:
        case Opcodes.RET:
            throw new RuntimeException(
                    "JSR/RET are not supported with computeFrames option");
        case Opcodes.GETSTATIC:
            push(cw, item.strVal3);
            break;
        case Opcodes.PUTSTATIC:
            pop(item.strVal3);
            break;
        case Opcodes.GETFIELD:
            pop(1);
            push(cw, item.strVal3);
            break;
        case Opcodes.PUTFIELD:
            pop(item.strVal3);
            pop();
            break;
        case Opcodes.INVOKEVIRTUAL:
        case Opcodes.INVOKESPECIAL:
        case Opcodes.INVOKESTATIC:
        case Opcodes.INVOKEINTERFACE:
            pop(item.strVal3);
            if (opcode != Opcodes.INVOKESTATIC) {
                t1 = pop();
                if (opcode == Opcodes.INVOKESPECIAL
                        && item.strVal2.charAt(0) == '<') {
                    init(t1);
                }
            }
            push(cw, item.strVal3);
            break;
        case Opcodes.INVOKEDYNAMIC:
            pop(item.strVal2);
            push(cw, item.strVal2);
            break;
        case Opcodes.NEW:
            push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg));
            break;
        case Opcodes.NEWARRAY:
            pop();
            switch (arg) {
            case Opcodes.T_BOOLEAN:
                push(ARRAY_OF | BOOLEAN);
                break;
            case Opcodes.T_CHAR:
                push(ARRAY_OF | CHAR);
                break;
            case Opcodes.T_BYTE:
                push(ARRAY_OF | BYTE);
                break;
            case Opcodes.T_SHORT:
                push(ARRAY_OF | SHORT);
                break;
            case Opcodes.T_INT:
                push(ARRAY_OF | INTEGER);
                break;
            case Opcodes.T_FLOAT:
                push(ARRAY_OF | FLOAT);
                break;
            case Opcodes.T_DOUBLE:
                push(ARRAY_OF | DOUBLE);
                break;
            // case Opcodes.T_LONG:
            default:
                push(ARRAY_OF | LONG);
                break;
            }
            break;
        case Opcodes.ANEWARRAY:
            String s = item.strVal1;
            pop();
            if (s.charAt(0) == '[') {
                push(cw, '[' + s);
            } else {
                push(ARRAY_OF | OBJECT | cw.addType(s));
            }
            break;
        case Opcodes.CHECKCAST:
            s = item.strVal1;
            pop();
            if (s.charAt(0) == '[') {
                push(cw, s);
            } else {
                push(OBJECT | cw.addType(s));
            }
            break;
        // case Opcodes.MULTIANEWARRAY:
        default:
            pop(arg);
            push(cw, item.strVal1);
            break;
        }
    }

    /**
     * Merges the input frame of the given basic block with the input and output
     * frames of this basic block. Returns <tt>true</tt> if the input frame of
     * the given label has been changed by this operation.
     *
     * @param cw
     *            the ClassWriter to which this label belongs.
     * @param frame
     *            the basic block whose input frame must be updated.
     * @param edge
     *            the kind of the {@link Edge} between this label and 'label'.
     *            See {@link Edge#info}.
     * @return <tt>true</tt> if the input frame of the given label has been
     *         changed by this operation.
     */
    final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
        boolean changed = false;
        int i, s, dim, kind, t;

        int nLocal = inputLocals.length;
        int nStack = inputStack.length;
        if (frame.inputLocals == null) {
            frame.inputLocals = new int[nLocal];
            changed = true;
        }

        for (i = 0; i < nLocal; ++i) {
            if (outputLocals != null && i < outputLocals.length) {
                s = outputLocals[i];
                if (s == 0) {
                    t = inputLocals[i];
                } else {
                    dim = s & DIM;
                    kind = s & KIND;
                    if (kind == BASE) {
                        t = s;
                    } else {
                        if (kind == LOCAL) {
                            t = dim + inputLocals[s & VALUE];
                        } else {
                            t = dim + inputStack[nStack - (s & VALUE)];
                        }
                        if ((s & TOP_IF_LONG_OR_DOUBLE) != 0
                                && (t == LONG || t == DOUBLE)) {
                            t = TOP;
                        }
                    }
                }
            } else {
                t = inputLocals[i];
            }
            if (initializations != null) {
                t = init(cw, t);
            }
            changed |= merge(cw, t, frame.inputLocals, i);
        }

        if (edge > 0) {
            for (i = 0; i < nLocal; ++i) {
                t = inputLocals[i];
                changed |= merge(cw, t, frame.inputLocals, i);
            }
            if (frame.inputStack == null) {
                frame.inputStack = new int[1];
                changed = true;
            }
            changed |= merge(cw, edge, frame.inputStack, 0);
            return changed;
        }

        int nInputStack = inputStack.length + owner.inputStackTop;
        if (frame.inputStack == null) {
            frame.inputStack = new int[nInputStack + outputStackTop];
            changed = true;
        }

        for (i = 0; i < nInputStack; ++i) {
            t = inputStack[i];
            if (initializations != null) {
                t = init(cw, t);
            }
            changed |= merge(cw, t, frame.inputStack, i);
        }
        for (i = 0; i < outputStackTop; ++i) {
            s = outputStack[i];
            dim = s & DIM;
            kind = s & KIND;
            if (kind == BASE) {
                t = s;
            } else {
                if (kind == LOCAL) {
                    t = dim + inputLocals[s & VALUE];
                } else {
                    t = dim + inputStack[nStack - (s & VALUE)];
                }
                if ((s & TOP_IF_LONG_OR_DOUBLE) != 0
                        && (t == LONG || t == DOUBLE)) {
                    t = TOP;
                }
            }
            if (initializations != null) {
                t = init(cw, t);
            }
            changed |= merge(cw, t, frame.inputStack, nInputStack + i);
        }
        return changed;
    }

    /**
     * Merges the type at the given index in the given type array with the given
     * type. Returns <tt>true</tt> if the type array has been modified by this
     * operation.
     *
     * @param cw
     *            the ClassWriter to which this label belongs.
     * @param t
     *            the type with which the type array element must be merged.
     * @param types
     *            an array of types.
     * @param index
     *            the index of the type that must be merged in 'types'.
     * @return <tt>true</tt> if the type array has been modified by this
     *         operation.
     */
    private static boolean merge(final ClassWriter cw, int t,
            final int[] types, final int index) {
        int u = types[index];
        if (u == t) {
            // if the types are equal, merge(u,t)=u, so there is no change
            return false;
        }
        if ((t & ~DIM) == NULL) {
            if (u == NULL) {
                return false;
            }
            t = NULL;
        }
        if (u == 0) {
            // if types[index] has never been assigned, merge(u,t)=t
            types[index] = t;
            return true;
        }
        int v;
        if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) {
            // if u is a reference type of any dimension
            if (t == NULL) {
                // if t is the NULL type, merge(u,t)=u, so there is no change
                return false;
            } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) {
                // if t and u have the same dimension and same base kind
                if ((u & BASE_KIND) == OBJECT) {
                    // if t is also a reference type, and if u and t have the
                    // same dimension merge(u,t) = dim(t) | common parent of the
                    // element types of u and t
                    v = (t & DIM) | OBJECT
                            | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE);
                } else {
                    // if u and t are array types, but not with the same element
                    // type, merge(u,t) = dim(u) - 1 | java/lang/Object
                    int vdim = ELEMENT_OF + (u & DIM);
                    v = vdim | OBJECT | cw.addType("java/lang/Object");
                }
            } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) {
                // if t is any other reference or array type, the merged type
                // is min(udim, tdim) | java/lang/Object, where udim is the
                // array dimension of u, minus 1 if u is an array type with a
                // primitive element type (and similarly for tdim).
                int tdim = (((t & DIM) == 0 || (t & BASE_KIND) == OBJECT) ? 0
                        : ELEMENT_OF) + (t & DIM);
                int udim = (((u & DIM) == 0 || (u & BASE_KIND) == OBJECT) ? 0
                        : ELEMENT_OF) + (u & DIM);
                v = Math.min(tdim, udim) | OBJECT
                        | cw.addType("java/lang/Object");
            } else {
                // if t is any other type, merge(u,t)=TOP
                v = TOP;
            }
        } else if (u == NULL) {
            // if u is the NULL type, merge(u,t)=t,
            // or TOP if t is not a reference type
            v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP;
        } else {
            // if u is any other type, merge(u,t)=TOP whatever t
            v = TOP;
        }
        if (u != v) {
            types[index] = v;
            return true;
        }
        return false;
    }
}


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

/**
 * A visitor to visit a Java class. The methods of this class must be called in
 * the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
 * <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
 * <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
 * <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
 * <tt>visitEnd</tt>.
 *
 * @author Eric Bruneton
 */
public abstract class ClassVisitor {

    /**
     * The ASM API version implemented by this visitor. The value of this field
     * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
     */
    protected final int api;

    /**
     * The class visitor to which this visitor must delegate method calls. May
     * be null.
     */
    protected ClassVisitor cv;

    /**
     * Constructs a new {@link ClassVisitor}.
     *
     * @param api
     *            the ASM API version implemented by this visitor. Must be one
     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
     */
    public ClassVisitor(final int api) {
        this(api, null);
    }

    /**
     * Constructs a new {@link ClassVisitor}.
     *
     * @param api
     *            the ASM API version implemented by this visitor. Must be one
     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
     * @param cv
     *            the class visitor to which this visitor must delegate method
     *            calls. May be null.
     */
    public ClassVisitor(final int api, final ClassVisitor cv) {
        if (api != Opcodes.ASM4 && api != Opcodes.ASM5) {
            throw new IllegalArgumentException();
        }
        this.api = api;
        this.cv = cv;
    }

    /**
     * Visits the header of the class.
     *
     * @param version
     *            the class version.
     * @param access
     *            the class's access flags (see {@link Opcodes}). This parameter
     *            also indicates if the class is deprecated.
     * @param name
     *            the internal name of the class (see
     *            {@link Type#getInternalName() getInternalName}).
     * @param signature
     *            the signature of this class. May be <tt>null</tt> if the class
     *            is not a generic one, and does not extend or implement generic
     *            classes or interfaces.
     * @param superName
     *            the internal of name of the super class (see
     *            {@link Type#getInternalName() getInternalName}). For
     *            interfaces, the super class is {@link Object}. May be
     *            <tt>null</tt>, but only for the {@link Object} class.
     * @param interfaces
     *            the internal names of the class's interfaces (see
     *            {@link Type#getInternalName() getInternalName}). May be
     *            <tt>null</tt>.
     */
    public void visit(int version, int access, String name, String signature,
            String superName, String[] interfaces) {
        if (cv != null) {
            cv.visit(version, access, name, signature, superName, interfaces);
        }
    }

    /**
     * Visits the source of the class.
     *
     * @param source
     *            the name of the source file from which the class was compiled.
     *            May be <tt>null</tt>.
     * @param debug
     *            additional debug information to compute the correspondance
     *            between source and compiled elements of the class. May be
     *            <tt>null</tt>.
     */
    public void visitSource(String source, String debug) {
        if (cv != null) {
            cv.visitSource(source, debug);
        }
    }

    /**
     * Visits the enclosing class of the class. This method must be called only
     * if the class has an enclosing class.
     *
     * @param owner
     *            internal name of the enclosing class of the class.
     * @param name
     *            the name of the method that contains the class, or
     *            <tt>null</tt> if the class is not enclosed in a method of its
     *            enclosing class.
     * @param desc
     *            the descriptor of the method that contains the class, or
     *            <tt>null</tt> if the class is not enclosed in a method of its
     *            enclosing class.
     */
    public void visitOuterClass(String owner, String name, String desc) {
        if (cv != null) {
            cv.visitOuterClass(owner, name, desc);
        }
    }

    /**
     * Visits an annotation of the class.
     *
     * @param desc
     *            the class descriptor of the annotation class.
     * @param visible
     *            <tt>true</tt> if the annotation is visible at runtime.
     * @return a visitor to visit the annotation values, or <tt>null</tt> if
     *         this visitor is not interested in visiting this annotation.
     */
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        if (cv != null) {
            return cv.visitAnnotation(desc, visible);
        }
        return null;
    }

    /**
     * Visits an annotation on a type in the class signature.
     *
     * @param typeRef
     *            a reference to the annotated type. The sort of this type
     *            reference must be {@link TypeReference#CLASS_TYPE_PARAMETER
     *            CLASS_TYPE_PARAMETER},
     *            {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND
     *            CLASS_TYPE_PARAMETER_BOUND} or
     *            {@link TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. See
     *            {@link TypeReference}.
     * @param typePath
     *            the path to the annotated type argument, wildcard bound, array
     *            element type, or static inner type within 'typeRef'. May be
     *            <tt>null</tt> if the annotation targets 'typeRef' as a whole.
     * @param desc
     *            the class descriptor of the annotation class.
     * @param visible
     *            <tt>true</tt> if the annotation is visible at runtime.
     * @return a visitor to visit the annotation values, or <tt>null</tt> if
     *         this visitor is not interested in visiting this annotation.
     */
    public AnnotationVisitor visitTypeAnnotation(int typeRef,
            TypePath typePath, String desc, boolean visible) {
        if (api < Opcodes.ASM5) {
            throw new RuntimeException();
        }
        if (cv != null) {
            return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
        }
        return null;
    }

    /**
     * Visits a non standard attribute of the class.
     *
     * @param attr
     *            an attribute.
     */
    public void visitAttribute(Attribute attr) {
        if (cv != null) {
            cv.visitAttribute(attr);
        }
    }

    /**
     * Visits information about an inner class. This inner class is not
     * necessarily a member of the class being visited.
     *
     * @param name
     *            the internal name of an inner class (see
     *            {@link Type#getInternalName() getInternalName}).
     * @param outerName
     *            the internal name of the class to which the inner class
     *            belongs (see {@link Type#getInternalName() getInternalName}).
     *            May be <tt>null</tt> for not member classes.
     * @param innerName
     *            the (simple) name of the inner class inside its enclosing
     *            class. May be <tt>null</tt> for anonymous inner classes.
     * @param access
     *            the access flags of the inner class as originally declared in
     *            the enclosing class.
     */
    public void visitInnerClass(String name, String outerName,
            String innerName, int access) {
        if (cv != null) {
            cv.visitInnerClass(name, outerName, innerName, access);
        }
    }

    /**
     * Visits a field of the class.
     *
     * @param access
     *            the field's access flags (see {@link Opcodes}). This parameter
     *            also indicates if the field is synthetic and/or deprecated.
     * @param name
     *            the field's name.
     * @param desc
     *            the field's descriptor (see {@link Type Type}).
     * @param signature
     *            the field's signature. May be <tt>null</tt> if the field's
     *            type does not use generic types.
     * @param value
     *            the field's initial value. This parameter, which may be
     *            <tt>null</tt> if the field does not have an initial value,
     *            must be an {@link Integer}, a {@link Float}, a {@link Long}, a
     *            {@link Double} or a {@link String} (for <tt>int</tt>,
     *            <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
     *            respectively). This parameter is only used for static
     *            fields. Its value is ignored for non static fields, which
     *            must be initialized through bytecode instructions in
     *            constructors or methods.
     * @return a visitor to visit field annotations and attributes, or
     *         <tt>null</tt> if this class visitor is not interested in visiting
     *         these annotations and attributes.
     */
    public FieldVisitor visitField(int access, String name, String desc,
            String signature, Object value) {
        if (cv != null) {
            return cv.visitField(access, name, desc, signature, value);
        }
        return null;
    }

    /**
     * Visits a method of the class. This method must return a new
     * {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called,
     * i.e., it should not return a previously returned visitor.
     *
     * @param access
     *            the method's access flags (see {@link Opcodes}). This
     *            parameter also indicates if the method is synthetic and/or
     *            deprecated.
     * @param name
     *            the method's name.
     * @param desc
     *            the method's descriptor (see {@link Type Type}).
     * @param signature
     *            the method's signature. May be <tt>null</tt> if the method
     *            parameters, return type and exceptions do not use generic
     *            types.
     * @param exceptions
     *            the internal names of the method's exception classes (see
     *            {@link Type#getInternalName() getInternalName}). May be
     *            <tt>null</tt>.
     * @return an object to visit the byte code of the method, or <tt>null</tt>
     *         if this class visitor is not interested in visiting the code of
     *         this method.
     */
    public MethodVisitor visitMethod(int access, String name, String desc,
            String signature, String[] exceptions) {
        if (cv != null) {
            return cv.visitMethod(access, name, desc, signature, exceptions);
        }
        return null;
    }

    /**
     * Visits the end of the class. This method, which is the last one to be
     * called, is used to inform the visitor that all the fields and methods of
     * the class have been visited.
     */
    public void visitEnd() {
        if (cv != null) {
            cv.visitEnd();
        }
    }
}


package org.nutz.dao.impl.jdbc.db2;

import org.nutz.dao.DB;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.entity.MappingField;
import org.nutz.dao.entity.PkType;
import org.nutz.dao.impl.jdbc.AbstractJdbcExpert;
import org.nutz.dao.jdbc.JdbcExpertConfigFile;
import org.nutz.dao.jdbc.ValueAdaptor;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.Pojo;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Pojos;

public class Db2JdbcExpert extends AbstractJdbcExpert {

	//表空间key
    private static final String META_TABLESPACE = "db2-tablespace";

    public Db2JdbcExpert(JdbcExpertConfigFile conf) {
        super(conf);
    }

    public String getDatabaseType() {
        return DB.DB2.name();
    }

    // TODO not tested!!
    public boolean createEntity(Dao dao, Entity<?> en) {
        StringBuilder sb = new StringBuilder("CREATE TABLE " + en.getTableName() + "(");
        // 创建字段
        for (MappingField mf : en.getMappingFields()) {
            if (mf.isReadonly())
                continue;
            sb.append('\n').append(mf.getColumnNameInSql());
            sb.append(' ').append(evalFieldType(mf));
            // 非主键的 @Name，应该加入唯一性约束
            if (mf.isName() && en.getPkType() != PkType.NAME) {
                sb.append(" UNIQUE NOT NULL");
            }
            // 普通字段
            else {
                if (mf.isNotNull() || mf.isPk())
                    sb.append(" NOT NULL");
                if (mf.hasDefaultValue()) {
                    addDefaultValue(sb, mf);
                }
                if (mf.isAutoIncreasement())
                    sb.append(" generated by default as identity ");
                if (mf.isPk() && en.getPks().size() == 1) {
                    sb.append(" primary key ");
                }
            }
            sb.append(',');
        }
        // 结束表字段设置
        sb.setCharAt(sb.length() - 1, ')');

        //指定表空间
        if(en.hasMeta(META_TABLESPACE)){
            sb.append(String.format(" IN %s", en.getMeta(META_TABLESPACE)) );
        }

        // 执行创建语句
        dao.execute(Sqls.create(sb.toString()));

        // 创建联合主键
        if (en.getPks().size() > 1) {
            sb = new StringBuilder();
            sb.append("ALTER TABLE ").append(en.getTableName()).append(" ADD CONSTRAINT PK_");
            sb.append(makePksName(en));
            sb.append(" PRIMARY KEY (");
            for (MappingField mf : en.getPks()) {
                sb.append(mf.getColumnNameInSql()).append(",");
            }
            sb.setCharAt(sb.length() - 1, ')');
            dao.execute(Sqls.create(sb.toString()));
        }

        // 创建关联表
        createRelation(dao, en);
        // 创建索引
        dao.execute(createIndexs(en).toArray(new Sql[0]));
        // 添加注释(表注释与字段注释)
        addComment(dao, en);

        return true;
    }

    public String evalFieldType(MappingField mf) {
        if (mf.getCustomDbType() != null)
            return mf.getCustomDbType();
        switch (mf.getColumnType()) {
        case BOOLEAN:
            return "SMALLINT";
        case INT:
            // 用户自定义了宽度
            // if (mf.getWidth() > 0)
            // return "decimal(" + mf.getWidth() + ")";
            // 用数据库的默认宽度
            return "INTEGER";

        case FLOAT:
            // 用户自定义了精度
            if (mf.getWidth() > 0 && mf.getPrecision() > 0) {
                return "decimal(" + mf.getWidth() + "," + mf.getPrecision() + ")";
            }
            // 用默认精度
            if (mf.getTypeMirror().isDouble())
                return "decimal(15,10)";
            return "FLOAT";
        case TIMESTAMP:
        	return "TIMESTAMP";
        default:
            break;
        }
        return super.evalFieldType(mf);
    }

    public void formatQuery(Pojo pojo) {
        Pager pager = pojo.getContext().getPager();
        // 需要进行分页
        if (null != pager && pager.getPageNumber() > 0) {
            // 之前插入
            pojo.insertFirst(Pojos.Items.wrap("SELECT * FROM ("
                                                + "SELECT ROW_NUMBER() OVER() AS ROWNUM, "
                                                + "T.* FROM ("));
            // 之后插入
            pojo.append(Pojos.Items.wrapf(    ") T) AS A WHERE ROWNUM BETWEEN %d AND %d",
                                            pager.getOffset() + 1,
                                            pager.getOffset() + pager.getPageSize()));
        }
    }

    public void formatQuery(Sql sql) {
        Pager pager = sql.getContext().getPager();
        if (null != pager && pager.getPageNumber() > 0) {
            String pre = "SELECT * FROM (SELECT ROW_NUMBER() OVER() AS ROWNUM, T.* FROM (";
            String last = String.format(    ") T) AS A WHERE ROWNUM BETWEEN %d AND %d",
                    pager.getOffset() + 1,
                    pager.getOffset() + pager.getPageSize());
            sql.setSourceSql(pre + sql.getSourceSql() + last);
        }
    }

    public ValueAdaptor getAdaptor(MappingField ef) {
        if (ef.getTypeMirror().isBoolean())
            return new DB2BooleanAdaptor();
        return super.getAdaptor(ef);
    }
}


package org.nutz.lang;

import java.util.ArrayList;
import java.util.List;

/**
 * A group of helper functions to counting some ...
 *
 * @author zozoh(zozohtnt@gmail.com)
 * @author pw
 */
public abstract class Maths {

    /**
     * 返回最大的一个
     *
     * @param nums
     *            需要比较的数组
     * @return 最大值
     */
    public static int max(int... nums) {
        return takeOne(new CompareSomeThing() {
            @Override
            public boolean compare(int arg0, int arg1) {
                return arg0 > arg1;
            }
        }, nums);
    }

    /**
     * 返回最小的一个
     *
     * @param nums
     *            需要比较的数组
     * @return 最小值
     */
    public static int min(int... nums) {
        return takeOne(new CompareSomeThing() {
            @Override
            public boolean compare(int arg0, int arg1) {
                return arg0 < arg1;
            }
        }, nums);
    }

    private interface CompareSomeThing {
        public boolean compare(int arg0, int arg1);
    }

    private static int takeOne(CompareSomeThing cp, int... nums) {
        if (null == nums || nums.length == 0)
            return 0;
        int re = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (cp.compare(nums[i], re))
                re = nums[i];
        }
        return re;
    }

    /**
     * Convert a binary string to a integer
     *
     * @param s
     *            binary string
     * @return integer
     */
    public static int bit(String s) {
        return Integer.valueOf(s, 2);
    }

    /**
     * Test current bit is match the given mask at least one bit or not.
     *
     * @param bs
     *            integer, bit map
     * @param mask
     *            another bit map
     * @return if one of bit value is '1' in mask, and it is also is '1' in bs
     *         return true, else false
     */
    public static boolean isMask(int bs, int mask) {
        return 0 != (mask & bs);
    }

    public static boolean isNoMask(int bs, int mask) {
        return 0 == (bs & mask);
    }

    /**
     * Test current bit is all match the give mask.
     *
     * @param bs
     *            integer, bit map
     * @param mask
     *            another bit map
     * @return if all bit value is '1' in mask, and it is also is '1' in bs
     *         return true, else false
     */
    public static boolean isMaskAll(int bs, int mask) {
        return 0 == ~((~mask) | bs);
    }

    /**
     * Get part of one integer as a new integer
     *
     * @param bs
     *            original integer
     * @param low
     *            the low bit position (inclusive), 0 base
     * @param high
     *            the high bit position (exclusive), 0 base
     * @return new integer
     */
    public static int extract(int bs, int low, int high) {
        bs = bs >> low;
        int mask = 0;
        for (int i = 0; i < (high - low); i++) {
            mask += 1 << i;
        }
        return bs & mask;
    }

    /**
     * 获得字符数组的全排列
     *
     * @param arr
     *            字符数组
     * @return 全排列
     */
    public static String[] permutation(char... arr) {
        return permutation(arr.length, arr);
    }

    /**
     * 按照指定长度, 获得字符数组的全排列
     *
     * @param arr
     *            字符数组
     * @return 全排列
     */
    public static String[] permutation(int length, char... arr) {
        if (arr == null || arr.length == 0 || length <= 0 || length > arr.length) {
            return null;
        }
        List<String> slist = new ArrayList<String>();
        char[] b = new char[length]; // 辅助空间，保存待输出组合数
        getCombination(slist, arr, length, 0, b, 0);
        return slist.toArray(new String[]{});
    }

    /**
     * 坐标点旋转计算方法。
     *
     * 坐标点（x1,y1）绕另一个坐标点（x2，y2）旋转角度（a）后的新坐标
     *
     *
     * @param x1
     *            被计算点横坐标
     * @param y1
     *            被计算点纵坐标
     * @param x2
     *            圆心横坐标
     * @param y2
     *            圆心纵坐标
     * @param a
     *            角度
     * @return （x3，y3）
     */
    public static int[] rotateXY(int x1, int y1, int x2, int y2, double a) {
        double l = (a * Math.PI) / 180;

        double cosv = Math.cos(l);
        double sinv = Math.sin(l);

        int newX = (int) ((x1 - x2) * cosv - (y1 - y2) * sinv + x2);
        int newY = (int) ((x1 - x2) * sinv + (y1 - y2) * cosv + y2);

        return new int[]{newX, newY};
    }

    // --------------------------- 以下为几个辅助方法

    private static void getCombination(List<String> slist,
                                       char[] a,
                                       int n,
                                       int begin,
                                       char[] b,
                                       int index) {
        if (n == 0) {// 如果够n个数了，输出b数组
            getAllPermutation(slist, b, 0);// 得到b的全排列
            return;
        }
        for (int i = begin; i < a.length; i++) {
            b[index] = a[i];
            getCombination(slist, a, n - 1, i + 1, b, index + 1);
        }

    }

    private static void getAllPermutation(List<String> slist, char[] a, int index) {
        /* 与a的元素个数相同则输出 */
        if (index == a.length - 1) {
            slist.add(String.valueOf(a));
            return;
        }
        for (int i = index; i < a.length; i++) {
            swap(a, index, i);
            getAllPermutation(slist, a, index + 1);
            swap(a, index, i);
        }
    }

    private static void swap(char[] arr, int i, int j) {
        char temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}


package org.nutz.mvc.impl;

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

import javax.servlet.http.HttpServletRequest;

import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.mvc.ActionContext;

public class MappingNode<T> {

    private T obj;

    private T asterisk; // 匹配 *

    private T remain; // 匹配 **

    private MappingNode<T> quesmark; // 匹配 ?

    private Map<String, MappingNode<T>> map; // 匹配精确的值

    public MappingNode() {
        map = new HashMap<String, MappingNode<T>>();
    }

    private void add(T obj, String[] ss, int off) {
        // 还有路径
        if (off < ss.length) {
            String key = ss[off].toLowerCase();
            off++;
            // '*'
            if ("*".equals(key)) {
                if (off < ss.length) {
                    throw Lang.makeThrow("char '*' should be the last item"
                                                 + " in a Path '../**/%s'",
                                         Lang.concat(off, ss.length - off, "/", ss));
                }
                asterisk = obj;
            }
            // '**'
            else if ("**".equals(key)) {
                if (off < ss.length) {
                    throw Lang.makeThrow("'**' should be the last item" + " in a Path '../**/%s'",
                                         Lang.concat(off, ss.length - off, "/", ss));
                }
                remain = obj;
            }
            // '?'
            else if ("?".equals(key)) {
                if (quesmark == null) // 也许这个节点之前就已经有值呢
                    quesmark = new MappingNode<T>();
                quesmark.add(obj, ss, off);
            }
            // 其它节点，加入 map
            else {
                MappingNode<T> node = map.get(key);
                if (null == node) {
                    node = new MappingNode<T>();
                    map.put(key, node);
                }
                node.add(obj, ss, off);
            }

        }
        // 没有路径了
        else {
            this.obj = obj;
        }
    }

    private T get(ActionContext ac, String[] ss, int off) {
        // 路径已经没有内容了，看看本节点是否有一个对象
        if (off >= ss.length) {
            return obj == null ? (asterisk == null ? remain : asterisk) : obj;
        }

        String key = ss[off];
        // 先在 map 里寻找，
        MappingNode<T> node = map.get(key.toLowerCase());
        if (null != node) {
            // 在子节点中查找
            T t = node.get(ac, ss, off + 1);
            if (t != null)
                return t;
            // 找不到的时候, 继续在当前节点找泛匹配(?或者*)
        }

        // 如果没有看看是否有 '?' 的匹配
        if (quesmark != null) {
            ac.getPathArgs().add(key);
            T t = quesmark.get(ac, ss, off + 1);
            if (t != null)
                return t;
            ac.getPathArgs().remove(ac.getPathArgs().size() - 1);
        }

        // 还没有则看看是否有 '*' 的匹配
        if (null != asterisk) {
            List<String> pathArgs = ac.getPathArgs();
            while (off < ss.length)
                pathArgs.add(ss[off++]);
            return asterisk;
        }

        // 最后看看是不是有 '**' 匹配
        if (null != remain) {
            String ph = Lang.concat(off, ss.length - off, "/", ss).toString();
            if (!Strings.isBlank(ac.getSuffix())) {
                HttpServletRequest req = ac.getRequest();
                String url = Strings.sBlank(req.getPathInfo(), req.getServletPath());
                // 看看有没有必要补一个 "/"
                if (url.endsWith("/." + ac.getSuffix())) {
                    ph += "/";
                }
                ph += "." + ac.getSuffix();
            }
            ac.getPathArgs().add(ph);
            return remain;
        }

        return null;
    }

    /**
     * 增加一个映射,将 obj 映射到 path 上,或 path 上的[?,*]
     */
    public void add(String path, T obj) {
        try {
            String[] ss = Strings.splitIgnoreBlank(path, "/");
            add(obj, ss, 0);
        }
        catch (Exception e) {
            throw Lang.wrapThrow(e, "Wrong Url path format '%s'", path);
        }
    }

    public T get(ActionContext ac, String path) {
        return get(ac, path, null);
    }

    public T get(ActionContext ac, String path, String suffix) {
        ac.setPath(path);
        ac.setPathArgs(new LinkedList<String>());
        String[] ss = Strings.splitIgnoreBlank(path, "/");
        return get(ac, ss, 0);
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        appendTo(sb, 0);
        return sb.toString();
    }

    private void appendTo(StringBuilder sb, int indent) {
        String prefix = Strings.dup("   ", indent);
        sb.append(prefix).append('<').append(Strings.sNull(obj, "null")).append('>');

        prefix = "\n   " + prefix;
        if (null != asterisk) {
            sb.append(prefix).append(" * : ").append(asterisk.toString());
        }
        if (null != quesmark) {
            sb.append(prefix).append(" ? : ");
            quesmark.appendTo(sb, indent + 1);
        }
        for (String key : map.keySet()) {
            sb.append(prefix).append(" '" + key + "' : ");
            map.get(key).appendTo(sb, indent + 1);
        }
    }

}


package org.nutz.dao;

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

import org.nutz.lang.Lang;
import org.nutz.trans.Atom;
import org.nutz.trans.Molecule;

/**
 * 定制 POJO 的字段过滤配置。
 *

 * 通过这类，可以指明当前线程的 DAO 操作，哪些对象，的哪些字段会被忽略
 *

 *
 *

 * 调用例子:
 *
 * @Table("jax_pf_fold")
 * public class JaxFold implements Serializable {
 *     private static final long serialVersionUID = 5990870005316924017L;
 *     @Column
 *     @Name
 *     //字符类型主键,用name来表示
 *     private String mldh;
 *     @Column
 *     private String mlmc;
 *     @Column
 *     private String sjmldh;
 *     @Column
 *     private Integer sxh;
 *     @Column
 *     private String bz;
 * }
 *
 * 例子1,一般表达式
 *
 * FieldFilter.create(JaxFold.class, "bz|mlmc|mldh").run(new Atom() {
 *     public void run() {
 *         nutDao.update(fold);
 *     }
 * });
 *
 *  执行的sql是:
 *  UPDATE jax_pf_fold SET sjmldh='235',bz='备注',mlmc='信息打印' WHERE mldh='2353'
 *  由于 sjmldh 和 mldh 匹配因此,也会被认定为参与操作的字段.
 *

 * 例子2,更严格的正则表达式
 *
 * FieldFilter.create(JaxFold.class, "ˆ(bz|mlmc|mldh)$").run(new Atom() {
 *     public void run() {
 *         nutDao.update(fold);
 *     }
 * });
 *
 *  执行的sql是:
 *  UPDATE jax_pf_fold SET bz='备注',mlmc='信息打印',sxh=2343 WHERE mldh='2353'
 *
 *

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

    private static ThreadLocal<FieldFilter> FF = new ThreadLocal<FieldFilter>();

    /**
     * 创建一个过滤器
     *
     * @param type
     *            POJO 类型
     * @param locked
     *            正则表达式，哪些字段会被忽略，语法请参看 Pattern 类的 JavaDoc
     * @return 字段过滤器
     */
    public static FieldFilter locked(Class<?> type, String locked) {
        return create(type, null, locked, true);
    }

    /**
     * 创建一个过滤器
     *
     * @param type
     *            POJO 类型
     * @param ignoreNull
     *            是否忽略 NULL 值字段
     * @return 字段过滤器
     */
    public static FieldFilter create(Class<?> type, boolean ignoreNull) {
        return create(type, null, null, ignoreNull);
    }

    /**
     * 创建一个默认不忽略 NULL 值字段的过滤器
     *
     * @param type
     *            POJO 类型
     * @param actived
     *            正则表达式，哪些字段会被操作，语法请参看 Pattern 类的 JavaDoc
     * @return 字段过滤器
     *
     * @see java.util.regex.Pattern
     */
    public static FieldFilter create(Class<?> type, String actived) {
        return create(type, actived, null, false);
    }

    /**
     * 创建一个过滤器
     *
     * @param type
     *            POJO 类型
     * @param actived
     *            正则表达式，哪些字段会被操作，语法请参看 Pattern 类的 JavaDoc
     * @param ignoreNull
     *            是否忽略 NULL 值字段
     * @return 字段过滤器
     *
     * @see java.util.regex.Pattern
     */
    public static FieldFilter create(Class<?> type, String actived, boolean ignoreNull) {
        return create(type, actived, null, ignoreNull);
    }

    /**
     * 创建一个过滤器
     *
     * @param type
     *            POJO 类型
     * @param actived
     *            正则表达式，哪些字段会被操作，语法请参看 Pattern 类的 JavaDoc
     * @param locked
     *            正则表达式，哪些字段会被忽略，语法请参看 Pattern 类的 JavaDoc
     * @param ignoreNull
     *            是否忽略 NULL 值字段
     * @return 字段过滤器
     *
     * @see java.util.regex.Pattern
     */
    public static FieldFilter create(    Class<?> type,
                                        String actived,
                                        String locked,
                                        boolean ignoreNull) {
        return create(type, FieldMatcher.make(actived, locked, ignoreNull));
    }

    /**
     * 创建一个过滤器
     *
     * @param type
     *            POJO 类型
     * @param mathcer
     *            字段匹配器
     * @return 字段过滤器
     */
    public static FieldFilter create(Class<?> type, FieldMatcher mathcer) {
        FieldFilter ff = new FieldFilter();
        ff.set(type, mathcer);
        return ff;
    }

    private FieldFilter() {
        map = new HashMap<Class<?>, FieldMatcher>();
    }

    private Map<Class<?>, FieldMatcher> map;

    /**
     * 为自身增加一个 POJO 的字段过滤设置
     *
     * @param type
     *            POJO 类型
     * @param ignoreNull
     *            是否忽略 NULL 值字段
     * @return 自身
     */
    public FieldFilter set(Class<?> type, boolean ignoreNull) {
        map.put(type, FieldMatcher.make(null, null, ignoreNull));
        return this;
    }

    /**
     * 为自身增加一个 POJO 的字段过滤设置
     *
     * @param type
     *            POJO 类型
     * @param actived
     *            正则表达式，哪些字段会被操作，语法请参看 Pattern 类的 JavaDoc
     * @return 自身
     */
    public FieldFilter set(Class<?> type, String actived) {
        map.put(type, FieldMatcher.make(actived, null, false));
        return this;
    }

    /**
     * 为自身增加一个 POJO 的字段过滤设置
     *
     * @param type
     *            POJO 类型
     * @param actived
     *            正则表达式，哪些字段会被操作，语法请参看 Pattern 类的 JavaDoc
     * @param ignoreNull
     *            是否忽略 NULL 值字段
     * @return 自身
     */
    public FieldFilter set(Class<?> type, String actived, boolean ignoreNull) {
        map.put(type, FieldMatcher.make(actived, null, ignoreNull));
        return this;
    }

    /**
     * 为自身增加一个 POJO 的字段过滤设置
     *
     * @param type
     *            POJO 类型
     * @param actived
     *            正则表达式，哪些字段会被操作，语法请参看 Pattern 类的 JavaDoc
     * @param locked
     *            正则表达式，哪些字段会被忽略，语法请参看 Pattern 类的 JavaDoc
     * @param ignoreNull
     *            是否忽略 NULL 值字段
     * @return 自身
     */
    public FieldFilter set(Class<?> type, String actived, String locked, boolean ignoreNull) {
        map.put(type, FieldMatcher.make(actived, locked, ignoreNull));
        return this;
    }

    /**
     * 为自身增加一个 POJO 的字段过滤设置
     *
     * @param type
     *            POJO 类型
     * @param matcher
     *            字段匹配器
     * @return 自身
     */
    public FieldFilter set(Class<?> type, FieldMatcher matcher) {
        map.put(type, matcher);
        return this;
    }

    /**
     * 移除一个 POJO 的字段过滤设置
     *
     * @param type
     *            POJO 类型
     * @return 自身
     */
    public FieldFilter remove(Class<?> type) {
        map.remove(type);
        return this;
    }

    /**
     * 根据 POJO 的类型，从 ThreadLocal 中获取字段过滤器
     *
     * @param type
     *            POJO 的类型
     * @return 字段过滤器
     */
    public static FieldMatcher get(Class<?> type) {
        FieldFilter ff = FF.get();
        if (null == ff)
            return null;
        return ff.map.get(type);
    }

    /**
     * @return 内部的文件类型与过滤器的映射表
     */
    public Map<Class<?>, FieldMatcher> map() {
        return map;
    }

    /**
     * 运行模板函数
     *
     * @param atom
     *            运行原子
     */
    public void run(Atom atom) {
        FF.set(this);
        try {
            atom.run();
        }
        catch (Exception e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            FF.remove();
        }
    }

    public <T> T run(Molecule<T> m) {
    	run((Atom)m);
    	return m.getObj();
    }
}