1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.apache.harmony.tests.java.nio.charset;
18
19import java.io.IOException;
20import java.nio.BufferOverflowException;
21import java.nio.ByteBuffer;
22import java.nio.CharBuffer;
23import java.nio.charset.CharacterCodingException;
24import java.nio.charset.Charset;
25import java.nio.charset.CharsetDecoder;
26import java.nio.charset.CharsetEncoder;
27import java.nio.charset.CoderMalfunctionError;
28import java.nio.charset.CoderResult;
29import java.nio.charset.CodingErrorAction;
30import java.nio.charset.MalformedInputException;
31import java.util.Arrays;
32
33import junit.framework.TestCase;
34
35public class CharsetDecoder2Test extends TestCase {
36
37    /**
38	 * @tests java.nio.charset.CharsetDecoder.CharsetDecoder(Charset, float,
39	 *        float)
40	 */
41	public void test_ConstructorLjava_nio_charset_CharsetFF() {
42		// Regression for HARMONY-142
43		try {
44			Charset cs = Charset.forName("UTF-8"); //$NON-NLS-1$
45			new MockCharsetDecoderForHarmony142(cs, 1.1f, 1);
46			fail("Assert 0: Should throw IllegalArgumentException."); //$NON-NLS-1$
47		} catch (IllegalArgumentException e) {
48			// expected
49		}
50	}
51
52	/*
53	 * MockCharsetDecoderForHarmony142: for constructor test
54	 */
55	static class MockCharsetDecoderForHarmony142 extends CharsetDecoder {
56		protected MockCharsetDecoderForHarmony142(Charset cs,
57				float averageBytesPerChar, float maxBytesPerChar) {
58			super(cs, averageBytesPerChar, maxBytesPerChar);
59		}
60
61		protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
62			return null;
63		}
64	}
65
66	/**
67	 * @tests java.nio.charset.CharsetDecoder#decode(java.nio.ByteBuffer)
68	 */
69	public void test_decode() throws CharacterCodingException {
70		// Regression for HARMONY-33
71//		ByteBuffer bb = ByteBuffer.allocate(1);
72//		bb.put(0, (byte) 77);
73//		CharsetDecoder decoder = Charset.forName("UTF-16").newDecoder();
74//		decoder.onMalformedInput(CodingErrorAction.REPLACE);
75//		decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
76//		decoder.decode(bb);
77
78		// Regression for HARMONY-67
79//		byte[] b = new byte[] { (byte) 1 };
80//		ByteBuffer buf = ByteBuffer.wrap(b);
81//		CharBuffer charbuf = Charset.forName("UTF-16").decode(buf);
82//		assertEquals("Assert 0: charset UTF-16", 1, charbuf.length());
83//
84//		charbuf = Charset.forName("UTF-16BE").decode(buf);
85//		assertEquals("Assert 1: charset UTF-16BE", 0, charbuf.length());
86//
87//		charbuf = Charset.forName("UTF-16LE").decode(buf);
88//		assertEquals("Assert 2: charset UTF16LE", 0, charbuf.length());
89
90		// Regression for HARMONY-99
91		CharsetDecoder decoder2 = Charset.forName("UTF-16").newDecoder();
92		decoder2.onMalformedInput(CodingErrorAction.REPORT);
93		decoder2.onUnmappableCharacter(CodingErrorAction.REPORT);
94		ByteBuffer in = ByteBuffer.wrap(new byte[] { 109, 97, 109 });
95		try {
96			decoder2.decode(in);
97			fail("Assert 3: MalformedInputException should have thrown");
98		} catch (MalformedInputException e) {
99			//expected
100		}
101	}
102
103    /*
104     * Test malfunction decode(ByteBuffer)
105     */
106    public void test_decodeLjava_nio_ByteBuffer() throws Exception {
107		MockMalfunctionCharset cs1 = new MockMalfunctionCharset(
108				"Harmony-124-1", null); //$NON-NLS-1$
109		try {
110			cs1.newDecoder().onMalformedInput(CodingErrorAction.REPLACE)
111					.onUnmappableCharacter(CodingErrorAction.REPLACE).decode(
112							ByteBuffer.wrap(new byte[] { 0x00, 0x11 }));
113			fail("Assert 0: should throw CoderMalfunctionError");  // NON-NLS-1$
114		} catch (CoderMalfunctionError e) {
115			// expected
116		}
117
118		MockMalfunctionCharset cs2 = new MockMalfunctionCharset(
119				"Harmony-124-2", null); //$NON-NLS-1$
120		try {
121			cs2.decode(ByteBuffer.wrap(new byte[] { 0x00, 0x11 }));
122			fail("Assert 1: Charset.decode should throw CoderMalfunctionError");  // NON-NLS-1
123		} catch (CoderMalfunctionError e) {
124			// expected
125		}
126	}
127
128	/*
129	 * Mock charset class with malfunction decode & encode.
130	 */
131	static final class MockMalfunctionCharset extends Charset {
132
133		public MockMalfunctionCharset(String canonicalName, String[] aliases) {
134			super(canonicalName, aliases);
135		}
136
137		public boolean contains(Charset cs) {
138			return false;
139		}
140
141		public CharsetDecoder newDecoder() {
142			return new MockMalfunctionDecoder(this);
143		}
144
145		public CharsetEncoder newEncoder() {
146			return new MockMalfunctionEncoder(this);
147		}
148	}
149
150	/*
151	 * Mock decoder. decodeLoop always throws unexpected exception.
152	 */
153	static class MockMalfunctionDecoder extends java.nio.charset.CharsetDecoder {
154
155		public MockMalfunctionDecoder(Charset cs) {
156			super(cs, 1, 10);
157		}
158
159		protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
160			throw new BufferOverflowException();
161		}
162	}
163
164	/*
165	 * Mock encoder. encodeLoop always throws unexpected exception.
166	 */
167	static class MockMalfunctionEncoder extends java.nio.charset.CharsetEncoder {
168
169		public MockMalfunctionEncoder(Charset cs) {
170			super(cs, 1, 3, new byte[] { (byte) '?' });
171		}
172
173		protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
174			throw new BufferOverflowException();
175		}
176	}
177
178	/*
179	 * Test the method decode(ByteBuffer) .
180	 */
181	public void testDecodeLjava_nio_ByteBuffer_ReplaceOverflow()
182			throws Exception {
183		String replaceString = "a";
184		Charset cs = Charset.forName("UTF-8");
185		MockMalformedDecoder decoder = new MockMalformedDecoder(cs);
186		decoder.onMalformedInput(CodingErrorAction.REPLACE);
187		decoder.replaceWith(replaceString);
188		CharBuffer out = CharBuffer.allocate(1);
189		// MockMalformedDecoder treats the second byte '0x38' as malformed,
190		// but "out" doesn't have enough space for replace string.
191		ByteBuffer in = ByteBuffer.wrap(new byte[] { 0x45, 0x38, 0x45, 0x45 });
192		CoderResult result = decoder.decode(in, out, false);
193		assertTrue(result.isOverflow());
194
195		// allocate enough space for "out"
196		out = CharBuffer.allocate(10);
197		// replace string should be put into "out" firstly,
198		// and then decode "in".
199		result = decoder.decode(in, out, true);
200		out.flip();
201		assertTrue(result.isUnderflow());
202		assertEquals("bb", out.toString());
203	}
204
205	/*
206	 * Mock decoder. It treats byte whose value is less than "0x40" as
207	 * malformed.
208	 */
209	static class MockMalformedDecoder extends java.nio.charset.CharsetDecoder {
210
211		public MockMalformedDecoder(Charset cs) {
212			super(cs, 1, 10);
213		}
214
215		/*
216		 * It treats byte whose value is less than "0x40" as malformed.
217		 * Otherwise, it's decoded as 'b'.
218		 */
219		protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
220			while (in.hasRemaining()) {
221				byte b = in.get();
222				if (b < 0x40) {
223					return CoderResult.malformedForLength(1);
224				}
225				if (!out.hasRemaining()) {
226					return CoderResult.OVERFLOW;
227				}
228				out.put((char) 'b');
229			}
230			return CoderResult.UNDERFLOW;
231		}
232	}
233
234
235    public void testInvalidDecoding() throws IOException {
236
237        byte[][] invalidSequences = new byte[][] {
238            // overlong NULL
239            { (byte) 0xC0, (byte) 0x80 },
240            // overlong ascii 'A'
241            { (byte) 0xC0, (byte) 0xC1 },
242            // overlong "/../"
243            { (byte) 0x2F, (byte) 0xC0, (byte) 0xAE, (byte) 0x2E, (byte) 0x2F },
244            // Invalid encoding 2r11111000 (sequence too long)
245            { (byte) 0xF8 },
246            // Invalid encoding 2r10000000 (sequence too short)
247            { (byte) 0x80 }
248        };
249
250        CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
251        decoder.onMalformedInput(CodingErrorAction.REPORT);
252        decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
253
254        /*
255         * When bytebuffer has a backing array...
256         */
257        for (byte[] bytes : invalidSequences) {
258            try {
259                CharBuffer cb = decoder.decode(ByteBuffer.wrap(bytes));
260                fail("No exception thrown on " + Arrays.toString(bytes) + " '" + cb + "'");
261            } catch (MalformedInputException expected) {
262            }
263        }
264
265        /*
266         * When bytebuffer has _not_ got a backing array...
267         */
268        for (byte[] bytes : invalidSequences) {
269            try {
270                ByteBuffer bb = ByteBuffer.allocateDirect(8);
271                bb.put(bytes).flip();
272                CharBuffer cb = decoder.decode(bb);
273                fail("No exception thrown on " + Arrays.toString(bytes) + " '" + cb + "'");
274            } catch (MalformedInputException expected) {
275            }
276        }
277    }
278
279}
280