/* * ) * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.cldc.i18n.j2me; import java.io.*; import com.sun.cldc.i18n.*; /** * Generic interface for stream conversion reading of * specific character encoded input streams. */ public class Gen_Reader extends StreamReader { /** Saved encoding string from construction. */ private String enc; /** Native handle for conversion routines. */ private int id; /** Local buffer to read converted characters. */ private byte[] buf; /** Maximum length of characters in local buffer. */ private int maxByteLen; /** * Constructor for generic reader. * @param inp_enc character encoding to use for byte to character * conversion. * @exception ClassNotFoundException is thrown if the conversion * class is not available */ Gen_Reader(String inp_enc) throws ClassNotFoundException { id = Conv.getHandler(inp_enc); if (id == -1) { // this lets Helper throw UnsupportedEncodingException throw new ClassNotFoundException(); } enc = inp_enc; maxByteLen = Conv.getMaxByteLength(id); pos = maxByteLen; tmp = new byte[pos]; buf = new byte[pos]; } /** * Generic routine to open an InputStream with a specific * character encoding. * * @param in the input stream to process * @param open_enc the character encoding of the input stream * @return Reader instance for converted characters * @throws UnsupportedEncodingException if encoding is not supported */ public Reader open(InputStream in, String open_enc) throws UnsupportedEncodingException { if (!open_enc.equals(enc)) { throw new UnsupportedEncodingException(); } init(); return super.open(in, open_enc); } /** * Read a single converted character. * * @return a single converted character * @exception IOException is thrown if the input stream * could not be read for the raw unconverted character */ synchronized public int read() throws IOException { int c = get(); if (c == -1) { return -1; } char[] cb = {(char)0xFFFD}; int bufLen = 0; buf[bufLen++] = (byte) c; boolean eof = false; for (int i = 1; i < maxByteLen; i++) { c = get(); if (c == -1) { eof = true; break; } buf[bufLen++] = (byte) c; } int bytelen = Conv.getByteLength(id, buf, 0, bufLen); /* the byte indicates that there are tailing bytes */ if (bytelen == -1) { /* if the stream is in the end, throw IOException */ if (eof) { throw new IOException(/* "incomplete byte" */); } /* put the read ahead bytes back to the stream */ for (int i = bufLen; i > 1; i--) { put(buf[--bufLen]); } /* return the leading byte as an unknown character */ return cb[0]; } /* the byte is invalid */ if (bytelen == 0) { for (int i = bufLen; i > 1; i--) { put(buf[--bufLen]); } return cb[0]; } /* put the read ahead bytes back to the stream */ if (bytelen < bufLen) { for (int i = bufLen; i > bytelen; i--) { put(buf[--bufLen]); } } int convLen = Conv.byteToChar(id, buf, 0, bufLen, cb, 0, 1); if (convLen != 1) { throw new IOException(/* "Converter error" */); } return cb[0]; } /** * Read a block of converted characters. * * @param cbuf output buffer for converted characters read * @param off initial offset into the provided buffer * @param len length of characters in the buffer * @return the number of converted characters, or -1 * if an error occurred in the input arguments * @exception IOException is thrown if the input stream * could not be read for the raw unconverted character */ synchronized public int read(char cbuf[], int off, int len) throws IOException { /* first, check that the stream has been reached to eof */ int c = get(); if (c == -1) { return -1; } put(c); /* put it back */ int maxlen = len * maxByteLen; if (buf.length < maxlen) { buf = new byte[maxlen]; } int bufLen = readNumOfChars(buf, len); int ret = 0; if (bufLen > 0) { ret = Conv.byteToChar(id, buf, 0, bufLen, cbuf, off, len); } if (buf.length > maxByteLen) { buf = new byte[maxByteLen]; } return ret; } /** * Skip over a number of bytes in the input stream. * * @param n number of bytes to bypass from the input stream. * @return the number of characters skipped * @throws IOException if an I/O error occurs */ public long skip(long n) throws IOException { if (n < 0L) { throw new IllegalArgumentException("skip value is negative"); } /* see the stream has been reached to eof */ int c = get(); if (c == -1) { return 0; } put(c); return readNumOfChars(null, (int) n); } /** * Get the size of the converted bytes as a Unicode * byte array. * * @param c array of bytes to compute size * @param offset offset in the provided buffer * @param length length of bytes to process * @return length of converted characters. */ public int sizeOf(byte[] c, int offset, int length) { return Conv.sizeOfByteInUnicode(id, c, offset, length); } /** * Reset the stream. * * @exception IOException If an I/O error occurs */ public void reset() throws IOException { init(); } /** * Read a specific number of characters from the * input stream. * * @param b array of bytes to read * @param num count of bytes to read * @return number of characters read * @exception IOException is thrown, if an error occurred * reading the raw input stream */ private int readNumOfChars(byte[] b, int num) throws IOException { int charsRead = 0; int bbLen = 0; byte bb[]; if (b == null) { bb = new byte[num * maxByteLen]; } else { bb = b; } boolean eof = false; while (charsRead < num) { int c = get(); if (c == -1) { break; } int offset = bbLen; bb[bbLen++] = (byte) c; for (int i = 1; i < maxByteLen; i++) { c = get(); if (c == -1) { eof = true; break; } bb[bbLen++] = (byte) c; } int readNum = bbLen - offset; int bytelen = Conv.getByteLength(id, bb, offset, readNum); /* the byte indicates that there are tailing bytes */ if (bytelen == -1) { /* save the leading byte for the next read */ if (eof) { for (int i = readNum; i > 0; i--) { put(bb[--bbLen]); } break; } /* put the rest of bytes back into the stream */ for (int i = readNum; i > 1; i--) { put(bb[--bbLen]); } /* the byte is invalid */ } else if (bytelen == 0) { for (int i = readNum; i > 1; i--) { put(bb[--bbLen]); } } else if (bytelen < readNum) { for (int i = readNum; i > bytelen; i--) { put(bb[--bbLen]); } } charsRead++; } if (b != null) { return bbLen; } else { return charsRead; } } /** Local buffer for conversion routines. */ private byte[] tmp; /** Current position in the temporary buffer. */ private int pos; /** * Get a byte from the temporary buffer or from the * input stream. * @return the next byte from the input stream or local buffer * @exception IOException is thrown, if an error occurred * reading the raw input stream */ private int get() throws IOException { if (pos < tmp.length) { return tmp[pos++] & 0xff; } return in.read(); } /** * Put a byte back into the temporary buffer. * @param b the character to put back in the temporary buffer * @exception IOException is thrown, if putting a character back * past the beginning of the local temporary buffer */ private void put(int b) throws IOException { if (pos == 0) { throw new IOException(); } tmp[--pos] = (byte) b; } /** reset the state */ private void init() { pos = maxByteLen; } }