package com.xiongyingqi.util;

public class Base64 {

	/* ******** P U B L I C F I E L D S ******** */

    public final static int NO_OPTIONS = 0;

    public final static int ENCODE = 1;

    public final static int DECODE = 0;

    public final static int GZIP = 2;

    public final static int DONT_BREAK_LINES = 8;

	/* ******** P R I V A T E F I E L D S ******** */

    private final static int MAX_LINE_LENGTH = 76;

    private final static byte EQUALS_SIGN = (byte) '=';

    private final static byte NEW_LINE = (byte) '\n';

    private final static String PREFERRED_ENCODING = "UTF-8";

    private final static byte[] ALPHABET;
    private final static byte[] _NATIVE_ALPHABET = /*
                                                     * May be something funny
													 * like EBCDIC
													 */
            {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
                    (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
                    (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
                    (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b',
                    (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i',
                    (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
                    (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w',
                    (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
                    (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+',
                    (byte) '/'};

    static {
        byte[] __bytes;
        try {
            __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
                    .getBytes(PREFERRED_ENCODING);
        } // end try
        catch (java.io.UnsupportedEncodingException use) {
            __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
        } // end catch
        ALPHABET = __bytes;
    } // end static

    private final static byte[] DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
            -5, -5, // Whitespace: Tab and Linefeed
            -9, -9, // Decimal 11 - 12
            -5, // Whitespace: Carriage Return
            -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
            // 26
            -9, -9, -9, -9, -9, // Decimal 27 - 31
            -5, // Whitespace: Space
            -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
            62, // Plus sign at decimal 43
            -9, -9, -9, // Decimal 44 - 46
            63, // Slash at decimal 47
            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
            -9, -9, -9, // Decimal 58 - 60
            -1, // Equals sign at decimal 61
            -9, -9, -9, // Decimal 62 - 64
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
            // 'N'
            14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
            // through 'Z'
            -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
            26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
            // through 'm'
            39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
            // through 'z'
            -9, -9, -9, -9 // Decimal 123 - 126
    /*
     * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
	 * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
	 */
    };

    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in
    // encoding
    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in
    // encoding

    private Base64() {
    }

	/* ******** E N C O D I N G M E T H O D S ******** */

    private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes) {
        encode3to4(threeBytes, 0, numSigBytes, b4, 0);
        return b4;
    } // end encode3to4

    private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes,
                                     byte[] destination, int destOffset) {
        // 1 2 3
        // 01234567890123456789012345678901 Bit position
        // --------000000001111111122222222 Array position from threeBytes
        // --------| || || || | Six bit groups to index ALPHABET
        // >>18 >>12 >> 6 >> 0 Right shift necessary
        // 0x3f 0x3f 0x3f Additional AND

        // Create buffer with zero-padding if there are only one or two
        // significant bytes passed in the array.
        // We have to shift left 24 in order to flush out the 1's that appear
        // when Java treats a value as negative that is cast from a byte to an
        // int.
        int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
                | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
                | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);

        switch (numSigBytes) {
            case 3:
                destination[destOffset] = ALPHABET[(inBuff >>> 18)];
                destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
                destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
                destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
                return destination;

            case 2:
                destination[destOffset] = ALPHABET[(inBuff >>> 18)];
                destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
                destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
                destination[destOffset + 3] = EQUALS_SIGN;
                return destination;

            case 1:
                destination[destOffset] = ALPHABET[(inBuff >>> 18)];
                destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
                destination[destOffset + 2] = EQUALS_SIGN;
                destination[destOffset + 3] = EQUALS_SIGN;
                return destination;

            default:
                return destination;
        } // end switch
    } // end encode3to4

    public static String encodeObject(java.io.Serializable serializableObject) {
        return encodeObject(serializableObject, NO_OPTIONS);
    } // end encodeObject

    public static String encodeObject(java.io.Serializable serializableObject, int options) {
        // Streams
        java.io.ByteArrayOutputStream baos = null;
        java.io.OutputStream b64os = null;
        java.io.ObjectOutputStream oos = null;
        java.util.zip.GZIPOutputStream gzos = null;

        // Isolate options
        int gzip = (options & GZIP);
        int dontBreakLines = (options & DONT_BREAK_LINES);

        try {
            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
            baos = new java.io.ByteArrayOutputStream();
            b64os = new OutputStream(baos, ENCODE | dontBreakLines);

            // GZip?
            if (gzip == GZIP) {
                gzos = new java.util.zip.GZIPOutputStream(b64os);
                oos = new java.io.ObjectOutputStream(gzos);
            } // end if: gzip
            else
                oos = new java.io.ObjectOutputStream(b64os);

            oos.writeObject(serializableObject);
        } // end try
        catch (java.io.IOException e) {
            return null;
        } // end catch
        finally {
            try {
                oos.close();
            } catch (Exception e) {
                // Nothing to do
            }
            try {
                gzos.close();
            } catch (Exception e) {
                // Nothing to do
            }
            try {
                b64os.close();
            } catch (Exception e) {
                // Nothing to do
            }
            try {
                baos.close();
            } catch (Exception e) {
                // Nothing to do
            }
        } // end finally

        // Return value according to relevant encoding.
        try {
            return new String(baos.toByteArray(), PREFERRED_ENCODING);
        } // end try
        catch (java.io.UnsupportedEncodingException uue) {
            return new String(baos.toByteArray());
        } // end catch

    } // end encode

    public static String encodeBytes(byte[] source) {
        return encodeBytes(source, 0, source.length, NO_OPTIONS);
    } // end encodeBytes

    public static String encodeBytes(byte[] source, int options) {
        return encodeBytes(source, 0, source.length, options);
    } // end encodeBytes

    public static String encodeBytes(byte[] source, int off, int len) {
        return encodeBytes(source, off, len, NO_OPTIONS);
    } // end encodeBytes

    public static String encodeBytes(byte[] source, int off, int len, int options) {
        // Isolate options
        int dontBreakLines = (options & DONT_BREAK_LINES);
        int gzip = (options & GZIP);

        // Compress?
        if (gzip == GZIP) {
            java.io.ByteArrayOutputStream baos = null;
            java.util.zip.GZIPOutputStream gzos = null;
            OutputStream b64os = null;

            try {
                // GZip -> Base64 -> ByteArray
                baos = new java.io.ByteArrayOutputStream();
                b64os = new OutputStream(baos, ENCODE | dontBreakLines);
                gzos = new java.util.zip.GZIPOutputStream(b64os);

                gzos.write(source, off, len);
                gzos.close();
            } // end try
            catch (java.io.IOException e) {
                return null;
            } // end catch
            finally {
                try {
                    gzos.close();
                } catch (Exception e) {
                    // Nothing to do
                }
                try {
                    b64os.close();
                } catch (Exception e) {
                    // Nothing to do
                }
                try {
                    baos.close();
                } catch (Exception e) {
                    // Nothing to do
                }
            } // end finally

            // Return value according to relevant encoding.
            try {
                return new String(baos.toByteArray(), PREFERRED_ENCODING);
            } // end try
            catch (java.io.UnsupportedEncodingException uue) {
                return new String(baos.toByteArray());
            } // end catch
        } // end if: compress

        // Else, don't compress. Better not to use streams at all then.
        else {
            // Convert option to boolean in way that code likes it.
            boolean breakLines = dontBreakLines == 0;

            int len43 = len * 4 / 3;
            byte[] outBuff = new byte[(len43) // Main 4:3
                    + ((len % 3) > 0 ? 4 : 0) // Account for padding
                    + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New
            // lines
            int d = 0;
            int e = 0;
            int len2 = len - 2;
            int lineLength = 0;
            for (; d < len2; d += 3, e += 4) {
                encode3to4(source, d + off, 3, outBuff, e);

                lineLength += 4;
                if (breakLines && lineLength == MAX_LINE_LENGTH) {
                    outBuff[e + 4] = NEW_LINE;
                    e++;
                    lineLength = 0;
                } // end if: end of line
            } // en dfor: each piece of array

            if (d < len) {
                encode3to4(source, d + off, len - d, outBuff, e);
                e += 4;
            } // end if: some padding needed

            // Return value according to relevant encoding.
            try {
                return new String(outBuff, 0, e, PREFERRED_ENCODING);
            } // end try
            catch (java.io.UnsupportedEncodingException uue) {
                return new String(outBuff, 0, e);
            } // end catch

        } // end else: don't compress

    } // end encodeBytes

	/* ******** D E C O D I N G M E T H O D S ******** */

    private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) {
        // Example: Dk==
        if (source[srcOffset + 2] == EQUALS_SIGN) {
            // Two ways to do the same thing. Don't know which way I like best.
            // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
            // )
            // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
            int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
                    | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);

            destination[destOffset] = (byte) (outBuff >>> 16);
            return 1;
        }

        // Example: DkL=
        else if (source[srcOffset + 3] == EQUALS_SIGN) {
            // Two ways to do the same thing. Don't know which way I like best.
            // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
            // )
            // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
            // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
            int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
                    | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
                    | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);

            destination[destOffset] = (byte) (outBuff >>> 16);
            destination[destOffset + 1] = (byte) (outBuff >>> 8);
            return 2;
        }

        // Example: DkLE
        else {
            try {
                // Two ways to do the same thing. Don't know which way I like
                // best.
                // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 )
                // >>> 6 )
                // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
                // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
                // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
                int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
                        | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
                        | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
                        | ((DECODABET[source[srcOffset + 3]] & 0xFF));

                destination[destOffset] = (byte) (outBuff >> 16);
                destination[destOffset + 1] = (byte) (outBuff >> 8);
                destination[destOffset + 2] = (byte) (outBuff);

                return 3;
            } catch (Exception e) {
                return -1;
            } // e nd catch
        }
    } // end decodeToBytes

    public static byte[] decode(byte[] source, int off, int len) {
        int len34 = len * 3 / 4;
        byte[] outBuff = new byte[len34]; // Upper limit on size of output
        int outBuffPosn = 0;

        byte[] b4 = new byte[4];
        int b4Posn = 0;
        int i;
        byte sbiCrop;
        byte sbiDecode;
        for (i = off; i < off + len; i++) {
            sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
            sbiDecode = DECODABET[sbiCrop];

            if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or
            // better
            {
                if (sbiDecode >= EQUALS_SIGN_ENC) {
                    b4[b4Posn++] = sbiCrop;
                    if (b4Posn > 3) {
                        outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
                        b4Posn = 0;

                        // If that was the equals sign, break out of 'for' loop
                        if (sbiCrop == EQUALS_SIGN)
                            break;
                    } // end if: quartet built

                } // end if: equals sign or better

            } // end if: white space, equals sign or better
            else {
                System.err.println("Bad Base64 input character at " + i + ": " + source[i]
                        + "(decimal)");
                return null;
            } // end else:
        } // each input character

        byte[] out = new byte[outBuffPosn];
        System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
        return out;
    } // end decode

    public static byte[] decode(String s) {
        byte[] bytes;
        try {
            bytes = s.getBytes(PREFERRED_ENCODING);
        } // end try
        catch (java.io.UnsupportedEncodingException uee) {
            bytes = s.getBytes();
        } // end catch
        // </change>

        // Decode
        bytes = decode(bytes, 0, bytes.length);

        // Check to see if it's gzip-compressed
        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
        if (bytes.length >= 2) {

            int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
            if (bytes != null && // In case decoding returned null
                    bytes.length >= 4 && // Don't want to get
                    // ArrayIndexOutOfBounds exception
                    java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
                java.io.ByteArrayInputStream bais = null;
                java.util.zip.GZIPInputStream gzis = null;
                java.io.ByteArrayOutputStream baos = null;
                byte[] buffer = new byte[2048];
                int length;

                try {
                    baos = new java.io.ByteArrayOutputStream();
                    bais = new java.io.ByteArrayInputStream(bytes);
                    gzis = new java.util.zip.GZIPInputStream(bais);

                    while ((length = gzis.read(buffer)) >= 0) {
                        baos.write(buffer, 0, length);
                    } // end while: reading input

                    // No error? Get new bytes.
                    bytes = baos.toByteArray();

                } // end try
                catch (java.io.IOException e) {
                    // Just return originally-decoded bytes
                } // end catch
                finally {
                    try {
                        baos.close();
                    } catch (Exception e) {
                        // Nothing to do
                    }
                    try {
                        gzis.close();
                    } catch (Exception e) {
                        // Nothing to do
                    }
                    try {
                        bais.close();
                    } catch (Exception e) {
                        // Nothing to do
                    }
                } // end finally

            } // end if: gzipped
        } // end if: bytes.length >= 2

        return bytes;
    } // end decode

    public static Object decodeToObject(String encodedObject) {
        // Decode and gunzip if necessary
        byte[] objBytes = decode(encodedObject);

        java.io.ByteArrayInputStream bais = null;
        java.io.ObjectInputStream ois = null;
        Object obj = null;

        try {
            bais = new java.io.ByteArrayInputStream(objBytes);
            ois = new java.io.ObjectInputStream(bais);

            obj = ois.readObject();
        } // end try
        catch (java.io.IOException e) {
            obj = null;
        } // end catch
        catch (ClassNotFoundException e) {
            obj = null;
        } // end catch
        finally {
            try {
                if (bais != null)
                    bais.close();
            } catch (Exception e) {
                // Nothing to do
            }
            try {
                if (ois != null)
                    ois.close();
            } catch (Exception e) {
                // Nothing to do
            }
        } // end finally

        return obj;
    } // end decodeObject

	/* ******** I N N E R C L A S S I N P U T S T R E A M ******** */

    public static class InputStream extends java.io.FilterInputStream {
        private boolean encode; // Encoding or decoding
        private int position; // Current position in the buffer
        private byte[] buffer; // Small buffer holding converted data
        private int bufferLength; // Length of buffer (3 or 4)
        private int numSigBytes; // Number of meaningful bytes in the buffer
        private int lineLength;
        private boolean breakLines; // Break lines at less than 80 characters

        public InputStream(java.io.InputStream in) {
            this(in, DECODE);
        } // end constructor

        public InputStream(java.io.InputStream in, int options) {
            super(in);
            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
            this.encode = (options & ENCODE) == ENCODE;
            this.bufferLength = encode ? 4 : 3;
            this.buffer = new byte[bufferLength];
            this.position = -1;
            this.lineLength = 0;
        } // end constructor

        public int read() throws java.io.IOException {
            // Do we need to get data?
            if (position < 0) {
                if (encode) {
                    byte[] b3 = new byte[3];
                    int numBinaryBytes = 0;
                    for (int i = 0; i < 3; i++) {
                        try {
                            int b = in.read();

                            // If end of stream, b is -1.
                            if (b >= 0) {
                                b3[i] = (byte) b;
                                numBinaryBytes++;
                            } // end if: not end of stream

                        } // end try: read
                        catch (java.io.IOException e) {
                            // Only a problem if we got no data at all.
                            if (i == 0)
                                throw e;

                        } // end catch
                    } // end for: each needed input byte

                    if (numBinaryBytes > 0) {
                        encode3to4(b3, 0, numBinaryBytes, buffer, 0);
                        position = 0;
                        numSigBytes = 4;
                    } // end if: got data
                    else {
                        return -1;
                    } // end else
                } // end if: encoding

                // Else decoding
                else {
                    byte[] b4 = new byte[4];
                    int i;
                    for (i = 0; i < 4; i++) {
                        // Read four "meaningful" bytes:
                        int b;
                        do {
                            b = in.read();
                        } while (b >= 0 && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC);

                        if (b < 0)
                            break; // Reads a -1 if end of stream

                        b4[i] = (byte) b;
                    } // end for: each needed input byte

                    if (i == 4) {
                        numSigBytes = decode4to3(b4, 0, buffer, 0);
                        position = 0;
                    } // end if: got four characters
                    else if (i == 0) {
                        return -1;
                    } // end else if: also padded correctly
                    else {
                        // Must have broken out from above.
                        throw new java.io.IOException("Improperly padded Base64 input.");
                    } // end

                } // end else: decode
            } // end else: get data

            // Got data?
            if (position >= 0) {
                // End of relevant data?
                if (/* !encode && */position >= numSigBytes)
                    return -1;

                if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
                    lineLength = 0;
                    return '\n';
                } // end if
                else {
                    lineLength++; // This isn't important when decoding
                    // but throwing an extra "if" seems
                    // just as wasteful.

                    int b = buffer[position++];

                    if (position >= bufferLength)
                        position = -1;

                    return b & 0xFF; // This is how you "cast" a byte that's
                    // intended to be unsigned.
                } // end else
            } // end if: position >= 0

            // Else error
            else {
                // When JDK1.4 is more accepted, use an assertion here.
                throw new java.io.IOException("Error in Base64 code reading stream.");
            } // end else
        } // end read

        public int read(byte[] dest, int off, int len) throws java.io.IOException {
            int i;
            int b;
            for (i = 0; i < len; i++) {
                b = read();

                // if( b < 0 && i == 0 )
                // return -1;

                if (b >= 0)
                    dest[off + i] = (byte) b;
                else if (i == 0)
                    return -1;
                else
                    break; // Out of 'for' loop
            } // end for: each byte read
            return i;
        } // end read

    } // end inner class InputStream

	/* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */

    public static class OutputStream extends java.io.FilterOutputStream {
        private boolean encode;
        private int position;
        private byte[] buffer;
        private int bufferLength;
        private int lineLength;
        private boolean breakLines;
        private byte[] b4; // Scratch used in a few places
        private boolean suspendEncoding;

        public OutputStream(java.io.OutputStream out) {
            this(out, ENCODE);
        } // end constructor

        public OutputStream(java.io.OutputStream out, int options) {
            super(out);
            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
            this.encode = (options & ENCODE) == ENCODE;
            this.bufferLength = encode ? 3 : 4;
            this.buffer = new byte[bufferLength];
            this.position = 0;
            this.lineLength = 0;
            this.suspendEncoding = false;
            this.b4 = new byte[4];
        } // end constructor

        public void write(int theByte) throws java.io.IOException {
            // Encoding suspended?
            if (suspendEncoding) {
                super.out.write(theByte);
                return;
            } // end if: supsended

            // Encode?
            if (encode) {
                buffer[position++] = (byte) theByte;
                if (position >= bufferLength) // Enough to encode.
                {
                    out.write(encode3to4(b4, buffer, bufferLength));

                    lineLength += 4;
                    if (breakLines && lineLength >= MAX_LINE_LENGTH) {
                        out.write(NEW_LINE);
                        lineLength = 0;
                    } // end if: end of line

                    position = 0;
                } // end if: enough to output
            } // end if: encoding

            // Else, Decoding
            else {
                // Meaningful Base64 character?
                if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) {
                    buffer[position++] = (byte) theByte;
                    if (position >= bufferLength) // Enough to output.
                    {
                        int len = Base64.decode4to3(buffer, 0, b4, 0);
                        out.write(b4, 0, len);
                        // out.write( Base64.decode4to3( buffer ) );
                        position = 0;
                    } // end if: enough to output
                } // end if: meaningful base64 character
                else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) {
                    throw new java.io.IOException("Invalid character in Base64 data.");
                } // end else: not white space either
            } // end else: decoding
        } // end write

