org.hsqldb.jdbc.JDBCClobFile Maven / Gradle / Ivy
/* Copyright (c) 2001-2011, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.jdbc;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.FileUtil;
import org.hsqldb.lib.InOutUtil;
import org.hsqldb.lib.KMPSearchAlgorithm;
/**
*
*
* HSQLDB-Specific Information:
*
* Starting with 2.1, in addition to HSQLDB driver support for both client-side
* in-memory and remote SQL CLOB data implementations, this class is provided
* to expose efficient, relatively high-performance CLOB operations over client
* accessible files.
*
* Design Notes
*
* Although it is possible to implement a transactional version of this class,
* the present implementation directly propagates changes to the underlying
* file such that changes become visible as soon as they are either
* implicitly or explicitly flushed to disk.
*
*
*
*
* @author boucherb@users
* @version 2.1.1
* @since HSQLDB 2.1
*/
public class JDBCClobFile implements java.sql.Clob {
/**
* Retrieves the number of characters
* in the CLOB
value
* designated by this Clob
object.
*
* @return length of the CLOB
in characters
* @exception SQLException if there is an error accessing the
* length of the CLOB
value
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.2
*/
public long length() throws SQLException {
checkClosed();
if (m_fixedWidthCharset) {
return m_file.length() / m_maxCharWidth;
}
ReaderAdapter adapter = null;
try {
adapter = new ReaderAdapter(m_file, 0, Long.MAX_VALUE);
final long length = adapter.skip(Long.MAX_VALUE);
return length;
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
} finally {
if (adapter != null) {
try {
adapter.close();
} catch (Exception ex) {}
}
}
}
/**
* Retrieves a copy of the specified substring
* in the CLOB
value
* designated by this Clob
object.
* The substring begins at position
* pos
and has up to length
consecutive
* characters.
*
* @param pos the first character of the substring to be extracted.
* The first character is at position 1.
* @param length the number of consecutive characters to be copied;
* the value for length must be 0 or greater
* @return a String
that is the specified substring in
* the CLOB
value designated by this Clob
object
* @exception SQLException if there is an error accessing the
* CLOB
value; if pos is less than 1 or length is
* less than 0
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.2
*/
public String getSubString(final long pos,
final int length) throws SQLException {
Reader reader = null;
CharArrayWriter writer = null;
try {
final int initialCapacity =
Math.min(InOutUtil.DEFAULT_COPY_BUFFER_SIZE, length);
//
reader = getCharacterStream(pos, length);
writer = new CharArrayWriter(initialCapacity);
//
InOutUtil.copy(reader, writer, length);
} catch (SQLException ex) {
throw ex;
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception ex) {}
}
}
return writer.toString();
}
/**
* Retrieves the CLOB
value designated by this Clob
* object as a java.io.Reader
object (or as a stream of
* characters).
*
* @return a java.io.Reader
object containing the
* CLOB
data
* @exception SQLException if there is an error accessing the
* CLOB
value
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see #setCharacterStream
* @since JDK 1.2
*/
public Reader getCharacterStream() throws SQLException {
return getCharacterStream(1, Long.MAX_VALUE);
}
/**
* Retrieves the CLOB
value designated by this Clob
* object as an ascii stream.
*
* @return a java.io.InputStream
object containing the
* CLOB
data
* @exception SQLException if there is an error accessing the
* CLOB
value
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see #setAsciiStream
* @since JDK 1.2
*/
public InputStream getAsciiStream() throws SQLException {
InputStream stream;
try {
stream = new JDBCBlobFile.InputStreamAdapter(m_file, 0,
Long.MAX_VALUE) {
public void close() throws IOException {
try {
super.close();
} finally {
m_streams.remove(this);
}
}
};
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
m_streams.add(stream);
return stream;
}
/**
* Retrieves the character position at which the specified char[]
* pattern
appears in the CLOB
value
* represented by this Clob
object. The search
* begins at position start
.
*
* @param pattern the substring for which to search
* @param start the position at which to begin searching; the first position
* is 1
* @return the position at which the substring appears or -1 if it is not
* present; the first position is 1
* @exception SQLException if there is an error accessing the
* CLOB
value or if pos is less than 1
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
*/
public long position(final char[] pattern,
final long start) throws SQLException {
if (start < 1) {
throw JDBCUtil.outOfRangeArgument("start: " + start);
} else if (pattern == null || pattern.length == 0
|| start > length()) {
return -1L;
}
Reader reader = null;
try {
reader = getCharacterStream(start, Long.MAX_VALUE);
final long matchOffset = KMPSearchAlgorithm.search(reader,
pattern, KMPSearchAlgorithm.computeTable(pattern));
return matchOffset == -1 ? -1
: start + matchOffset;
} catch (SQLException ex) {
throw ex;
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception ex) {}
}
}
}
/**
* Retrieves the character position at which the specified
* Clob
object searchstr
appears in this
* Clob
object. The search begins at position
* start
.
*
* @param searchstr the Clob
object for which to search
* @param start the position at which to begin searching; the first
* position is 1
* @return the position at which the Clob
object appears
* or -1 if it is not present; the first position is 1
* @exception SQLException if there is an error accessing the
* CLOB
value or if start is less than 1
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.2
*/
public long position(String searchstr, long start) throws SQLException {
return position(searchstr == null ? null
: searchstr.toCharArray(), start);
}
/**
* Retrieves the character position at which the specified
* Clob
object searchstr
appears in this
* Clob
object. The search begins at position
* start
.
*
* @param pattern the Clob
object for which to search
* @param start the position at which to begin searching; the first
* position is 1
* @return the position at which the Clob
object appears
* or -1 if it is not present; the first position is 1
* @exception SQLException if there is an error accessing the
* CLOB
value or if start is less than 1
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.2
*/
public long position(final Clob pattern,
final long start) throws SQLException {
long patternLength;
if (start < 1) {
throw JDBCUtil.outOfRangeArgument("start: " + start);
} else if ((patternLength = pattern == null ? 0
: pattern.length()) == 0) {
return -1L;
} else if (patternLength > Integer.MAX_VALUE) {
throw JDBCUtil.outOfRangeArgument("pattern.length(): "
+ patternLength);
}
char[] charPattern;
if (pattern instanceof JDBCClob) {
charPattern = ((JDBCClob) pattern).data().toCharArray();
} else {
Reader reader = null;
CharArrayWriter writer = new CharArrayWriter();
try {
reader = pattern.getCharacterStream();
InOutUtil.copy(reader, writer, patternLength);
} catch (IOException ex) {
throw JDBCUtil.sqlException(ex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {}
}
}
charPattern = writer.toCharArray();
}
return position(charPattern, start);
}
//---------------------------- jdbc 3.0 -----------------------------------
/**
* Writes the given Java String
to the CLOB
* value that this Clob
object designates at the position
* pos
. The string will overwrite the existing characters
* in the Clob
object starting at the position
* pos
. If the end of the Clob
value is reached
* while writing the given string, then the length of the Clob
* value will be increased to accommodate the extra characters.
*
* Note: If the value specified for pos
* is greater then the length+1 of the CLOB
value then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
* @param pos the position at which to start writing to the CLOB
* value that this Clob
object represents;
* The first position is 1
* @param str the string to be written to the CLOB
* value that this Clob
designates
* @return the number of characters written
* @exception SQLException if there is an error accessing the
* CLOB
value or if pos is less than 1
*
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.4
*/
public int setString(final long pos,
final String str) throws SQLException {
return setString(pos, str, 0, str == null ? 0
: str.length());
}
/**
* Writes len
characters of str
, starting
* at character offset
, to the CLOB
value
* that this Clob
represents. The string will overwrite the existing characters
* in the Clob
object starting at the position
* pos
. If the end of the Clob
value is reached
* while writing the given string, then the length of the Clob
* value will be increased to accommodate the extra characters.
*
* Note: If the value specified for pos
* is greater then the length+1 of the CLOB
value then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
* @param pos the position at which to start writing to this
* CLOB
object; The first position is 1
* @param str the string to be written to the CLOB
* value that this Clob
object represents
* @param offset the offset into str
to start reading
* the characters to be written
* @param len the number of characters to be written
* @return the number of characters written
* @exception SQLException if there is an error accessing the
* CLOB
value or if pos is less than 1
*
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.4
*/
public int setString(final long pos, final String str, final int offset,
final int len) throws SQLException {
if (str == null) {
throw JDBCUtil.nullArgument("str");
}
Writer writer = null;
try {
writer = setCharacterStream(pos);
writer.write(str, offset, len);
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
} finally {
if (writer != null) {
try {
writer.close();
} catch (Exception ex) {}
}
}
return len;
}
/**
* Retrieves a stream to be used to write Ascii characters to the
* CLOB
value that this Clob
object represents,
* starting at position pos
. Characters written to the stream
* will overwrite the existing characters
* in the Clob
object starting at the position
* pos
. If the end of the Clob
value is reached
* while writing characters to the stream, then the length of the Clob
* value will be increased to accommodate the extra characters.
*
* Note: If the value specified for pos
* is greater then the length+1 of the CLOB
value then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
* @param pos the position at which to start writing to this
* CLOB
object; The first position is 1
* @return the stream to which ASCII encoded characters can be written
* @exception SQLException if there is an error accessing the
* CLOB
value or if pos is less than 1
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see #getAsciiStream
*
* @since JDK 1.4
*/
public OutputStream setAsciiStream(long pos) throws SQLException {
if (pos < 1) {
throw JDBCUtil.invalidArgument("pos: " + pos);
}
checkClosed();
createFile();
OutputStream stream;
try {
stream = new JDBCBlobFile.OutputStreamAdapter(m_file, pos - 1) {
public void close() throws IOException {
try {
super.close();
} finally {
m_streams.remove(this);
}
}
};
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
m_streams.add(stream);
return stream;
}
/**
* Retrieves a stream to be used to write a stream of Unicode characters
* to the CLOB
value that this Clob
object
* represents, at position pos
. Characters written to the stream
* will overwrite the existing characters
* in the Clob
object starting at the position
* pos
. If the end of the Clob
value is reached
* while writing characters to the stream, then the length of the Clob
* value will be increased to accommodate the extra characters.
*
* Note: If the value specified for pos
* is greater then the length+1 of the CLOB
value then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
* @param pos the position at which to start writing to the
* CLOB
value; The first position is 1
*
* @return a stream to which Unicode encoded characters can be written
* @exception SQLException if there is an error accessing the
* CLOB
value or if pos is less than 1
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see #getCharacterStream
*
* @since JDK 1.4
*/
public Writer setCharacterStream(final long pos) throws SQLException {
if (pos < 1) {
throw JDBCUtil.invalidArgument("pos: " + pos);
}
checkClosed();
createFile();
Writer writer;
try {
final WriterAdapter adapter = new WriterAdapter(m_file, pos - 1) {
public void close() throws IOException {
try {
super.close();
} finally {
m_streams.remove(this);
}
}
};
writer = new BufferedWriter(adapter);
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
m_streams.add(writer);
return writer;
}
/**
* Truncates the CLOB
value that this Clob
* designates to have a length of len
* characters.
*
* Note: If the value specified for pos
* is greater then the length+1 of the CLOB
value then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
* @param len the length, in characters, to which the CLOB
value
* should be truncated
* @exception SQLException if there is an error accessing the
* CLOB
value or if len is less than 0
*
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.4
*/
public void truncate(long len) throws SQLException {
if (len < 0) {
throw JDBCUtil.invalidArgument("len: " + len);
}
checkClosed();
ReaderAdapter adapter = null;
RandomAccessFile randomAccessFile = null;
long filePointer;
try {
adapter = new ReaderAdapter(m_file, len, Long.MAX_VALUE);
filePointer = adapter.getFilePointer();
adapter.close();
randomAccessFile = new RandomAccessFile(m_file, "rw");
randomAccessFile.setLength(filePointer);
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
} finally {
if (adapter != null) {
try {
adapter.close();
} catch (Exception ex) {}
}
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (Exception ex) {}
}
}
}
/**
* This method frees the Clob
object and releases the resources the resources
* that it holds. The object is invalid once the free
method
* is called.
*
* After free
has been called, any attempt to invoke a
* method other than free
will result in a SQLException
* being thrown. If free
is called multiple times, the subsequent
* calls to free
are treated as a no-op.
*
* @throws SQLException if an error occurs releasing
* the Clob's resources
*
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.4
*/
public synchronized void free() throws SQLException {
if (m_closed) {
return;
}
m_closed = true;
final List streams = new ArrayList();
streams.addAll(m_streams);
m_streams = null;
for (Iterator itr = streams.iterator(); itr.hasNext(); ) {
final Object stream = itr.next();
if (stream instanceof InputStream) {
try {
((InputStream) stream).close();
} catch (Exception ex) {
//
}
} else if (stream instanceof OutputStream) {
try {
((OutputStream) stream).close();
} catch (Exception ex) {
//
}
}
}
if (m_deleteOnFree) {
try {
m_file.delete();
} catch (Exception e) {}
}
}
/**
* Returns a Reader
object that contains a partial Clob
value, starting
* with the character specified by pos, which is length characters in length.
*
* @param pos the offset to the first character of the partial value to
* be retrieved. The first character in the Clob is at position 1.
* @param length the length in characters of the partial value to be retrieved.
* @return Reader
through which the partial Clob
value can be read.
* @throws SQLException if pos is less than 1 or if pos is greater than the number of
* characters in the Clob
or if pos + length is greater than the number of
* characters in the Clob
*
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since 1.6
*/
public Reader getCharacterStream(long pos,
long length) throws SQLException {
if (pos < 1) {
throw JDBCUtil.outOfRangeArgument("pos: " + pos);
}
pos--;
if (length < 0) {
throw JDBCUtil.outOfRangeArgument("length: " + length);
}
Reader reader;
try {
reader = new ReaderAdapter(m_file, pos, length) {
public void close() throws IOException {
try {
super.close();
} finally {
m_streams.remove(this);
}
}
};
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
m_streams.add(reader);
return reader;
}
/**
* Retrieves the canonical File object denoting the file that
* backs this CLOB.
*
* @return the file that backs this CLOB.
*/
public File getFile() {
return m_file;
}
/**
*
* @return the name of the character encoding used to read and write
* character data in the underlying files, as well as to determine
* the character length and character offsets into the underlying
* file
*/
public String getEncoding() {
return m_encoding;
}
/**
* Retrieves whether an attempt to delete the backing file
* is made in response to invocation of {@link #free()}.
*
* @return true if backing file deletion is attempted; otherwise false.
*/
public boolean isDeleteOnFree() {
return m_deleteOnFree;
}
/**
* Assigns whether an attempt to delete the backing file
* is made in response to invocation of {@link #free()}.
*
* @param deleteOnFree the new value to assign
*/
public void setDeleteOnFree(boolean deleteOnFree) {
m_deleteOnFree = deleteOnFree;
}
/**
* Ensures this object is freed in response to finalization.
*/
protected void finalize() throws Throwable {
try {
super.finalize();
} finally {
try {
this.free();
} catch (Throwable throwable) {}
}
}
//--------------------------------------------------------------------------
// Internal Implementation
//--------------------------------------------------------------------------
public static final String TEMP_FILE_PREFIX = "hsql_jdbc_clob_file_";
public static final String TEMP_FILE_SUFFIX = ".tmp";
//
private final File m_file;
//
private boolean m_closed;
private boolean m_deleteOnFree;
private String m_encoding;
private Charset m_charset;
private CharsetEncoder m_encoder;
private boolean m_fixedWidthCharset;
private int m_maxCharWidth;
private List m_streams = new ArrayList();
/**
* Convenience constructor for {@link
* #JDBCClobFile(java.lang.String)
* JDBCClobFile((String)null)}.
*
* @throws SQLException if the platform encoding is unsupported,
* the temp file cannot be created or some other
* error occurs that prevents the construction of a
* valid instance of this class.
*/
public JDBCClobFile() throws SQLException {
this((String) null);
}
/**
* Constructs a new JDBCClobFile instance backed by an File object
* created by File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX),
* using the given encoding to read and write file content.
*
* @param encoding the name of the character encoding used to read and write
* character data in the underlying file, as well as to determine
* the character length of and character offsets into the underlying
* file. Specify null to denote the platform encoding.
*
* @throws SQLException if the given encoding is unsupported,
* the backing temp file could not be created or if a security
* manager exists and its {@link
* java.lang.SecurityManager#checkWrite(java.lang.String)}
* method does not allow a file to be created.
*/
public JDBCClobFile(String encoding) throws SQLException {
try {
setEncoding(encoding);
m_file = File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX);
m_deleteOnFree = true;
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
}
/**
* Convenience constructor for {@link
* #JDBCClobFile(java.io.File, java.lang.String)
* JDBCClobFile(file,null)}.
*
* @param file that is to back the new CLOB instance.
*
* @throws SQLException if an I/O error occurs, which is possible because the
* construction of the canonical pathname may require
* file-system queries; a required system property value
* cannot be accessed; a security manager exists and its
* {@link java.lang.SecurityManager#checkRead}
* method denies read access to the file
*/
public JDBCClobFile(File file) throws SQLException {
this(file, null);
}
/**
* Constructs a new JDBCClobFile instance backed by the given File object
* using the given encoding to read and write file content.
*
* @param file that is to back the new CLOB instance.
* @param encoding the name of the character encoding used to read and write
* character data in the underlying file, as well as to determine
* the character length of and character offsets into the underlying
* file. Specify null to denote the platform encoding.
*
* @throws SQLException if the given encoding is unsupported;
* an I/O error occurs, which is possible because the
* construction of the canonical pathname may require
* file-system queries; a required system property value
* cannot be accessed; a security manager exists and its
* {@link java.lang.SecurityManager#checkRead}
* method denies read access to the file
*/
public JDBCClobFile(File file, String encoding) throws SQLException {
if (file == null) {
throw JDBCUtil.nullArgument("file");
}
try {
setEncoding(encoding);
m_file = file.getCanonicalFile();
checkIsFile( /*checkExists*/false);
m_deleteOnFree = false;
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
}
protected final void setEncoding(final String encoding)
throws UnsupportedEncodingException {
final Charset charSet = charsetForName(encoding);
final CharsetEncoder encoder = charSet.newEncoder().onMalformedInput(
CodingErrorAction.REPLACE).onUnmappableCharacter(
CodingErrorAction.REPLACE);
final float maxBytesPerChar = encoder.maxBytesPerChar();
final float averageBytesPerChar = encoder.averageBytesPerChar();
final boolean fixedWidthCharset =
(maxBytesPerChar == Math.round(maxBytesPerChar))
&& (maxBytesPerChar == averageBytesPerChar);
//
m_fixedWidthCharset = fixedWidthCharset;
m_maxCharWidth = Math.round(maxBytesPerChar);
m_charset = charSet;
m_encoder = encoder;
m_encoding = m_charset.name();
}
protected static Charset charsetForName(final String charsetName)
throws UnsupportedEncodingException {
String csn = charsetName;
if (csn == null) {
csn = Charset.defaultCharset().name();
}
try {
if (Charset.isSupported(csn)) {
return Charset.forName(csn);
}
} catch (IllegalCharsetNameException x) {}
throw new UnsupportedEncodingException(csn);
}
protected final void checkIsFile(boolean checkExists) throws SQLException {
boolean exists = false;
boolean isFile = false;
try {
exists = m_file.exists();
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
if (exists) {
try {
isFile = m_file.isFile();
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
}
if (exists) {
if (!isFile) {
throw JDBCUtil.invalidArgument("Is not a file: " + m_file);
}
} else if (checkExists) {
throw JDBCUtil.invalidArgument("Does not exist: " + m_file);
}
}
protected void checkClosed() throws SQLException {
if (m_closed) {
throw JDBCUtil.sqlException(ErrorCode.X_07501);
}
}
protected void createFile() throws SQLException {
try {
if (!m_file.exists()) {
FileUtil.getFileUtil().makeParentDirectories(m_file);
m_file.createNewFile();
}
} catch (Exception ex) {
throw JDBCUtil.sqlException(ex);
}
checkIsFile( /*checkExists*/true);
}
protected class WriterAdapter extends Writer {
private final RandomAccessFile m_randomAccessFile;
public WriterAdapter(final File file,
final long pos)
throws FileNotFoundException, IOException {
if (file == null) {
throw new NullPointerException("file");
}
if (pos < 0) {
throw new IllegalArgumentException("pos: " + pos);
}
ReaderAdapter reader = null;
long filePointer;
try {
reader = new ReaderAdapter(file, pos, Long.MAX_VALUE);
filePointer = reader.getFilePointer();
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception ex) {}
}
}
m_randomAccessFile = new RandomAccessFile(file, "rw");
m_randomAccessFile.seek(filePointer);
}
public void flush() throws IOException {
m_randomAccessFile.getFD().sync();
}
public void close() throws IOException {
m_randomAccessFile.close();
}
public void write(char[] cbuf, int off, int len) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter writer = m_encoding == null
? new OutputStreamWriter(baos)
: new OutputStreamWriter(baos,
m_charset);
writer.write(cbuf, off, len);
writer.close();
m_randomAccessFile.write(baos.toByteArray());
}
}
protected class ReaderAdapter extends Reader {
//
private static final int CHARBUFFER_CAPACTIY = 128;
//
private final Reader m_reader;
private long m_remaining = Long.MAX_VALUE;
private long m_filePointer;
private ByteBuffer m_byteBuffer;
private CharBuffer m_charBuffer;
public ReaderAdapter(final File file, final long pos,
final long length)
throws FileNotFoundException, IOException {
if (file == null) {
throw new NullPointerException("file");
}
if (pos < 0) {
throw new IllegalArgumentException("pos: " + pos);
}
if (length < 0) {
throw new IllegalArgumentException("length: " + length);
}
//
if (!m_fixedWidthCharset) {
final int charCapacity = CHARBUFFER_CAPACTIY;
final int byteCapacity = charCapacity * m_maxCharWidth;
m_charBuffer = CharBuffer.allocate(charCapacity);
m_byteBuffer = ByteBuffer.allocate(byteCapacity);
}
final FileInputStream fis = new FileInputStream(file);
final BufferedInputStream bis = new BufferedInputStream(fis);
final InputStreamReader isr = new InputStreamReader(bis,
m_charset);
m_reader = isr;
// seek character position 'pos'
for (long i = 0; i < pos; i++) {
final int ch = read();
if (ch == -1) {
break;
}
}
// important - do not assign until *after* seek above.
m_remaining = length;
}
public int read(final char[] cbuf, final int off,
int len) throws IOException {
final long l_remaining = m_remaining;
if (l_remaining <= 0) {
return -1;
} else if (l_remaining < len) {
len = (int) l_remaining;
}
int charsRead = m_reader.read(cbuf, off, len);
if (charsRead == -1) {
return -1;
} else if (charsRead > l_remaining) {
charsRead = (int) l_remaining;
m_remaining = 0;
} else {
m_remaining -= charsRead;
}
int bytesRead;
if (m_fixedWidthCharset) {
bytesRead = (m_maxCharWidth * charsRead);
} else {
final boolean reallocate = (charsRead
> m_charBuffer.capacity());
final CharBuffer cb = reallocate
? CharBuffer.allocate(charsRead)
: m_charBuffer;
final ByteBuffer bb = reallocate
? ByteBuffer.allocate(charsRead
* m_maxCharWidth)
: m_byteBuffer;
//
cb.clear();
bb.clear();
cb.put(cbuf, off, charsRead);
cb.flip();
m_encoder.encode(cb, bb, /*endOfinput*/ true);
bb.flip();
bytesRead = bb.limit();
if (reallocate) {
m_byteBuffer = bb;
m_charBuffer = cb;
}
}
m_filePointer += bytesRead;
return charsRead;
}
public void close() throws IOException {
m_reader.close();
}
public long getFilePointer() {
return m_filePointer;
}
}
}