Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/sql/rowset/serial/SerialClob.java
38918 views
/*1* Copyright (c) 2003, 2013, 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* <h3> Thread safety </h3>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* @author Jonathan Bruce52*/53public class SerialClob implements Clob, Serializable, Cloneable {5455/**56* A serialized array of characters containing the data of the SQL57* <code>CLOB</code> value that this <code>SerialClob</code> object58* represents.59*60* @serial61*/62private char buf[];6364/**65* Internal Clob representation if SerialClob is initialized with a66* Clob. Null if SerialClob is initialized with a char[].67*/68private Clob clob;6970/**71* The length in characters of this <code>SerialClob</code> object's72* internal array of characters.73*74* @serial75*/76private long len;7778/**79* The original length in characters of this <code>SerialClob</code>80* object's internal array of characters.81*82* @serial83*/84private long origLen;8586/**87* Constructs a <code>SerialClob</code> object that is a serialized version of88* the given <code>char</code> array.89* <p>90* The new <code>SerialClob</code> object is initialized with the data from the91* <code>char</code> array, thus allowing disconnected <code>RowSet</code>92* objects to establish a serialized <code>Clob</code> object without touching93* the data source.94*95* @param ch the char array representing the <code>Clob</code> object to be96* serialized97* @throws SerialException if an error occurs during serialization98* @throws SQLException if a SQL error occurs99*/100public SerialClob(char ch[]) throws SerialException, SQLException {101102// %%% JMB. Agreed. Add code here to throw a SQLException if no103// support is available for locatorsUpdateCopy=false104// Serializing locators is not supported.105106len = ch.length;107buf = new char[(int)len];108for (int i = 0; i < len ; i++){109buf[i] = ch[i];110}111origLen = len;112clob = null;113}114115/**116* Constructs a <code>SerialClob</code> object that is a serialized117* version of the given <code>Clob</code> object.118* <P>119* The new <code>SerialClob</code> object is initialized with the120* data from the <code>Clob</code> object; therefore, the121* <code>Clob</code> object should have previously brought the122* SQL <code>CLOB</code> value's data over to the client from123* the database. Otherwise, the new <code>SerialClob</code> object124* object will contain no data.125* <p>126* Note: The <code>Clob</code> object supplied to this constructor must127* return non-null for both the <code>Clob.getCharacterStream()</code>128* and <code>Clob.getAsciiStream</code> methods. This <code>SerialClob</code>129* constructor cannot serialize a <code>Clob</code> object in this instance130* and will throw an <code>SQLException</code> object.131*132* @param clob the <code>Clob</code> object from which this133* <code>SerialClob</code> object is to be constructed; cannot be null134* @throws SerialException if an error occurs during serialization135* @throws SQLException if a SQL error occurs in capturing the CLOB;136* if the <code>Clob</code> object is a null; or if either of the137* <code>Clob.getCharacterStream()</code> and <code>Clob.getAsciiStream()</code>138* methods on the <code>Clob</code> returns a null139* @see java.sql.Clob140*/141public SerialClob(Clob clob) throws SerialException, SQLException {142143if (clob == null) {144throw new SQLException("Cannot instantiate a SerialClob " +145"object with a null Clob object");146}147len = clob.length();148this.clob = clob;149buf = new char[(int)len];150int read = 0;151int offset = 0;152153try (Reader charStream = clob.getCharacterStream()) {154if (charStream == null) {155throw new SQLException("Invalid Clob object. The call to getCharacterStream " +156"returned null which cannot be serialized.");157}158159// Note: get an ASCII stream in order to null-check it,160// even though we don't do anything with it.161try (InputStream asciiStream = clob.getAsciiStream()) {162if (asciiStream == null) {163throw new SQLException("Invalid Clob object. The call to getAsciiStream " +164"returned null which cannot be serialized.");165}166}167168try (Reader reader = new BufferedReader(charStream)) {169do {170read = reader.read(buf, offset, (int)(len - offset));171offset += read;172} while (read > 0);173}174} catch (java.io.IOException ex) {175throw new SerialException("SerialClob: " + ex.getMessage());176}177178origLen = len;179}180181/**182* Retrieves the number of characters in this <code>SerialClob</code>183* object's array of characters.184*185* @return a <code>long</code> indicating the length in characters of this186* <code>SerialClob</code> object's array of character187* @throws SerialException if an error occurs;188* if {@code free} had previously been called on this object189*/190public long length() throws SerialException {191isValid();192return len;193}194195/**196* Returns this <code>SerialClob</code> object's data as a stream197* of Unicode characters. Unlike the related method, <code>getAsciiStream</code>,198* a stream is produced regardless of whether the <code>SerialClob</code> object199* was created with a <code>Clob</code> object or a <code>char</code> array.200*201* @return a <code>java.io.Reader</code> object containing this202* <code>SerialClob</code> object's data203* @throws SerialException if an error occurs;204* if {@code free} had previously been called on this object205*/206public java.io.Reader getCharacterStream() throws SerialException {207isValid();208return (java.io.Reader) new CharArrayReader(buf);209}210211/**212* Retrieves the <code>CLOB</code> value designated by this <code>SerialClob</code>213* object as an ascii stream. This method forwards the <code>getAsciiStream</code>214* call to the underlying <code>Clob</code> object in the event that this215* <code>SerialClob</code> object is instantiated with a <code>Clob</code>216* object. If this <code>SerialClob</code> object is instantiated with217* a <code>char</code> array, a <code>SerialException</code> object is thrown.218*219* @return a <code>java.io.InputStream</code> object containing220* this <code>SerialClob</code> object's data221* @throws SerialException if this {@code SerialClob} object was not222* instantiated with a <code>Clob</code> object;223* if {@code free} had previously been called on this object224* @throws SQLException if there is an error accessing the225* <code>CLOB</code> value represented by the <code>Clob</code> object226* that was used to create this <code>SerialClob</code> object227*/228public java.io.InputStream getAsciiStream() throws SerialException, SQLException {229isValid();230if (this.clob != null) {231return this.clob.getAsciiStream();232} else {233throw new SerialException("Unsupported operation. SerialClob cannot " +234"return a the CLOB value as an ascii stream, unless instantiated " +235"with a fully implemented Clob object.");236}237}238239/**240* Returns a copy of the substring contained in this241* <code>SerialClob</code> object, starting at the given position242* and continuing for the specified number or characters.243*244* @param pos the position of the first character in the substring245* to be copied; the first character of the246* <code>SerialClob</code> object is at position247* <code>1</code>; must not be less than <code>1</code>,248* and the sum of the starting position and the length249* of the substring must be less than the length of this250* <code>SerialClob</code> object251* @param length the number of characters in the substring to be252* returned; must not be greater than the length of253* this <code>SerialClob</code> object, and the254* sum of the starting position and the length255* of the substring must be less than the length of this256* <code>SerialClob</code> object257* @return a <code>String</code> object containing a substring of258* this <code>SerialClob</code> object beginning at the259* given position and containing the specified number of260* consecutive characters261* @throws SerialException if either of the arguments is out of bounds;262* if {@code free} had previously been called on this object263*/264public String getSubString(long pos, int length) throws SerialException {265266isValid();267if (pos < 1 || pos > this.length()) {268throw new SerialException("Invalid position in SerialClob object set");269}270271if ((pos-1) + length > this.length()) {272throw new SerialException("Invalid position and substring length");273}274275try {276return new String(buf, (int)pos - 1, length);277278} catch (StringIndexOutOfBoundsException e) {279throw new SerialException("StringIndexOutOfBoundsException: " +280e.getMessage());281}282283}284285/**286* Returns the position in this <code>SerialClob</code> object287* where the given <code>String</code> object begins, starting288* the search at the specified position. This method returns289* <code>-1</code> if the pattern is not found.290*291* @param searchStr the <code>String</code> object for which to292* search293* @param start the position in this <code>SerialClob</code> object294* at which to start the search; the first position is295* <code>1</code>; must not be less than <code>1</code> nor296* greater than the length of this <code>SerialClob</code> object297* @return the position at which the given <code>String</code> object298* begins, starting the search at the specified position;299* <code>-1</code> if the given <code>String</code> object is300* not found or the starting position is out of bounds; position301* numbering for the return value starts at <code>1</code>302* @throws SerialException if the {@code free} method had been303* previously called on this object304* @throws SQLException if there is an error accessing the Clob value305* from the database.306*/307public long position(String searchStr, long start)308throws SerialException, SQLException {309isValid();310if (start < 1 || start > len) {311return -1;312}313314char pattern[] = searchStr.toCharArray();315316int pos = (int)start-1;317int i = 0;318long patlen = pattern.length;319320while (pos < len) {321if (pattern[i] == buf[pos]) {322if (i + 1 == patlen) {323return (pos + 1) - (patlen - 1);324}325i++; pos++; // increment pos, and i326327} else if (pattern[i] != buf[pos]) {328pos++; // increment pos only329}330}331return -1; // not found332}333334/**335* Returns the position in this <code>SerialClob</code> object336* where the given <code>Clob</code> signature begins, starting337* the search at the specified position. This method returns338* <code>-1</code> if the pattern is not found.339*340* @param searchStr the <code>Clob</code> object for which to search341* @param start the position in this <code>SerialClob</code> object342* at which to begin the search; the first position is343* <code>1</code>; must not be less than <code>1</code> nor344* greater than the length of this <code>SerialClob</code> object345* @return the position at which the given <code>Clob</code>346* object begins in this <code>SerialClob</code> object,347* at or after the specified starting position348* @throws SerialException if an error occurs locating the Clob signature;349* if the {@code free} method had been previously called on this object350* @throws SQLException if there is an error accessing the Clob value351* from the database352*/353public long position(Clob searchStr, long start)354throws SerialException, SQLException {355isValid();356return position(searchStr.getSubString(1,(int)searchStr.length()), start);357}358359/**360* Writes the given Java <code>String</code> to the <code>CLOB</code>361* value that this <code>SerialClob</code> object represents, at the position362* <code>pos</code>.363*364* @param pos the position at which to start writing to the <code>CLOB</code>365* value that this <code>SerialClob</code> object represents; the first366* position is <code>1</code>; must not be less than <code>1</code> nor367* greater than the length of this <code>SerialClob</code> object368* @param str the string to be written to the <code>CLOB</code>369* value that this <code>SerialClob</code> object represents370* @return the number of characters written371* @throws SerialException if there is an error accessing the372* <code>CLOB</code> value; if an invalid position is set; if an373* invalid offset value is set; if number of bytes to be written374* is greater than the <code>SerialClob</code> length; or the combined375* values of the length and offset is greater than the Clob buffer;376* if the {@code free} method had been previously called on this object377*/378public int setString(long pos, String str) throws SerialException {379return (setString(pos, str, 0, str.length()));380}381382/**383* Writes <code>len</code> characters of <code>str</code>, starting384* at character <code>offset</code>, to the <code>CLOB</code> value385* that this <code>Clob</code> represents.386*387* @param pos the position at which to start writing to the <code>CLOB</code>388* value that this <code>SerialClob</code> object represents; the first389* position is <code>1</code>; must not be less than <code>1</code> nor390* greater than the length of this <code>SerialClob</code> object391* @param str the string to be written to the <code>CLOB</code>392* value that this <code>Clob</code> object represents393* @param offset the offset into <code>str</code> to start reading394* the characters to be written395* @param length the number of characters to be written396* @return the number of characters written397* @throws SerialException if there is an error accessing the398* <code>CLOB</code> value; if an invalid position is set; if an399* invalid offset value is set; if number of bytes to be written400* is greater than the <code>SerialClob</code> length; or the combined401* values of the length and offset is greater than the Clob buffer;402* if the {@code free} method had been previously called on this object403*/404public int setString(long pos, String str, int offset, int length)405throws SerialException {406isValid();407String temp = str.substring(offset);408char cPattern[] = temp.toCharArray();409410if (offset < 0 || offset > str.length()) {411throw new SerialException("Invalid offset in byte array set");412}413414if (pos < 1 || pos > this.length()) {415throw new SerialException("Invalid position in Clob object set");416}417418if ((long)(length) > origLen) {419throw new SerialException("Buffer is not sufficient to hold the value");420}421422if ((length + offset) > str.length()) {423// need check to ensure length + offset !> bytes.length424throw new SerialException("Invalid OffSet. Cannot have combined offset " +425" and length that is greater that the Blob buffer");426}427428int i = 0;429pos--; //values in the array are at position one less430while ( i < length || (offset + i +1) < (str.length() - offset ) ) {431this.buf[(int)pos + i ] = cPattern[offset + i ];432i++;433}434return i;435}436437/**438* Retrieves a stream to be used to write Ascii characters to the439* <code>CLOB</code> value that this <code>SerialClob</code> object represents,440* starting at position <code>pos</code>. This method forwards the441* <code>setAsciiStream()</code> call to the underlying <code>Clob</code> object in442* the event that this <code>SerialClob</code> object is instantiated with a443* <code>Clob</code> object. If this <code>SerialClob</code> object is instantiated444* with a <code>char</code> array, a <code>SerialException</code> object is thrown.445*446* @param pos the position at which to start writing to the447* <code>CLOB</code> object448* @return the stream to which ASCII encoded characters can be written449* @throws SerialException if SerialClob is not instantiated with a450* Clob object;451* if the {@code free} method had been previously called on this object452* @throws SQLException if there is an error accessing the453* <code>CLOB</code> value454* @see #getAsciiStream455*/456public java.io.OutputStream setAsciiStream(long pos)457throws SerialException, SQLException {458isValid();459if (this.clob != null) {460return this.clob.setAsciiStream(pos);461} else {462throw new SerialException("Unsupported operation. SerialClob cannot " +463"return a writable ascii stream\n unless instantiated with a Clob object " +464"that has a setAsciiStream() implementation");465}466}467468/**469* Retrieves a stream to be used to write a stream of Unicode characters470* to the <code>CLOB</code> value that this <code>SerialClob</code> object471* represents, at position <code>pos</code>. This method forwards the472* <code>setCharacterStream()</code> call to the underlying <code>Clob</code>473* object in the event that this <code>SerialClob</code> object is instantiated with a474* <code>Clob</code> object. If this <code>SerialClob</code> object is instantiated with475* a <code>char</code> array, a <code>SerialException</code> is thrown.476*477* @param pos the position at which to start writing to the478* <code>CLOB</code> value479*480* @return a stream to which Unicode encoded characters can be written481* @throws SerialException if the SerialClob is not instantiated with482* a Clob object;483* if the {@code free} method had been previously called on this object484* @throws SQLException if there is an error accessing the485* <code>CLOB</code> value486* @see #getCharacterStream487*/488public java.io.Writer setCharacterStream(long pos)489throws SerialException, SQLException {490isValid();491if (this.clob != null) {492return this.clob.setCharacterStream(pos);493} else {494throw new SerialException("Unsupported operation. SerialClob cannot " +495"return a writable character stream\n unless instantiated with a Clob object " +496"that has a setCharacterStream implementation");497}498}499500/**501* Truncates the <code>CLOB</code> value that this <code>SerialClob</code>502* object represents so that it has a length of <code>len</code>503* characters.504* <p>505* Truncating a <code>SerialClob</code> object to length 0 has the effect of506* clearing its contents.507*508* @param length the length, in bytes, to which the <code>CLOB</code>509* value should be truncated510* @throws SerialException if there is an error accessing the511* <code>CLOB</code> value;512* if the {@code free} method had been previously called on this object513*/514public void truncate(long length) throws SerialException {515isValid();516if (length > len) {517throw new SerialException518("Length more than what can be truncated");519} else {520len = length;521// re-size the buffer522523if (len == 0) {524buf = new char[] {};525} else {526buf = (this.getSubString(1, (int)len)).toCharArray();527}528}529}530531532/**533* Returns a {@code Reader} object that contains a partial534* {@code SerialClob} value, starting535* with the character specified by pos, which is length characters in length.536*537* @param pos the offset to the first character of the partial value to538* be retrieved. The first character in the {@code SerialClob} is at position 1.539* @param length the length in characters of the partial value to be retrieved.540* @return {@code Reader} through which the partial {@code SerialClob}541* value can be read.542* @throws SQLException if pos is less than 1 or if pos is greater than the543* number of characters in the {@code SerialClob} or if pos + length544* is greater than the number of characters in the {@code SerialClob};545* @throws SerialException if the {@code free} method had been previously546* called on this object547* @since 1.6548*/549public Reader getCharacterStream(long pos, long length) throws SQLException {550isValid();551if (pos < 1 || pos > len) {552throw new SerialException("Invalid position in Clob object set");553}554555if ((pos-1) + length > len) {556throw new SerialException("Invalid position and substring length");557}558if (length <= 0) {559throw new SerialException("Invalid length specified");560}561return new CharArrayReader(buf, (int)pos, (int)length);562}563564/**565* This method frees the {@code SeriableClob} object and releases the566* resources that it holds.567* The object is invalid once the {@code free} method is called.568* <p>569* If {@code free} is called multiple times, the subsequent570* calls to {@code free} are treated as a no-op.571* </P>572* @throws SQLException if an error occurs releasing573* the Clob's resources574* @since 1.6575*/576public void free() throws SQLException {577if (buf != null) {578buf = null;579if (clob != null) {580clob.free();581}582clob = null;583}584}585586/**587* Compares this SerialClob to the specified object. The result is {@code588* true} if and only if the argument is not {@code null} and is a {@code589* SerialClob} object that represents the same sequence of characters as this590* object.591*592* @param obj The object to compare this {@code SerialClob} against593*594* @return {@code true} if the given object represents a {@code SerialClob}595* equivalent to this SerialClob, {@code false} otherwise596*597*/598public boolean equals(Object obj) {599if (this == obj) {600return true;601}602if (obj instanceof SerialClob) {603SerialClob sc = (SerialClob)obj;604if (this.len == sc.len) {605return Arrays.equals(buf, sc.buf);606}607}608return false;609}610611/**612* Returns a hash code for this {@code SerialClob}.613* @return a hash code value for this object.614*/615public int hashCode() {616return ((31 + Arrays.hashCode(buf)) * 31 + (int)len) * 31 + (int)origLen;617}618619/**620* Returns a clone of this {@code SerialClob}. The copy will contain a621* reference to a clone of the internal character array, not a reference622* to the original internal character array of this {@code SerialClob} object.623* The underlying {@code Clob} object will be set to null.624*625* @return a clone of this SerialClob626*/627public Object clone() {628try {629SerialClob sc = (SerialClob) super.clone();630sc.buf = (buf != null) ? Arrays.copyOf(buf, (int)len) : null;631sc.clob = null;632return sc;633} catch (CloneNotSupportedException ex) {634// this shouldn't happen, since we are Cloneable635throw new InternalError();636}637}638639/**640* readObject is called to restore the state of the SerialClob from641* a stream.642*/643private void readObject(ObjectInputStream s)644throws IOException, ClassNotFoundException {645646ObjectInputStream.GetField fields = s.readFields();647char[] tmp = (char[])fields.get("buf", null);648if (tmp == null)649throw new InvalidObjectException("buf is null and should not be!");650buf = tmp.clone();651len = fields.get("len", 0L);652if (buf.length != len)653throw new InvalidObjectException("buf is not the expected size");654origLen = fields.get("origLen", 0L);655clob = (Clob) fields.get("clob", null);656}657658/**659* writeObject is called to save the state of the SerialClob660* to a stream.661*/662private void writeObject(ObjectOutputStream s)663throws IOException, ClassNotFoundException {664665ObjectOutputStream.PutField fields = s.putFields();666fields.put("buf", buf);667fields.put("len", len);668fields.put("origLen", origLen);669// Note: this check to see if it is an instance of Serializable670// is for backwards compatibiity671fields.put("clob", clob instanceof Serializable ? clob : null);672s.writeFields();673}674675/**676* Check to see if this object had previously had its {@code free} method677* called678*679* @throws SerialException680*/681private void isValid() throws SerialException {682if (buf == null) {683throw new SerialException("Error: You cannot call a method on a "684+ "SerialClob instance once free() has been called.");685}686}687688/**689* The identifier that assists in the serialization of this {@code SerialClob}690* object.691*/692static final long serialVersionUID = -1662519690087375313L;693}694695696