        public void write(byte[] theBytes, int off, int len) throws java.io.IOException {
            // Encoding suspended?
            if (suspendEncoding) {
                super.out.write(theBytes, off, len);
                return;
            } // end if: supsended

            for (int i = 0; i < len; i++) {
                write(theBytes[off + i]);
            } // end for: each byte written

        } // end write

        public void flushBase64() throws java.io.IOException {
            if (position > 0) {
                if (encode) {
                    out.write(encode3to4(b4, buffer, position));
                    position = 0;
                } // end if: encoding
                else {
                    throw new java.io.IOException("Base64 input not properly padded.");
                } // end else: decoding
            } // end if: buffer partially full

        } // end flush

        public void close() throws java.io.IOException {
            // 1. Ensure that pending characters are written
            flushBase64();

            // 2. Actually close the stream
            // Base class both flushes and closes.
            super.close();

            buffer = null;
            out = null;
        } // end close

        public void suspendEncoding() throws java.io.IOException {
            flushBase64();
            this.suspendEncoding = true;
        } // end suspendEncoding

        public void resumeEncoding() {
            this.suspendEncoding = false;
        } // end resumeEncoding

    } // end inner class OutputStream
} // end class Base64


package com.xiongyingqi.util;

import java.util.Random;

/**
 * 基于密钥和时间生成动态密码
 *
 * @author xiongyingqi xiongyingqi.com
 * @version 2014-2-11 下午9:08:39
 */
