Path: blob/master/src/java.sql.rowset/share/classes/javax/sql/rowset/serial/SerialClob.java
40948 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.sql.rowset.serial;2627import java.sql.*;28import java.io.*;29import java.util.Arrays;3031/**32* A serialized mapping in the Java programming language of an SQL33* <code>CLOB</code> value.34* <P>35* The <code>SerialClob</code> class provides a constructor for creating36* an instance from a <code>Clob</code> object. Note that the <code>Clob</code>37* object should have brought the SQL <code>CLOB</code> value's data over38* to the client before a <code>SerialClob</code> object39* is constructed from it. The data of an SQL <code>CLOB</code> value can40* be materialized on the client as a stream of Unicode characters.41* <P>42* <code>SerialClob</code> methods make it possible to get a substring43* from a <code>SerialClob</code> object or to locate the start of44* a pattern of characters.45*46* <h2> Thread safety </h2>47*48* <p> A SerialClob is not safe for use by multiple concurrent threads. If a49* SerialClob is to be used by more than one thread then access to the SerialClob50* should be controlled by appropriate synchronization.51*52* @author Jonathan Bruce53* @since 1.554*/55public class SerialClob implements Clob, Serializable, Cloneable {5657/**58* A serialized array of characters containing the data of the SQL59* <code>CLOB</code> value that this <code>SerialClob</code> object60* represents.61*62* @serial63*/64private char buf[];6566/**67* Internal Clob representation if SerialClob is initialized with a68* Clob. Null if SerialClob is initialized with a char[].69*/70@SuppressWarnings("serial") // Not statically typed as Serializable; checked in writeObject71private Clob clob;7273/**74* The length in characters of this <code>SerialClob</code> object's75* internal array of characters.76*77* @serial78*/79private long len;8081/**82* The original length in characters of this <code>SerialClob</code>83* object's internal array of characters.84*85* @serial86*/87private long origLen;8889/**90* Constructs a <code>SerialClob</code> object that is a serialized version of91* the given <code>char</code> array.92* <p>93* The new <code>SerialClob</code> object is initialized with the data from the94* <code>char</code> array, thus allowing disconnected <code>RowSet</code>95* objects to establish a serialized <code>Clob</code> object without touching96* the data source.97*98* @param ch the char array representing the <code>Clob</code> object to be99* serialized100* @throws SerialException if an error occurs during serialization101* @throws SQLException if a SQL error occurs102*/103public SerialClob(char ch[]) throws SerialException, SQLException {104105// %%% JMB. Agreed. Add code here to throw a SQLException if no106// support is available for locatorsUpdateCopy=false107// Serializing locators is not supported.108109len = ch.length;110buf = new char[(int)len];111for (int i = 0; i < len ; i++){112buf[i] = ch[i];113}114origLen = len;115clob = null;116}117118/**119* Constructs a <code>SerialClob</code> object that is a serialized120* version of the given <code>Clob</code> object.121* <P>122* The new <code>SerialClob</code> object is initialized with the123* data from the <code>Clob</code> object; therefore, the124* <code>Clob</code> object should have previously brought the125* SQL <code>CLOB</code> value's data over to the client from126* the database. Otherwise, the new <code>SerialClob</code> object127* object will contain no data.128* <p>129* Note: The <code>Clob</code> object supplied to this constructor must130* return non-null for both the <code>Clob.getCharacterStream()</code>131* and <code>Clob.getAsciiStream</code> methods. This <code>SerialClob</code>132* constructor cannot serialize a <code>Clob</code> object in this instance133* and will throw an <code>SQLException</code> object.134*135* @param clob the <code>Clob</code> object from which this136* <code>SerialClob</code> object is to be constructed; cannot be null137* @throws SerialException if an error occurs during serialization138* @throws SQLException if a SQL error occurs in capturing the CLOB;139* if the <code>Clob</code> object is a null; or if either of the140* <code>Clob.getCharacterStream()</code> and <code>Clob.getAsciiStream()</code>141* methods on the <code>Clob</code> returns a null142* @see java.sql.Clob143*/144public SerialClob(Clob clob) throws SerialException, SQLException {145146if (clob == null) {147throw new SQLException("Cannot instantiate a SerialClob " +148"object with a null Clob object");149}150len = clob.length();151this.clob = clob;152buf = new char[(int)len];153int read = 0;154int offset = 0;155156try (Reader charStream = clob.getCharacterStream()) {157if (charStream == null) {158throw new SQLException("Invalid Clob object. The call to getCharacterStream " +159"returned null which cannot be serialized.");160}161162// Note: get an ASCII stream in order to null-check it,163// even though we don't do anything with it.164try (InputStream asciiStream = clob.getAsciiStream()) {165if (asciiStream == null) {166throw new SQLException("Invalid Clob object. The call to getAsciiStream " +167"returned null which cannot be serialized.");168}169}170171try (Reader reader = new BufferedReader(charStream)) {172do {173read = reader.read(buf, offset, (int)(len - offset));174offset += read;175} while (read > 0);176}177} catch (java.io.IOException ex) {178throw new SerialException("SerialClob: " + ex.getMessage());179}180181origLen = len;182}183184/**185* Retrieves the number of characters in this <code>SerialClob</code>186* object's array of characters.187*188* @return a <code>long</code> indicating the length in characters of this189* <code>SerialClob</code> object's array of character190* @throws SerialException if an error occurs;191* if {@code free} had previously been called on this object192*/193public long length() throws SerialException {194isValid();195return len;196}197198/**199* Returns this <code>SerialClob</code> object's data as a stream200* of Unicode characters. Unlike the related method, <code>getAsciiStream</code>,201* a stream is produced regardless of whether the <code>SerialClob</code> object202* was created with a <code>Clob</code> object or a <code>char</code> array.203*204* @return a <code>java.io.Reader</code> object containing this205* <code>SerialClob</code> object's data206* @throws SerialException if an error occurs;207* if {@code free} had previously been called on this object208*/209public java.io.Reader getCharacterStream() throws SerialException {210isValid();211return (java.io.Reader) new CharArrayReader(buf);212}213214/**215* Retrieves the <code>CLOB</code> value designated by this <code>SerialClob</code>216* object as an ascii stream. This method forwards the <code>getAsciiStream</code>217* call to the underlying <code>Clob</code> object in the event that this218* <code>SerialClob</code> object is instantiated with a <code>Clob</code>219* object. If this <code>SerialClob</code> object is instantiated with220* a <code>char</code> array, a <code>SerialException</code> object is thrown.221*222* @return a <code>java.io.InputStream</code> object containing223* this <code>SerialClob</code> object's data224* @throws SerialException if this {@code SerialClob} object was not225* instantiated with a <code>Clob</code> object;226* if {@code free} had previously been called on this object227* @throws SQLException if there is an error accessing the228* <code>CLOB</code> value represented by the <code>Clob</code> object229* that was used to create this <code>SerialClob</code> object230*/231public java.io.InputStream getAsciiStream() throws SerialException, SQLException {232isValid();233if (this.clob != null) {234return this.clob.getAsciiStream();235} else {236throw new SerialException("Unsupported operation. SerialClob cannot " +237"return a the CLOB value as an ascii stream, unless instantiated " +238"with a fully implemented Clob object.");239}240}241242/**243* Returns a copy of the substring contained in this244* <code>SerialClob</code> object, starting at the given position245* and continuing for the specified number or characters.246*247* @param pos the position of the first character in the substring248* to be copied; the first character of the249* <code>SerialClob</code> object is at position250* <code>1</code>; must not be less than <code>1</code>,251* and the sum of the starting position and the length252* of the substring must be less than the length of this253* <code>SerialClob</code> object254* @param length the number of characters in the substring to be255* returned; must not be greater than the length of256* this <code>SerialClob</code> object, and the257* sum of the starting position and the length258* of the substring must be less than the length of this259* <code>SerialClob</code> object260* @return a <code>String</code> object containing a substring of261* this <code>SerialClob</code> object beginning at the262* given position and containing the specified number of263* consecutive characters264* @throws SerialException if either of the arguments is out of bounds;265* if {@code free} had previously been called on this object266*/267public String getSubString(long pos, int length) throws SerialException {268269isValid();270if (pos < 1 || pos > this.length()) {271throw new SerialException("Invalid position in SerialClob object set");272}273274if ((pos-1) + length > this.length()) {275throw new SerialException("Invalid position and substring length");276}277278try {279return new String(buf, (int)pos - 1, length);280281} catch (StringIndexOutOfBoundsException e) {282throw new SerialException("StringIndexOutOfBoundsException: " +283e.getMessage());284}285286}287288/**289* Returns the position in this <code>SerialClob</code> object290* where the given <code>String</code> object begins, starting291* the search at the specified position. This method returns292* <code>-1</code> if the pattern is not found.293*294* @param searchStr the <code>String</code> object for which to295* search296* @param start the position in this <code>SerialClob</code> object297* at which to start the search; the first position is298* <code>1</code>; must not be less than <code>1</code> nor299* greater than the length of this <code>SerialClob</code> object300* @return the position at which the given <code>String</code> object301* begins, starting the search at the specified position;302* <code>-1</code> if the given <code>String</code> object is303* not found or the starting position is out of bounds; position304* numbering for the return value starts at <code>1</code>305* @throws SerialException if the {@code free} method had been306* previously called on this object307* @throws SQLException if there is an error accessing the Clob value308* from the database.309*/310public long position(String searchStr, long start)311throws SerialException, SQLException {312isValid();313if (start < 1 || start > len) {314return -1;315}316317char pattern[] = searchStr.toCharArray();318319int pos = (int)start-1;320int i = 0;321long patlen = pattern.length;322323while (pos < len) {324if (pattern[i] == buf[pos]) {325if (i + 1 == patlen) {326return (pos + 1) - (patlen - 1);327}328i++; pos++; // increment pos, and i329330} else if (pattern[i] != buf[pos]) {331pos++; // increment pos only332}333}334return -1; // not found335}336337/**338* Returns the position in this <code>SerialClob</code> object339* where the given <code>Clob</code> signature begins, starting340* the search at the specified position. This method returns341* <code>-1</code> if the pattern is not found.342*343* @param searchStr the <code>Clob</code> object for which to search344* @param start the position in this <code>SerialClob</code> object345* at which to begin the search; the first position is346* <code>1</code>; must not be less than <code>1</code> nor347* greater than the length of this <code>SerialClob</code> object348* @return the position at which the given <code>Clob</code>349* object begins in this <code>SerialClob</code> object,350* at or after the specified starting position351* @throws SerialException if an error occurs locating the Clob signature;352* if the {@code free} method had been previously called on this object353* @throws SQLException if there is an error accessing the Clob value354* from the database355*/356public long position(Clob searchStr, long start)357throws SerialException, SQLException {358isValid();359return position(searchStr.getSubString(1,(int)searchStr.length()), start);360}361362/**363* Writes the given Java <code>String</code> to the <code>CLOB</code>364* value that this <code>SerialClob</code> object represents, at the position365* <code>pos</code>.366*367* @param pos the position at which to start writing to the <code>CLOB</code>368* value that this <code>SerialClob</code> object represents; the first369* position is <code>1</code>; must not be less than <code>1</code> nor370* greater than the length of this <code>SerialClob</code> object371* @param str the string to be written to the <code>CLOB</code>372* value that this <code>SerialClob</code> object represents373* @return the number of characters written374* @throws SerialException if there is an error accessing the375* <code>CLOB</code> value; if an invalid position is set; if an376* invalid offset value is set; if number of bytes to be written377* is greater than the <code>SerialClob</code> length; or the combined378* values of the length and offset is greater than the Clob buffer;379* if the {@code free} method had been previously called on this object380*/381public int setString(long pos, String str) throws SerialException {382return (setString(pos, str, 0, str.length()));383}384385/**386* Writes <code>len</code> characters of <code>str</code>, starting387* at character <code>offset</code>, to the <code>CLOB</code> value388* that this <code>Clob</code> represents.389*390* @param pos the position at which to start writing to the <code>CLOB</code>391* value that this <code>SerialClob</code> object represents; the first392* position is <code>1</code>; must not be less than <code>1</code> nor393* greater than the length of this <code>SerialClob</code> object394* @param str the string to be written to the <code>CLOB</code>395* value that this <code>Clob</code> object represents396* @param offset the offset into <code>str</code> to start reading397* the characters to be written398* @param length the number of characters to be written399* @return the number of characters written400* @throws SerialException if there is an error accessing the401* <code>CLOB</code> value; if an invalid position is set; if an402* invalid offset value is set; if number of bytes to be written403* is greater than the <code>SerialClob</code> length; or the combined404* values of the length and offset is greater than the Clob buffer;405* if the {@code free} method had been previously called on this object406*/407public int setString(long pos, String str, int offset, int length)408throws SerialException {409isValid();410String temp = str.substring(offset);411char cPattern[] = temp.toCharArray();412413if (offset < 0 || offset > str.length()) {414throw new SerialException("Invalid offset in byte array set");415}416417if (pos < 1 || pos > this.length()) {418throw new SerialException("Invalid position in Clob object set");419}420421if ((long)(length) > origLen) {422throw new SerialException("Buffer is not sufficient to hold the value");423}424425if ((length + offset) > str.length()) {426// need check to ensure length + offset !> bytes.length427throw new SerialException("Invalid OffSet. Cannot have combined offset " +428" and length that is greater that the Blob buffer");429}430431int i = 0;432pos--; //values in the array are at position one less433while ( i < length || (offset + i +1) < (str.length() - offset ) ) {434this.buf[(int)pos + i ] = cPattern[offset + i ];435i++;436}437return i;438}439440/**441* Retrieves a stream to be used to write Ascii characters to the442* <code>CLOB</code> value that this <code>SerialClob</code> object represents,443* starting at position <code>pos</code>. This method forwards the444* <code>setAsciiStream()</code> call to the underlying <code>Clob</code> object in445* the event that this <code>SerialClob</code> object is instantiated with a446* <code>Clob</code> object. If this <code>SerialClob</code> object is instantiated447* with a <code>char</code> array, a <code>SerialException</code> object is thrown.448*449* @param pos the position at which to start writing to the450* <code>CLOB</code> object451* @return the stream to which ASCII encoded characters can be written452* @throws SerialException if SerialClob is not instantiated with a453* Clob object;454* if the {@code free} method had been previously called on this object455* @throws SQLException if there is an error accessing the456* <code>CLOB</code> value457* @see #getAsciiStream458*/459public java.io.OutputStream setAsciiStream(long pos)460throws SerialException, SQLException {461isValid();462if (this.clob != null) {463return this.clob.setAsciiStream(pos);464} else {465throw new SerialException("Unsupported operation. SerialClob cannot " +466"return a writable ascii stream\n unless instantiated with a Clob object " +467"that has a setAsciiStream() implementation");468}469}470471/**472* Retrieves a stream to be used to write a stream of Unicode characters473* to the <code>CLOB</code> value that this <code>SerialClob</code> object474* represents, at position <code>pos</code>. This method forwards the475* <code>setCharacterStream()</code> call to the underlying <code>Clob</code>476* object in the event that this <code>SerialClob</code> object is instantiated with a477* <code>Clob</code> object. If this <code>SerialClob</code> object is instantiated with478* a <code>char</code> array, a <code>SerialException</code> is thrown.479*480* @param pos the position at which to start writing to the481* <code>CLOB</code> value482*483* @return a stream to which Unicode encoded characters can be written484* @throws SerialException if the SerialClob is not instantiated with485* a Clob object;486* if the {@code free} method had been previously called on this object487* @throws SQLException if there is an error accessing the488* <code>CLOB</code> value489* @see #getCharacterStream490*/491public java.io.Writer setCharacterStream(long pos)492throws SerialException, SQLException {493isValid();494if (this.clob != null) {495return this.clob.setCharacterStream(pos);496} else {497throw new SerialException("Unsupported operation. SerialClob cannot " +498"return a writable character stream\n unless instantiated with a Clob object " +499"that has a setCharacterStream implementation");500}501}502503/**504* Truncates the <code>CLOB</code> value that this <code>SerialClob</code>505* object represents so that it has a length of <code>len</code>506* characters.507* <p>508* Truncating a <code>SerialClob</code> object to length 0 has the effect of509* clearing its contents.510*511* @param length the length, in bytes, to which the <code>CLOB</code>512* value should be truncated513* @throws SerialException if there is an error accessing the514* <code>CLOB</code> value;515* if the {@code free} method had been previously called on this object516*/517public void truncate(long length) throws SerialException {518isValid();519if (length > len) {520throw new SerialException521("Length more than what can be truncated");522} else {523len = length;524// re-size the buffer525526if (len == 0) {527buf = new char[] {};528} else {529buf = (this.getSubString(1, (int)len)).toCharArray();530}531}532}533534535/**536* Returns a {@code Reader} object that contains a partial537* {@code SerialClob} value, starting538* with the character specified by pos, which is length characters in length.539*540* @param pos the offset to the first character of the partial value to541* be retrieved. The first character in the {@code SerialClob} is at position 1.542* @param length the length in characters of the partial value to be retrieved.543* @return {@code Reader} through which the partial {@code SerialClob}544* value can be read.545* @throws SQLException if pos is less than 1 or if pos is greater than the546* number of characters in the {@code SerialClob} or if pos + length547* is greater than the number of characters in the {@code SerialClob};548* @throws SerialException if the {@code free} method had been previously549* called on this object550* @since 1.6551*/552public Reader getCharacterStream(long pos, long length) throws SQLException {553isValid();554if (pos < 1 || pos > len) {555throw new SerialException("Invalid position in Clob object set");556}557558if ((pos-1) + length > len) {559throw new SerialException("Invalid position and substring length");560}561if (length <= 0) {562throw new SerialException("Invalid length specified");563}564return new CharArrayReader(buf, (int)pos, (int)length);565}566567/**568* This method frees the {@code SerialClob} object and releases the569* resources that it holds.570* The object is invalid once the {@code free} method is called.571* <p>572* If {@code free} is called multiple times, the subsequent573* calls to {@code free} are treated as a no-op.574* </P>575* @throws SQLException if an error occurs releasing576* the Clob's resources577* @since 1.6578*/579public void free() throws SQLException {580if (buf != null) {581buf = null;582if (clob != null) {583clob.free();584}585clob = null;586}587}588589/**590* Compares this SerialClob to the specified object. The result is {@code591* true} if and only if the argument is not {@code null} and is a {@code592* SerialClob} object that represents the same sequence of characters as this593* object.594*595* @param obj The object to compare this {@code SerialClob} against596*597* @return {@code true} if the given object represents a {@code SerialClob}598* equivalent to this SerialClob, {@code false} otherwise599*600*/601public boolean equals(Object obj) {602if (this == obj) {603return true;604}605if (obj instanceof SerialClob) {606SerialClob sc = (SerialClob)obj;607if (this.len == sc.len) {608return Arrays.equals(buf, sc.buf);609}610}611return false;612}613614/**615* Returns a hash code for this {@code SerialClob}.616* @return a hash code value for this object.617*/618public int hashCode() {619return ((31 + Arrays.hashCode(buf)) * 31 + (int)len) * 31 + (int)origLen;620}621622/**623* Returns a clone of this {@code SerialClob}. The copy will contain a624* reference to a clone of the internal character array, not a reference625* to the original internal character array of this {@code SerialClob} object.626* The underlying {@code Clob} object will be set to null.627*628* @return a clone of this SerialClob629*/630public Object clone() {631try {632SerialClob sc = (SerialClob) super.clone();633sc.buf = (buf != null) ? Arrays.copyOf(buf, (int)len) : null;634sc.clob = null;635return sc;636} catch (CloneNotSupportedException ex) {637// this shouldn't happen, since we are Cloneable638throw new InternalError();639}640}641642/**643* readObject is called to restore the state of the SerialClob from644* a stream.645* @param s the {@code ObjectInputStream} to read from.646*647* @throws ClassNotFoundException if the class of a serialized object648* could not be found.649* @throws IOException if an I/O error occurs.650*/651private void readObject(ObjectInputStream s)652throws IOException, ClassNotFoundException {653654ObjectInputStream.GetField fields = s.readFields();655char[] tmp = (char[])fields.get("buf", null);656if (tmp == null)657throw new InvalidObjectException("buf is null and should not be!");658buf = tmp.clone();659len = fields.get("len", 0L);660if (buf.length != len)661throw new InvalidObjectException("buf is not the expected size");662origLen = fields.get("origLen", 0L);663clob = (Clob) fields.get("clob", null);664}665666/**667* writeObject is called to save the state of the SerialClob668* to a stream.669* @param s the {@code ObjectOutputStream} to write to.670* @throws IOException if an I/O error occurs.671*/672private void writeObject(ObjectOutputStream s)673throws IOException {674675ObjectOutputStream.PutField fields = s.putFields();676fields.put("buf", buf);677fields.put("len", len);678fields.put("origLen", origLen);679// Note: this check to see if it is an instance of Serializable680// is for backwards compatibility681fields.put("clob", clob instanceof Serializable ? clob : null);682s.writeFields();683}684685/**686* Check to see if this object had previously had its {@code free} method687* called688*689* @throws SerialException690*/691private void isValid() throws SerialException {692if (buf == null) {693throw new SerialException("Error: You cannot call a method on a "694+ "SerialClob instance once free() has been called.");695}696}697698/**699* The identifier that assists in the serialization of this {@code SerialClob}700* object.701*/702static final long serialVersionUID = -1662519690087375313L;703}704705706