Frames | No Frames |
1: /* ChannelReader.java -- 2: Copyright (C) 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package gnu.java.nio; 40: 41: import java.io.IOException; 42: import java.io.Reader; 43: import java.nio.ByteBuffer; 44: import java.nio.CharBuffer; 45: import java.nio.channels.ReadableByteChannel; 46: import java.nio.charset.CharsetDecoder; 47: import java.nio.charset.CoderResult; 48: import java.nio.charset.CodingErrorAction; 49: 50: /** 51: * A Reader implementation that works using a ReadableByteChannel and a 52: * CharsetDecoder. 53: * 54: * <p> 55: * This is a bridge between NIO <-> IO character decoding. 56: * </p> 57: * 58: * @author Robert Schuster 59: */ 60: public class ChannelReader extends Reader 61: { 62: 63: private static final int DEFAULT_BUFFER_CAP = 8192; 64: 65: private ReadableByteChannel channel; 66: 67: private CharsetDecoder decoder; 68: 69: private ByteBuffer byteBuffer; 70: 71: private CharBuffer charBuffer; 72: 73: public ChannelReader(ReadableByteChannel channel, CharsetDecoder decoder, 74: int minBufferCap) 75: { 76: this.channel = channel; 77: this.decoder = decoder; 78: 79: // JDK reports errors, so we do the same. 80: decoder.onMalformedInput(CodingErrorAction.REPORT); 81: decoder.onUnmappableCharacter(CodingErrorAction.REPORT); 82: decoder.reset(); 83: 84: int size = (minBufferCap == -1) ? DEFAULT_BUFFER_CAP : minBufferCap; 85: 86: // Allocates the buffers and prepares them for reading, because that is the 87: // first operation being done on them. 88: byteBuffer = ByteBuffer.allocate(size); 89: byteBuffer.flip(); 90: charBuffer = CharBuffer.allocate((int) (size * decoder.averageCharsPerByte())); 91: } 92: 93: public int read(char[] buf, int offset, int count) throws IOException 94: { 95: // I declared channel being null meaning that the reader is closed. 96: if (!channel.isOpen()) 97: throw new IOException("Reader was already closed."); 98: 99: // I declared decoder being null meaning that there is no more data to read 100: // and convert. 101: if (decoder == null) 102: return -1; 103: 104: // Stores the amount of character being read. It -1 so that if no conversion 105: // occured the caller will see this as an 'end of file'. 106: int sum = -1; 107: 108: // Copies any characters which may be left from the last invocation into the 109: // destination array. 110: if (charBuffer.remaining() > 0) 111: { 112: sum = Math.min(count, charBuffer.remaining()); 113: charBuffer.get(buf, offset, sum); 114: 115: // Updates the control variables according to the latest copy operation. 116: offset += sum; 117: count -= sum; 118: } 119: 120: // Copies the character which have not been put in the destination array to 121: // the beginning. If data is actually copied count will be 0. If no data is 122: // copied count is >0 and we can now convert some more characters. 123: charBuffer.compact(); 124: 125: int converted = 0; 126: boolean last = false; 127: 128: while (count != 0) 129: { 130: // Tries to convert some bytes (Which will intentionally fail in the 131: // first place because we have not read any bytes yet.) 132: CoderResult result = decoder.decode(byteBuffer, charBuffer, last); 133: if (result.isMalformed() || result.isUnmappable()) 134: { 135: // JDK throws exception when bytes are malformed for sure. 136: // FIXME: Unsure what happens when a character is simply 137: // unmappable. 138: result.throwException(); 139: } 140: 141: // Marks that we should end this loop regardless whether the caller 142: // wants more chars or not, when this was the last conversion. 143: if (last) 144: { 145: decoder = null; 146: } 147: else if (result.isUnderflow()) 148: { 149: // We need more bytes to do the conversion. 150: 151: // Copies the not yet converted bytes to the beginning making it 152: // being able to receive more bytes. 153: byteBuffer.compact(); 154: 155: // Reads in another bunch of bytes for being converted. 156: if (channel.read(byteBuffer) == -1) 157: { 158: // If there is no more data available in the channel we mark 159: // that state for the final character conversion run which is 160: // done in the next loop iteration. 161: last = true; 162: } 163: 164: // Prepares the byteBuffer for the next character conversion run. 165: byteBuffer.flip(); 166: } 167: 168: // Prepares the charBuffer for being drained. 169: charBuffer.flip(); 170: 171: converted = Math.min(count, charBuffer.remaining()); 172: charBuffer.get(buf, offset, converted); 173: 174: // Copies characters which have not yet being copied into the char-Array 175: // to the beginning making it possible to read them later (If data is 176: // really copied here, then the caller has received enough characters so 177: // far.). 178: charBuffer.compact(); 179: 180: // Updates the control variables according to the latest copy operation. 181: offset += converted; 182: count -= converted; 183: 184: // Updates the amount of transferred characters. 185: sum += converted; 186: 187: if (decoder == null) 188: { 189: break; 190: } 191: 192: // Now that more characters have been transfered we let the loop decide 193: // what to do next. 194: } 195: 196: // Makes the charBuffer ready for reading on the next invocation. 197: charBuffer.flip(); 198: 199: return sum; 200: } 201: 202: public void close() throws IOException 203: { 204: channel.close(); 205: 206: // Makes sure all intermediate data is released by the decoder. 207: if (decoder != null) 208: decoder.reset(); 209: } 210: 211: }