001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb.jdbc;
032:
033: import java.io.StringReader;
034: import java.sql.Clob;
035: import java.sql.SQLException;
036:
037: import org.hsqldb.Trace;
038: import org.hsqldb.lib.AsciiStringInputStream;
039:
040: // boucherb@users 2004-03/04-xx - doc 1.7.2 - javadocs updated; methods put in
041: // correct (historical, interface
042: // declared) order
043: // boucherb@users 2004-03/04-xx - patch 1.7.2 - null check for constructor (a
044: // null CLOB value is Java null,
045: // not a Clob object with null
046: // data);moderate thread safety;
047: // simplification; optimization
048: // of operations between jdbcClob
049: // instances
050:
051: /**
052: * The mapping in the Java<sup><font size=-2>TM</font></sup> programming
053: * language for the SQL CLOB type. <p>
054: *
055: * Provides methods for getting the length of an SQL CLOB (Character Large
056: * Object) value, for materializing a CLOB value on the client, and for
057: * searching for a substring or CLOB object within a CLOB value. <p>
058: *
059: * <!-- start Release-specific documentation -->
060: * <div class="ReleaseSpecificDocumentation">
061: * <h3>HSQLDB-Specific Information:</h3> <p>
062: *
063: * Including 1.8.x, the HSQLDB driver does not implement Clob using an SQL
064: * locator(CLOB). That is, an HSQLDB Clob object does not contain a logical
065: * pointer to SQL CLOB data; rather it directly contains an immutable
066: * representation of the data (a String object). As a result, an HSQLDB
067: * Clob object itself is valid beyond the duration of the transaction in which
068: * is was created, although it does not necessarily represent a corresponding
069: * value on the database. <p>
070: *
071: * Currently, the interface methods for updating a CLOB value are
072: * unsupported. However, the truncate method is supported for local use.
073: * </div>
074: * <!-- end release-specific documentation -->
075: *
076: * @author boucherb@users
077: * @version 1.7.2
078: * @since JDK 1.2, HSQLDB 1.7.2
079: */
080: public final class jdbcClob implements Clob {
081:
082: volatile String data;
083:
084: /**
085: * Constructs a new jdbcClob object wrapping the given character
086: * sequence. <p>
087: *
088: * This constructor is used internally to retrieve result set values as
089: * Clob objects, yet it must be public to allow access from other packages.
090: * As such (in the interest of efficiency) this object maintains a reference
091: * to the given String object rather than making a copy and so it is
092: * gently suggested (in the interest of effective memory management) that
093: * extenal clients using this constructor either take pause to consider
094: * the implications or at least take care to provide a String object whose
095: * internal character buffer is not much larger than required to represent
096: * the value.
097: *
098: * @param data the character sequence representing the Clob value
099: * @throws SQLException if the argument is null
100: */
101: public jdbcClob(final String data) throws SQLException {
102:
103: if (data == null) {
104: throw Util
105: .sqlException(Trace.INVALID_JDBC_ARGUMENT, "null");
106: }
107:
108: this .data = data;
109: }
110:
111: /**
112: * Retrieves the number of characters in the <code>CLOB</code> value
113: * designated by this <code>Clob</code> object.
114: *
115: * @return length of the <code>CLOB</code> in characters
116: * @exception SQLException if there is an error accessing the
117: * length of the <code>CLOB</code> value
118: *
119: * @since JDK 1.2, HSQLDB 1.7.2
120: */
121: public long length() throws SQLException {
122:
123: final String ldata = data;
124:
125: return ldata.length();
126: }
127:
128: /**
129: * Retrieves a copy of the specified substring in the <code>CLOB</code>
130: * value designated by this <code>Clob</code> object. The substring begins
131: * at position <code>pos</code> and has up to <code>length</code>
132: * consecutive characters. <p>
133: *
134: * <!-- start release-specific documentation -->
135: * <div class="ReleaseSpecificDocumentation">
136: * <h3>HSQLDB-Specific Information:</h3> <p>
137: *
138: * The official specification above is ambiguous in that it does not
139: * precisely indicate the policy to be observed when
140: * pos > this.length() - length. One policy would be to retrieve the
141: * characters from pos to this.length(). Another would be to throw
142: * an exception. HSQLDB observes the later policy.
143: * </div>
144: * <!-- end release-specific documentation -->
145: *
146: * @param pos the first character of the substring to be extracted.
147: * The first character is at position 1.
148: * @param length the number of consecutive characters to be copied
149: * @return a <code>String</code> that is the specified substring in
150: * the <code>CLOB</code> value designated by this
151: * <code>Clob</code> object
152: * @exception SQLException if there is an error accessing the
153: * <code>CLOB</code> value
154: *
155: * @since JDK 1.2, HSQLDB 1.7.2
156: */
157: public String getSubString(long pos, final int length)
158: throws SQLException {
159:
160: final String ldata = data;
161: final int dlen = ldata.length();
162:
163: pos--;
164:
165: if (pos < 0 || pos > dlen) {
166: Util.sqlException(Trace.INVALID_JDBC_ARGUMENT, "pos: "
167: + (pos + 1L));
168: }
169:
170: if (length < 0 || length > dlen - pos) {
171: throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT,
172: "length: " + length);
173: }
174:
175: if (pos == 0 && length == dlen) {
176: return ldata;
177: }
178:
179: return ldata.substring((int) pos, (int) pos + length);
180: }
181:
182: /**
183: * Retrieves the <code>CLOB</code> value designated by this
184: * <code>Clob</code> object as a <code>java.io.Reader</code> object
185: * (or as a stream of characters).
186: *
187: * @return a <code>java.io.Reader</code> object containing the
188: * <code>CLOB</code> data
189: * @exception SQLException if there is an error accessing the
190: * <code>CLOB</code> value
191: * @see #setCharacterStream
192: *
193: * @since JDK 1.2, HSQLDB 1.7.2
194: */
195: public java.io.Reader getCharacterStream() throws SQLException {
196:
197: final String ldata = data;
198:
199: return new StringReader(ldata);
200: }
201:
202: /**
203: * Retrieves the <code>CLOB</code> value designated by this
204: * <code>Clob</code> object as an ascii stream.
205: *
206: * @return a <code>java.io.InputStream</code> object containing the
207: * <code>CLOB</code> data
208: * @exception SQLException if there is an error accessing the
209: * <code>CLOB</code> value
210: * @see #setAsciiStream
211: *
212: * @since JDK 1.2, HSQLDB 1.7.2
213: */
214: public java.io.InputStream getAsciiStream() throws SQLException {
215:
216: final String ldata = data;
217:
218: return new AsciiStringInputStream(ldata);
219: }
220:
221: /**
222: * Retrieves the character position at which the specified substring
223: * <code>searchstr</code> appears in the SQL <code>CLOB</code> value
224: * represented by this <code>Clob</code> object. The search
225: * begins at position <code>start</code>.
226: *
227: * @param searchstr the substring for which to search
228: * @param start the position at which to begin searching; the
229: * first position is 1
230: * @return the position at which the substring appears or -1 if it is not
231: * present; the first position is 1
232: * @exception SQLException if there is an error accessing the
233: * <code>CLOB</code> value
234: *
235: * @since JDK 1.2, HSQLDB 1.7.2
236: */
237: public long position(final String searchstr, long start)
238: throws SQLException {
239:
240: if (searchstr == null || start > Integer.MAX_VALUE) {
241: return -1;
242: }
243:
244: final String ldata = data;
245: final int pos = ldata.indexOf(searchstr, (int) --start);
246:
247: return (pos < 0) ? -1 : pos + 1;
248: }
249:
250: /**
251: * Retrieves the character position at which the specified
252: * <code>Clob</code> object <code>searchstr</code> appears in this
253: * <code>Clob</code> object. The search begins at position
254: * <code>start</code>.
255: *
256: * @param searchstr the <code>Clob</code> object for which to search
257: * @param start the position at which to begin searching; the first
258: * position is 1
259: * @return the position at which the <code>Clob</code> object appears
260: * or -1 if it is not present; the first position is 1
261: * @exception SQLException if there is an error accessing the
262: * <code>CLOB</code> value
263: *
264: * @since JDK 1.2, HSQLDB 1.7.2
265: */
266: public long position(final Clob searchstr, long start)
267: throws SQLException {
268:
269: if (searchstr == null) {
270: return -1;
271: }
272:
273: final String ldata = data;
274: final long dlen = ldata.length();
275: final long sslen = searchstr.length();
276:
277: start--; //***** FOIRGOT THIS *******
278:
279: // This is potentially much less expensive than materializing a large
280: // substring from some other vendor's CLOB. Indeed, we should probably
281: // do the comparison piecewise, using an in-memory buffer (or temp-files
282: // when available), if it is detected that the input CLOB is very long.
283: if (start > dlen - sslen) {
284: return -1;
285: }
286:
287: // by now, we know sslen and start are both < Integer.MAX_VALUE
288: String s;
289:
290: if (searchstr instanceof jdbcClob) {
291: s = ((jdbcClob) searchstr).data;
292: } else {
293: s = searchstr.getSubString(1L, (int) sslen);
294: }
295:
296: final int pos = ldata.indexOf(s, (int) start);
297:
298: return (pos < 0) ? -1 : pos + 1;
299: }
300:
301: //---------------------------- jdbc 3.0 -----------------------------------
302:
303: /**
304: * Writes the given Java <code>String</code> to the <code>CLOB</code>
305: * value that this <code>Clob</code> object designates at the position
306: * <code>pos</code>. <p>
307: *
308: * <!-- start release-specific documentation -->
309: * <div class="ReleaseSpecificDocumentation">
310: * <h3>HSQLDB-Specific Information:</h3> <p>
311: *
312: * HSLQDB 1.7.2 does not support this feature. <p>
313: *
314: * Calling this method always throws an <code>SQLException</code>.
315: * </div>
316: * <!-- end release-specific documentation -->
317: *
318: * @param pos the position at which to start writing to the
319: * <code>CLOB</code> value that this <code>Clob</code> object
320: * represents
321: * @param str the string to be written to the <code>CLOB</code>
322: * value that this <code>Clob</code> designates
323: * @return the number of characters written
324: * @exception SQLException if there is an error accessing the
325: * <code>CLOB</code> value
326: *
327: * @since JDK 1.4, HSQLDB 1.7.2
328: */
329: public int setString(long pos, String str) throws SQLException {
330: throw Util.notSupported();
331: }
332:
333: /**
334: * Writes <code>len</code> characters of <code>str</code>, starting
335: * at character <code>offset</code>, to the <code>CLOB</code> value
336: * that this <code>Clob</code> represents. <p>
337: *
338: * <!-- start release-specific documentation -->
339: * <div class="ReleaseSpecificDocumentation">
340: * <h3>HSQLDB-Specific Information:</h3> <p>
341: *
342: * HSLQDB 1.7.2 does not support this feature. <p>
343: *
344: * Calling this method always throws an <code>SQLException</code>.
345: * </div>
346: * <!-- end release-specific documentation -->
347: *
348: * @param pos the position at which to start writing to this
349: * <code>CLOB</code> object
350: * @param str the string to be written to the <code>CLOB</code>
351: * value that this <code>Clob</code> object represents
352: * @param offset the offset into <code>str</code> to start reading
353: * the characters to be written
354: * @param len the number of characters to be written
355: * @return the number of characters written
356: * @exception SQLException if there is an error accessing the
357: * <code>CLOB</code> value
358: *
359: * @since JDK 1.4, HSQLDB 1.7.2
360: */
361: public int setString(long pos, String str, int offset, int len)
362: throws SQLException {
363: throw Util.notSupported();
364: }
365:
366: /**
367: * Retrieves a stream to be used to write Ascii characters to the
368: * <code>CLOB</code> value that this <code>Clob</code> object represents,
369: * starting at position <code>pos</code>. <p>
370: *
371: * <!-- start release-specific documentation -->
372: * <div class="ReleaseSpecificDocumentation">
373: * <h3>HSQLDB-Specific Information:</h3> <p>
374: *
375: * HSLQDB 1.7.2 does not support this feature. <p>
376: *
377: * Calling this method always throws an <code>SQLException</code>.
378: * </div>
379: * <!-- end release-specific documentation -->
380: *
381: * @param pos the position at which to start writing to this
382: * <code>CLOB</code> object
383: * @return the stream to which ASCII encoded characters can be written
384: * @exception SQLException if there is an error accessing the
385: * <code>CLOB</code> value
386: * @see #getAsciiStream
387: *
388: * @since JDK 1.4, HSQLDB 1.7.2
389: */
390: public java.io.OutputStream setAsciiStream(long pos)
391: throws SQLException {
392: throw Util.notSupported();
393: }
394:
395: /**
396: * Retrieves a stream to be used to write a stream of Unicode characters
397: * to the <code>CLOB</code> value that this <code>Clob</code> object
398: * represents, at position <code>pos</code>. <p>
399: *
400: * <!-- start release-specific documentation -->
401: * <div class="ReleaseSpecificDocumentation">
402: * <h3>HSQLDB-Specific Information:</h3> <p>
403: *
404: * HSLQDB 1.7.2 does not support this feature. <p>
405: *
406: * Calling this method always throws an <code>SQLException</code>.
407: * </div>
408: * <!-- end release-specific documentation -->
409: *
410: * @param pos the position at which to start writing to the
411: * <code>CLOB</code> value
412: *
413: * @return a stream to which Unicode encoded characters can be written
414: * @exception SQLException if there is an error accessing the
415: * <code>CLOB</code> value
416: * @see #getCharacterStream
417: *
418: * @since JDK 1.4, HSQLDB 1.7.2
419: */
420: public java.io.Writer setCharacterStream(long pos)
421: throws SQLException {
422: throw Util.notSupported();
423: }
424:
425: /**
426: * Truncates the <code>CLOB</code> value that this <code>Clob</code>
427: * designates to have a length of <code>len</code>
428: * characters. <p>
429: *
430: * <!-- start release-specific documentation -->
431: * <div class="ReleaseSpecificDocumentation">
432: * <h3>HSQLDB-Specific Information:</h3> <p>
433: *
434: * This operation affects only the client-side value; it has no effect upon
435: * the value as it is stored in the database.
436: * </div>
437: * <!-- end release-specific documentation -->
438: *
439: * @param len the length, in bytes, to which the <code>CLOB</code> value
440: * should be truncated
441: * @exception SQLException if there is an error accessing the
442: * <code>CLOB</code> value
443: *
444: * @since JDK 1.4, HSQLDB 1.7.2
445: */
446: public void truncate(final long len) throws SQLException {
447:
448: final String ldata = data;
449: final long dlen = ldata.length();
450: final long chars = len >> 1;
451:
452: if (chars == dlen) {
453:
454: // nothing has changed, so there's nothing to be done
455: } else if (len < 0 || chars > dlen) {
456: throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT, Long
457: .toString(len));
458: } else {
459:
460: // use new String() to ensure we get rid of slack
461: data = new String(ldata.substring(0, (int) chars));
462: }
463: }
464: }
|