1/*
2 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.cs;
27
28import java.nio.ByteBuffer;
29import java.nio.CharBuffer;
30import java.nio.charset.Charset;
31import java.nio.charset.CoderResult;
32import java.nio.charset.CharsetDecoder;
33import java.nio.charset.CharsetEncoder;
34
35class UTF_32Coder {
36    protected static final int BOM_BIG = 0xFEFF;
37    protected static final int BOM_LITTLE = 0xFFFE0000;
38    protected static final int NONE = 0;
39    protected static final int BIG = 1;
40    protected static final int LITTLE = 2;
41
42    protected static class Decoder extends CharsetDecoder {
43        private int currentBO;
44        private int expectedBO;
45
46        protected Decoder(Charset cs, int bo) {
47            super(cs, 0.25f, 1.0f);
48            this.expectedBO = bo;
49            this.currentBO = NONE;
50        }
51
52        private int getCP(ByteBuffer src) {
53            return (currentBO==BIG)
54              ?(((src.get() & 0xff) << 24) |
55                ((src.get() & 0xff) << 16) |
56                ((src.get() & 0xff) <<  8) |
57                (src.get() & 0xff))
58              :((src.get() & 0xff) |
59                ((src.get() & 0xff) <<  8) |
60                ((src.get() & 0xff) << 16) |
61                ((src.get() & 0xff) << 24));
62        }
63
64        protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
65            if (src.remaining() < 4)
66                return CoderResult.UNDERFLOW;
67            int mark = src.position();
68            int cp;
69            try {
70                if (currentBO == NONE) {
71                    cp = ((src.get() & 0xff) << 24) |
72                         ((src.get() & 0xff) << 16) |
73                         ((src.get() & 0xff) <<  8) |
74                         (src.get() & 0xff);
75                    if (cp == BOM_BIG && expectedBO != LITTLE) {
76                        currentBO = BIG;
77                        mark += 4;
78                    } else if (cp == BOM_LITTLE && expectedBO != BIG) {
79                        currentBO = LITTLE;
80                        mark += 4;
81                    } else {
82                        if (expectedBO == NONE)
83                            currentBO = BIG;
84                        else
85                            currentBO = expectedBO;
86                        src.position(mark);
87                    }
88                }
89                while (src.remaining() >= 4) {
90                    cp = getCP(src);
91                    if (Character.isBmpCodePoint(cp)) {
92                        if (!dst.hasRemaining())
93                            return CoderResult.OVERFLOW;
94                        mark += 4;
95                        dst.put((char) cp);
96                    } else if (Character.isValidCodePoint(cp)) {
97                        if (dst.remaining() < 2)
98                            return CoderResult.OVERFLOW;
99                        mark += 4;
100                        dst.put(Character.highSurrogate(cp));
101                        dst.put(Character.lowSurrogate(cp));
102                    } else {
103                        return CoderResult.malformedForLength(4);
104                    }
105                }
106                return CoderResult.UNDERFLOW;
107            } finally {
108                src.position(mark);
109            }
110        }
111        protected void implReset() {
112            currentBO = NONE;
113        }
114    }
115
116    protected static class Encoder extends CharsetEncoder {
117        private boolean doBOM = false;
118        private boolean doneBOM = true;
119        private int byteOrder;
120
121        protected void put(int cp, ByteBuffer dst) {
122            if (byteOrder==BIG) {
123                dst.put((byte)(cp >> 24));
124                dst.put((byte)(cp >> 16));
125                dst.put((byte)(cp >> 8));
126                dst.put((byte)cp);
127            } else {
128                dst.put((byte)cp);
129                dst.put((byte)(cp >>  8));
130                dst.put((byte)(cp >> 16));
131                dst.put((byte)(cp >> 24));
132            }
133        }
134
135        protected Encoder(Charset cs, int byteOrder, boolean doBOM) {
136            super(cs, 4.0f,
137                  doBOM?8.0f:4.0f,
138                  (byteOrder==BIG)?new byte[]{(byte)0, (byte)0, (byte)0xff, (byte)0xfd}
139                                  :new byte[]{(byte)0xfd, (byte)0xff, (byte)0, (byte)0});
140            this.byteOrder = byteOrder;
141            this.doBOM = doBOM;
142            this.doneBOM = !doBOM;
143        }
144
145        protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
146            int mark = src.position();
147            if (!doneBOM && src.hasRemaining()) {
148                if (dst.remaining() < 4)
149                    return CoderResult.OVERFLOW;
150                put(BOM_BIG, dst);
151                doneBOM = true;
152            }
153            try {
154                while (src.hasRemaining()) {
155                    char c = src.get();
156                    if (!Character.isSurrogate(c)) {
157                        if (dst.remaining() < 4)
158                            return CoderResult.OVERFLOW;
159                        mark++;
160                        put(c, dst);
161                    } else if (Character.isHighSurrogate(c)) {
162                        if (!src.hasRemaining())
163                            return CoderResult.UNDERFLOW;
164                        char low = src.get();
165                        if (Character.isLowSurrogate(low)) {
166                            if (dst.remaining() < 4)
167                                return CoderResult.OVERFLOW;
168                            mark += 2;
169                            put(Character.toCodePoint(c, low), dst);
170                        } else {
171                            return CoderResult.malformedForLength(1);
172                        }
173                    } else {
174                        // assert Character.isLowSurrogate(c);
175                        return CoderResult.malformedForLength(1);
176                    }
177                }
178                return CoderResult.UNDERFLOW;
179            } finally {
180                src.position(mark);
181            }
182        }
183
184        protected void implReset() {
185            doneBOM = !doBOM;
186        }
187
188    }
189}
190