1 /* 2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.sql.rowset.serial; 27 28 import java.sql.*; 29 import java.io.*; 30 import java.util.Map; 31 import java.net.URL; 32 import java.util.Arrays; 33 34 /** 35 * A serialized version of an <code>Array</code> 36 * object, which is the mapping in the Java programming language of an SQL 37 * <code>ARRAY</code> value. 38 * <P> 39 * The <code>SerialArray</code> class provides a constructor for creating 40 * a <code>SerialArray</code> instance from an <code>Array</code> object, 41 * methods for getting the base type and the SQL name for the base type, and 42 * methods for copying all or part of a <code>SerialArray</code> object. 43 * <P> 44 * Note: In order for this class to function correctly, a connection to the 45 * data source 46 * must be available in order for the SQL <code>Array</code> object to be 47 * materialized (have all of its elements brought to the client server) 48 * if necessary. At this time, logical pointers to the data in the data source, 49 * such as locators, are not currently supported. 50 */ 51 public class SerialArray implements Array, Serializable, Cloneable { 52 53 /** 54 * A serialized array in which each element is an <code>Object</code> 55 * in the Java programming language that represents an element 56 * in the SQL <code>ARRAY</code> value. 57 * @serial 58 */ 59 private Object[] elements; 60 61 /** 62 * The SQL type of the elements in this <code>SerialArray</code> object. The 63 * type is expressed as one of the constants from the class 64 * <code>java.sql.Types</code>. 65 * @serial 66 */ 67 private int baseType; 68 69 /** 70 * The type name used by the DBMS for the elements in the SQL <code>ARRAY</code> 71 * value that this <code>SerialArray</code> object represents. 72 * @serial 73 */ 74 private String baseTypeName; 75 76 /** 77 * The number of elements in this <code>SerialArray</code> object, which 78 * is also the number of elements in the SQL <code>ARRAY</code> value 79 * that this <code>SerialArray</code> object represents. 80 * @serial 81 */ 82 private int len; 83 84 /** 85 * Constructs a new <code>SerialArray</code> object from the given 86 * <code>Array</code> object, using the given type map for the custom 87 * mapping of each element when the elements are SQL UDTs. 88 * <P> 89 * This method does custom mapping if the array elements are a UDT 90 * and the given type map has an entry for that UDT. 91 * Custom mapping is recursive, 92 * meaning that if, for instance, an element of an SQL structured type 93 * is an SQL structured type that itself has an element that is an SQL 94 * structured type, each structured type that has a custom mapping will be 95 * mapped according to the given type map. 96 * <P> 97 * The new <code>SerialArray</code> 98 * object contains the same elements as the <code>Array</code> object 99 * from which it is built, except when the base type is the SQL type 100 * <code>STRUCT</code>, <code>ARRAY</code>, <code>BLOB</code>, 101 * <code>CLOB</code>, <code>DATALINK</code> or <code>JAVA_OBJECT</code>. 102 * In this case, each element in the new 103 * <code>SerialArray</code> object is the appropriate serialized form, 104 * that is, a <code>SerialStruct</code>, <code>SerialArray</code>, 105 * <code>SerialBlob</code>, <code>SerialClob</code>, 106 * <code>SerialDatalink</code>, or <code>SerialJavaObject</code> object. 107 * <P> 108 * Note: (1) The <code>Array</code> object from which a <code>SerialArray</code> 109 * object is created must have materialized the SQL <code>ARRAY</code> value's 110 * data on the client before it is passed to the constructor. Otherwise, 111 * the new <code>SerialArray</code> object will contain no data. 112 * <p> 113 * Note: (2) If the <code>Array</code> contains <code>java.sql.Types.JAVA_OBJECT</code> 114 * types, the <code>SerialJavaObject</code> constructor is called where checks 115 * are made to ensure this object is serializable. 116 * <p> 117 * Note: (3) The <code>Array</code> object supplied to this constructor cannot 118 * return <code>null</code> for any <code>Array.getArray()</code> methods. 119 * <code>SerialArray</code> cannot serialize null array values. 120 * 121 * 122 * @param array the <code>Array</code> object to be serialized 123 * @param map a <code>java.util.Map</code> object in which 124 * each entry consists of 1) a <code>String</code> object 125 * giving the fully qualified name of a UDT (an SQL structured type or 126 * distinct type) and 2) the 127 * <code>Class</code> object for the <code>SQLData</code> implementation 128 * that defines how the UDT is to be mapped. The <i>map</i> 129 * parameter does not have any effect for <code>Blob</code>, 130 * <code>Clob</code>, <code>DATALINK</code>, or 131 * <code>JAVA_OBJECT</code> types. 132 * @throws SerialException if an error occurs serializing the 133 * <code>Array</code> object 134 * @throws SQLException if a database access error occurs or if the 135 * <i>array</i> or the <i>map</i> values are <code>null</code> 136 */ 137 public SerialArray(Array array, Map<String,Class<?>> map) 138 throws SerialException, SQLException 139 { 140 141 if ((array == null) || (map == null)) { 142 throw new SQLException("Cannot instantiate a SerialArray " + 143 "object with null parameters"); 144 } 145 146 if ((elements = (Object[])array.getArray()) == null) { 147 throw new SQLException("Invalid Array object. Calls to Array.getArray() " + 148 "return null value which cannot be serialized"); 149 } 150 151 elements = (Object[])array.getArray(map); 152 baseType = array.getBaseType(); 153 baseTypeName = array.getBaseTypeName(); 154 len = elements.length; 155 156 switch (baseType) { 157 case java.sql.Types.STRUCT: 158 for (int i = 0; i < len; i++) { 159 elements[i] = new SerialStruct((Struct)elements[i], map); 160 } 161 break; 162 163 case java.sql.Types.ARRAY: 164 for (int i = 0; i < len; i++) { 165 elements[i] = new SerialArray((Array)elements[i], map); 166 } 167 break; 168 169 case java.sql.Types.BLOB: 170 for (int i = 0; i < len; i++) { 171 elements[i] = new SerialBlob((Blob)elements[i]); 172 } 173 break; 174 175 case java.sql.Types.CLOB: 176 for (int i = 0; i < len; i++) { 177 elements[i] = new SerialClob((Clob)elements[i]); 178 } 179 break; 180 181 case java.sql.Types.DATALINK: 182 for (int i = 0; i < len; i++) { 183 elements[i] = new SerialDatalink((URL)elements[i]); 184 } 185 break; 186 187 case java.sql.Types.JAVA_OBJECT: 188 for (int i = 0; i < len; i++) { 189 elements[i] = new SerialJavaObject(elements[i]); 190 } 191 } 192 } 193 194 /** 195 * This method frees the <code>Array</code> object and releases the resources that 196 * it holds. The object is invalid once the <code>free</code> 197 * method is called. 198 *<p> 199 * After <code>free</code> has been called, any attempt to invoke a 200 * method other than <code>free</code> will result in a <code>SQLException</code> 201 * being thrown. If <code>free</code> is called multiple times, the subsequent 202 * calls to <code>free</code> are treated as a no-op. 203 *<p> 204 * 205 * @throws SQLException if an error occurs releasing 206 * the Array's resources 207 * @exception SQLFeatureNotSupportedException if the JDBC driver does not support 208 * this method 209 * @since 1.6 210 */ 211 public void free() throws SQLException { 212 throw new SQLFeatureNotSupportedException("Feature not supported"); 213 } 214 215 /** 216 * Constructs a new <code>SerialArray</code> object from the given 217 * <code>Array</code> object. 218 * <P> 219 * This constructor does not do custom mapping. If the base type of the array 220 * is an SQL structured type and custom mapping is desired, the constructor 221 * <code>SerialArray(Array array, Map map)</code> should be used. 222 * <P> 223 * The new <code>SerialArray</code> 224 * object contains the same elements as the <code>Array</code> object 225 * from which it is built, except when the base type is the SQL type 226 * <code>BLOB</code>, 227 * <code>CLOB</code>, <code>DATALINK</code> or <code>JAVA_OBJECT</code>. 228 * In this case, each element in the new 229 * <code>SerialArray</code> object is the appropriate serialized form, 230 * that is, a <code>SerialBlob</code>, <code>SerialClob</code>, 231 * <code>SerialDatalink</code>, or <code>SerialJavaObject</code> object. 232 * <P> 233 * Note: (1) The <code>Array</code> object from which a <code>SerialArray</code> 234 * object is created must have materialized the SQL <code>ARRAY</code> value's 235 * data on the client before it is passed to the constructor. Otherwise, 236 * the new <code>SerialArray</code> object will contain no data. 237 * <p> 238 * Note: (2) The <code>Array</code> object supplied to this constructor cannot 239 * return <code>null</code> for any <code>Array.getArray()</code> methods. 240 * <code>SerialArray</code> cannot serialize <code>null</code> array values. 241 * 242 * @param array the <code>Array</code> object to be serialized 243 * @throws SerialException if an error occurs serializing the 244 * <code>Array</code> object 245 * @throws SQLException if a database access error occurs or the 246 * <i>array</i> parameter is <code>null</code>. 247 */ 248 public SerialArray(Array array) throws SerialException, SQLException { 249 if (array == null) { 250 throw new SQLException("Cannot instantiate a SerialArray " + 251 "object with a null Array object"); 252 } 253 254 if ((elements = (Object[])array.getArray()) == null) { 255 throw new SQLException("Invalid Array object. Calls to Array.getArray() " + 256 "return null value which cannot be serialized"); 257 } 258 259 //elements = (Object[])array.getArray(); 260 baseType = array.getBaseType(); 261 baseTypeName = array.getBaseTypeName(); 262 len = elements.length; 263 264 switch (baseType) { 265 266 case java.sql.Types.BLOB: 267 for (int i = 0; i < len; i++) { 268 elements[i] = new SerialBlob((Blob)elements[i]); 269 } 270 break; 271 272 case java.sql.Types.CLOB: 273 for (int i = 0; i < len; i++) { 274 elements[i] = new SerialClob((Clob)elements[i]); 275 } 276 break; 277 278 case java.sql.Types.DATALINK: 279 for (int i = 0; i < len; i++) { 280 elements[i] = new SerialDatalink((URL)elements[i]); 281 } 282 break; 283 284 case java.sql.Types.JAVA_OBJECT: 285 for (int i = 0; i < len; i++) { 286 elements[i] = new SerialJavaObject(elements[i]); 287 } 288 break; 289 290 } 291 292 293 } 294 295 /** 296 * Returns a new array that is a copy of this <code>SerialArray</code> 297 * object. 298 * 299 * @return a copy of this <code>SerialArray</code> object as an 300 * <code>Object</code> in the Java programming language 301 * @throws SerialException if an error occurs retrieving a copy of 302 * this <code>SerialArray</code> object 303 */ 304 public Object getArray() throws SerialException { 305 Object dst = new Object[len]; 306 System.arraycopy((Object)elements, 0, dst, 0, len); 307 return dst; 308 } 309 310 //[if an error occurstype map used??] 311 /** 312 * Returns a new array that is a copy of this <code>SerialArray</code> 313 * object, using the given type map for the custom 314 * mapping of each element when the elements are SQL UDTs. 315 * <P> 316 * This method does custom mapping if the array elements are a UDT 317 * and the given type map has an entry for that UDT. 318 * Custom mapping is recursive, 319 * meaning that if, for instance, an element of an SQL structured type 320 * is an SQL structured type that itself has an element that is an SQL 321 * structured type, each structured type that has a custom mapping will be 322 * mapped according to the given type map. 323 * 324 * @param map a <code>java.util.Map</code> object in which 325 * each entry consists of 1) a <code>String</code> object 326 * giving the fully qualified name of a UDT and 2) the 327 * <code>Class</code> object for the <code>SQLData</code> implementation 328 * that defines how the UDT is to be mapped 329 * @return a copy of this <code>SerialArray</code> object as an 330 * <code>Object</code> in the Java programming language 331 * @throws SerialException if an error occurs 332 */ 333 public Object getArray(Map<String, Class<?>> map) throws SerialException { 334 Object dst[] = new Object[len]; 335 System.arraycopy((Object)elements, 0, dst, 0, len); 336 return dst; 337 } 338 339 /** 340 * Returns a new array that is a copy of a slice 341 * of this <code>SerialArray</code> object, starting with the 342 * element at the given index and containing the given number 343 * of consecutive elements. 344 * 345 * @param index the index into this <code>SerialArray</code> object 346 * of the first element to be copied; 347 * the index of the first element is <code>0</code> 348 * @param count the number of consecutive elements to be copied, starting 349 * at the given index 350 * @return a copy of the designated elements in this <code>SerialArray</code> 351 * object as an <code>Object</code> in the Java programming language 352 * @throws SerialException if an error occurs 353 */ 354 public Object getArray(long index, int count) throws SerialException { 355 Object dst = new Object[count]; 356 System.arraycopy((Object)elements, (int)index, dst, 0, count); 357 return dst; 358 } 359 360 /** 361 * Returns a new array that is a copy of a slice 362 * of this <code>SerialArray</code> object, starting with the 363 * element at the given index and containing the given number 364 * of consecutive elements. 365 * <P> 366 * This method does custom mapping if the array elements are a UDT 367 * and the given type map has an entry for that UDT. 368 * Custom mapping is recursive, 369 * meaning that if, for instance, an element of an SQL structured type 370 * is an SQL structured type that itself has an element that is an SQL 371 * structured type, each structured type that has a custom mapping will be 372 * mapped according to the given type map. 373 * 374 * @param index the index into this <code>SerialArray</code> object 375 * of the first element to be copied; the index of the 376 * first element in the array is <code>0</code> 377 * @param count the number of consecutive elements to be copied, starting 378 * at the given index 379 * @param map a <code>java.util.Map</code> object in which 380 * each entry consists of 1) a <code>String</code> object 381 * giving the fully qualified name of a UDT and 2) the 382 * <code>Class</code> object for the <code>SQLData</code> implementation 383 * that defines how the UDT is to be mapped 384 * @return a copy of the designated elements in this <code>SerialArray</code> 385 * object as an <code>Object</code> in the Java programming language 386 * @throws SerialException if an error occurs 387 */ 388 public Object getArray(long index, int count, Map<String,Class<?>> map) 389 throws SerialException 390 { 391 Object dst = new Object[count]; 392 System.arraycopy((Object)elements, (int)index, dst, 0, count); 393 return dst; 394 } 395 396 /** 397 * Retrieves the SQL type of the elements in this <code>SerialArray</code> 398 * object. The <code>int</code> returned is one of the constants in the class 399 * <code>java.sql.Types</code>. 400 * 401 * @return one of the constants in <code>java.sql.Types</code>, indicating 402 * the SQL type of the elements in this <code>SerialArray</code> object 403 * @throws SerialException if an error occurs 404 */ 405 public int getBaseType() throws SerialException { 406 return baseType; 407 } 408 409 /** 410 * Retrieves the DBMS-specific type name for the elements in this 411 * <code>SerialArray</code> object. 412 * 413 * @return the SQL type name used by the DBMS for the base type of this 414 * <code>SerialArray</code> object 415 * @throws SerialException if an error occurs 416 */ 417 public String getBaseTypeName() throws SerialException { 418 return baseTypeName; 419 } 420 421 /** 422 * Retrieves a <code>ResultSet</code> object holding the elements of 423 * the subarray that starts at 424 * index <i>index</i> and contains up to <i>count</i> successive elements. 425 * This method uses the connection's type map to map the elements of 426 * the array if the map contains 427 * an entry for the base type. Otherwise, the standard mapping is used. 428 * 429 * @param index the index into this <code>SerialArray</code> object 430 * of the first element to be copied; the index of the 431 * first element in the array is <code>0</code> 432 * @param count the number of consecutive elements to be copied, starting 433 * at the given index 434 * @return a <code>ResultSet</code> object containing the designated 435 * elements in this <code>SerialArray</code> object, with a 436 * separate row for each element 437 * @throws SerialException, which in turn throws an 438 * <code>UnsupportedOperationException</code>, if this method is called 439 */ 440 public ResultSet getResultSet(long index, int count) throws SerialException { 441 throw new UnsupportedOperationException(); 442 } 443 444 /** 445 * 446 * Retrieves a <code>ResultSet</code> object that contains all of 447 * the elements of the SQL <code>ARRAY</code> 448 * value represented by this <code>SerialArray</code> object. This method uses 449 * the specified map for type map customizations unless the base type of the 450 * array does not match a user-defined type (UDT) in <i>map</i>, in 451 * which case it uses the 452 * standard mapping. This version of the method <code>getResultSet</code> 453 * uses either the given type map or the standard mapping; it never uses the 454 * type map associated with the connection. 455 * 456 * @param map a <code>java.util.Map</code> object in which 457 * each entry consists of 1) a <code>String</code> object 458 * giving the fully qualified name of a UDT and 2) the 459 * <code>Class</code> object for the <code>SQLData</code> implementation 460 * that defines how the UDT is to be mapped 461 * @return a <code>ResultSet</code> object containing all of the 462 * elements in this <code>SerialArray</code> object, with a 463 * separate row for each element 464 * @throws SerialException, which in turn throws an 465 * <code>UnsupportedOperationException</code>, if this method is called 466 */ 467 public ResultSet getResultSet(Map<String, Class<?>> map) 468 throws SerialException 469 { 470 throw new UnsupportedOperationException(); 471 } 472 473 /** 474 * Retrieves a <code>ResultSet</code> object that contains all of 475 * the elements in the <code>ARRAY</code> value that this 476 * <code>SerialArray</code> object represents. 477 * If appropriate, the elements of the array are mapped using the connection's 478 * type map; otherwise, the standard mapping is used. 479 * 480 * @return a <code>ResultSet</code> object containing all of the 481 * elements in this <code>SerialArray</code> object, with a 482 * separate row for each element 483 * @throws SerialException if called, which in turn throws an 484 * <code>UnsupportedOperationException</code>, if this method is called 485 */ 486 public ResultSet getResultSet() throws SerialException { 487 throw new UnsupportedOperationException(); 488 } 489 490 491 /** 492 * Retrieves a result set holding the elements of the subarray that starts at 493 * Retrieves a <code>ResultSet</code> object that contains a subarray of the 494 * elements in this <code>SerialArray</code> object, starting at 495 * index <i>index</i> and containing up to <i>count</i> successive 496 * elements. This method uses 497 * the specified map for type map customizations unless the base type of the 498 * array does not match a user-defined type (UDT) in <i>map</i>, in 499 * which case it uses the 500 * standard mapping. This version of the method <code>getResultSet</code> uses 501 * either the given type map or the standard mapping; it never uses the type 502 * map associated with the connection. 503 * 504 * @param index the index into this <code>SerialArray</code> object 505 * of the first element to be copied; the index of the 506 * first element in the array is <code>0</code> 507 * @param count the number of consecutive elements to be copied, starting 508 * at the given index 509 * @param map a <code>java.util.Map</code> object in which 510 * each entry consists of 1) a <code>String</code> object 511 * giving the fully qualified name of a UDT and 2) the 512 * <code>Class</code> object for the <code>SQLData</code> implementation 513 * that defines how the UDT is to be mapped 514 * @return a <code>ResultSet</code> object containing the designated 515 * elements in this <code>SerialArray</code> object, with a 516 * separate row for each element 517 * @throws SerialException if called, which in turn throws an 518 * <code>UnsupportedOperationException</code> 519 */ 520 public ResultSet getResultSet(long index, int count, 521 Map<String,Class<?>> map) 522 throws SerialException 523 { 524 throw new UnsupportedOperationException(); 525 } 526 527 /** 528 * Compares this SerialArray to the specified object. The result is {@code 529 * true} if and only if the argument is not {@code null} and is a {@code 530 * SerialArray} object whose elements are identical to this object's elements 531 * 532 * @param obj The object to compare this {@code SerialArray} against 533 * 534 * @return {@code true} if the given object represents a {@code SerialArray} 535 * equivalent to this SerialArray, {@code false} otherwise 536 * 537 */ 538 public boolean equals(Object obj) { 539 if (this == obj) { 540 return true; 541 } 542 543 if (obj instanceof SerialArray) { 544 SerialArray sa = (SerialArray)obj; 545 if(baseType == sa.baseType && baseTypeName.equals(sa.baseTypeName)) { 546 return Arrays.equals(elements, sa.elements); 547 } 548 } 549 return false; 550 } 551 552 /** 553 * Returns a hash code for this SerialArray. The hash code for a 554 * {@code SerialArray} object is computed using the hash codes 555 * of the elements of the {@code SerialArray} object 556 * 557 * @return a hash code value for this object. 558 */ 559 public int hashCode() { 560 return (((31 + Arrays.hashCode(elements)) * 31 + len) * 31 + 561 baseType) * 31 + baseTypeName.hashCode(); 562 } 563 564 /** 565 * Returns a clone of this {@code SerialArray}. The copy will contain a 566 * reference to a clone of the underlying objects array, not a reference 567 * to the original underlying object array of this {@code SerialArray} object. 568 * 569 * @return a clone of this SerialArray 570 */ 571 public Object clone() { 572 try { 573 SerialArray sa = (SerialArray) super.clone(); 574 sa.elements = Arrays.copyOf(elements, len); 575 return sa; 576 } catch (CloneNotSupportedException ex) { 577 // this shouldn't happen, since we are Cloneable 578 throw new InternalError(); 579 } 580 581 } 582 583 /** 584 * readObject is called to restore the state of the {@code SerialArray} from 585 * a stream. 586 */ 587 private void readObject(ObjectInputStream s) 588 throws IOException, ClassNotFoundException { 589 590 ObjectInputStream.GetField fields = s.readFields(); 591 Object[] tmp = (Object[])fields.get("elements", null); 592 if (tmp == null) 593 throw new InvalidObjectException("elements is null and should not be!"); 594 elements = tmp.clone(); 595 len = fields.get("len", 0); 596 if(elements.length != len) 597 throw new InvalidObjectException("elements is not the expected size"); 598 599 baseType = fields.get("baseType", 0); 600 baseTypeName = (String)fields.get("baseTypeName", null); 601 } 602 603 /** 604 * writeObject is called to save the state of the {@code SerialArray} 605 * to a stream. 606 */ 607 private void writeObject(ObjectOutputStream s) 608 throws IOException, ClassNotFoundException { 609 610 ObjectOutputStream.PutField fields = s.putFields(); 611 fields.put("elements", elements); 612 fields.put("len", len); 613 fields.put("baseType", baseType); 614 fields.put("baseTypeName", baseTypeName); 615 s.writeFields(); 616 } 617 618 /** 619 * The identifier that assists in the serialization of this <code>SerialArray</code> 620 * object. 621 */ 622 static final long serialVersionUID = -8466174297270688520L; 623 }