public class DynamicPassword {
    /**
     * 密钥长度
     */
    public static final int KEY_LENGTH = 16;
    /**
     * 最小单位（秒）
     */
    private static final int SECOND = 1000;
    /**
     * 默认密码刷新时间
     */
    public static final int DEFAULT_REFRESH_TIME = 4;

    /**
     * 随机生成指定长度字节数的密钥
     *
2014-2-11 下午9:10:17
     *
     * @param length
     * @return byte[]
     */
    public byte[] generateKey(int length) {
        if (length <= 0) {
            throw new IllegalArgumentException("长度为：" + length + "， 指定的密钥长度错误！");
        }
        byte[] bts = new byte[length];

        Random random = new Random();
        for (int i = 0; i < length; i++) {
            bts[i] = (byte) random.nextInt(255);
        }
        return bts;
    }

    public byte[] getTimeBytes() {
        return getTimeBytes(DEFAULT_REFRESH_TIME);
    }

    /**
     * 根据指定的时间刷新周期获取时间字节
     *
2014-2-11 下午9:13:21
     *
     * @param seconds
     * @return
     */
    public byte[] getTimeBytes(int seconds) {
        if (seconds <= 0) {
            throw new IllegalArgumentException("秒数为：" + seconds
                    + "， 指定的密钥更新周期错误！");
        }
        long currentTimeMillis = System.currentTimeMillis();
        // System.out.println(currentTimeMillis);
        // 对秒数取整，比如seconds=30，那么21点00分18秒与21点00分29秒是相同的结果
        currentTimeMillis /= seconds * SECOND;

        // long currentTimeMillisRs = currentTimeMillis * seconds *
        // SECOND;//还原时间位数
        // System.out.println(currentTimeMillisRs);
        // Date date = new Date(currentTimeMillisRs);
        // String dateStr = DateHelper.FORMATTER_LONG.format(date);
        // System.out.println(dateStr);

        return ByteHelper.longToBytes(currentTimeMillis);
    }

    /**
     * 对字节进行迭代运算，这样结果就不具备规律性
     *
2014-2-11 下午9:14:18
     *
     * @param key
     * @return
     */
    public byte[] generateCode(byte[] key, int refreshTime) {
        byte[] bs = getTimeBytes(refreshTime);
        int length = bs.length;
        for (int i = 0; i < length; i++) {
            byte b = bs[i];
            for (int j = 0; j < length; j++) {
                bs[i] = (byte) (bs[j] | b);
                bs[i] = (byte) (bs[j] ^ b);
            }
        }

        int keyLength = key.length;
        byte[] rs = new byte[keyLength];
        System.arraycopy(key, 0, rs, 0, keyLength);
        for (int i = 0; i < keyLength; i++) {
            byte k = rs[i];
            for (int j = 0; j < length; j++) {
                rs[i] = (byte) (bs[j] ^ k);
                rs[i] = (byte) (bs[j] | k);
            }
        }
        // String string = Base64.encodeBytes(rs);
        // System.out.println(string);
        return rs;
    }

    public long bytesToLong(byte[] bts) {
        int inputLength = bts.length;
        long data = 0;
        long temp = 0;
        for (int i = 0; i < inputLength; i++) {
            data <<= 8;
            byte b = bts[i];
            temp = b & 0xFF;
            data |= temp;
        }
        return data;
    }

    public byte[] compressBytes(byte[] bts, int length) {
        int inputLength = bts.length;
        if (inputLength < length) {
            throw new ArrayIndexOutOfBoundsException("无法压缩：目标参数length小于bts的长度！");
        }

        float multiple = inputLength / length;// 倍数
        int leastLength = inputLength % length;// 剩余字节数

        int index = 0;

        int i = 0;
        byte[] rs = new byte[length];
        while (i < length) {
            byte bt = 0;
            for (int j = 0; j < multiple; j++) {
                bt += bts[index++];
            }
            rs[i++] = bt;
        }

        for (int j = 0; j < length; j++) {
            for (int k = 0; k < leastLength; k++) {
                rs[j] ^= bts[index + k];
            }
        }
//	System.out.println("rs =========== " + rs.length);
//	System.out.println("index ========== " + index);
//	System.out.println("leastLength ========== " + leastLength);
//	System.out.println("multiple ========== " + multiple);
//	System.out.println("inputLength ========== " + inputLength);
//	System.out.println("length ========== " + length);

        return rs;
    }

    public void printBytes(byte[] bts) {
        for (int i = 0; i < bts.length; i++) {
            byte b = bts[i];
            System.out.println(b);
        }
    }

    public long generatePassword(byte[] key, int refreshTime) {
        int minApproach = minApproach(6);
        int i = minApproach / 8;
        if (minApproach % 8 > 0) {
            i++;
        }

        byte[] rs = generateCode(key, refreshTime);
        byte[] data = compressBytes(rs, i);
//	printBytes(data);
//	String string = Base64.encodeBytes(data);
//	System.out.println(string);
        long code = bytesToLong(data);
        long l = (long) (code & 999999);// 最大的为999999
        return l;
    }

