org.objectstyle.cayenne.util.MemoryClob Maven / Gradle / Ivy
/* ====================================================================
*
* The ObjectStyle Group Software License, version 1.1
* ObjectStyle Group - http://objectstyle.org/
*
* Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
* of the software. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution, if any,
* must include the following acknowlegement:
* "This product includes software developed by independent contributors
* and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
* or promote products derived from this software without prior written
* permission. For written permission, email
* "andrus at objectstyle dot org".
*
* 5. Products derived from this software may not be called "ObjectStyle"
* or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
* names without prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 THE OBJECTSTYLE GROUP OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals and hosted on ObjectStyle Group web site. For more
* information on the ObjectStyle Group, please see
* .
*/
package org.objectstyle.cayenne.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.sql.Clob;
import java.sql.SQLException;
import org.objectstyle.cayenne.CayenneRuntimeException;
/**
* A Clob implementation that stores contents in memory.
*
* This implementation is based on jdbcClob from HSQLDB (copyright HSQL Development
* Group).
*
*
* @since 1.2
* @author Andrus Adamchik
*/
public class MemoryClob implements Clob {
volatile String data;
/**
* Constructs a new jdbcClob object wrapping the given character sequence.
*
* This constructor is used internally to retrieve result set values as Clob objects,
* yet it must be public to allow access from other packages. As such (in the interest
* of efficiency) this object maintains a reference to the given String object rather
* than making a copy and so it is gently suggested (in the interest of effective
* memory management) that extenal clients using this constructor either take pause to
* consider the implications or at least take care to provide a String object whose
* internal character buffer is not much larger than required to represent the value.
*
* @param data the character sequence representing the Clob value
* @throws SQLException if the argument is null
*/
public MemoryClob(String data) {
if (data == null) {
throw new CayenneRuntimeException("Null data");
}
this.data = data;
}
/**
* 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
*/
public long length() throws SQLException {
final String ldata = data;
return ldata.length();
}
/**
* 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.
*/
public String getSubString(long pos, final int length) throws SQLException {
final String ldata = data;
final int dlen = ldata.length();
pos--;
if (pos < 0 || pos > dlen) {
new CayenneRuntimeException("Invalid position: " + (pos + 1L));
}
if (length < 0 || length > dlen - pos) {
throw new CayenneRuntimeException("Invalid length: " + length);
}
if (pos == 0 && length == dlen) {
return ldata;
}
return ldata.substring((int) pos, (int) pos + length);
}
/**
* 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
*/
public java.io.Reader getCharacterStream() throws SQLException {
final String ldata = data;
return new StringReader(ldata);
}
/**
* 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
*/
public java.io.InputStream getAsciiStream() throws SQLException {
final String ldata = data;
return new AsciiStringInputStream(ldata);
}
/**
* Retrieves the character position at which the specified substring
* searchstr
appears in the SQL CLOB
value represented
* by this Clob
object. The search begins at position
* start
.
*
* @param searchstr 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
*/
public long position(final String searchstr, long start) throws SQLException {
if (searchstr == null || start > Integer.MAX_VALUE) {
return -1;
}
final String ldata = data;
final int pos = ldata.indexOf(searchstr, (int) --start);
return (pos < 0) ? -1 : pos + 1;
}
/**
* 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
*/
public long position(final Clob searchstr, long start) throws SQLException {
if (searchstr == null) {
return -1;
}
final String ldata = data;
final long dlen = ldata.length();
final long sslen = searchstr.length();
// This is potentially much less expensive than materializing a large
// substring from some other vendor's CLOB. Indeed, we should probably
// do the comparison piecewise, using an in-memory buffer (or temp-files
// when available), if it is detected that the input CLOB is very long.
if (start > dlen - sslen) {
return -1;
}
// by now, we know sslen and start are both < Integer.MAX_VALUE
String s;
if (searchstr instanceof MemoryClob) {
s = ((MemoryClob) searchstr).data;
}
else {
s = searchstr.getSubString(1L, (int) sslen);
}
final int pos = ldata.indexOf(s, (int) start);
return (pos < 0) ? -1 : pos + 1;
}
/**
* Writes the given Java String
to the CLOB
value that
* this Clob
object designates at the position pos
.
* Calling this method always throws an SQLException
.
*/
public int setString(long pos, String str) throws SQLException {
throw new CayenneRuntimeException("Not supported");
}
/**
* Writes len
characters of str
, starting at character
* offset
, to the CLOB
value that this
* Clob
represents. Calling this method always throws an
* SQLException
.
*/
public int setString(long pos, String str, int offset, int len) throws SQLException {
throw new CayenneRuntimeException("Not supported");
}
/**
* Retrieves a stream to be used to write Ascii characters to the CLOB
* value that this Clob
object represents, starting at position
* pos
.
*
* Calling this method always throws an SQLException
.
*/
public java.io.OutputStream setAsciiStream(long pos) throws SQLException {
throw new CayenneRuntimeException("Not supported");
}
/**
* 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
.
*
* Calling this method always throws an SQLException
.
*/
public java.io.Writer setCharacterStream(long pos) throws SQLException {
throw new CayenneRuntimeException("Not supported");
}
/**
* Truncates the CLOB
value that this Clob
designates to
* have a length of len
characters.
*
*/
public void truncate(final long len) throws SQLException {
final String ldata = data;
final long dlen = ldata.length();
final long chars = len >> 1;
if (chars == dlen) {
// nothing has changed, so there's nothing to be done
}
else if (len < 0 || chars > dlen) {
throw new CayenneRuntimeException("Invalid length: " + len);
}
else {
// use new String() to ensure we get rid of slack
data = new String(ldata.substring(0, (int) chars));
}
}
class AsciiStringInputStream extends InputStream {
protected int strOffset = 0;
protected int charOffset = 0;
protected int available;
protected String str;
public AsciiStringInputStream(String s) {
str = s;
available = s.length() * 2;
}
public int doRead() throws IOException {
if (available == 0) {
return -1;
}
available--;
char c = str.charAt(strOffset);
if (charOffset == 0) {
charOffset = 1;
return (c & 0x0000ff00) >> 8;
}
else {
charOffset = 0;
strOffset++;
return c & 0x000000ff;
}
}
public int read() throws IOException {
doRead();
return doRead();
}
public int available() throws IOException {
return available / 2;
}
}
}