Source for gnu.java.nio.ChannelWriter

   1: /* ChannelWriter.java -- nio / writer bridge
   2:    Copyright (C) 2006 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.Writer;
  43: import java.nio.ByteBuffer;
  44: import java.nio.CharBuffer;
  45: import java.nio.channels.WritableByteChannel;
  46: import java.nio.charset.CharsetEncoder;
  47: import java.nio.charset.CoderResult;
  48: 
  49: /**
  50:  * A Writer implementation that works by wrapping an NIO channel.
  51:  */
  52: public class ChannelWriter
  53:     extends Writer
  54: {
  55:   private static final int DEFAULT_BUFFER_CAP = 8192;
  56: 
  57:   /**
  58:    * The output channel.
  59:    */
  60:   private WritableByteChannel byteChannel;
  61: 
  62:   /**
  63:    * The encoder to use.
  64:    */
  65:   private CharsetEncoder enc;
  66: 
  67:   /**
  68:    * The byte buffer.  Translated characters are stored here on their way out.
  69:    */
  70:   private ByteBuffer byteBuffer;
  71: 
  72:   /**
  73:    * The character buffer.  Characters are stored here on their way into
  74:    * the encoder.
  75:    */
  76:   private CharBuffer charBuffer;
  77: 
  78:   private void writeBuffer() throws IOException
  79:   {
  80:     byteBuffer.flip();
  81:     byteChannel.write(byteBuffer);
  82:   }
  83: 
  84:   /**
  85:    * Create a new instance, given the output byte channel, the encoder
  86:    * to use, and the minimum buffer capacity.
  87:    */
  88:   public ChannelWriter(WritableByteChannel ch, CharsetEncoder enc,
  89:                        int minBufferCap)
  90:   {
  91:     this.byteChannel = ch;
  92:     this.enc = enc;
  93:     if (minBufferCap == -1)
  94:       minBufferCap = DEFAULT_BUFFER_CAP;
  95:     this.byteBuffer
  96:       = ByteBuffer.allocate((int) (minBufferCap * enc.maxBytesPerChar()));
  97:     this.charBuffer = CharBuffer.allocate(minBufferCap);
  98:     this.charBuffer.clear();
  99:   }
 100: 
 101:   /* (non-Javadoc)
 102:    * @see java.io.Writer#flush()
 103:    */
 104:   public void flush() throws IOException
 105:   {
 106:     // Presumably if we have characters in our buffer, it is
 107:     // due to an underflow.  So we don't bother trying to flush
 108:     // that here.
 109:   }
 110: 
 111:   /* (non-Javadoc)
 112:    * @see java.io.Writer#close()
 113:    */
 114:   public void close() throws IOException
 115:   {
 116:     synchronized (lock)
 117:       {
 118:         if (enc == null)
 119:           throw new IOException("writer already closed");
 120: 
 121:         byteBuffer.clear();
 122:         charBuffer.flip();
 123:         CoderResult res = enc.encode(charBuffer, byteBuffer, true);
 124:         if (res.isError() || res.isMalformed() || res.isUnmappable())
 125:           res.throwException();
 126:         writeBuffer();
 127: 
 128:         byteBuffer.clear();
 129:         res = enc.flush(byteBuffer);
 130:         if (res.isError() || res.isMalformed() || res.isUnmappable())
 131:           res.throwException();
 132:         writeBuffer();
 133:         enc = null;
 134:       }
 135:   }
 136: 
 137:   /* (non-Javadoc)
 138:    * @see java.io.Writer#write(char[], int, int)
 139:    */
 140:   public void write(char[] buf, int offset, int len) throws IOException
 141:   {
 142:     synchronized (lock)
 143:       {
 144:         if (enc == null)
 145:           throw new IOException("writer already closed");
 146:         int lastLen = -1;
 147:         while (len > 0)
 148:           {
 149:             // Copy data into our character buffer.
 150:             int allowed = Math.min(charBuffer.remaining(), len);
 151:             charBuffer.put(buf, offset, allowed);
 152:             // Update for the next pass through the loop.
 153:             offset += allowed;
 154:             len -= allowed;
 155:             charBuffer.flip();
 156:             // If we didn't make any progress, we want to clean up
 157:             // and save our state for the next write().
 158:             if (len == lastLen)
 159:               {
 160:                 if (len <= charBuffer.remaining())
 161:                   {
 162:                     charBuffer.put(buf, offset, len);
 163:                     charBuffer.flip();
 164:                   }
 165:                 else
 166:                   {
 167:                     CharBuffer ncb = CharBuffer.allocate(charBuffer.length()
 168:                                                          + len);
 169:                     ncb.put(charBuffer);
 170:                     ncb.put(buf, offset, len);
 171:                     charBuffer = ncb;
 172:                   }
 173:                 break;
 174:               }
 175:             lastLen = len;
 176: 
 177:             // Convert.
 178:             byteBuffer.clear();
 179:             CoderResult res = enc.encode(charBuffer, byteBuffer, false);
 180:             // Compact here, as we want to leave the buffer in the
 181:             // right state for any future put()s.
 182:             charBuffer.compact();
 183:             if (res.isError() || res.isMalformed() || res.isUnmappable())
 184:               res.throwException();
 185:             // Write the byte buffer to the output channel.
 186:             writeBuffer();
 187:           }
 188:       }
 189:   }
 190: }