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: synchronized (lock) 96: { 97: // I declared channel being null meaning that the reader is closed. 98: if (!channel.isOpen()) 99: throw new IOException("Reader was already closed."); 100: 101: // I declared decoder being null meaning that there is no more data to read 102: // and convert. 103: if (decoder == null) 104: return -1; 105: 106: // Stores the amount of character being read. It -1 so that if no conversion 107: // occured the caller will see this as an 'end of file'. 108: int sum = -1; 109: 110: // Copies any characters which may be left from the last invocation into the 111: // destination array. 112: if (charBuffer.remaining() > 0) 113: { 114: sum = Math.min(count, charBuffer.remaining()); 115: charBuffer.get(buf, offset, sum); 116: 117: // Updates the control variables according to the latest copy operation. 118: offset += sum; 119: count -= sum; 120: } 121: 122: // Copies the character which have not been put in the destination array to 123: // the beginning. If data is actually copied count will be 0. If no data is 124: // copied count is >0 and we can now convert some more characters. 125: charBuffer.compact(); 126: 127: int converted = 0; 128: boolean last = false; 129: 130: while (count != 0) 131: { 132: // Tries to convert some bytes (Which will intentionally fail in the 133: // first place because we have not read any bytes yet.) 134: CoderResult result = decoder.decode(byteBuffer, charBuffer, last); 135: if (result.isMalformed() || result.isUnmappable()) 136: { 137: // JDK throws exception when bytes are malformed for sure. 138: // FIXME: Unsure what happens when a character is simply 139: // unmappable. 140: result.throwException(); 141: } 142: 143: // Marks that we should end this loop regardless whether the caller 144: // wants more chars or not, when this was the last conversion. 145: if (last) 146: { 147: decoder = null; 148: } 149: else if (result.isUnderflow()) 150: { 151: // We need more bytes to do the conversion. 152: 153: // Copies the not yet converted bytes to the beginning making it 154: // being able to receive more bytes. 155: byteBuffer.compact(); 156: 157: // Reads in another bunch of bytes for being converted. 158: if (channel.read(byteBuffer) == -1) 159: { 160: // If there is no more data available in the channel we mark 161: // that state for the final character conversion run which is 162: // done in the next loop iteration. 163: last = true; 164: } 165: 166: // Prepares the byteBuffer for the next character conversion run. 167: byteBuffer.flip(); 168: } 169: 170: // Prepares the charBuffer for being drained. 171: charBuffer.flip(); 172: 173: converted = Math.min(count, charBuffer.remaining()); 174: charBuffer.get(buf, offset, converted); 175: 176: // Copies characters which have not yet being copied into the char-Array 177: // to the beginning making it possible to read them later (If data is 178: // really copied here, then the caller has received enough characters so 179: // far.). 180: charBuffer.compact(); 181: 182: // Updates the control variables according to the latest copy operation. 183: offset += converted; 184: count -= converted; 185: 186: // Updates the amount of transferred characters. 187: sum += converted; 188: 189: if (decoder == null) 190: { 191: break; 192: } 193: 194: // Now that more characters have been transfered we let the loop decide 195: // what to do next. 196: } 197: 198: // Makes the charBuffer ready for reading on the next invocation. 199: charBuffer.flip(); 200: 201: return sum; 202: } 203: } 204: 205: public void close() throws IOException 206: { 207: synchronized (lock) 208: { 209: channel.close(); 210: 211: // Makes sure all intermediate data is released by the decoder. 212: if (decoder != null) 213: decoder.reset(); 214: } 215: } 216: 217: }