All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.jdbc.JDBCClobFile Maven / Gradle / Ivy

The newest version!
/* 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; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy