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.ByteArrayInputStream;
034: import java.io.InputStream;
035: import java.io.OutputStream;
036: import java.sql.Blob;
037: import java.sql.SQLException;
038:
039: import org.hsqldb.Trace;
040:
041: // boucherb@users 2004-04-xx - patch 1.7.2 - position and truncate methods
042: // implemented; minor changes for moderate thread
043: // safety and optimal performance
044: // boucherb@users 2004-04-xx - doc 1.7.2 - javadocs updated; methods put in
045: // correct (historical, interface declared) order
046:
047: /**
048: * The representation (mapping) in the Java<sup><font size=-2>TM</font></sup>
049: * programming language of an SQL BLOB value. <p>
050: *
051: * Provides methods for getting the length of an SQL BLOB (Binary Large Object)
052: * value, for materializing a BLOB value on the client, and for determining the
053: * position of an octet sequence (byte pattern) within a BLOB value. <p>
054: *
055: * <!-- start Release-specific documentation -->
056: * <div class="ReleaseSpecificDocumentation">
057: * <h3>HSQLDB-Specific Information:</h3> <p>
058: *
059: * Including 1.8.x, the HSQLDB driver does not implement Blob using an SQL
060: * locator(BLOB). That is, an HSQLDB Blob object does not contain a logical
061: * pointer to SQL BLOB data; rather it directly contains a representation of
062: * the data (a byte array). As a result, an HSQLDB Blob object is itself
063: * valid beyond the duration of the transaction in which is was created,
064: * although it does not necessarily represent a corresponding value
065: * on the database. <p>
066: *
067: * Currently, the interface methods for updating a BLOB value are
068: * unsupported. However, the truncate method is supported for local use.
069: * </div>
070: * <!-- start Release-specific documentation -->
071: *
072: * @author james house jhouse@part.net
073: * @author boucherb@users
074: * @version 1.7.2
075: * @since JDK 1.2, HSQLDB 1.7.2
076: */
077: public class jdbcBlob implements Blob {
078:
079: volatile byte[] data;
080:
081: /**
082: * Constructs a new jdbcBlob instance wrapping the given octet sequence. <p>
083: *
084: * This constructor is used internally to retrieve result set values as
085: * Blob objects, yet it must be public to allow access from other packages.
086: * As such (in the interest of efficiency) this object maintains a reference
087: * to the given octet sequence rather than making a copy; special care
088: * should be taken by extenal clients never to use this constructor with a
089: * byte array object that may later be modified extenally.
090: *
091: * @param data the octet sequence representing the Blob value
092: * @throws SQLException if the argument is null
093: */
094: public jdbcBlob(final byte[] data) throws SQLException {
095:
096: if (data == null) {
097: throw Util
098: .sqlException(Trace.INVALID_JDBC_ARGUMENT, "null");
099: }
100:
101: this .data = data; // (byte[]) data.clone();
102: }
103:
104: /**
105: * Returns the number of bytes in the <code>BLOB</code> value
106: * designated by this <code>Blob</code> object.
107: *
108: * @return length of the <code>BLOB</code> in bytes
109: * @exception SQLException if there is an error accessing the
110: * length of the <code>BLOB</code>
111: *
112: * @since JDK 1.2, HSQLDB 1.7.2
113: */
114: public long length() throws SQLException {
115:
116: final byte[] ldata = data;
117:
118: return ldata.length;
119: }
120:
121: /**
122: * Retrieves all or part of the <code>BLOB</code> value that this
123: * <code>Blob</code> object represents, as an array of bytes. This
124: * <code>byte</code> array contains up to <code>length</code>
125: * consecutive bytes starting at position <code>pos</code>. <p>
126: *
127: * <!-- start release-specific documentation -->
128: * <div class="ReleaseSpecificDocumentation">
129: * <h3>HSQLDB-Specific Information:</h3> <p>
130: *
131: * The official specification above is ambiguous in that it does not
132: * precisely indicate the policy to be observed when
133: * pos > this.length() - length. One policy would be to retrieve the
134: * octets from pos to this.length(). Another would be to throw an
135: * exception. HSQLDB observes the later policy.
136: * </div>
137: *
138: * @param pos the ordinal position of the first byte in the
139: * <code>BLOB</code> value to be extracted; the first byte is at
140: * position 1
141: * @param length the number of consecutive bytes to be copied
142: * @return a byte array containing up to <code>length</code>
143: * consecutive bytes from the <code>BLOB</code> value designated
144: * by this <code>Blob</code> object, starting with the
145: * byte at position <code>pos</code>
146: * @exception SQLException if there is an error accessing the
147: * <code>BLOB</code> value
148: * @see #setBytes
149: *
150: * @since JDK 1.2, HSQLDB 1.7.2
151: */
152: public byte[] getBytes(long pos, final int length)
153: throws SQLException {
154:
155: final byte[] ldata = data;
156: final int dlen = ldata.length;
157:
158: pos--;
159:
160: if (pos < 0 || pos > dlen) {
161: throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT,
162: "pos: " + (pos + 1));
163: }
164:
165: if (length < 0 || length > dlen - pos) {
166: throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT,
167: "length: " + length);
168: }
169:
170: final byte[] out = new byte[length];
171:
172: System.arraycopy(ldata, (int) pos, out, 0, length);
173:
174: return out;
175: }
176:
177: /**
178: * Retrieves the <code>BLOB</code> value designated by this
179: * <code>Blob</code> instance as a stream.
180: *
181: * @return a stream containing the <code>BLOB</code> data
182: * @exception SQLException if there is an error accessing the
183: * <code>BLOB</code> value
184: * @see #setBinaryStream
185: *
186: * @since JDK 1.2, HSQLDB 1.7.2
187: */
188: public InputStream getBinaryStream() throws SQLException {
189:
190: final byte[] ldata = data;
191:
192: return new ByteArrayInputStream(ldata);
193: }
194:
195: /**
196: * Retrieves the byte position at which the specified byte array
197: * <code>pattern</code> begins within the <code>BLOB</code>
198: * value that this <code>Blob</code> object represents. The
199: * search for <code>pattern</code> begins at position
200: * <code>start</code>. <p>
201: *
202: * @param pattern the byte array for which to search
203: * @param start the position at which to begin searching; the
204: * first position is 1
205: * @return the position at which the pattern appears, else -1
206: * @exception SQLException if there is an error accessing the
207: * <code>BLOB</code>
208: *
209: * @since JDK 1.2, HSQLDB 1.7.2
210: */
211: public long position(final byte[] pattern, long start)
212: throws SQLException {
213:
214: final byte[] ldata = data;
215: final int dlen = ldata.length;
216:
217: if (start > dlen || pattern == null) {
218: return -1;
219: } else if (start < 1) {
220: start = 0;
221: } else {
222: start--;
223: }
224:
225: final int plen = pattern.length;
226:
227: if (plen == 0 || start > dlen - plen) {
228: return -1;
229: }
230:
231: final int stop = dlen - plen;
232: final byte b0 = pattern[0];
233:
234: outer_loop: for (int i = (int) start; i <= stop; i++) {
235: if (ldata[i] != b0) {
236: continue;
237: }
238:
239: int len = plen;
240: int doffset = i;
241: int poffset = 0;
242: boolean match = true;
243:
244: while (len-- > 0) {
245: if (ldata[doffset++] != pattern[poffset++]) {
246: continue outer_loop;
247: }
248: }
249:
250: return i + 1;
251: }
252:
253: return -1;
254: }
255:
256: /**
257: * Retrieves the byte position in the <code>BLOB</code> value
258: * designated by this <code>Blob</code> object at which
259: * <code>pattern</code> begins. The search begins at position
260: * <code>start</code>.
261: *
262: * @param pattern the <code>Blob</code> object designating
263: * the <code>BLOB</code> value for which to search
264: * @param start the position in the <code>BLOB</code> value
265: * at which to begin searching; the first position is 1
266: * @return the position at which the pattern begins, else -1
267: * @exception SQLException if there is an error accessing the
268: * <code>BLOB</code> value
269: *
270: * @since JDK 1.2, HSQLDB 1.7.2
271: */
272: public long position(final Blob pattern, long start)
273: throws SQLException {
274:
275: final byte[] ldata = data;
276: final int dlen = ldata.length;
277:
278: if (start > dlen || pattern == null) {
279: return -1;
280: } else if (start < 1) {
281: start = 0;
282: } else {
283: start--;
284: }
285:
286: final long plen = pattern.length();
287:
288: if (plen == 0 || start > ((long) dlen) - plen) {
289: return -1;
290: }
291:
292: // by now, we know plen <= Integer.MAX_VALUE
293: final int iplen = (int) plen;
294: byte[] bap;
295:
296: if (pattern instanceof jdbcBlob) {
297: bap = ((jdbcBlob) pattern).data;
298: } else {
299: bap = pattern.getBytes(1, iplen);
300: }
301:
302: final int stop = dlen - iplen;
303: final byte b0 = bap[0];
304:
305: outer_loop: for (int i = (int) start; i <= stop; i++) {
306: if (ldata[i] != b0) {
307: continue;
308: }
309:
310: int len = iplen;
311: int doffset = i;
312: int poffset = 0;
313:
314: while (len-- > 0) {
315: if (ldata[doffset++] != bap[poffset++]) {
316: continue outer_loop;
317: }
318: }
319:
320: return i + 1;
321: }
322:
323: return -1;
324: }
325:
326: // -------------------------- JDBC 3.0 -----------------------------------
327:
328: /**
329: * Writes the given array of bytes to the <code>BLOB</code> value that
330: * this <code>Blob</code> object represents, starting at position
331: * <code>pos</code>, and returns the number of bytes written. <p>
332: *
333: * <!-- start release-specific documentation -->
334: * <div class="ReleaseSpecificDocumentation">
335: * <h3>HSQLDB-Specific Information:</h3> <p>
336: *
337: * HSLQDB 1.7.2 does not support this feature. <p>
338: *
339: * Calling this method always throws an <code>SQLException</code>.
340: * </div>
341: * <!-- end release-specific documentation -->
342: *
343: * @param pos the position in the <code>BLOB</code> object at which
344: * to start writing
345: * @param bytes the array of bytes to be written to the <code>BLOB</code>
346: * value that this <code>Blob</code> object represents
347: * @return the number of bytes written
348: * @exception SQLException if there is an error accessing the
349: * <code>BLOB</code> value
350: * @see #getBytes
351: *
352: * @since JDK 1.4, HSQLDB 1.7.2
353: */
354: public int setBytes(long pos, byte[] bytes) throws SQLException {
355: throw Util.notSupported();
356: }
357:
358: /**
359: * Writes all or part of the given <code>byte</code> array to the
360: * <code>BLOB</code> value that this <code>Blob</code> object represents
361: * and returns the number of bytes written.
362: * Writing starts at position <code>pos</code> in the <code>BLOB</code>
363: * value; <code>len</code> bytes from the given byte array are written. <p>
364: *
365: * <!-- start release-specific documentation -->
366: * <div class="ReleaseSpecificDocumentation">
367: * <h3>HSQLDB-Specific Information:</h3> <p>
368: *
369: * HSLQDB 1.7.2 does not support this feature. <p>
370: *
371: * Calling this method always throws an <code>SQLException</code>.
372: * </div>
373: * <!-- end release-specific documentation -->
374: *
375: * @param pos the position in the <code>BLOB</code> object at which
376: * to start writing
377: * @param bytes the array of bytes to be written to this <code>BLOB</code>
378: * object
379: * @param offset the offset into the array <code>bytes</code> at which
380: * to start reading the bytes to be set
381: * @param len the number of bytes to be written to the <code>BLOB</code>
382: * value from the array of bytes <code>bytes</code>
383: * @return the number of bytes written
384: * @exception SQLException if there is an error accessing the
385: * <code>BLOB</code> value
386: * @see #getBytes
387: *
388: * @since JDK 1.4, HSQLDB 1.7.2
389: */
390: public int setBytes(long pos, byte[] bytes, int offset, int len)
391: throws SQLException {
392: throw Util.notSupported();
393: }
394:
395: /**
396: * Retrieves a stream that can be used to write to the <code>BLOB</code>
397: * value that this <code>Blob</code> object represents. The stream begins
398: * 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: * HSQLDB 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 in the <code>BLOB</code> value at which
411: * to start writing
412: * @return a <code>java.io.OutputStream</code> object to which data can
413: * be written
414: * @exception SQLException if there is an error accessing the
415: * <code>BLOB</code> value
416: * @see #getBinaryStream
417: *
418: * @since JDK 1.4, HSQLDB 1.7.2
419: */
420: public OutputStream setBinaryStream(long pos) throws SQLException {
421: throw Util.notSupported();
422: }
423:
424: /**
425: * Truncates the <code>BLOB</code> value that this <code>Blob</code>
426: * object represents to be <code>len</code> bytes in length.
427: *
428: * <!-- start release-specific documentation -->
429: * <div class="ReleaseSpecificDocumentation">
430: * <h3>HSQLDB-Specific Information:</h3> <p>
431: *
432: * This operation affects only the client-side value; it has no effect upon
433: * the value as it is stored in the database.
434: * </div>
435: * <!-- end release-specific documentation -->
436: *
437: * @param len the length, in bytes, to which the <code>BLOB</code> value
438: * that this <code>Blob</code> object represents should be truncated
439: * @exception SQLException if there is an error accessing the
440: * <code>BLOB</code> value
441: *
442: * @since JDK 1.4, HSQLDB 1.7.2
443: */
444: public void truncate(final long len) throws SQLException {
445:
446: final byte[] ldata = data;
447:
448: if (len < 0 || len > ldata.length) {
449: throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT, Long
450: .toString(len));
451: }
452:
453: if (len == ldata.length) {
454: return;
455: }
456:
457: byte[] newData = new byte[(int) len];
458:
459: System.arraycopy(ldata, 0, newData, 0, (int) len);
460:
461: data = newData;
462: }
463:
464: // public static void main(String[] args) throws Exception {
465: //
466: // System.out.println("--------------------------------");
467: // System.out.println((new jdbcBlob(new byte[0])).position(new byte[]{1}, 1));
468: // System.out.println((new jdbcBlob(new byte[]{1})).position(new byte[0], 1));
469: // System.out.println((new jdbcBlob(new byte[]{1})).position((byte[])null, 1));
470: //
471: // System.out.println("--------------------------------");
472: // byte[] data1 = new byte[]{0,1,2,1,2,3,2,3,4,2,3,4,5,2,3,4,5,0,1,2,
473: // 1,2,3,2,3,4,2,3,4,5,2,3,4};
474: // byte[] pattern = new byte[]{2,3,4,5};
475: //
476: // jdbcBlob blob1 = new jdbcBlob(data1);
477: // jdbcBlob blob2 = new jdbcBlob(pattern);
478: //
479: // for (int i = -1; i <= data1.length + 1; i++) {
480: // System.out.println(blob1.position(pattern, i));
481: // }
482: //
483: // System.out.println("--------------------------------");
484: //
485: // for (int i = -1; i <= data1.length + 1; i++) {
486: // System.out.println(blob1.position(blob2, i));
487: // }
488: //
489: // System.out.println("--------------------------------");
490: //
491: // new jdbcBlob(null);
492: // }
493: }
|