comparison src/main/java/org/monetdb/jdbc/MonetBlob.java @ 391:f523727db392

Moved Java classes from packages starting with nl.cwi.monetdb.* to package org.monetdb.* This naming complies to the Java Package Naming convention as MonetDB's main website is www.monetdb.org.
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 12 Nov 2020 22:02:01 +0100 (2020-11-12)
parents src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java@54137aeb1f92
children bf9f6b6ecf40
comparison
equal deleted inserted replaced
390:6199e0be3c6e 391:f523727db392
1 /*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V.
7 */
8
9 package org.monetdb.jdbc;
10
11 import java.io.ByteArrayInputStream;
12 import java.io.InputStream;
13 import java.io.OutputStream;
14 import java.sql.Blob;
15 import java.sql.SQLException;
16 import java.sql.SQLFeatureNotSupportedException;
17
18 /**
19 * The MonetBlob class implements the {@link java.sql.Blob} interface.
20 *
21 * Because MonetDB/SQL currently has no support for streams, this class is a
22 * shallow wrapper of a byte[]. It is more or less supplied to
23 * enable an application that depends on it to run. It may be obvious
24 * that it is a real resource expensive workaround that contradicts the
25 * benefits for a Blob: avoidance of huge resource consumption.
26 * <b>Use of this class is highly discouraged.</b>
27 *
28 * @author Fabian Groffen
29 */
30 public final class MonetBlob implements Blob {
31 private byte[] buf;
32
33 /* constructors */
34 protected MonetBlob(final byte[] data) {
35 buf = data;
36 }
37
38 protected MonetBlob(final String hexString) {
39 buf = hexStrToByteArray(hexString);
40 }
41
42
43 /* class utility methods */
44 static final byte[] hexStrToByteArray(final String hexString) {
45 // unpack the HEX (BLOB) notation to real bytes
46 final int len = hexString.length() / 2;
47 final byte[] buf = new byte[len];
48 for (int i = 0; i < len; i++) {
49 // was buf[i] = (byte)Integer.parseInt(hexString.substring(2 * i, (2 * i) + 2), 16);
50 buf[i] = (byte) ((Character.digit(hexString.charAt(2 * i), 16) << 4)
51 + Character.digit(hexString.charAt((2 * i) +1), 16));
52 }
53 return buf;
54 }
55
56 /* internal utility method */
57 private final void checkBufIsNotNull() throws SQLException {
58 if (buf == null)
59 throw new SQLException("This MonetBlob has been freed", "M1M20");
60 }
61
62 //== begin interface Blob
63
64 /**
65 * This method frees the Blob object and releases the resources that
66 * it holds. The object is invalid once the free method is called.
67 *
68 * After free has been called, any attempt to invoke a method other
69 * than free will result in a SQLException being thrown. If free is
70 * called multiple times, the subsequent calls to free are treated
71 * as a no-op.
72 *
73 * @throws SQLException if an error occurs releasing the Blob's resources
74 */
75 @Override
76 public void free() throws SQLException {
77 buf = null;
78 }
79
80 /**
81 * Retrieves the BLOB value designated by this Blob instance as a stream.
82 *
83 * @return a stream containing the BLOB data
84 * @throws SQLException if there is an error accessing the BLOB value
85 */
86 @Override
87 public InputStream getBinaryStream() throws SQLException {
88 checkBufIsNotNull();
89 return new ByteArrayInputStream(buf);
90 }
91
92 /**
93 * Returns an InputStream object that contains a partial Blob value,
94 * starting with the byte specified by pos, which is length bytes in
95 * length.
96 *
97 * @param pos the offset to the first byte of the partial value to
98 * be retrieved. The first byte in the Blob is at position 1
99 * @param length the length in bytes of the partial value to be retrieved
100 * @return InputStream through which the partial Blob value can be read.
101 * @throws SQLException if pos is less than 1 or if pos is
102 * greater than the number of bytes in the Blob or if pos +
103 * length is greater than the number of bytes in the Blob
104 */
105 @Override
106 public InputStream getBinaryStream(final long pos, final long length)
107 throws SQLException
108 {
109 checkBufIsNotNull();
110 if (pos < 1 || pos > buf.length) {
111 throw new SQLException("Invalid pos value: " + pos, "M1M05");
112 }
113 if (length < 0 || pos - 1 + length > buf.length) {
114 throw new SQLException("Invalid length value: " + length, "M1M05");
115 }
116 return new ByteArrayInputStream(buf, (int) pos - 1, (int) length);
117 }
118
119 /**
120 * Retrieves all or part of the BLOB value that this Blob object
121 * represents, as an array of bytes. This byte array contains up to
122 * length consecutive bytes starting at position pos.
123 *
124 * @param pos the ordinal position of the first byte in the BLOB
125 * value to be extracted; the first byte is at position 1.
126 * @param length the number of consecutive bytes to be copied
127 * @return a byte array containing up to length consecutive bytes
128 * from the BLOB value designated by this Blob object,
129 * starting with the byte at position pos.
130 * @throws SQLException if there is an error accessing the BLOB value
131 */
132 @Override
133 public byte[] getBytes(final long pos, final int length) throws SQLException {
134 checkBufIsNotNull();
135 if (pos < 1 || pos > buf.length) {
136 throw new SQLException("Invalid pos value: " + pos, "M1M05");
137 }
138 if (length < 0 || pos - 1 + length > buf.length) {
139 throw new SQLException("Invalid length value: " + length, "M1M05");
140 }
141
142 try {
143 return java.util.Arrays.copyOfRange(buf, (int) pos - 1, (int) pos - 1 + length);
144 } catch (IndexOutOfBoundsException e) {
145 throw new SQLException(e.getMessage(), "M0M10");
146 }
147 }
148
149 /**
150 * Returns the number of bytes in the BLOB value designated by this
151 * Blob object.
152 *
153 * @return length of the BLOB in bytes
154 * @throws SQLException if there is an error accessing the length
155 * of the BLOB value
156 */
157 @Override
158 public long length() throws SQLException {
159 checkBufIsNotNull();
160 return (long)buf.length;
161 }
162
163 /**
164 * Retrieves the byte position in the BLOB value designated by this
165 * Blob object at which pattern begins. The search begins at position start.
166 *
167 * @param pattern the Blob object designating the BLOB value for
168 * which to search
169 * @param start the position in the BLOB value at which to begin
170 * searching; the first position is 1
171 * @return the position at which the pattern begins, else -1
172 * @throws SQLException if there is an error accessing the BLOB value
173 */
174 @Override
175 public long position(final Blob pattern, final long start) throws SQLException {
176 if (pattern == null) {
177 throw new SQLException("Missing pattern object", "M1M05");
178 }
179 // buf and input argument start will be checked in method position(byte{}, long)
180 return position(pattern.getBytes(1L, (int)pattern.length()), start);
181 }
182
183 /**
184 * Retrieves the byte position at which the specified byte array
185 * pattern begins within the BLOB value that this Blob object
186 * represents. The search for pattern begins at position start.
187 *
188 * @param pattern the byte array for which to search
189 * @param start the position at which to begin searching;
190 * the first position is 1
191 * @return the position at which the pattern appears, else -1
192 * @throws SQLException if there is an error accessing the BLOB value
193 */
194 @Override
195 public long position(final byte[] pattern, final long start) throws SQLException {
196 checkBufIsNotNull();
197 if (pattern == null) {
198 throw new SQLException("Missing pattern object", "M1M05");
199 }
200 if (start < 1 || start > buf.length) {
201 throw new SQLException("Invalid start value: " + start, "M1M05");
202 }
203 try {
204 final int patternLength = pattern.length;
205 final int maxPos = buf.length - patternLength;
206 for (int i = (int)(start - 1); i < maxPos; i++) {
207 int j;
208 for (j = 0; j < patternLength; j++) {
209 if (buf[i + j] != pattern[j])
210 break;
211 }
212 if (j == patternLength)
213 // found a match
214 return i;
215 }
216 } catch (IndexOutOfBoundsException e) {
217 throw new SQLException(e.getMessage(), "M0M10");
218 }
219 return -1;
220 }
221
222 /**
223 * Retrieves a stream that can be used to write to the BLOB value
224 * that this Blob object represents. The stream begins at position
225 * pos. The bytes written to the stream will overwrite the existing
226 * bytes in the Blob object starting at the position pos. If the end
227 * of the Blob value is reached while writing to the stream, then
228 * the length of the Blob value will be increased to accomodate the
229 * extra bytes.
230 *
231 * @param pos the position in the BLOB value at which to start
232 * writing; the first position is 1
233 * @return a java.io.OutputStream object to which data can be written
234 * @throws SQLException if there is an error accessing the BLOB
235 * value or if pos is less than 1
236 * @throws SQLFeatureNotSupportedException if the JDBC driver does
237 * not support this method
238 */
239 @Override
240 public OutputStream setBinaryStream(final long pos) throws SQLException {
241 throw MonetWrapper.newSQLFeatureNotSupportedException("setBinaryStream");
242 }
243
244 /**
245 * Writes the given array of bytes to the BLOB value that this Blob
246 * object represents, starting at position pos, and returns the
247 * number of bytes written.
248 *
249 * @param pos the position in the BLOB object at which to start writing
250 * @param bytes the array of bytes to be written to the BLOB value
251 * that this Blob object represents
252 * @return the number of bytes written
253 * @throws SQLException if there is an error accessing the
254 * BLOB value or if pos is less than 1
255 */
256 @Override
257 public int setBytes(final long pos, final byte[] bytes) throws SQLException {
258 // buf and input arguments will be checked in method setBytes(long, byte{}, int, int)
259 final int len = (bytes != null) ? bytes.length : 0;
260 return setBytes(pos, bytes, 1, len);
261 }
262
263 /**
264 * Writes all or part of the given byte array to the BLOB value that
265 * this Blob object represents and returns the number of bytes written.
266 * Writing starts at position pos in the BLOB value; len bytes from
267 * the given byte array are written.
268 *
269 * @param pos the position in the BLOB object at which to start writing
270 * @param bytes the array of bytes to be written to this BLOB object
271 * @param offset the offset into the array bytes at which to start
272 * reading the bytes to be set
273 * @param len the number of bytes to be written to the BLOB value
274 * from the array of bytes bytes
275 * @return the number of bytes written
276 * @throws SQLException if there is an error accessing the
277 * BLOB value or if pos is less than 1
278 */
279 @Override
280 public int setBytes(final long pos, final byte[] bytes, int offset, final int len)
281 throws SQLException
282 {
283 checkBufIsNotNull();
284 if (bytes == null) {
285 throw new SQLException("Missing bytes[] object", "M1M05");
286 }
287 if (pos < 1 || pos > buf.length) {
288 throw new SQLException("Invalid pos value: " + pos, "M1M05");
289 }
290 if (len < 0 || pos + len > buf.length) {
291 throw new SQLException("Invalid len value: " + len, "M1M05");
292 }
293 if (offset < 0 || offset > bytes.length) {
294 throw new SQLException("Invalid offset value: " + offset, "M1M05");
295 }
296
297 try {
298 offset--;
299 /* transactions? what are you talking about? */
300 for (int i = (int)pos; i < len; i++)
301 buf[i] = bytes[offset + i];
302 } catch (IndexOutOfBoundsException e) {
303 throw new SQLException(e.getMessage(), "M0M10");
304 }
305 return len;
306 }
307
308 /**
309 * Truncates the BLOB value that this Blob object represents to be
310 * len bytes in length.
311 *
312 * @param len the length, in bytes, to which the BLOB value
313 * should be truncated
314 * @throws SQLException if there is an error accessing the BLOB value
315 */
316 @Override
317 public void truncate(final long len) throws SQLException {
318 checkBufIsNotNull();
319 if (len < 0 || len > buf.length) {
320 throw new SQLException("Invalid len value: " + len, "M1M05");
321 }
322 if (buf.length > len) {
323 buf = java.util.Arrays.copyOf(buf, (int)len);
324 }
325 }
326 }