1. /*
  2. * @(#)SerialClob.java 1.11 04/08/10
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.sql.rowset.serial;
  8. import java.sql.*;
  9. import java.io.*;
  10. /**
  11. * A serialized mapping in the Java programming language of an SQL
  12. * <code>CLOB</code> value.
  13. * <P>
  14. * The <code>SerialClob</code> class provides a constructor for creating
  15. * an instance from a <code>Clob</code> object. Note that the <code>Clob</code>
  16. * object should have brought the SQL <code>CLOB</code> value's data over
  17. * to the client before a <code>SerialClob</code> object
  18. * is constructed from it. The data of an SQL <code>CLOB</code> value can
  19. * be materialized on the client as a stream of Unicode characters.
  20. * <P>
  21. * <code>SerialClob</code> methods make it possible to get a substring
  22. * from a <code>SerialClob</code> object or to locate the start of
  23. * a pattern of characters.
  24. *
  25. * @author Jonathan Bruce
  26. */
  27. public class SerialClob implements Clob, Serializable, Cloneable {
  28. /**
  29. * A serialized array of characters containing the data of the SQL
  30. * <code>CLOB</code> value that this <code>SerialClob</code> object
  31. * represents.
  32. *
  33. * @serial
  34. */
  35. private char buf[];
  36. /**
  37. * Internal Clob representation if SerialClob is intialized with a
  38. * Clob
  39. */
  40. private Clob clob;
  41. /**
  42. * The length in characters of this <code>SerialClob</code> object's
  43. * internal array of characters.
  44. *
  45. * @serial
  46. */
  47. private long len;
  48. /**
  49. * The original length in characters of tgus <code>SerialClob</code>
  50. * objects internal array of characters.
  51. *
  52. * @serial
  53. */
  54. private long origLen;
  55. /**
  56. * Constructs a <code>SerialClob</code> object that is a serialized version of
  57. * the given <code>char</code> array.
  58. * <p>
  59. * The new <code>SerialClob</code> object is initialized with the data from the
  60. * <code>char</code> array, thus allowing disconnected <code>RowSet</code>
  61. * objects to establish a serialized <code>Clob</code> object without touching
  62. * the data source.
  63. *
  64. * @param ch the char array representing the <code>Clob</code> object to be
  65. * serialized
  66. * @throws SerialException if an error occurs during serialization
  67. * @throws SQLException if a SQL error occurs
  68. */
  69. public SerialClob(char ch[]) throws SerialException, SQLException {
  70. // %%% JMB. Agreed. Add code here to throw a SQLException if no
  71. // support is available for locatorsUpdateCopy=false
  72. // Serializing locators is not supported.
  73. len = ch.length;
  74. buf = new char[(int)len];
  75. for (int i = 0; i < len ; i++){
  76. buf[i] = ch[i];
  77. }
  78. origLen = len;
  79. }
  80. /**
  81. * Constructs a <code>SerialClob</code> object that is a serialized
  82. * version of the given <code>Clob</code> object.
  83. * <P>
  84. * The new <code>SerialClob</code> object is initialized with the
  85. * data from the <code>Clob</code> object; therefore, the
  86. * <code>Clob</code> object should have previously brought the
  87. * SQL <code>CLOB</code> value's data over to the client from
  88. * the database. Otherwise, the new <code>SerialClob</code> object
  89. * object will contain no data.
  90. * <p>
  91. * Note: The <code>Clob</code> object supplied to this constructor cannot
  92. * return <code>null</code> for the <code>Clob.getCharacterStream()</code>
  93. * and <code>Clob.getAsciiStream</code> methods. This <code>SerialClob</code>
  94. * constructor cannot serialize a <code>Clob</code> object in this instance
  95. * and will throw an <code>SQLException</code> object.
  96. *
  97. * @param clob the <code>Clob</code> object from which this
  98. * <code>SerialClob</code> object is to be constructed; cannot be null
  99. * @throws SerialException if an error occurs during serialization
  100. * @throws SQLException if a SQL error occurs in capturing the CLOB;
  101. * if the <code>Clob</code> object is a null; or if at least one of the
  102. * <code>Clob.getCharacterStream()</code> and <code>Clob.getAsciiStream()</code>
  103. * methods on the <code>Clob</code> return a null
  104. * @see java.sql.Clob
  105. */
  106. public SerialClob(Clob clob) throws SerialException, SQLException {
  107. if (clob == null) {
  108. throw new SQLException("Cannot instantiate a SerialClob " +
  109. "object with a null Clob object");
  110. }
  111. len = clob.length();
  112. this.clob = clob;
  113. buf = new char[(int)len];
  114. int read = 0;
  115. int offset = 0;
  116. BufferedReader reader;
  117. if (clob.getCharacterStream() == null || clob.getAsciiStream() == null) {
  118. throw new SQLException("Invalid Clob object. Calls to getCharacterStream " +
  119. "or getAsciiStream return null which cannot be serialized.");
  120. }
  121. try {
  122. reader = new BufferedReader(clob.getCharacterStream());
  123. do {
  124. read = reader.read(buf, offset, (int)(len - offset));
  125. offset += read;
  126. } while (read > 0);
  127. } catch (java.io.IOException ex) {
  128. throw new SerialException("SerialClob: " + ex.getMessage());
  129. }
  130. origLen = len;
  131. }
  132. /**
  133. * Retrieves the number of characters in this <code>SerialClob</code>
  134. * object's array of characters.
  135. *
  136. * @return a <code>long</code> indicating the length in characters of this
  137. * <code>SerialClob</code> object's array of character
  138. * @throws SerialException if an error occurs
  139. */
  140. public long length() throws SerialException {
  141. return len;
  142. }
  143. /**
  144. * Returns this <code>SerialClob</code> object's data as a stream
  145. * of Unicode characters. Unlike the related method, <code>getAsciiStream</code>,
  146. * a stream is produced regardless of whether the <code>SerialClob</code> object
  147. * was created with a <code>Clob</code> object or a <code>char</code> array.
  148. *
  149. * @return a <code>java.io.Reader</code> object containing this
  150. * <code>SerialClob</code> object's data
  151. * @throws SerialException if an error occurs
  152. */
  153. public java.io.Reader getCharacterStream() throws SerialException {
  154. return (java.io.Reader) new CharArrayReader(buf);
  155. }
  156. /**
  157. * Retrieves the <code>CLOB</code> value designated by this <code>SerialClob</code>
  158. * object as an ascii stream. This method forwards the <code>getAsciiStream</code>
  159. * call to the underlying <code>Clob</code> object in the event that this
  160. * <code>SerialClob</code> object is instantiated with a <code>Clob</code>
  161. * object. If this <code>SerialClob</code> object is instantiated with
  162. * a <code>char</code> array, a <code>SerialException</code> object is thrown.
  163. *
  164. * @return a <code>java.io.InputStream</code> object containing
  165. * this <code>SerialClob</code> object's data
  166. * @throws SerialException if this <code>SerialClob<code> object was not instantiated
  167. * with a <code>Clob</code> object
  168. * @throws SQLException if there is an error accessing the
  169. * <code>CLOB</code> value represented by the <code>Clob</code> object that was
  170. * used to create this <code>SerialClob</code> object
  171. */
  172. public java.io.InputStream getAsciiStream() throws SerialException, SQLException {
  173. if (this.clob != null) {
  174. return this.clob.getAsciiStream();
  175. } else {
  176. throw new SerialException("Unsupported operation. SerialClob cannot " +
  177. "return a the CLOB value as an ascii stream, unless instantiated " +
  178. "with a fully implemented Clob object.");
  179. }
  180. }
  181. /**
  182. * Returns a copy of the substring contained in this
  183. * <code>SerialClob</code> object, starting at the given position
  184. * and continuing for the specified number or characters.
  185. *
  186. * @param pos the position of the first character in the substring
  187. * to be copied; the first character of the
  188. * <code>SerialClob</code> object is at position
  189. * <code>1</code> must not be less than <code>1</code>,
  190. * and the sum of the starting position and the length
  191. * of the substring must be less than the length of this
  192. * <code>SerialClob</code> object
  193. * @param length the number of characters in the substring to be
  194. * returned; must not be greater than the length of
  195. * this <code>SerialClob</code> object, and the
  196. * sum of the starting position and the length
  197. * of the substring must be less than the length of this
  198. * <code>SerialClob</code> object
  199. * @return a <code>String</code> object containing a substring of
  200. * this <code>SerialClob</code> object beginning at the
  201. * given position and containing the specified number of
  202. * consecutive characters
  203. * @throws SerialException if either of the arguments is out of bounds
  204. */
  205. public String getSubString(long pos, int length) throws SerialException {
  206. if (pos < 1 || pos > this.length()) {
  207. throw new SerialException("Invalid position in BLOB object set");
  208. }
  209. if ((pos-1) + length > this.length()) {
  210. throw new SerialException("Invalid position and substring length");
  211. }
  212. try {
  213. return new String(buf, (int)pos - 1, length);
  214. } catch (StringIndexOutOfBoundsException e) {
  215. throw new SerialException("StringIndexOutOfBoundsException: " +
  216. e.getMessage());
  217. }
  218. }
  219. /**
  220. * Returns the position in this <code>SerialClob</code> object
  221. * where the given <code>String</code> object begins, starting
  222. * the search at the specified position. This method returns
  223. * <code>-1</code> if the pattern is not found.
  224. *
  225. * @param searchStr the <code>String</code> object for which to
  226. * search
  227. * @param start the position in this <code>SerialClob</code> object
  228. * at which to start the search; the first position is
  229. * <code>1</code> must not be less than <code>1</code> nor
  230. * greater than the length of this <code>SerialClob</code> object
  231. * @return the position at which the given <code>String</code> object
  232. * begins, starting the search at the specified position;
  233. * <code>-1</code> if the given <code>String</code> object is
  234. * not found or the starting position is out of bounds; position
  235. * numbering for the return value starts at <code>1</code>
  236. * @throws SerialException if an error occurs locating the String signature
  237. * @throws SQLException if there is an error accessing the Blob value
  238. * from the database.
  239. */
  240. public long position(String searchStr, long start)
  241. throws SerialException, SQLException {
  242. if (start < 1 || start > len) {
  243. return -1;
  244. }
  245. char pattern[] = searchStr.toCharArray();
  246. int pos = (int)start-1;
  247. int i = 0;
  248. long patlen = pattern.length;
  249. while (pos < len) {
  250. if (pattern[i] == buf[pos]) {
  251. if (i + 1 == patlen) {
  252. return (pos + 1) - (patlen - 1);
  253. }
  254. i++; pos++; // increment pos, and i
  255. } else if (pattern[i] != buf[pos]) {
  256. pos++; // increment pos only
  257. }
  258. }
  259. return -1; // not found
  260. }
  261. /**
  262. * Returns the position in this <code>SerialClob</code> object
  263. * where the given <code>Clob</code> signature begins, starting
  264. * the search at the specified position. This method returns
  265. * <code>-1</code> if the pattern is not found.
  266. *
  267. * @param searchStr the <code>Clob</code> object for which to search
  268. * @param start the position in this <code>SerialClob</code> object
  269. * at which to begin the search; the first position is
  270. * <code>1</code> must not be less than <code>1</code> nor
  271. * greater than the length of this <code>SerialClob</code> object
  272. * @return the position at which the given <code>Clob</code>
  273. * object begins in this <code>SerialClob</code> object,
  274. * at or after the specified starting position
  275. * @throws SerialException if an error occurs locating the Clob signature
  276. * @throws SQLException if there is an error accessing the Blob value
  277. * from the database
  278. */
  279. public long position(Clob searchStr, long start)
  280. throws SerialException, SQLException {
  281. char cPattern[] = null;
  282. try {
  283. java.io.Reader r = searchStr.getCharacterStream();
  284. cPattern = new char[(int)searchStr.length()];
  285. r.read(cPattern);
  286. } catch (IOException e) {
  287. throw new SerialException("Error streaming Clob search data");
  288. }
  289. return position(new String(cPattern), start);
  290. }
  291. /**
  292. * Writes the given Java <code>String</code> to the <code>CLOB</code>
  293. * value that this <code>SerialClob</code> object represents, at the position
  294. * <code>pos</code>.
  295. *
  296. * @param pos the position at which to start writing to the <code>CLOB</code>
  297. * value that this <code>SerialClob</code> object represents; the first
  298. * position is <code>1</code> must not be less than <code>1</code> nor
  299. * greater than the length of this <code>SerialClob</code> object
  300. * @param str the string to be written to the <code>CLOB</code>
  301. * value that this <code>SerialClob</code> object represents
  302. * @return the number of characters written
  303. * @throws SerialException if there is an error accessing the
  304. * <code>CLOB</code> value; if an invalid position is set; if an
  305. * invalid offset value is set; if number of bytes to be written
  306. * is greater than the <code>SerialClob</code> length; or the combined
  307. * values of the length and offset is greater than the Clob buffer
  308. */
  309. public int setString(long pos, String str) throws SerialException {
  310. return (setString(pos, str, 0, str.length()));
  311. }
  312. /**
  313. * Writes <code>len</code> characters of <code>str</code>, starting
  314. * at character <code>offset</code>, to the <code>CLOB</code> value
  315. * that this <code>Clob</code> represents.
  316. *
  317. * @param pos the position at which to start writing to the <code>CLOB</code>
  318. * value that this <code>SerialClob</code> object represents; the first
  319. * position is <code>1</code> must not be less than <code>1</code> nor
  320. * greater than the length of this <code>SerialClob</code> object
  321. * @param str the string to be written to the <code>CLOB</code>
  322. * value that this <code>Clob</code> object represents
  323. * @param offset the offset into <code>str</code> to start reading
  324. * the characters to be written
  325. * @param length the number of characters to be written
  326. * @return the number of characters written
  327. * @throws SerialException if there is an error accessing the
  328. * <code>CLOB</code> value; if an invalid position is set; if an
  329. * invalid offset value is set; if number of bytes to be written
  330. * is greater than the <code>SerialClob</code> length; or the combined
  331. * values of the length and offset is greater than the Clob buffer
  332. */
  333. public int setString(long pos, String str, int offset, int length)
  334. throws SerialException {
  335. String temp = str.substring(offset);
  336. char cPattern[] = temp.toCharArray();
  337. if (offset < 0 || offset > str.length()) {
  338. throw new SerialException("Invalid offset in byte array set");
  339. }
  340. if (pos < 1 || pos > this.length()) {
  341. throw new SerialException("Invalid position in BLOB object set");
  342. }
  343. if ((long)(length) > origLen) {
  344. throw new SerialException("Buffer is not sufficient to hold the value");
  345. }
  346. if ((length + offset) > str.length()) {
  347. // need check to ensure length + offset !> bytes.length
  348. throw new SerialException("Invalid OffSet. Cannot have combined offset " +
  349. " and length that is greater that the Blob buffer");
  350. }
  351. int i = 0;
  352. pos--; //values in the array are at position one less
  353. while ( i < length || (offset + i +1) < (str.length() - offset ) ) {
  354. this.buf[(int)pos + i ] = cPattern[offset + i ];
  355. i++;
  356. }
  357. return i;
  358. }
  359. /**
  360. * Retrieves a stream to be used to write Ascii characters to the
  361. * <code>CLOB</code> value that this <code>SerialClob</code> object represents,
  362. * starting at position <code>pos</code>. This method forwards the
  363. * <code>setAsciiStream()</code> call to the underlying <code>Clob</code> object in
  364. * the event that this <code>SerialClob</code> object is instantiated with a
  365. * <code>Clob</code> object. If this <code>SerialClob</code> object is instantiated
  366. * with a <code>char</code> array, a <code>SerialException</code> object is thrown.
  367. *
  368. * @param pos the position at which to start writing to the
  369. * <code>CLOB</code> object
  370. * @return the stream to which ASCII encoded characters can be written
  371. * @throws SerialException if SerialClob is not instantiated with a
  372. * Clob object that supports <code>setAsciiStream</code>
  373. * @throws SQLException if there is an error accessing the
  374. * <code>CLOB</code> value
  375. * @see #getAsciiStream
  376. */
  377. public java.io.OutputStream setAsciiStream(long pos)
  378. throws SerialException, SQLException {
  379. if (this.clob.setAsciiStream(pos) != null) {
  380. return this.clob.setAsciiStream(pos);
  381. } else {
  382. throw new SerialException("Unsupported operation. SerialClob cannot " +
  383. "return a writable ascii stream\n unless instantiated with a Clob object " +
  384. "that has a setAsciiStream() implementation");
  385. }
  386. }
  387. /**
  388. * Retrieves a stream to be used to write a stream of Unicode characters
  389. * to the <code>CLOB</code> value that this <code>SerialClob</code> object
  390. * represents, at position <code>pos</code>. This method forwards the
  391. * <code>setCharacterStream()</code> call to the underlying <code>Clob</code>
  392. * object in the event that this <code>SerialClob</code> object is instantiated with a
  393. * <code>Clob</code> object. If this <code>SerialClob</code> object is instantiated with
  394. * a <code>char</code> array, a <code>SerialException</code> is thrown.
  395. *
  396. * @param pos the position at which to start writing to the
  397. * <code>CLOB</code> value
  398. *
  399. * @return a stream to which Unicode encoded characters can be written
  400. * @throws SerialException if the SerialClob is not instantiated with
  401. * a Clob object that supports <code>setCharacterStream</code>
  402. * @throws SQLException if there is an error accessing the
  403. * <code>CLOB</code> value
  404. * @see #getCharacterStream
  405. */
  406. public java.io.Writer setCharacterStream(long pos)
  407. throws SerialException, SQLException {
  408. if (this.clob.setCharacterStream(pos) != null) {
  409. return this.clob.setCharacterStream(pos);
  410. } else {
  411. throw new SerialException("Unsupported operation. SerialClob cannot " +
  412. "return a writable character stream\n unless instantiated with a Clob object " +
  413. "that has a setCharacterStream implementation");
  414. }
  415. }
  416. /**
  417. * Truncates the <code>CLOB</code> value that this <code>SerialClob</code>
  418. * object represents so that it has a length of <code>len</code>
  419. * characters.
  420. * <p>
  421. * Truncating a <code>SerialClob</code> object to length 0 has the effect of
  422. * clearing its contents.
  423. *
  424. * @param length the length, in bytes, to which the <code>CLOB</code>
  425. * value should be truncated
  426. * @throws SQLException if there is an error accessing the
  427. * <code>CLOB</code> value
  428. */
  429. public void truncate(long length) throws SerialException {
  430. if (length > len) {
  431. throw new SerialException
  432. ("Length more than what can be truncated");
  433. } else {
  434. len = length;
  435. // re-size the buffer
  436. if (len == 0) {
  437. buf = new char[] {};
  438. } else {
  439. buf = (this.getSubString(1, (int)len)).toCharArray();
  440. }
  441. }
  442. }
  443. /**
  444. * The identifier that assists in the serialization of this <code>SerialClob</code>
  445. * object.
  446. */
  447. static final long serialVersionUID = -1662519690087375313L;
  448. }