// jTDS JDBC Driver for Microsoft SQL Server and Sybase
// Copyright (C) 2004 The jTDS Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
package net.sourceforge.jtds.jdbc;
import java.io.*;
import java.sql.Clob;
import java.sql.SQLException;
import net.sourceforge.jtds.util.BlobBuffer;
/**
* An in-memory or disk based representation of character data.
* <p/>
* Implementation note:
* <ol>
* <li>This implementation stores the CLOB data in a byte array managed by
* the <code>BlobBuffer</code> class. Each character is stored in 2
* sequential bytes using UTF-16LE encoding.
* <li>As a consequence of using UTF-16LE, Unicode 3.1 supplementary
* characters may require an additional 2 bytes of storage. This
* implementation assumes that character position parameters supplied to
* <code>getSubstring</code>, <code>position</code> and the
* <code>set</code> methods refer to 16 bit characters only. The presence
* of supplementary characters will cause the wrong characters to be
* accessed.
* <li>For the same reasons although the position method will return the
* correct start position for any given pattern in the array, the returned
* value may be different to that expected if supplementary characters
* exist in the text preceding the pattern.
* </ol>
*
* @author Brian Heineman
* @author Mike Hutchinson
* @version $Id: ClobImpl.java,v 1.36.2.3 2009-12-30 08:45:34 ickzon Exp $
*/
public class ClobImpl implements Clob {
/**
* 0 length <code>String</code> as initial value for empty
* <code>Clob</code>s.
*/
private static final String EMPTY_CLOB = "";
/** The underlying <code>BlobBuffer</code>. */
private final BlobBuffer blobBuffer;
/**
* Constructs a new empty <code>Clob</code> instance.
*
* @param connection a reference to the parent connection object
*/
ClobImpl(JtdsConnection connection) {
this(connection, EMPTY_CLOB);
}
/**
* Constructs a new initialized <code>Clob</code> instance.
*
* @param connection a reference to the parent connection object
* @param str the <code>String</code> object to encapsulate
*/
ClobImpl(JtdsConnection connection, String str) {
if (str == null) {
throw new IllegalArgumentException("str cannot be null");
}
blobBuffer = new BlobBuffer(connection.getBufferDir(), connection.getLobBuffer());
try {
byte[] data = str.getBytes("UTF-16LE");
blobBuffer.setBuffer(data, false);
} catch (UnsupportedEncodingException e) {
// This should never happen!
throw new IllegalStateException("UTF-16LE encoding is not supported.");
}
}
/**
* Obtain this object's backing <code>BlobBuffer</code> object.
*
* @return the underlying <code>BlobBuffer</code>
*/
BlobBuffer getBlobBuffer() {
return this.blobBuffer;
}
//
// ---- java.sql.Blob interface methods from here ----
//
public InputStream getAsciiStream() throws SQLException {
return blobBuffer.getBinaryStream(true);
}
public Reader getCharacterStream() throws SQLException {
try {
return new BufferedReader(new InputStreamReader(
blobBuffer.getBinaryStream(false), "UTF-16LE"));
} catch (UnsupportedEncodingException e) {
// This should never happen!
throw new IllegalStateException(
"UTF-16LE encoding is not supported.");
}
}
public String getSubString(long pos, int length) throws SQLException {
if (length == 0) {
return EMPTY_CLOB;
}
try {
byte data[] = blobBuffer.getBytes((pos - 1) * 2 + 1, length * 2);
return new String(data, "UTF-16LE");
} catch (IOException e) {
throw new SQLException(Messages.get("error.generic.ioerror",
e.getMessage()),
"HY000");
}
}
public long length() throws SQLException {
return blobBuffer.getLength() / 2;
}
public long position(String searchStr, long start) throws SQLException {
if (searchStr == null) {
throw new SQLException(
Messages.get("error.clob.searchnull"), "HY009");
}
try {
byte[] pattern = searchStr.getBytes("UTF-16LE");
int pos = blobBuffer.position(pattern, (start - 1) * 2 + 1);
return (pos < 0) ? pos : (pos - 1) / 2 + 1;
} catch (UnsupportedEncodingException e) {
// This should never happen!
throw new IllegalStateException(
"UTF-16LE encoding is not supported.");
}
}
public long position(Clob searchStr, long start) throws SQLException {
if (searchStr == null) {
throw new SQLException(
Messages.get("error.clob.searchnull"), "HY009");
}
BlobBuffer bbuf = ((ClobImpl) searchStr).getBlobBuffer();
byte[] pattern = bbuf.getBytes(1, (int) bbuf.getLength());
int pos = blobBuffer.position(pattern, (start - 1) * 2 + 1);
return (pos < 0) ? pos : (pos - 1) / 2 + 1;
}
public OutputStream setAsciiStream(final long pos) throws SQLException {
return blobBuffer.setBinaryStream((pos - 1) * 2 + 1, true);
}
public Writer setCharacterStream(final long pos) throws SQLException {
try {
return new BufferedWriter(new OutputStreamWriter(
blobBuffer.setBinaryStream((pos - 1) * 2 + 1, false),
"UTF-16LE"));
} catch (UnsupportedEncodingException e) {
// Should never happen
throw new IllegalStateException("UTF-16LE encoding is not supported.");
}
}
public int setString(long pos, String str) throws SQLException {
if (str == null) {
throw new SQLException(
Messages.get("error.clob.strnull"), "HY009");
}
return setString(pos, str, 0, str.length());
}
public int setString(long pos, String str, int offset, int len)
throws SQLException {
if (offset < 0 || offset > str.length()) {
throw new SQLException(Messages.get(
"error.blobclob.badoffset"), "HY090");
}
if (len < 0 || offset + len > str.length()) {
throw new SQLException(
Messages.get("error.blobclob.badlen"), "HY090");
}
try {
byte[] data = str.substring(offset, offset + len)
.getBytes("UTF-16LE");
// No need to force BlobBuffer to copy the bytes as this is a local
// buffer and cannot be corrupted by the user.
return blobBuffer.setBytes(
(pos - 1) * 2 + 1, data, 0, data.length, false);
} catch (UnsupportedEncodingException e) {
// This should never happen!
throw new IllegalStateException(
"UTF-16LE encoding is not supported.");
}
}
public void truncate(long len) throws SQLException {
blobBuffer.truncate(len * 2);
}
/////// JDBC4 demarcation, do NOT put any JDBC3 code below this line ///////
public void free() throws SQLException {
// TODO Auto-generated method stub
throw new AbstractMethodError();
}
public Reader getCharacterStream(long pos, long length) throws SQLException {
// TODO Auto-generated method stub
throw new AbstractMethodError();
}
}