    public static void main(String[] args) {
//	double a = (1 << 16) / (Math.pow(10, 6) - 1);
//
//        final byte[] key = generateKey(KEY_LENGTH);
//        Long keyL = ByteHelper.bytesToLong(key);
//        byte[] keys = ByteHelper.longToBytes(keyL);
//        Long keyLs = ByteHelper.bytesToLong(keys);
//        EntityHelper.print(keyL);
//        EntityHelper.print(keyLs);

        for (int i = 0; i < 10; i++) {
            final int j = i;
            Thread thread = new Thread() {
                @Override
                public void run() {
                    DynamicPassword dynamicPassword = new DynamicPassword();
                    final byte[] key = dynamicPassword.generateKey(KEY_LENGTH);
                    while (true) {
                        System.out.println(j + "当前动态密码：" + dynamicPassword.generatePassword(key, DEFAULT_REFRESH_TIME));
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            thread.start();
        }

//	long s = bytesToLong(new byte[]{ (byte) 255, (byte) 255 });
//	System.out.println(s);
    }

    /**
     * 获取最接近digit位数字的二进制表示的数（一定大于digit位的数字）的大小
     * 例如，如果digit为6，那么表示999999最接近的二进制数字为1048576(2的20次方)，结果返回20
     *
2014-1-21 下午5:27:53
     *
     * @param digit
     * @return
     */
    public static int minApproach(int digit) {
        int max = (int) (Math.pow(10, digit) - 1);
//	System.out.println(max);
        int i = 0;
        while (max > 0) {
            max >>= 1;
            i++;
        }
//	int k = 1 << i;
//	System.out.println(k);
//	System.out.println(i);
        return i;
    }
}


/*
 *
 *  JarArchive.java  cpdetector
 *  Copyright (C) Achim Westermann, created on 30.10.2004, 01:39:02
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this collection are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the cpDetector code in [sub] packages info.monitorenter and
 * cpdetector.
 *
 * The Initial Developer of the Original Code is
 * Achim Westermann <achim.westermann@gmx.de>.
 *
 * Portions created by the Initial Developer are Copyright (c) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** *
 *
 * If you modify or optimize the code in a useful way please let me know.
 * Achim.Westermann@gmx.de
 */

package info.monitorenter.cpdetector.io;

import info.monitorenter.cpdetector.util.collections.ITreeNode;
import info.monitorenter.cpdetector.util.collections.TreeNodeUniqueChildren;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * A jar file that pretends to be a simple file by extending {@link java.io.File}.
 *

 *
 * @author Achim Westermann
 *
 */
public class JarArchive extends File {

    protected JarFile jar;

    protected Set childs = new TreeSet();

    /**
     * @param pathname
     * @throws java.io.IOException
     */
    public JarArchive(String pathname)
    throws IOException {

        super(pathname);
        this.jar = new JarFile(pathname);
        ITreeNode root = this.parseTree();
        System.out.println("tree:");
        System.out.println(root.toString() + '\n');
        this.buildTree(root, this);
    }

    /**
     * Only for the subclass to avoid double-parsing.
     */
    private JarArchive(File f) {

        super(f.getAbsolutePath());
    }

    /**
     * Build a Tree from the entries.
     *
     */
    private ITreeNode parseTree() {

        ITreeNode root = new TreeNodeUniqueChildren();
        ITreeNode newnode, oldnode;
        Enumeration entries = this.jar.entries();
        String entry;
        while (entries.hasMoreElements()) {
            newnode = root;
            oldnode = root;
            entry = ((JarEntry)entries.nextElement()).getName();
            System.out.println("Entry: " + entry);
            StringTokenizer tokenizer = new StringTokenizer(entry, "/");
            while (tokenizer.hasMoreElements()) {
                String path = tokenizer.nextToken();
                newnode = new TreeNodeUniqueChildren(path);
                oldnode.addChildNode(newnode);
                oldnode = newnode;
            }
        }
        return root;
    }

    protected void buildTree(ITreeNode node, JarArchive element) throws IOException {

        List childNodes = node.getAllChildren();
        Iterator childNodesIt = childNodes.iterator();
        ITreeNode childNode;
        JarElement child;
        JarEntry entry;
        String search;
        while (childNodesIt.hasNext()) {
            childNode = (ITreeNode)childNodesIt.next();
            search = getSearchPath(childNode);
            System.out.println("Searching for: " + search);
            entry = this.jar.getJarEntry(search);
            if (entry == null) {
                System.err.println("Entry for " + search + " (" + this.jar.getName() + ")is null!!!");
            } else {
                System.out.println("Entry: " + entry.toString());
                child = new JarElement(entry, this);
                child.buildTree(childNode, this);
                this.childs.add(child);
            }

        }
        System.out.println(this + " has finished building...");
    }

    /**
     * Little helper that transforms (back) the nodes of the tree that was parsed from the entries during initialization
     * to canonical strings for entries.
     */
    private String getSearchPath(ITreeNode node) {

        List l = new LinkedList();
        node.getUserObjectPathFromRoot(l);
        Iterator it = l.iterator();
        StringBuffer ret = new StringBuffer();
        int i = 0;
        String token;
        while (it.hasNext()) {
            // avoid the "root" token.
            token = it.next().toString();
            if (i != 0) {
                ret.append(token);
                if (it.hasNext()) {
                    ret.append('/');
                }
            }

            i++;
        }
        return ret.toString();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#canRead()
     */
    public boolean canRead() {

        return true;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#canWrite()
     */
    public boolean canWrite() {

        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#compareTo(java.io.File)
     */
    public int compareTo(File pathname) {

        return super.compareTo(pathname);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#createNewFile()
     */
    public boolean createNewFile() throws IOException {

        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#delete()
     */
    public boolean delete() {

        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#deleteOnExit()
     */
    public void deleteOnExit() {

    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals(Object obj) {

        return super.equals(obj);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#exists()
     */
    public boolean exists() {

        return true;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#getAbsoluteFile()
     */
    public File getAbsoluteFile() {

        return super.getAbsoluteFile();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#getAbsolutePath()
     */
    public String getAbsolutePath() {

        String ret = super.getAbsolutePath();
        return ret;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#getCanonicalFile()
     */
    public File getCanonicalFile() throws IOException {

        return super.getCanonicalFile();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#getCanonicalPath()
     */
    public String getCanonicalPath() throws IOException {

        return super.getCanonicalPath();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#getName()
     */
    public String getName() {

        return super.getName();

    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#getParent()
     */
    public String getParent() {

        return super.getParent();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#getParentFile()
     */
    public File getParentFile() {

        return super.getParentFile();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#getPath()
     */
    public String getPath() {

        return super.getPath();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {

        return super.hashCode();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#isAbsolute()
     */
    public boolean isAbsolute() {

        return super.isAbsolute();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#isDirectory()
     */
    public boolean isDirectory() {

        return true;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#isFile()
     */
    public boolean isFile() {

        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#isHidden()
     */
    public boolean isHidden() {

        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#lastModified()
     */
    public long lastModified() {

        return super.lastModified();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#length()
     */
    public long length() {

        return super.length();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#list()
     */
    public String[] list() {

        String[] ret = new String[this.childs.size()];
        Iterator it = this.childs.iterator();
        for (int i = 0; it.hasNext(); i++) {
            ret[i] = it.next().toString();
        }
        return ret;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#list(java.io.FilenameFilter)
     */
    public String[] list(FilenameFilter filter) {

        List ret = new LinkedList();
        Iterator it = this.childs.iterator();
        File next;
        while (it.hasNext()) {
            next = (File)it.next();
            if (filter.accept(next.getParentFile(), next.getName())) {
                ret.add(next.toString());
            }
        }
        return (String[])ret.toArray(new String[ret.size()]);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#listFiles()
     */
    public File[] listFiles() {

        File[] ret = new File[this.childs.size()];
        Iterator it = this.childs.iterator();
        for (int i = 0; it.hasNext(); i++) {
            ret[i] = (File)it.next();
        }
        return ret;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#listFiles(java.io.FileFilter)
     */
    public File[] listFiles(FileFilter filter) {

        List ret = new LinkedList();
        Iterator it = this.childs.iterator();
        File next;
        while (it.hasNext()) {
            next = (File)it.next();
            if (filter.accept(next)) {
                ret.add(next);
            }
        }
        return (File[])ret.toArray(new File[ret.size()]);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#listFiles(java.io.FilenameFilter)
     */
    public File[] listFiles(FilenameFilter filter) {

        List ret = new LinkedList();
        Iterator it = this.childs.iterator();
        File next;
        while (it.hasNext()) {
            next = (File)it.next();
            if (filter.accept(next.getParentFile(), next.getName())) {
                ret.add(next);
            }
        }
        return (File[])ret.toArray(new File[ret.size()]);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#mkdir()
     */
    public boolean mkdir() {

        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#mkdirs()
     */
    public boolean mkdirs() {

        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#renameTo(java.io.File)
     */
    public boolean renameTo(File dest) {

        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#setLastModified(long)
     */
    public boolean setLastModified(long time) {

        return super.setLastModified(time);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#setReadOnly()
     */
    public boolean setReadOnly() {

        return super.setReadOnly();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    public String toString() {

        return super.toString();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#toURI()
     */
    public URI toURI() {

        return super.toURI();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.File#toURL()
     */
    public URL toURL() throws MalformedURLException {

        return super.toURL();
    }

    class JarElement extends JarArchive {

        private File m_parent;

        private JarEntry m_entry;

        /**
         * Creates an element with the given entry and parent file.

         *
         * @param entry the contained entry.
         *
         * @param parent the parent file.
         *
         * @throws java.io.IOException if sth goes wrong.
         */
        JarElement(JarEntry entry, File parent)
        throws IOException {

            super(new File(JarArchive.this.jar.getName()));
            this.m_parent = parent;
            if (entry == null) {
                System.err.println("Entry is null.");
            }
            this.m_entry = entry;
            this.jar = JarArchive.this.jar;
        }

        /*
         * protected void buildTree(ITreeNode node,JarArchive element) throws IOException{ List childNodes =
         * node.getAllChildren(); Iterator childNodesIt = childNodes.iterator(); ITreeNode childNode; JarElement child;
         * JarEntry entry; String search; while(childNodesIt.hasNext()){ childNode = (ITreeNode)childNodesIt.next();
         * search = this.entry.getName()+"/"+childNode.getUserObject().toString(); entry = this.jar.getJarEntry(search);
         * child = new JarElement( entry, this ); this.childs.add(child); child.buildTree(childNode,this); } }
         *
         */
        /*
         * (non-Javadoc)
         *
         * @see java.io.File#getAbsoluteFile()
         */
        public File getAbsoluteFile() {

            return this;
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#getAbsolutePath()
         */
        public String getAbsolutePath() {

            String prefix = JarArchive.this.jar.getName();
            String postfix = this.m_entry.getName();
            return prefix + "/" + postfix;
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#getName()
         */
        public String getName() {

            return this.m_entry.getName();
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#getParent()
         */
        public String getParent() {

            return this.m_parent.getAbsolutePath();
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#getParentFile()
         */
        public File getParentFile() {

            return this.m_parent;
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#isDirectory()
         */
        public boolean isDirectory() {

            // would not work, don't know why (false, even if is a dir):
            // this.entry.isDirectory();
            return this.childs.size() != 0;
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#isFile()
         */
        public boolean isFile() {

            return !this.isDirectory();
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#lastModified()
         */
        public long lastModified() {

            return this.m_entry.getTime();
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#length()
         */
        public long length() {

            return this.m_entry.getSize();
        }

        public String toString() {

            return this.m_entry.toString();
        }

        /*
         * (non-Javadoc)
         *
         * @see java.io.File#getPath()
         */
        public String getPath() {

            return this.m_entry.getName();
        }
    }

}


package com.xiongyingqi.jackson.helper;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 在当前线程内保存ObjectMapper供Jackson2HttpMessageConverter使用
 * Created by 瑛琪xiongyingqi.com on 2014/4/1 0001.
 */
public class ThreadJacksonMixInHolder {
    private static ThreadLocal<ThreadJacksonMixInHolder> holderThreadLocal = new ThreadLocal<ThreadJacksonMixInHolder>();
    private Set<Map.Entry<Class<?>, Class<?>>> mixIns;
    private ObjectMapper mapper;
    private org.codehaus.jackson.map.ObjectMapper codehausMapper;

    /**
     * 根据当前MixIn集合生成objectMapper

     *

     * 注意：该方法在返回mapper对象之后调用clear方法，如果再次调用builderMapper()肯定会保存
     *
     * @return
     */
    public static ObjectMapper builderMapper() {
        ThreadJacksonMixInHolder holder = holderThreadLocal.get();
        if (holder.mapper == null && isContainsMixIn()) {
            holder.mapper = new ObjectMapper();
            for (Map.Entry<Class<?>, Class<?>> mixIn : holder.mixIns) {
                holder.mapper.addMixInAnnotations(mixIn.getKey(), mixIn.getValue());
            }
        }
        clear();// 如果不调用clear可能导致线程内的数据是脏的！
        return holder.mapper;
    }

    /**
     * 根据当前MixIn集合生成objectMapper
     *
     * @return
     */
    public static org.codehaus.jackson.map.ObjectMapper builderCodehausMapper() {
        ThreadJacksonMixInHolder holder = holderThreadLocal.get();
        if (holder.codehausMapper == null && isContainsMixIn()) {
            holder.codehausMapper = new org.codehaus.jackson.map.ObjectMapper();
            for (Map.Entry<Class<?>, Class<?>> mixIn : holder.mixIns) {
                holder.codehausMapper.getDeserializationConfig().addMixInAnnotations(mixIn.getKey(), mixIn.getValue());
                holder.codehausMapper.getSerializationConfig().addMixInAnnotations(mixIn.getKey(), mixIn.getValue());
            }
        }
        clear();// 如果不调用clear可能导致线程内的数据是脏的！
        return holder.codehausMapper;
    }

    /**
     * 清除当前线程内的数据
     */
    public static void clear() {
        holderThreadLocal.set(null);
//        holderThreadLocal.remove();
    }

    /**
     * 设置MixIn集合到线程内，如果线程内已经存在数据，则会先清除
     *
     * @param resetMixIns
     */
    public static void setMixIns(Set<Map.Entry<Class<?>, Class<?>>> resetMixIns) {
        ThreadJacksonMixInHolder holder = holderThreadLocal.get();
        if (holder == null) {
            holder = new ThreadJacksonMixInHolder();
            holderThreadLocal.set(holder);
        }
        holder.mixIns = resetMixIns;
    }

    /**
     * 不同于setMixIns，addMixIns为增加MixIn集合到线程内，即不会清除已经保存的数据
     *
2014年4月4日 下午12:08:15
     *
     * @param toAddMixIns
     */
    public static void addMixIns(Set<Map.Entry<Class<?>, Class<?>>> toAddMixIns) {
        ThreadJacksonMixInHolder holder = holderThreadLocal.get();
        if (holder == null) {
            holder = new ThreadJacksonMixInHolder();
            holderThreadLocal.set(holder);
        }
        if (holder.mixIns == null) {
            holder.mixIns = new HashSet<Map.Entry<Class<?>, Class<?>>>();
        }
        holder.mixIns.addAll(toAddMixIns);
    }

    /**
     * 获取线程内的MixIn集合

     * 注意：为了防止线程执行完毕之后仍然存在有数据，请务必适时调用clear()方法
     *
     * @return
     * @see com.xiongyingqi.jackson.helper.ThreadJacksonMixInHolder#builderMapper()
     * @see com.xiongyingqi.jackson.helper.ThreadJacksonMixInHolder#builderCodehausMapper()
     * @see com.xiongyingqi.jackson.helper.ThreadJacksonMixInHolder#clear()
     */
    public static Set<Map.Entry<Class<?>, Class<?>>> getMixIns() {
        ThreadJacksonMixInHolder holder = holderThreadLocal.get();
        return holder.mixIns;
    }

    /**
     * 判断当前线程是否存在MixIn集合
     *
     * @return
     */
    public static boolean isContainsMixIn() {
        if (holderThreadLocal.get() == null) {
            return false;
        }
        if (holderThreadLocal.get().mixIns != null && holderThreadLocal.get().mixIns.size() > 0) {
            return true;
        }
        return false;
    }

}


/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xiongyingqi.util.xml;

import com.xiongyingqi.util.StringUtils;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.util.XMLEventConsumer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * SAX {@code ContentHandler} that transforms callback calls to {@code XMLEvent}s
 * and writes them to a {@code XMLEventConsumer}.
 *
 * @author Arjen Poutsma
 * @see javax.xml.stream.events.XMLEvent
 * @see javax.xml.stream.util.XMLEventConsumer
 * @since 3.0
 */
class StaxEventContentHandler extends AbstractStaxContentHandler {

    private final XMLEventFactory eventFactory;

    private final XMLEventConsumer eventConsumer;

    /**
     * Construct a new instance of the {@code StaxEventContentHandler} that writes to the given
     * {@code XMLEventConsumer}. A default {@code XMLEventFactory} will be created.
     *
     * @param consumer the consumer to write events to
     */
    StaxEventContentHandler(XMLEventConsumer consumer) {
        this.eventFactory = XMLEventFactory.newInstance();
        this.eventConsumer = consumer;
    }

    /**
     * Construct a new instance of the {@code StaxEventContentHandler} that uses the given
     * event factory to create events and writes to the given {@code XMLEventConsumer}.
     *
     * @param consumer the consumer to write events to
     * @param factory  the factory used to create events
     */
    StaxEventContentHandler(XMLEventConsumer consumer, XMLEventFactory factory) {
        this.eventFactory = factory;
        this.eventConsumer = consumer;
    }

    @Override
    public void setDocumentLocator(Locator locator) {
        if (locator != null) {
            this.eventFactory.setLocation(new LocatorLocationAdapter(locator));
        }
    }

    @Override
    protected void startDocumentInternal() throws XMLStreamException {
        consumeEvent(this.eventFactory.createStartDocument());
    }

    @Override
    protected void endDocumentInternal() throws XMLStreamException {
        consumeEvent(this.eventFactory.createEndDocument());
    }

    @Override
    protected void startElementInternal(QName name, Attributes atts, SimpleNamespaceContext namespaceContext)
            throws XMLStreamException {

        List<Attribute> attributes = getAttributes(atts);
        List<Namespace> namespaces = createNamespaces(namespaceContext);
        consumeEvent(this.eventFactory.createStartElement(name, attributes.iterator(),
                (namespaces != null ? namespaces.iterator() : null)));
    }

    @Override
    protected void endElementInternal(QName name, SimpleNamespaceContext namespaceContext) throws XMLStreamException {
        List<Namespace> namespaces = createNamespaces(namespaceContext);
        consumeEvent(this.eventFactory.createEndElement(name, namespaces != null ? namespaces.iterator() : null));
    }

    @Override
    protected void charactersInternal(char[] ch, int start, int length) throws XMLStreamException {
        consumeEvent(this.eventFactory.createCharacters(new String(ch, start, length)));
    }

    @Override
    protected void ignorableWhitespaceInternal(char[] ch, int start, int length) throws XMLStreamException {
        consumeEvent(this.eventFactory.createIgnorableSpace(new String(ch, start, length)));
    }

    @Override
    protected void processingInstructionInternal(String target, String data) throws XMLStreamException {
        consumeEvent(this.eventFactory.createProcessingInstruction(target, data));
    }

    private void consumeEvent(XMLEvent event) throws XMLStreamException {
        this.eventConsumer.add(event);
    }

    /**
     * Create and return a list of {@code NameSpace} objects from the {@code NamespaceContext}.
     */
    private List<Namespace> createNamespaces(SimpleNamespaceContext namespaceContext) {
        if (namespaceContext == null) {
            return null;
        }

        List<Namespace> namespaces = new ArrayList<Namespace>();
        String defaultNamespaceUri = namespaceContext.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX);
        if (StringUtils.hasLength(defaultNamespaceUri)) {
            namespaces.add(this.eventFactory.createNamespace(defaultNamespaceUri));
        }
        for (Iterator<String> iterator = namespaceContext.getBoundPrefixes(); iterator.hasNext(); ) {
            String prefix = iterator.next();
            String namespaceUri = namespaceContext.getNamespaceURI(prefix);
            namespaces.add(this.eventFactory.createNamespace(prefix, namespaceUri));
        }
        return namespaces;
    }

    private List<Attribute> getAttributes(Attributes attributes) {
        List<Attribute> list = new ArrayList<Attribute>();
        for (int i = 0; i < attributes.getLength(); i++) {
            QName name = toQName(attributes.getURI(i), attributes.getQName(i));
            if (!("xmlns".equals(name.getLocalPart()) || "xmlns".equals(name.getPrefix()))) {
                list.add(this.eventFactory.createAttribute(name, attributes.getValue(i)));
            }
        }
        return list;
    }

    /* No operation */
    @Override
    protected void skippedEntityInternal(String name) throws XMLStreamException {
    }

    private static final class LocatorLocationAdapter implements Location {

        private final Locator locator;

        public LocatorLocationAdapter(Locator locator) {
            this.locator = locator;
        }

        @Override
        public int getLineNumber() {
            return this.locator.getLineNumber();
        }

        @Override
        public int getColumnNumber() {
            return this.locator.getColumnNumber();
        }

        @Override
        public int getCharacterOffset() {
            return -1;
        }

        @Override
        public String getPublicId() {
            return this.locator.getPublicId();
        }

        @Override
        public String getSystemId() {
            return this.locator.getSystemId();
        }
    }

}


package com.xiongyingqi.jackson.impl;

import com.xiongyingqi.jackson.FilterPropertyHandler;
import com.xiongyingqi.jackson.annotation.AllowProperty;
import com.xiongyingqi.jackson.annotation.IgnoreProperties;
import com.xiongyingqi.jackson.annotation.IgnoreProperty;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.ObjectMapper;
import com.xiongyingqi.jackson.helper.ThreadJacksonMixInHolder;
import com.xiongyingqi.util.EntityHelper;
import com.xiongyingqi.util.StringHelper;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.*;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonEncoding;

import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;

//@IgnoreProperty(pojo = YxUserRoleRelation.class, name = { "id", "yxUser" })

/**
 * 使用代理来创建jackson的MixInAnnotation注解接口

 * 如果使用本实现方法，一定要配置在web.xml中配置过滤器WebContextFilter，否则无法输出json到客户端
 *
 * @author 瑛琪 xiongyingqi.com
 * @version 2013-10-25 下午2:31:21
 */
public class Jackson1JavassistFilterPropertyHandler implements FilterPropertyHandler {

    public static final Logger LOGGER = Logger.getLogger(Jackson1JavassistFilterPropertyHandler.class);

    /**
     * 注解的方法对应生成的代理类映射表
     */
    private static Map<Method, Map<Class<?>, Class<?>>> proxyMethodMap = new HashMap<Method, Map<Class<?>, Class<?>>>();

    /**
     * String数组的hashCode与生成的对应的代理类的映射表
     */
    private static Map<Integer, Class<?>> proxyMixInAnnotationMap = new HashMap<Integer, Class<?>>();

    private static String[] globalIgnoreProperties = new String[]{"hibernateLazyInitializer",
            "handler"};

    /**
     * 如果是标注的SpringMVC中的Controller方法，则应判断是否注解了@ResponseBody
     */
    private boolean isResponseBodyAnnotation;
    /**
     * 创建代理接口的唯一值索引
     */
    private static int proxyIndex;

    public Jackson1JavassistFilterPropertyHandler() {
    }

    public Jackson1JavassistFilterPropertyHandler(String[] globalIgnoreProperties) {
        Jackson1JavassistFilterPropertyHandler.globalIgnoreProperties = globalIgnoreProperties;
    }

    /**
     * 为了减少包的依赖大小，新版本不再支持对@ResponseBody的支持，请在Aop中使用以下方法自行判断：
     *

     *     Method.isAnnotationPresent(ResponseBody.class)
     *

     *
     * @param isResponseBodyAnnotation 如果是标注的SpringMVC中的Controller方法，则应判断是否注解了@ResponseBody
     */
    @Deprecated
    public Jackson1JavassistFilterPropertyHandler(boolean isResponseBodyAnnotation) {
        this.isResponseBodyAnnotation = isResponseBodyAnnotation;
    }

    /**
     *

     * 2013-10-28 上午11:11:24
     *
     * @param collection
     * @param names
     * @return
     */
    private Collection<String> checkAndPutToCollection(Collection<String> collection, String[] names) {
        if (collection == null) {
            collection = new HashSet<String>();
        }
        Collections.addAll(collection, names);
        return collection;
    }

    private Collection<String> putGlobalIgnoreProperties(Collection<String> collection) {
        if (globalIgnoreProperties != null) {
            if (collection == null) {
                collection = new HashSet<String>();
            }
            for (int i = 0; i < globalIgnoreProperties.length; i++) {
                String name = globalIgnoreProperties[i];
                collection.add(name);
            }
        }
        return collection;
    }

    /**
     * 处理IgnoreProperties注解

     * 2013-10-30 下午6:15:41
     *
     * @param properties
     * @param pojoAndNamesMap
     */
    private void processIgnorePropertiesAnnotation(IgnoreProperties properties,
                                                   Map<Class<?>, Collection<String>> pojoAndNamesMap) {
        IgnoreProperty[] values = properties.value();

        AllowProperty[] allowProperties = properties.allow();

        if (allowProperties != null) {
            for (AllowProperty allowProperty : allowProperties) {
                processAllowPropertyAnnotation(allowProperty, pojoAndNamesMap);
            }
        }

        if (values != null) {
            for (IgnoreProperty property : values) {
                processIgnorePropertyAnnotation(property, pojoAndNamesMap);
            }
        }

    }

    /**
     * 处理IgnoreProperty注解

     * 2013-10-30 下午6:16:08
     *
     * @param property
     * @param pojoAndNamesMap
     */
    private void processIgnorePropertyAnnotation(IgnoreProperty property,
                                                 Map<Class<?>, Collection<String>> pojoAndNamesMap) {
        String[] names = property.name();
        Class<?> pojoClass = property.pojo();
        // Class<?> proxyAnnotationInterface = createMixInAnnotation(names);//
        // 根据注解创建代理接口

        Collection<String> nameCollection = pojoAndNamesMap.get(pojoClass);
        nameCollection = checkAndPutToCollection(nameCollection, names);
        pojoAndNamesMap.put(pojoClass, nameCollection);
    }

    /**
     * 处理AllowProperty注解

     * 2013-10-30 下午6:16:08
     *
     * @param property
     * @param pojoAndNamesMap
     */
    private void processAllowPropertyAnnotation(AllowProperty property,
                                                Map<Class<?>, Collection<String>> pojoAndNamesMap) {
        String[] allowNames = property.name();
        Class<?> pojoClass = property.pojo();

        Collection<String> ignoreProperties = EntityHelper
                .getUnstaticClassFieldNameCollection(pojoClass);

        Collection<String> allowNameCollection = new ArrayList<String>();
        Collections.addAll(allowNameCollection, allowNames);

        Collection<String> nameCollection = pojoAndNamesMap.get(pojoClass);
        if (nameCollection != null) {
            nameCollection.removeAll(allowNameCollection);
        } else {
            ignoreProperties.removeAll(allowNameCollection);
            nameCollection = ignoreProperties;
        }
        pojoAndNamesMap.put(pojoClass, nameCollection);
    }

    /**
     * 根据方法获取过滤映射表

     * 2013-10-25 下午2:47:34
     *
     * @param method 注解了 @IgnoreProperties 或 @IgnoreProperty 的方法（所在的类）
     * @return Map pojo与其属性的映射表
     */
    public Map<Class<?>, Class<?>> getProxyMixInAnnotation(Method method) {
//        if (isResponseBodyAnnotation && !method.isAnnotationPresent(ResponseBody.class)) {
//            return null;
//        }
        Map<Class<?>, Class<?>> map = proxyMethodMap.get(method);// 从缓存中查找是否存在

        if (map != null && map.entrySet().size() > 0) {// 如果已经读取该方法的注解信息，则从缓存中读取
            return map;
        } else {
            map = new HashMap<Class<?>, Class<?>>();
        }

        Class<?> clazzOfMethodIn = method.getDeclaringClass();// 方法所在的class

        Map<Class<?>, Collection<String>> pojoAndNamesMap = new HashMap<Class<?>, Collection<String>>();

        IgnoreProperties classIgnoreProperties = clazzOfMethodIn
                .getAnnotation(IgnoreProperties.class);
        IgnoreProperty classIgnoreProperty = clazzOfMethodIn.getAnnotation(IgnoreProperty.class);
        AllowProperty classAllowProperty = clazzOfMethodIn.getAnnotation(AllowProperty.class);

        IgnoreProperties ignoreProperties = method.getAnnotation(IgnoreProperties.class);
        IgnoreProperty ignoreProperty = method.getAnnotation(IgnoreProperty.class);
        AllowProperty allowProperty = method.getAnnotation(AllowProperty.class);

        if (allowProperty != null) {// 方法上的AllowProperty注解
            processAllowPropertyAnnotation(allowProperty, pojoAndNamesMap);
        }
        if (classAllowProperty != null) {
            processAllowPropertyAnnotation(classAllowProperty, pojoAndNamesMap);
        }

        if (classIgnoreProperties != null) {// 类上的IgnoreProperties注解
            processIgnorePropertiesAnnotation(classIgnoreProperties, pojoAndNamesMap);
        }
        if (classIgnoreProperty != null) {// 类上的IgnoreProperty注解
            processIgnorePropertyAnnotation(classIgnoreProperty, pojoAndNamesMap);
        }

        if (ignoreProperties != null) {// 方法上的IgnoreProperties注解
            processIgnorePropertiesAnnotation(ignoreProperties, pojoAndNamesMap);
        }
        if (ignoreProperty != null) {// 方法上的IgnoreProperties注解
            processIgnorePropertyAnnotation(ignoreProperty, pojoAndNamesMap);
        }

        Set<Entry<Class<?>, Collection<String>>> entries = pojoAndNamesMap.entrySet();
        for (Iterator<Entry<Class<?>, Collection<String>>> iterator = entries.iterator(); iterator
                .hasNext(); ) {
            Entry<Class<?>, Collection<String>> entry = (Entry<Class<?>, Collection<String>>) iterator
                    .next();
            Collection<String> nameCollection = entry.getValue();
            nameCollection = putGlobalIgnoreProperties(nameCollection);// 将全局过滤字段放入集合内
            String[] names = nameCollection.toArray(new String[]{});

            // EntityHelper.print(entry.getKey());
            // for (int i = 0; i < names.length; i++) {
            // String name = names[i];
            // EntityHelper.print(name);
            // }
            Class<?> clazz = createMixInAnnotation(names);

            map.put(entry.getKey(), clazz);
        }

        proxyMethodMap.put(method, map);
        return map;
    }

    /**
     * 创建jackson的代理注解接口类

     * 2013-10-25 上午11:59:50
     *
     * @param names 要生成的字段
     * @return 代理接口类
     */
    private Class<?> createMixInAnnotation(String[] names) {
        Class<?> clazz = null;
        clazz = proxyMixInAnnotationMap.get(StringHelper.hashCodeOfStringArray(names));
        if (clazz != null) {
            return clazz;
        }

        ClassPool pool = ClassPool.getDefault();

        // 创建代理接口
        CtClass cc = pool.makeInterface("ProxyMixInAnnotation" + System.currentTimeMillis()
                + proxyIndex++);

        ClassFile ccFile = cc.getClassFile();
        ConstPool constpool = ccFile.getConstPool();

        // create the annotation
        AnnotationsAttribute attr = new AnnotationsAttribute(constpool,
                AnnotationsAttribute.visibleTag);
        // 创建JsonIgnoreProperties注解
        Annotation jsonIgnorePropertiesAnnotation = new Annotation(
                JsonIgnoreProperties.class.getName(), constpool);

        BooleanMemberValue ignoreUnknownMemberValue = new BooleanMemberValue(false, constpool);

        ArrayMemberValue arrayMemberValue = new ArrayMemberValue(constpool);// value的数组成员

        Collection<MemberValue> memberValues = new HashSet<MemberValue>();
        for (int i = 0; i < names.length; i++) {
            String name = names[i];
            StringMemberValue memberValue = new StringMemberValue(constpool);// 将name值设入注解内
            memberValue.setValue(name);
            memberValues.add(memberValue);
        }
        arrayMemberValue.setValue(memberValues.toArray(new MemberValue[]{}));

        jsonIgnorePropertiesAnnotation.addMemberValue("value", arrayMemberValue);
        jsonIgnorePropertiesAnnotation.addMemberValue("ignoreUnknown", ignoreUnknownMemberValue);

        attr.addAnnotation(jsonIgnorePropertiesAnnotation);
        ccFile.addAttribute(attr);

        // generate the class
        try {
            clazz = cc.toClass();
            proxyMixInAnnotationMap.put(StringHelper.hashCodeOfStringArray(names), clazz);
            // JsonIgnoreProperties ignoreProperties = (JsonIgnoreProperties)
            // clazz
            // .getAnnotation(JsonIgnoreProperties.class);

            // EntityHelper.print(ignoreProperties);
            //
            // EntityHelper.print(clazz);

            // try {
            // Object instance = clazz.newInstance();
            // EntityHelper.print(instance);
            //
            // } catch (InstantiationException e) {
            // e.printStackTrace();
            // } catch (IllegalAccessException e) {
            // e.printStackTrace();
            // }
        } catch (CannotCompileException e) {
            e.printStackTrace();
        }

        // right
        // mthd.getMethodInfo().addAttribute(attr);

        return clazz;

    }

    @Override
    public Object filterProperties(Method method, Object object) {

        Map<Class<?>, Class<?>> map = getProxyMixInAnnotation(method);
        if (map == null || map.entrySet().size() == 0) {// 如果该方法上没有注解，则返回原始对象
            return object;
        }

//		Set<Entry<Class<?>, Class<?>>> entries = map.entrySet();
//		for (Iterator<Entry<Class<?>, Class<?>>> iterator = entries.iterator(); iterator.hasNext();) {
//			Entry<Class<?>, Class<?>> entry = (Entry<Class<?>, Class<?>>) iterator.next();
//			//			EntityHelper.print(entry.getKey());
//			Class<?> clazz = entry.getValue();
//			//			EntityHelper.print(clazz.getAnnotation(JsonIgnoreProperties.class));
//		}

//		ObjectMapper mapper = createObjectMapper(map);
        ThreadJacksonMixInHolder.addMixIns(getEntries(map));
//		try {
//			HttpServletResponse response = WebContext.getInstance().getResponse();
//			writeJson(mapper, response, object);
//		} catch (WebContextAlreadyClearedException e) {
//			e.printStackTrace();
//		}

        return object;
    }

    public Set<Entry<Class<?>, Class<?>>> getEntries(Map<Class<?>, Class<?>> map) {
        Set<Entry<Class<?>, Class<?>>> entries = map.entrySet();
        return entries;
    }

    /**
     * 根据指定的过滤表创建jackson对象

     * 2013-10-25 下午2:46:43
     *
     * @param map 过滤表
     * @return ObjectMapper
     */
    private ObjectMapper createObjectMapper(Map<Class<?>, Class<?>> map) {
        ObjectMapper mapper = new ObjectMapper();
        Set<Entry<Class<?>, Class<?>>> entries = map.entrySet();
        for (Iterator<Entry<Class<?>, Class<?>>> iterator = entries.iterator(); iterator.hasNext(); ) {
            Entry<Class<?>, Class<?>> entry = iterator.next();
            mapper.getSerializationConfig().addMixInAnnotations(entry.getKey(), entry.getValue());
//            mapper.addMixInAnnotations(entry.getKey(), entry.getValue());
        }
        return mapper;
    }

    /**
     * 根据方法上的注解生成objectMapper
     *
     * @param method
     * @return
     */
    public ObjectMapper createObjectMapper(Method method) {
        return createObjectMapper(getProxyMixInAnnotation(method));
    }

//    /**
//     * 将结果输出到response

//     * 2013-10-25 下午2:28:40
//     *
//     * @param objectMapper
//     * @param response
//     * @param object
//     */
//    private void writeJson(ObjectMapper objectMapper, HttpServletResponse response, Object object) {
//        response.setContentType("application/json");
//
//        JsonEncoding encoding = getJsonEncoding(response.getCharacterEncoding());
//        JsonGenerator jsonGenerator = null;
//        try {
//            jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(
//                    response.getOutputStream(), encoding);
//        } catch (IOException e1) {
//            e1.printStackTrace();
//        }
//
//        // A workaround for JsonGenerators not applying serialization features
//        // https://github.com/FasterXML/jackson-databind/issues/12
//        if (objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
//            jsonGenerator.useDefaultPrettyPrinter();
//        }
//
//        try {
//            objectMapper.writeValue(jsonGenerator, object);
//        } catch (JsonProcessingException ex) {
//            LOGGER.error(ex);
//
//            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(),
//                    ex);
//        } catch (IOException e) {
//            LOGGER.error(e);
//            // e.printStackTrace();
//        }
//
//    }

    /**
     *

     * 2013-10-25 下午12:29:58
     *
     * @param characterEncoding
     * @return
     */
    private JsonEncoding getJsonEncoding(String characterEncoding) {
        for (JsonEncoding encoding : JsonEncoding.values()) {
            if (characterEncoding.equals(encoding.getJavaName())) {
                return encoding;
            }
        }
        return JsonEncoding.UTF8;
    }

}


package com.xiongyingqi.util;

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

/**
 * Created by xiongyingqi on 14-3-10.
 */
public class MessageEncryptionHelper {
    public static final boolean toUpperCase = true;

    /**
     * 将摘要信息转换为相应的编码
     *
     * @param code    编码类型
     * @param message 摘要信息
     * @return 相应的编码字符串
     */
    private static String encode(String code, String message) {
        return encode(code, message.getBytes());
    }

    private static String encode(String code, byte[] message) {
        MessageDigest md;
        String encode = null;
        try {
            md = MessageDigest.getInstance(code);
            encode = byteArrayToHexString(md.digest(message));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return encode;
    }

    private static String byteArrayToHexString(byte[] digest) {
        StringBuffer md5StrBuff = new StringBuffer();

        for (int i = 0; i < digest.length; i++) {
            if (Integer.toHexString(0xFF & digest[i]).length() == 1) {
                md5StrBuff.append("0").append(Integer.toHexString(0xFF & digest[i]));
            } else {
                String hex = Integer.toHexString(0xFF & digest[i]);
                if (toUpperCase) {
                    hex = hex.toUpperCase();
                }
                md5StrBuff.append(hex);
            }
        }
        return md5StrBuff.toString();
    }

    /**
     * 将摘要信息转换成MD5编码
     *
     * @param message 摘要信息
     * @return MD5编码之后的字符串
     */
    public static String md5Encode(String message) {
        return encode("MD5", message);
    }

    /**
     * 将摘要信息转换成SHA编码
     *
     * @param message 摘要信息
     * @return SHA编码之后的字符串
     */
    public static String shaEncode(String message) {
        return encode("SHA", message);
    }

    /**
     * 将摘要信息转换成SHA-256编码
     *
     * @param message 摘要信息
     * @return SHA-256编码之后的字符串
     */
    public static String sha256Encode(String message) {
        return encode("SHA-256", message);
    }

    /**
     * 将摘要信息转换成SHA-512编码
     *
     * @param message 摘要信息
     * @return SHA-512编码之后的字符串
     */
    public static String sha512Encode(String message) {
        return encode("SHA-512", message);
    }

    /**
     * 将摘要信息转换成SHA编码
     *
     * @param message 摘要信息
     * @return SHA编码之后的字符串
     */
    public static String shaEncode(byte[] message) {
        return encode("SHA", message);
    }

    /**
     * 将摘要信息转换成SHA-256编码
     *
     * @param message 摘要信息
     * @return SHA-256编码之后的字符串
     */
    public static String sha256Encode(byte[] message) {
        return encode("SHA-256", message);
    }

    /**
     * 将摘要信息转换成SHA-512编码
     *
     * @param message 摘要信息
     * @return SHA-512编码之后的字符串
     */
    public static String sha512Encode(byte[] message) {
        return encode("SHA-512", message);
    }

    /**
     * 加密方法
     * <pre>
     * // 1.1  首先要创建一个密匙
     * // DES算法要求有一个可信任的随机数源
     * SecureRandom sr = new SecureRandom();
     * // 为我们选择的DES算法生成一个KeyGenerator对象
     * KeyGenerator kg = KeyGenerator.getInstance("DES");
     * kg.init(sr);
     * // 生成密匙
     * SecretKey key = kg.generateKey();
     * // 获取密匙数据
     * byte rawKeyData[] = key.getEncoded();
     * //        byte rawKeyData[] = "sucretsa".getBytes();
     * System.out.println("密匙长度===" + rawKeyData.length);
     * System.out.println("密匙Base64===" + Base64.encodeBytes(rawKeyData));
     *
     * String str = "hi.baidu.com/beijingalana"; // 待加密数据
     * // 2.1  调用加密方法
     * byte[] encryptedData = desEncrypt(rawKeyData, str.getBytes());
     * // 3.1  调用解密方法
     * byte[] decrypt = desDecrypt(rawKeyData, encryptedData);
     * System.out.println("解密===" + new String(decrypt));
     * </pre>
     *
     * @param rawKeyData 密钥
     * @param data       要加密的数据
     * @throws java.security.InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws javax.crypto.IllegalBlockSizeException
     * @throws javax.crypto.BadPaddingException
     * @throws javax.crypto.NoSuchPaddingException
     * @throws java.security.spec.InvalidKeySpecException
     */
    public static byte[] desEncrypt(byte rawKeyData[], byte[] data)
            throws InvalidKeyException, NoSuchAlgorithmException,
            IllegalBlockSizeException, BadPaddingException,
            NoSuchPaddingException, InvalidKeySpecException {
        // DES算法要求有一个可信任的随机数源
        SecureRandom sr = new SecureRandom();
        // 从原始密匙数据创建一个DESKeySpec对象
        DESKeySpec dks = new DESKeySpec(rawKeyData);
        // 创建一个密匙工厂，然后用它把DESKeySpec转换成一个SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey key = keyFactory.generateSecret(dks);
        // Cipher对象实际完成加密操作
        Cipher cipher = Cipher.getInstance("DES");
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.ENCRYPT_MODE, key, sr);
        // 现在，获取数据并加密
//        byte data[] = str.getBytes();
        // 正式执行加密操作
        byte[] encryptedData = cipher.doFinal(data);
//        System.out.println("加密后===>" + encryptedData);
        return encryptedData;
    }

    /**
     * 解密方法
     *
     * @param rawKeyData
     * @param encryptedData
     * @return byte[]
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeySpecException
     */
    public static byte[] desDecrypt(byte rawKeyData[], byte[] encryptedData)
            throws IllegalBlockSizeException, BadPaddingException,
            InvalidKeyException, NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeySpecException {
        // DES算法要求有一个可信任的随机数源
        SecureRandom sr = new SecureRandom();
        // 从原始密匙数据创建一个DESKeySpec对象
        DESKeySpec dks = new DESKeySpec(rawKeyData);
        // 创建一个密匙工厂，然后用它把DESKeySpec对象转换成一个SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey key = keyFactory.generateSecret(dks);
        // Cipher对象实际完成解密操作
        Cipher cipher = Cipher.getInstance("DES");
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, key, sr);
        // 正式执行解密操作
        byte decryptedData[] = cipher.doFinal(encryptedData);
//        System.out.println("解密后===>" + new String(decryptedData));
        return decryptedData;
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException {
        EntityHelper.print(sha256Encode(""));
        EntityHelper.print(sha512Encode(""));
        EntityHelper.print(md5Encode(""));

        // 1.1 >>> 首先要创建一个密匙
        // DES算法要求有一个可信任的随机数源
        SecureRandom sr = new SecureRandom();
        // 为我们选择的DES算法生成一个KeyGenerator对象
        KeyGenerator kg = KeyGenerator.getInstance("DES");
        kg.init(sr);
        // 生成密匙
        SecretKey key = kg.generateKey();
        // 获取密匙数据
        byte rawKeyData[] = key.getEncoded();
//        byte rawKeyData[] = "sucretsa".getBytes();
        System.out.println("密匙长度===>" + rawKeyData.length);
        System.out.println("密匙Base64===>" + Base64.encodeBytes(rawKeyData));

        String str = "hi.baidu.com/beijingalana"; // 待加密数据
        // 2.1 >>> 调用加密方法
        byte[] encryptedData = desEncrypt(rawKeyData, str.getBytes());
        encryptedData = desEncrypt(rawKeyData, encryptedData);
        // 3.1 >>> 调用解密方法
        byte[] decrypt = desDecrypt(rawKeyData, encryptedData);
        decrypt = desDecrypt(rawKeyData, decrypt);
        System.out.println("解密===>" + new String(decrypt));
    }

}


/*
 *  ${file_name} of project cpdetector, <purpose>
 *  Copyright ${year} (C) Achim Westermann, created on 11.04.2004.
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this collection are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the cpDetector code in [sub] packages info.monitorenter and
 * cpdetector.
 *
 * The Initial Developer of the Original Code is
 * Achim Westermann <achim.westermann@gmx.de>.
 *
 * Portions created by the Initial Developer are Copyright (c) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** *
 */
 package info.monitorenter.cpdetector.io;

import info.monitorenter.util.Entry;
import info.monitorenter.util.FileUtil;
import info.monitorenter.util.StringUtil;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author Achim Westermann
 *
 */
public class ClassFileFilterIsA implements IClassFileFilter, FileFilter {

  protected Set superclasses = new TreeSet();

  protected File[] classpaths;

  /**
   *
   */
  public ClassFileFilterIsA() {
    super();
    this.scanClassPath();
  }

  private void scanClassPath() {
    URLClassLoader urlcl = (URLClassLoader) this.getClass().getClassLoader();
    URL[] urls = urlcl.getURLs();
    // TODO: remove debug printing.
    List collect = new LinkedList();
    File f;
    for (int i = 0; i < urls.length; i++) {
      f = this.urlToFile(urls[i]);
      if (f != null) {
        collect.add(f);
      }
    }
    this.classpaths = (File[]) collect.toArray(new File[collect.size()]);
  }

  private File urlToFile(URL url) {
    File ret = null;
    if (url.getProtocol().equalsIgnoreCase("file")) {
      ret = new File(url.getFile());
    }
    return ret;
  }

  /**
   *

   * Adds the given argument to the acceptance filter. The filter will only
   * return true for a Class a in the following condition:
   *
   *

   * (a instanceof c)
   *

   *
   *

   * This method may be called several times. The accept method will return true
   * if the given Class is derived from any of the internal super classes.
   *

   *
   * @param c
   *          A non-final Class.
   * @return true, if the given class could be added. It is not added, if
   *         argument c is final (makes no sense as a final class is never a
   *         superclass) or the given Class was already contained.
   *
   */
  public synchronized boolean addSuperClass(Class c) {
    boolean ret = false;
    if ((c.getModifiers() & Modifier.FINAL) != 0) {
    }
    else {
      ret = this.superclasses.add(c);
    }
    return ret;
  }

  /**
   * @return true if the given Class is derived from any of the internal super
   *         classes.
   * @see #addSuperClass(Class)
   * @see IClassFileFilter#accept(Class)
   */
  public boolean accept(Class c) {
    boolean ret = false;
    Iterator it = this.superclasses.iterator();
    while (it.hasNext() && !ret) {
      ret = ((Class) it.next()).isAssignableFrom(c);
    }
    return ret;
  }

  /**
   * Tries to detect (from the file path and the Classloaders urls) and load the
   * corresponding class and delegates to {@link #accept(Class)}.
   *
   * @see java.io.FileFilter#accept(java.io.File)
   */
  public boolean accept(File pathname) {
    boolean ret = false;
    if (pathname.isDirectory()) {
      ret = true;
    }
    else {
      String ext = FileUtil.cutExtension(pathname.getName()).getValue().toString();
      if (ext.equals("jar")) {
        // is a "directory":
        ret = true;
      }
      else if (ext.equals("class")) {
        Class cl = this.forFile(pathname);
        if (cl != null) {
          ret = this.accept(cl);
        }
      }
      else {
        ret = false;
      }
    }
    return ret;
  }

  private Class forFile(File f) {
    Class ret = null;
    Map.Entry searchpath = new Entry("", "");
    String filename = f.getAbsolutePath();
    for (int i = 0; i < this.classpaths.length && (searchpath.getKey().equals("")); i++) {
      searchpath = StringUtil.prefixIntersection(this.classpaths[i].getAbsolutePath(), filename);
    }
    if (!searchpath.getKey().equals("")) {
      // the given file might be a full match of the classpath (e.g. a jarfile)
      if (!searchpath.getValue().equals("")) {

        // remove trailing .class:
        // bad reuse of filename
        filename = (String) FileUtil.cutExtension((String) searchpath.getValue()).getKey();
        // file path to class syntax. First worked with "file.separator", but the file may be some
        // facade (e.g. a jar entry) that sticks to unix syntax even on windows machines....
        filename = (filename).replace('/', '.');
        filename = (filename).replace('\\', '.');
        // remove potential leading dots:
        if (filename.charAt(0) == '.') {
          filename = filename.substring(1);
        }
        try {
          ret = Class.forName(filename);
        } catch (ClassNotFoundException e) {
          e.printStackTrace();
        }
      }
    }
    return ret;
  }

  public static void main(String[] args) {
    ClassFileFilterIsA test = new ClassFileFilterIsA();
    System.out.println("Adding interface: " + FileFilter.class.getName() + " to instance test (" + test.getClass().getName() + ")");
    test.addSuperClass(FileFilter.class);
    System.out.println("test.accept(" + test.getClass().getName() + " : " + test.accept(test.getClass()));
    System.out.println("test.accept(new File(\"bin/cpdetector/io/ClassfileFilterIsA.class\").getAbsoluteFile()) : "
        + test.accept(new File("bin/cpdetector/io/ClassfileFilterIsA.class").getAbsoluteFile()));
  }
}