gt:                         int bytesRead = stream.read(byteBuffer, 0, blockSize);
context: ///////////////////////////////////////////////////////////////////////////////
  //                                                                             
  // JTOpen (IBM Toolbox for Java - OSS version)                                 
  //                                                                             
  // Filename: SQLClobLocator.java
  //                                                                             
  // The source code contained herein is licensed under the IBM Public License   
  // Version 1.0, which has been approved by the Open Source Initiative.         
  // Copyright (C) 1997-2006 International Business Machines Corporation and     
  // others. All rights reserved.                                                
  //                                                                             
  ///////////////////////////////////////////////////////////////////////////////
  
  package com.ibm.as400.access;
  
  import java.io.*;
  import java.math.BigDecimal;
  import java.sql.Array;
  import java.sql.Blob;
  import java.sql.Clob;
  import java.sql.Date;
  /* ifdef JDBC40 */
  import java.sql.NClob;
  import java.sql.RowId;
  /* endif */ 
  import java.sql.SQLException;
  /* ifdef JDBC40 */
  import java.sql.SQLXML;
  /* endif */ 
  import java.sql.Time;
  import java.sql.Timestamp;
  import java.util.Calendar;
  
  
  final class SQLClobLocator implements SQLLocator
  {
      static final String copyright = "Copyright (C) 1997-2006 International Business Machines Corporation and others.";
  
      private AS400JDBCConnection     connection_;
      private SQLConversionSettings   settings_;
      private ConvTable               converter_;
      private int                     id_;
      private JDLobLocator            locator_;
      private int                     maxLength_;
      private int                     truncated_;
      private boolean                 outOfBounds_; 
      private int                     columnIndex_;
      private String                  value_; //@loch //Note that value_ is not used as the output for a ResultSet.getX() call.  We Get the value from a call to the JDLocator (not from value_) and not from the savedObject_, unless resultSet.updateX(obj1) is called followed by a obj2 = resultSet.getX()
  
      private Object savedObject_; // This is the AS400JDBCBlobLocator or InputStream or whatever got set into us.
      private int scale_; // This is actually the length that got set into us.
  
      SQLClobLocator(AS400JDBCConnection connection,
                     int id,
                     int maxLength, 
                     SQLConversionSettings settings,
                     ConvTable converter,
                     int columnIndex)
      {
          connection_     = connection;
          id_             = id;
          locator_        = new JDLobLocator(connection, id, maxLength, false);
          maxLength_      = maxLength;
          truncated_ = 0; outOfBounds_ = false; 
          settings_       = settings;
          converter_      = converter;
          columnIndex_    = columnIndex;
      }
  
      public Object clone()
      {
          return new SQLClobLocator(connection_, id_, maxLength_, settings_, converter_, columnIndex_);
      }
  
      public void setHandle(int handle)
      {
          locator_.setHandle(handle);
      }
      
      //@loch
      public int getHandle()
      {
          return locator_.getHandle();
      }
  
      //---------------------------------------------------------//
      //                                                         //
      // CONVERSION TO AND FROM RAW BYTES                        //
      //                                                         //
      //---------------------------------------------------------//
  
      public void convertFromRawBytes(byte[] rawBytes, int offset, ConvTable ccsidConverter)
      throws SQLException
      {
          int locatorHandle = BinaryConverter.byteArrayToInt(rawBytes, offset);
          locator_.setHandle(locatorHandle);
          locator_.setColumnIndex(columnIndex_);
      }
  
      //@CRS - This is only called from AS400JDBCPreparedStatement in one place.
      public void convertToRawBytes(byte[] rawBytes, int offset, ConvTable ccsidConverter)
      throws SQLException
      {
          BinaryConverter.intToByteArray(locator_.getHandle(), rawBytes, offset);
  
          // Now we write our saved data to the system, because the prepared statement is being executed.
          // We used to write the data to the system on the call to set(), but this messed up
          // batch executes, since the host server only reserves temporary space for locator handles one row at a time.
          // See the toObject() method in this class for more details.
          if(savedObject_ != null) writeToServer();
      }
  
      //---------------------------------------------------------//
      //                                                         //
      // SET METHODS                                             //
      //                                                         //
      //---------------------------------------------------------//
  
      public void set(Object object, Calendar calendar, int scale)
      throws SQLException
      {
          //@selins1 make same as SQLClob
          // If it's a String we check for data truncation.
          if(object instanceof String)
          {
              String s = (String)object;
              int length = s.length(); 
              truncated_ = (length > maxLength_ ? length-maxLength_ : 0);  
              outOfBounds_ = false; 
          }
          else if( !(object instanceof Reader) &&
             !(object instanceof InputStream) &&
             (JDUtilities.JDBCLevel_ >= 20 && !(object instanceof Clob))
  /* ifdef JDBC40 */
              && !(object instanceof SQLXML)  
  /* endif */ 
             )
          {
              JDError.throwSQLException(this, JDError.EXC_DATA_TYPE_MISMATCH);
          }
  
          savedObject_ = object;
          if(scale != -1) scale_ = scale; // Skip resetting it if we don't know the real length
      }
  
      //@loch method to temporary convert from object input to output before even going to host (writeToServer() does the conversion needed before writing to host)
      //This will only be used when resultSet.updateX(obj1) is called followed by a obj2 = resultSet.getX()
      //Purpose is to do a local type conversion from obj1 to obj2 like other non-locator lob types
      private void doConversion()
      throws SQLException
      {
          int length_ = scale_;
  
          if( length_ == -1)
          {
              try{
                  //try to get length from locator
                  length_ = (int)locator_.getLength();        
              }catch(Exception e){ }
          }
          
          try
          {
              Object object = savedObject_;
              if(savedObject_ instanceof String)
              {
                  value_ = (String)object;
              }
              else if(object instanceof Reader)
              {
                  if(length_ >= 0)
                  {
                      try
                      {
                          int blockSize = length_ < AS400JDBCPreparedStatement.LOB_BLOCK_SIZE ? length_ : AS400JDBCPreparedStatement.LOB_BLOCK_SIZE;
                          Reader stream = (Reader)object;
                          StringBuffer buf = new StringBuffer();
                          char[] charBuffer = new char[blockSize];
                          int totalCharsRead = 0;
                          int charsRead = stream.read(charBuffer, 0, blockSize);
                          while(charsRead > -1 && totalCharsRead < length_)
                          {
                              buf.append(charBuffer, 0, charsRead);
                              totalCharsRead += charsRead;
                              int charsRemaining = length_ - totalCharsRead;
                              if(charsRemaining < blockSize)
                              {
                                  blockSize = charsRemaining;
                              }
                              charsRead = stream.read(charBuffer, 0, blockSize);
                          }
                          value_ = buf.toString();
  
                          if(value_.length() < length_)
                          {
                              // a length longer than the stream was specified
                              JDError.throwSQLException(this, JDError.EXC_DATA_TYPE_MISMATCH);
                          }
                      }
                      catch(IOException ie)
                      {
                          JDError.throwSQLException(this, JDError.EXC_INTERNAL, ie);
                      }
                  }
                  else if(length_ == -2)//@readerlen new else-if block (read all data)
                  {
                      try
                      {
                          int blockSize = AS400JDBCPreparedStatement.LOB_BLOCK_SIZE;
                          Reader stream = (Reader)object;
                          StringBuffer buf = new StringBuffer();
                          char[] charBuffer = new char[blockSize];
                          int totalCharsRead = 0;
                          int charsRead = stream.read(charBuffer, 0, blockSize);
                          while(charsRead > -1)
                          {
                              buf.append(charBuffer, 0, charsRead);
                              totalCharsRead += charsRead;
                              charsRead = stream.read(charBuffer, 0, blockSize);
                          }
                          value_ = buf.toString();
  
                      }
                      catch(IOException ie)
                      {
                          JDError.throwSQLException(this, JDError.EXC_INTERNAL, ie);
                      }
                  }
                  else
                  {
                      JDError.throwSQLException(this, JDError.EXC_DATA_TYPE_MISMATCH);
                  }
              }
              else if( object instanceof Clob)  
              {
                  Clob clob = (Clob)object;
                  value_ = clob.getSubString(1, (int)clob.length());
              }
  /* ifdef JDBC40 */
              else if( object instanceof SQLXML ) 
              {
                  SQLXML xml = (SQLXML)object;
                  value_ = xml.getString();
              }
  /* endif */ 
              else
              {
                  JDError.throwSQLException(this, JDError.EXC_DATA_TYPE_MISMATCH);
              }
  
              // Truncate if necessary.
              int valueLength = value_.length();
              if(valueLength > maxLength_)
              {
                  value_ = value_.substring(0, maxLength_);
              }
          }
          finally
          {
             //nothing
          }
      }
      
      private void writeToServer()
      throws SQLException
      {
          try
          {
              Object object = savedObject_;
              if(object instanceof String)
              {
                  String string = (String)object;
                  int bidiStringType = settings_.getBidiStringType();
                  if(bidiStringType == -1) bidiStringType = converter_.bidiStringType_;
  
                  BidiConversionProperties bidiConversionProperties = new BidiConversionProperties(bidiStringType);  //@KBA
                  bidiConversionProperties.setBidiImplicitReordering(settings_.getBidiImplicitReordering());         //@KBA
                  bidiConversionProperties.setBidiNumericOrderingRoundTrip(settings_.getBidiNumericOrdering());      //@KBA
  
                  byte[] bytes = converter_.stringToByteArray(string, bidiConversionProperties);  //@KBC changed to use bidiConversionProperties instead of bidiStringType
                  locator_.writeData(0L, bytes, true);                //@K1C
              }
              else if(object instanceof Reader)
              {
                  int length = scale_; // hack to get the length into the set method
                  // Need to write even if there are 0 bytes in case we are batching and
                  // the host server reuses the same handle for the previous locator; otherwise,
                  // we'll have data in the current row from the previous row.
                  if (length == 0) 
                  {
                    locator_.writeData(0, new byte[0], 0, 0, true);               //@K1C
                  }
                  else if(length > 0)
                  {
                      try
                      {
                          int blockSize = length < AS400JDBCPreparedStatement.LOB_BLOCK_SIZE ? length : AS400JDBCPreparedStatement.LOB_BLOCK_SIZE;
                          int bidiStringType = settings_.getBidiStringType();
                          if(bidiStringType == -1) bidiStringType = converter_.bidiStringType_;
  
                          BidiConversionProperties bidiConversionProperties = new BidiConversionProperties(bidiStringType);  //@KBA
                          bidiConversionProperties.setBidiImplicitReordering(settings_.getBidiImplicitReordering());         //@KBA
                          bidiConversionProperties.setBidiNumericOrderingRoundTrip(settings_.getBidiNumericOrdering());      //@KBA
  
                          ReaderInputStream stream = new ReaderInputStream((Reader)savedObject_, converter_.getCcsid(), bidiConversionProperties, blockSize); //@KBC changed to use bidiConversionProperties instead of bidiStringType
                          byte[] byteBuffer = new byte[blockSize];
                          int totalBytesRead = 0;
                          int bytesRead = stream.read(byteBuffer, 0, blockSize);
                          while(bytesRead > -1 && totalBytesRead < length)
                          {
                              locator_.writeData((long)totalBytesRead, byteBuffer, 0, bytesRead, true); // totalBytesRead is our offset.  @K1C
                              totalBytesRead += bytesRead;
                              int bytesRemaining = length - totalBytesRead;
                              if(bytesRemaining < blockSize)
                              {
                                  blockSize = bytesRemaining;
                                  if(stream.available() == 0 && blockSize != 0)
                                  {
                                      stream.close(); //@scan1
                                      stream = new ReaderInputStream((Reader)savedObject_, converter_.getCcsid(), bidiConversionProperties, blockSize); // do this so we don't read more chars out of the Reader than we have to. //@KBC changed to use bidiConversionProperties instead of bidiStringType
                                  }
                              }
                              bytesRead = stream.read(byteBuffer, 0, blockSize);
                          }
                          stream.close(); //@scan1
                          
                          if(totalBytesRead < length)
                          {
                              // a length longer than the stream was specified
                              JDError.throwSQLException(this, JDError.EXC_DATA_TYPE_MISMATCH);
                          }
  
                      }
                      catch(IOException ie)
                      {
                          JDError.throwSQLException(this, JDError.EXC_INTERNAL, ie);
                      }
                  }
                  else if(length == -2)//@readerlen new else-if block (read all data)
                  {
                      try
                      {
                          //String readerStr = JDUtilities.readerToString((Reader)savedObject_);
                          int blockSize = AS400JDBCPreparedStatement.LOB_BLOCK_SIZE;
                          int bidiStringType = settings_.getBidiStringType();
                          if(bidiStringType == -1) bidiStringType = converter_.bidiStringType_;
  
                          BidiConversionProperties bidiConversionProperties = new BidiConversionProperties(bidiStringType);  //@KBA
                          bidiConversionProperties.setBidiImplicitReordering(settings_.getBidiImplicitReordering());         //@KBA
                          bidiConversionProperties.setBidiNumericOrderingRoundTrip(settings_.getBidiNumericOrdering());      //@KBA
  
                          ReaderInputStream stream = new ReaderInputStream((Reader)savedObject_, converter_.getCcsid(), bidiConversionProperties, blockSize); //@KBC changed to use bidiConversionProperties instead of bidiStringType
                          byte[] byteBuffer = new byte[blockSize];
                          int totalBytesRead = 0;
  