196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/* ====================================================================
296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (c) 2006 J.T. Beetstra
396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Permission is hereby granted, free of charge, to any person obtaining
596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * a copy of this software and associated documentation files (the
696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * "Software"), to deal in the Software without restriction, including
796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * without limitation the rights to use, copy, modify, merge, publish,
896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distribute, sublicense, and/or sell copies of the Software, and to
996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * permit persons to whom the Software is furnished to do so, subject to
1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * the following conditions:
1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * The above copyright notice and this permission notice shall be
1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * included in all copies or substantial portions of the Software.
1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * ====================================================================
2396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
2496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.beetstra.jutf7;
2696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.nio.ByteBuffer;
2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.nio.CharBuffer;
2996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.nio.charset.CharsetDecoder;
3096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.nio.charset.CoderResult;
3196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
3296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/**
3396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * <p>
3496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * The CharsetDecoder used to decode both variants of the UTF-7 charset and the
3596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * modified-UTF-7 charset.
3696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * </p>
3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
3896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @author Jaap Beetstra
3996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
4096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectclass UTF7StyleCharsetDecoder extends CharsetDecoder {
4196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private final Base64Util base64;
4296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private final byte shift;
4396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private final byte unshift;
4496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private final boolean strict;
4596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean base64mode;
4696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private int bitsRead;
4796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private int tempChar;
4896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean justShifted;
4996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean justUnshifted;
5096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
5196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    UTF7StyleCharsetDecoder(UTF7StyleCharset cs, Base64Util base64, boolean strict) {
5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        super(cs, 0.6f, 1.0f);
5396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        this.base64 = base64;
5496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        this.strict = strict;
5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        this.shift = cs.shift();
5696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        this.unshift = cs.unshift();
5796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
5896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
5996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /*
6096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * (non-Javadoc)
6196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @see java.nio.charset.CharsetDecoder#decodeLoop(java.nio.ByteBuffer,
6296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * java.nio.CharBuffer)
6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        while (in.hasRemaining()) {
6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            byte b = in.get();
6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (base64mode) {
6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (b == unshift) {
6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (base64bitsWaiting())
7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        return malformed(in);
7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (justShifted) {
7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        if (!out.hasRemaining())
7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            return overflow(in);
7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        out.put((char)shift);
7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    } else
7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        justUnshifted = true;
7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    setUnshifted();
7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                } else {
7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (!out.hasRemaining())
8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        return overflow(in);
8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    CoderResult result = handleBase64(in, out, b);
8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (result != null)
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        return result;
8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                justShifted = false;
8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            } else {
8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (b == shift) {
8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    base64mode = true;
8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (justUnshifted && strict)
9096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        return malformed(in);
9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    justShifted = true;
9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    continue;
9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (!out.hasRemaining())
9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return overflow(in);
9696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                out.put((char)b);
9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                justUnshifted = false;
9896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
9996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return CoderResult.UNDERFLOW;
10196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private CoderResult overflow(ByteBuffer in) {
10496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        in.position(in.position() - 1);
10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return CoderResult.OVERFLOW;
10696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
10996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * <p>
11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Decodes a byte in <i>base 64 mode</i>. Will directly write a character to
11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * the output buffer if completed.
11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * </p>
11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     *
11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param in The input buffer
11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param out The output buffer
11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param lastRead Last byte read from the input buffer
11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return CoderResult.malformed if a non-base 64 character was encountered
11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     *         in strict mode, null otherwise
11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
12096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private CoderResult handleBase64(ByteBuffer in, CharBuffer out, byte lastRead) {
12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        CoderResult result = null;
12296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int sextet = base64.getSextet(lastRead);
12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (sextet >= 0) {
12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            bitsRead += 6;
12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (bitsRead < 16) {
12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                tempChar += sextet << (16 - bitsRead);
12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            } else {
12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                bitsRead -= 16;
12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                tempChar += sextet >> (bitsRead);
13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                out.put((char)tempChar);
13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                tempChar = (sextet << (16 - bitsRead)) & 0xFFFF;
13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
13396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        } else {
13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (strict)
13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return malformed(in);
13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            out.put((char)lastRead);
13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (base64bitsWaiting())
13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                result = malformed(in);
13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            setUnshifted();
14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return result;
14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /*
14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * (non-Javadoc)
14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @see java.nio.charset.CharsetDecoder#implFlush(java.nio.CharBuffer)
14796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
14896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    protected CoderResult implFlush(CharBuffer out) {
14996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if ((base64mode && strict) || base64bitsWaiting())
15096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return CoderResult.malformedForLength(1);
15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return CoderResult.UNDERFLOW;
15296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
15396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
15496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /*
15596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * (non-Javadoc)
15696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @see java.nio.charset.CharsetDecoder#implReset()
15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
15896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    protected void implReset() {
15996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        setUnshifted();
16096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        justUnshifted = false;
16196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
16296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
16496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * <p>
16596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Resets the input buffer position to just before the last byte read, and
16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * returns a result indicating to skip the last byte.
16796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * </p>
16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     *
16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param in The input buffer
17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return CoderResult.malformedForLength(1);
17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private CoderResult malformed(ByteBuffer in) {
17396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        in.position(in.position() - 1);
17496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return CoderResult.malformedForLength(1);
17596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
17696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
17796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
17896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return True if there are base64 encoded characters waiting to be written
17996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean base64bitsWaiting() {
18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return tempChar != 0 || bitsRead >= 6;
18296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * <p>
18696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Updates internal state to reflect the decoder is no longer in <i>base 64
18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * mode</i>
18896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * </p>
18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private void setUnshifted() {
19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        base64mode = false;
19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        bitsRead = 0;
19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        tempChar = 0;
19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project}
196