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 package javax.sql.rowset.serial;
  26 
  27 import java.sql.*;
  28 import java.util.Arrays;
  29 import java.util.Map;
  30 
  31 /**
  32  * An input stream used for custom mapping user-defined types (UDTs).
  33  * An <code>SQLInputImpl</code> object is an input stream that contains a
  34  * stream of values that are the attributes of a UDT.
  35  * <p>
  36  * This class is used by the driver behind the scenes when the method
  37  * <code>getObject</code> is called on an SQL structured or distinct type
  38  * that has a custom mapping; a programmer never invokes
  39  * <code>SQLInputImpl</code> methods directly. They are provided here as a
  40  * convenience for those who write <code>RowSet</code> implementations.
  41  * <P>
  42  * The <code>SQLInputImpl</code> class provides a set of
  43  * reader methods analogous to the <code>ResultSet</code> getter
  44  * methods.  These methods make it possible to read the values in an
  45  * <code>SQLInputImpl</code> object.
  46  * <P>
  47  * The method <code>wasNull</code> is used to determine whether the
  48  * the last value read was SQL <code>NULL</code>.
  49  * <P>When the method <code>getObject</code> is called with an
  50  * object of a class implementing the interface <code>SQLData</code>,
  51  * the JDBC driver calls the method <code>SQLData.getSQLType</code>
  52  * to determine the SQL type of the UDT being custom mapped. The driver
  53  * creates an instance of <code>SQLInputImpl</code>, populating it with the
  54  * attributes of the UDT.  The driver then passes the input
  55  * stream to the method <code>SQLData.readSQL</code>, which in turn
  56  * calls the <code>SQLInputImpl</code> reader methods
  57  * to read the attributes from the input stream.
  58  * @see java.sql.SQLData
  59  */
  60 public class SQLInputImpl implements SQLInput {
  61 
  62     /**
  63      * <code>true</code> if the last value returned was <code>SQL NULL</code>;
  64      * <code>false</code> otherwise.
  65      */
  66     private boolean lastValueWasNull;
  67 
  68     /**
  69      * The current index into the array of SQL structured type attributes
  70      * that will be read from this <code>SQLInputImpl</code> object and
  71      * mapped to the fields of a class in the Java programming language.
  72      */
  73     private int idx;
  74 
  75     /**
  76      * The array of attributes to be read from this stream.  The order
  77      * of the attributes is the same as the order in which they were
  78      * listed in the SQL definition of the UDT.
  79      */
  80     private Object attrib[];
  81 
  82     /**
  83      * The type map to use when the method <code>readObject</code>
  84      * is invoked. This is a <code>java.util.Map</code> object in which
  85      * there may be zero or more entries.  Each entry consists of the
  86      * fully qualified name of a UDT (the value to be mapped) and the
  87      * <code>Class</code> object for a class that implements
  88      * <code>SQLData</code> (the Java class that defines how the UDT
  89      * will be mapped).
  90      */
  91     private Map<String,Class<?>> map;
  92 
  93 
  94     /**
  95      * Creates an <code>SQLInputImpl</code> object initialized with the
  96      * given array of attributes and the given type map. If any of the
  97      * attributes is a UDT whose name is in an entry in the type map,
  98      * the attribute will be mapped according to the corresponding
  99      * <code>SQLData</code> implementation.
 100      *
 101      * @param attributes an array of <code>Object</code> instances in which
 102      *        each element is an attribute of a UDT. The order of the
 103      *        attributes in the array is the same order in which
 104      *        the attributes were defined in the UDT definition.
 105      * @param map a <code>java.util.Map</code> object containing zero or more
 106      *        entries, with each entry consisting of 1) a <code>String</code>
 107      *        giving the fully
 108      *        qualified name of the UDT and 2) the <code>Class</code> object
 109      *        for the <code>SQLData</code> implementation that defines how
 110      *        the UDT is to be mapped
 111      * @throws SQLException if the <code>attributes</code> or the <code>map</code>
 112      *        is a <code>null</code> value
 113      */
 114 
 115     public SQLInputImpl(Object[] attributes, Map<String,Class<?>> map)
 116         throws SQLException
 117     {
 118         if ((attributes == null) || (map == null)) {
 119             throw new SQLException("Cannot instantiate a SQLInputImpl " +
 120             "object with null parameters");
 121         }
 122         // assign our local reference to the attribute stream
 123         attrib = Arrays.copyOf(attributes, attributes.length);
 124         // init the index point before the head of the stream
 125         idx = -1;
 126         // set the map
 127         this.map = map;
 128     }
 129 
 130 
 131     /**
 132      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 133      * as an <code>Object</code> in the Java programming language.
 134      *
 135      * @return the next value in the input stream
 136      *         as an <code>Object</code> in the Java programming language
 137      * @throws SQLException if the read position is located at an invalid
 138      *         position or if there are no further values in the stream
 139      */
 140     private Object getNextAttribute() throws SQLException {
 141         if (++idx >= attrib.length) {
 142             throw new SQLException("SQLInputImpl exception: Invalid read " +
 143                                    "position");
 144         } else {
 145             return attrib[idx];
 146         }
 147     }
 148 
 149 
 150     //================================================================
 151     // Methods for reading attributes from the stream of SQL data.
 152     // These methods correspond to the column-accessor methods of
 153     // java.sql.ResultSet.
 154     //================================================================
 155 
 156     /**
 157      * Retrieves the next attribute in this <code>SQLInputImpl</code> object as
 158      * a <code>String</code> in the Java programming language.
 159      * <p>
 160      * This method does not perform type-safe checking to determine if the
 161      * returned type is the expected type; this responsibility is delegated
 162      * to the UDT mapping as defined by a <code>SQLData</code>
 163      * implementation.
 164      * <p>
 165      * @return the next attribute in this <code>SQLInputImpl</code> object;
 166      *     if the value is <code>SQL NULL</code>, return <code>null</code>
 167      * @throws SQLException if the read position is located at an invalid
 168      *     position or if there are no further values in the stream.
 169      */
 170     public String readString() throws SQLException {
 171 
 172         String attrib = (String)getNextAttribute();
 173 
 174         if (attrib == null) {
 175             lastValueWasNull = true;
 176             return null;
 177         } else {
 178             lastValueWasNull = false;
 179             return attrib;
 180         }
 181     }
 182 
 183     /**
 184      * Retrieves the next attribute in this <code>SQLInputImpl</code> object as
 185      * a <code>boolean</code> in the Java programming language.
 186      * <p>
 187      * This method does not perform type-safe checking to determine if the
 188      * returned type is the expected type; this responsibility is delegated
 189      * to the UDT mapping as defined by a <code>SQLData</code>
 190      * implementation.
 191      * <p>
 192      * @return the next attribute in this <code>SQLInputImpl</code> object;
 193      *     if the value is <code>SQL NULL</code>, return <code>null</code>
 194      * @throws SQLException if the read position is located at an invalid
 195      *     position or if there are no further values in the stream.
 196      */
 197     public boolean readBoolean() throws SQLException {
 198 
 199         Boolean attrib = (Boolean)getNextAttribute();
 200 
 201         if (attrib == null) {
 202             lastValueWasNull = true;
 203             return false;
 204         } else {
 205             lastValueWasNull = false;
 206             return attrib.booleanValue();
 207         }
 208     }
 209 
 210     /**
 211      * Retrieves the next attribute in this <code>SQLInputImpl</code> object as
 212      * a <code>byte</code> in the Java programming language.
 213      * <p>
 214      * This method does not perform type-safe checking to determine if the
 215      * returned type is the expected type; this responsibility is delegated
 216      * to the UDT mapping as defined by a <code>SQLData</code>
 217      * implementation.
 218      * <p>
 219      * @return the next attribute in this <code>SQLInputImpl</code> object;
 220      *     if the value is <code>SQL NULL</code>, return <code>null</code>
 221      * @throws SQLException if the read position is located at an invalid
 222      *     position or if there are no further values in the stream
 223      */
 224     public byte readByte() throws SQLException {
 225         Byte attrib = (Byte)getNextAttribute();
 226 
 227         if (attrib == null) {
 228             lastValueWasNull = true;
 229             return (byte)0;
 230         } else {
 231             lastValueWasNull = false;
 232             return attrib.byteValue();
 233         }
 234     }
 235 
 236     /**
 237      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 238      * as a <code>short</code> in the Java programming language.
 239      * <P>
 240      * This method does not perform type-safe checking to determine if the
 241      * returned type is the expected type; this responsibility is delegated
 242      * to the UDT mapping as defined by a <code>SQLData</code> implementation.
 243      * <P>
 244      * @return the next attribute in this <code>SQLInputImpl</code> object;
 245      *       if the value is <code>SQL NULL</code>, return <code>null</code>
 246      * @throws SQLException if the read position is located at an invalid
 247      *       position or if there are no more values in the stream
 248      */
 249     public short readShort() throws SQLException {
 250         Short attrib = (Short)getNextAttribute();
 251 
 252         if (attrib == null) {
 253             lastValueWasNull = true;
 254             return (short)0;
 255         } else {
 256             lastValueWasNull = false;
 257             return attrib.shortValue();
 258         }
 259     }
 260 
 261     /**
 262      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 263      * as an <code>int</code> in the Java programming language.
 264      * <P>
 265      * This method does not perform type-safe checking to determine if the
 266      * returned type is the expected type; this responsibility is delegated
 267      * to the UDT mapping as defined by a <code>SQLData</code> implementation.
 268      * <P>
 269      * @return the next attribute in this <code>SQLInputImpl</code> object;
 270      *       if the value is <code>SQL NULL</code>, return <code>null</code>
 271      * @throws SQLException if the read position is located at an invalid
 272      *       position or if there are no more values in the stream
 273      */
 274     public int readInt() throws SQLException {
 275         Integer attrib = (Integer)getNextAttribute();
 276 
 277         if (attrib == null) {
 278             lastValueWasNull = true;
 279             return 0;
 280         } else {
 281             lastValueWasNull = false;
 282             return attrib.intValue();
 283         }
 284     }
 285 
 286     /**
 287      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 288      * as a <code>long</code> in the Java programming language.
 289      * <P>
 290      * This method does not perform type-safe checking to determine if the
 291      * returned type is the expected type; this responsibility is delegated
 292      * to the UDT mapping as defined by a <code>SQLData</code> implementation.
 293      * <P>
 294      * @return the next attribute in this <code>SQLInputImpl</code> object;
 295      *       if the value is <code>SQL NULL</code>, return <code>null</code>
 296      * @throws SQLException if the read position is located at an invalid
 297      *       position or if there are no more values in the stream
 298      */
 299     public long readLong() throws SQLException {
 300         Long attrib = (Long)getNextAttribute();
 301 
 302         if (attrib == null) {
 303             lastValueWasNull = true;
 304             return (long)0;
 305         } else {
 306             lastValueWasNull = false;
 307             return attrib.longValue();
 308         }
 309     }
 310 
 311     /**
 312      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 313      * as a <code>float</code> in the Java programming language.
 314      * <P>
 315      * This method does not perform type-safe checking to determine if the
 316      * returned type is the expected type; this responsibility is delegated
 317      * to the UDT mapping as defined by a <code>SQLData</code> implementation.
 318      * <P>
 319      * @return the next attribute in this <code>SQLInputImpl</code> object;
 320      *       if the value is <code>SQL NULL</code>, return <code>null</code>
 321      * @throws SQLException if the read position is located at an invalid
 322      *       position or if there are no more values in the stream
 323      */
 324     public float readFloat() throws SQLException {
 325         Float attrib = (Float)getNextAttribute();
 326 
 327         if (attrib == null) {
 328             lastValueWasNull = true;
 329             return (float)0;
 330         } else {
 331             lastValueWasNull = false;
 332             return attrib.floatValue();
 333         }
 334     }
 335 
 336     /**
 337      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 338      * as a <code>double</code> in the Java programming language.
 339      * <P>
 340      * This method does not perform type-safe checking to determine if the
 341      * returned type is the expected type; this responsibility is delegated
 342      * to the UDT mapping as defined by a <code>SQLData</code> implementation.
 343      * <P>
 344      * @return the next attribute in this <code>SQLInputImpl</code> object;
 345      *       if the value is <code>SQL NULL</code>, return <code>null</code>
 346      * @throws SQLException if the read position is located at an invalid
 347      *       position or if there are no more values in the stream
 348      */
 349     public double readDouble() throws SQLException {
 350         Double attrib = (Double)getNextAttribute();
 351 
 352         if (attrib == null) {
 353             lastValueWasNull = true;
 354             return (double)0;
 355         } else {
 356             lastValueWasNull = false;
 357             return attrib.doubleValue();
 358         }
 359     }
 360 
 361     /**
 362      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 363      * as a <code>java.math.BigDecimal</code>.
 364      * <P>
 365      * This method does not perform type-safe checking to determine if the
 366      * returned type is the expected type; this responsibility is delegated
 367      * to the UDT mapping as defined by a <code>SQLData</code> implementation.
 368      * <P>
 369      * @return the next attribute in this <code>SQLInputImpl</code> object;
 370      *       if the value is <code>SQL NULL</code>, return <code>null</code>
 371      * @throws SQLException if the read position is located at an invalid
 372      *       position or if there are no more values in the stream
 373      */
 374     public java.math.BigDecimal readBigDecimal() throws SQLException {
 375         java.math.BigDecimal attrib = (java.math.BigDecimal)getNextAttribute();
 376 
 377         if (attrib == null) {
 378             lastValueWasNull = true;
 379             return null;
 380         } else {
 381             lastValueWasNull = false;
 382             return attrib;
 383         }
 384     }
 385 
 386     /**
 387      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 388      * as an array of bytes.
 389      * <p>
 390      * This method does not perform type-safe checking to determine if the
 391      * returned type is the expected type; this responsibility is delegated
 392      * to the UDT mapping as defined by a <code>SQLData</code> implementation.
 393      * <P>
 394      * @return the next attribute in this <code>SQLInputImpl</code> object;
 395      *       if the value is <code>SQL NULL</code>, return <code>null</code>
 396      * @throws SQLException if the read position is located at an invalid
 397      *       position or if there are no more values in the stream
 398      */
 399     public byte[] readBytes() throws SQLException {
 400         byte[] attrib = (byte[])getNextAttribute();
 401 
 402         if (attrib == null) {
 403             lastValueWasNull = true;
 404             return null;
 405         } else {
 406             lastValueWasNull = false;
 407             return attrib;
 408         }
 409     }
 410 
 411     /**
 412      * Retrieves the next attribute in this <code>SQLInputImpl</code> as
 413      * a <code>java.sql.Date</code> object.
 414      * <P>
 415      * This method does not perform type-safe checking to determine if the
 416      * returned type is the expected type; this responsibility is delegated
 417      * to the UDT mapping as defined by a <code>SQLData</code> implementation.
 418      * <P>
 419      * @return the next attribute in this <code>SQLInputImpl</code> object;
 420      *       if the value is <code>SQL NULL</code>, return <code>null</code>
 421      * @throws SQLException if the read position is located at an invalid
 422      *       position or if there are no more values in the stream
 423      */
 424     public java.sql.Date readDate() throws SQLException {
 425         java.sql.Date attrib = (java.sql.Date)getNextAttribute();
 426 
 427         if (attrib == null) {
 428             lastValueWasNull = true;
 429             return null;
 430         } else {
 431             lastValueWasNull = false;
 432             return attrib;
 433         }
 434     }
 435 
 436     /**
 437      * Retrieves the next attribute in this <code>SQLInputImpl</code> object as
 438      * a <code>java.sql.Time</code> object.
 439      * <P>
 440      * This method does not perform type-safe checking to determine if the
 441      * returned type is the expected type as this responsibility is delegated
 442      * to the UDT mapping as implemented by a <code>SQLData</code>
 443      * implementation.
 444      *
 445      * @return the attribute; if the value is <code>SQL NULL</code>, return
 446      * <code>null</code>
 447      * @throws SQLException if the read position is located at an invalid
 448      * position; or if there are no further values in the stream.
 449      */
 450     public java.sql.Time readTime() throws SQLException {
 451         java.sql.Time attrib = (java.sql.Time)getNextAttribute();
 452 
 453         if (attrib == null) {
 454             lastValueWasNull = true;
 455             return null;
 456         } else {
 457             lastValueWasNull = false;
 458             return attrib;
 459         }
 460     }
 461 
 462     /**
 463      * Retrieves the next attribute in this <code>SQLInputImpl</code> object as
 464      * a <code>java.sql.Timestamp</code> object.
 465      *
 466      * @return the attribute; if the value is <code>SQL NULL</code>, return
 467      * <code>null</code>
 468      * @throws SQLException if the read position is located at an invalid
 469      * position; or if there are no further values in the stream.
 470      */
 471     public java.sql.Timestamp readTimestamp() throws SQLException {
 472         java.sql.Timestamp attrib = (java.sql.Timestamp)getNextAttribute();
 473 
 474         if (attrib == null) {
 475             lastValueWasNull = true;
 476             return null;
 477         } else {
 478             lastValueWasNull = false;
 479             return attrib;
 480         }
 481     }
 482 
 483     /**
 484      * Retrieves the next attribute in this <code>SQLInputImpl</code> object
 485      * as a stream of Unicode characters.
 486      * <P>
 487      * This method does not perform type-safe checking to determine if the
 488      * returned type is the expected type as this responsibility is delegated
 489      * to the UDT mapping as implemented by a <code>SQLData</code>
 490      * implementation.
 491      *
 492      * @return the attribute; if the value is <code>SQL NULL</code>, return <code>null</code>
 493      * @throws SQLException if the read position is located at an invalid
 494      * position; or if there are no further values in the stream.
 495      */
 496     public java.io.Reader readCharacterStream() throws SQLException {
 497         java.io.Reader attrib = (java.io.Reader)getNextAttribute();
 498 
 499         if (attrib == null) {
 500             lastValueWasNull = true;
 501             return null;
 502         } else {
 503             lastValueWasNull = false;
 504             return attrib;
 505         }
 506     }
 507 
 508     /**
 509      * Returns the next attribute in this <code>SQLInputImpl</code> object
 510      * as a stream of ASCII characters.
 511      * <P>
 512      * This method does not perform type-safe checking to determine if the
 513      * returned type is the expected type as this responsibility is delegated
 514      * to the UDT mapping as implemented by a <code>SQLData</code>
 515      * implementation.
 516      *
 517      * @return the attribute; if the value is <code>SQL NULL</code>,
 518      * return <code>null</code>
 519      * @throws SQLException if the read position is located at an invalid
 520      * position; or if there are no further values in the stream.
 521      */
 522     public java.io.InputStream readAsciiStream() throws SQLException {
 523         java.io.InputStream attrib = (java.io.InputStream)getNextAttribute();
 524 
 525         if (attrib == null) {
 526             lastValueWasNull = true;
 527             return null;
 528         } else {
 529             lastValueWasNull = false;
 530             return attrib;
 531         }
 532     }
 533 
 534     /**
 535      * Returns the next attribute in this <code>SQLInputImpl</code> object
 536      * as a stream of uninterpreted bytes.
 537      * <P>
 538      * This method does not perform type-safe checking to determine if the
 539      * returned type is the expected type as this responsibility is delegated
 540      * to the UDT mapping as implemented by a <code>SQLData</code>
 541      * implementation.
 542      *
 543      * @return the attribute; if the value is <code>SQL NULL</code>, return
 544      * <code>null</code>
 545      * @throws SQLException if the read position is located at an invalid
 546      * position; or if there are no further values in the stream.
 547      */
 548     public java.io.InputStream readBinaryStream() throws SQLException {
 549         java.io.InputStream attrib = (java.io.InputStream)getNextAttribute();
 550 
 551         if (attrib == null) {
 552             lastValueWasNull = true;
 553             return null;
 554         } else {
 555             lastValueWasNull = false;
 556             return attrib;
 557         }
 558     }
 559 
 560     //================================================================
 561     // Methods for reading items of SQL user-defined types from the stream.
 562     //================================================================
 563 
 564     /**
 565      * Retrieves the value at the head of this <code>SQLInputImpl</code>
 566      * object as an <code>Object</code> in the Java programming language.  The
 567      * actual type of the object returned is determined by the default
 568      * mapping of SQL types to types in the Java programming language unless
 569      * there is a custom mapping, in which case the type of the object
 570      * returned is determined by this stream's type map.
 571      * <P>
 572      * The JDBC technology-enabled driver registers a type map with the stream
 573      * before passing the stream to the application.
 574      * <P>
 575      * When the datum at the head of the stream is an SQL <code>NULL</code>,
 576      * this method returns <code>null</code>.  If the datum is an SQL
 577      * structured or distinct type with a custom mapping, this method
 578      * determines the SQL type of the datum at the head of the stream,
 579      * constructs an object of the appropriate class, and calls the method
 580      * <code>SQLData.readSQL</code> on that object. The <code>readSQL</code>
 581      * method then calls the appropriate <code>SQLInputImpl.readXXX</code>
 582      * methods to retrieve the attribute values from the stream.
 583      *
 584      * @return the value at the head of the stream as an <code>Object</code>
 585      *         in the Java programming language; <code>null</code> if
 586      *         the value is SQL <code>NULL</code>
 587      * @throws SQLException if the read position is located at an invalid
 588      * position; or if there are no further values in the stream.
 589      */
 590     public Object readObject() throws SQLException {
 591         Object attrib = getNextAttribute();
 592 
 593         if (attrib == null) {
 594             lastValueWasNull = true;
 595             return null;
 596         } else {
 597             lastValueWasNull = false;
 598             if (attrib instanceof Struct) {
 599                 Struct s = (Struct)attrib;
 600                 // look up the class in the map
 601                 Class<?> c = map.get(s.getSQLTypeName());
 602                 if (c != null) {
 603                     // create new instance of the class
 604                     SQLData obj = null;
 605                     try {
 606                         obj = (SQLData)c.newInstance();
 607                     } catch (java.lang.InstantiationException ex) {
 608                         throw new SQLException("Unable to instantiate: " +
 609                                                ex.getMessage());
 610                     } catch (java.lang.IllegalAccessException ex) {
 611                         throw new SQLException("Unable to instantiate: " +
 612                                                ex.getMessage());
 613                     }
 614                     // get the attributes from the struct
 615                     Object attribs[] = s.getAttributes(map);
 616                     // create the SQLInput "stream"
 617                     SQLInputImpl sqlInput = new SQLInputImpl(attribs, map);
 618                     // read the values...
 619                     obj.readSQL(sqlInput, s.getSQLTypeName());
 620                     return obj;
 621                 }
 622             }
 623             return attrib;
 624         }
 625     }
 626 
 627     /**
 628      * Retrieves the value at the head of this <code>SQLInputImpl</code> object
 629      * as a <code>Ref</code> object in the Java programming language.
 630      *
 631      * @return a <code>Ref</code> object representing the SQL
 632      *         <code>REF</code> value at the head of the stream; if the value
 633      *         is <code>SQL NULL</code> return <code>null</code>
 634      * @throws SQLException if the read position is located at an invalid
 635      *         position; or if there are no further values in the stream.
 636      */
 637     public Ref readRef() throws SQLException {
 638         Ref attrib = (Ref)getNextAttribute();
 639 
 640         if (attrib == null) {
 641             lastValueWasNull = true;
 642             return null;
 643         } else {
 644             lastValueWasNull = false;
 645             return attrib;
 646         }
 647     }
 648 
 649     /**
 650      * Retrieves the <code>BLOB</code> value at the head of this
 651      * <code>SQLInputImpl</code> object as a <code>Blob</code> object
 652      * in the Java programming language.
 653      * <P>
 654      * This method does not perform type-safe checking to determine if the
 655      * returned type is the expected type as this responsibility is delegated
 656      * to the UDT mapping as implemented by a <code>SQLData</code>
 657      * implementation.
 658      *
 659      * @return a <code>Blob</code> object representing the SQL
 660      *         <code>BLOB</code> value at the head of this stream;
 661      *         if the value is <code>SQL NULL</code>, return
 662      *         <code>null</code>
 663      * @throws SQLException if the read position is located at an invalid
 664      * position; or if there are no further values in the stream.
 665      */
 666     public Blob readBlob() throws SQLException {
 667         Blob attrib = (Blob)getNextAttribute();
 668 
 669         if (attrib == null) {
 670             lastValueWasNull = true;
 671             return null;
 672         } else {
 673             lastValueWasNull = false;
 674             return attrib;
 675         }
 676     }
 677 
 678     /**
 679      * Retrieves the <code>CLOB</code> value at the head of this
 680      * <code>SQLInputImpl</code> object as a <code>Clob</code> object
 681      * in the Java programming language.
 682      * <P>
 683      * This method does not perform type-safe checking to determine if the
 684      * returned type is the expected type as this responsibility is delegated
 685      * to the UDT mapping as implemented by a <code>SQLData</code>
 686      * implementation.
 687      *
 688      * @return a <code>Clob</code> object representing the SQL
 689      *         <code>CLOB</code> value at the head of the stream;
 690      *         if the value is <code>SQL NULL</code>, return
 691      *         <code>null</code>
 692      * @throws SQLException if the read position is located at an invalid
 693      * position; or if there are no further values in the stream.
 694      */
 695     public Clob readClob() throws SQLException {
 696 
 697         Clob attrib = (Clob)getNextAttribute();
 698         if (attrib == null) {
 699             lastValueWasNull = true;
 700             return null;
 701         } else {
 702             lastValueWasNull = false;
 703             return attrib;
 704         }
 705     }
 706 
 707     /**
 708      * Reads an SQL <code>ARRAY</code> value from the stream and
 709      * returns it as an <code>Array</code> object in the Java programming
 710      * language.
 711      * <P>
 712      * This method does not perform type-safe checking to determine if the
 713      * returned type is the expected type as this responsibility is delegated
 714      * to the UDT mapping as implemented by a <code>SQLData</code>
 715      * implementation.
 716      *
 717      * @return an <code>Array</code> object representing the SQL
 718      *         <code>ARRAY</code> value at the head of the stream; *
 719      *         if the value is <code>SQL NULL</code>, return
 720      *         <code>null</code>
 721      * @throws SQLException if the read position is located at an invalid
 722      * position; or if there are no further values in the stream.
 723 
 724      */
 725     public Array readArray() throws SQLException {
 726         Array attrib = (Array)getNextAttribute();
 727 
 728         if (attrib == null) {
 729             lastValueWasNull = true;
 730             return null;
 731         } else {
 732             lastValueWasNull = false;
 733             return attrib;
 734         }
 735     }
 736 
 737     /**
 738      * Ascertains whether the last value read from this
 739      * <code>SQLInputImpl</code> object was <code>null</code>.
 740      *
 741      * @return <code>true</code> if the SQL value read most recently was
 742      *         <code>null</code>; otherwise, <code>false</code>; by default it
 743      *         will return false
 744      * @throws SQLException if an error occurs determining the last value
 745      *         read was a <code>null</code> value or not;
 746      */
 747     public boolean wasNull() throws SQLException {
 748         return lastValueWasNull;
 749     }
 750 
 751     /**
 752      * Reads an SQL <code>DATALINK</code> value from the stream and
 753      * returns it as an <code>URL</code> object in the Java programming
 754      * language.
 755      * <P>
 756      * This method does not perform type-safe checking to determine if the
 757      * returned type is the expected type as this responsibility is delegated
 758      * to the UDT mapping as implemented by a <code>SQLData</code>
 759      * implementation.
 760      *
 761      * @return an <code>URL</code> object representing the SQL
 762      *         <code>DATALINK</code> value at the head of the stream; *
 763      *         if the value is <code>SQL NULL</code>, return
 764      *         <code>null</code>
 765      * @throws SQLException if the read position is located at an invalid
 766      * position; or if there are no further values in the stream.
 767      */
 768     public java.net.URL readURL() throws SQLException {
 769         throw new SQLException("Operation not supported");
 770     }
 771 
 772     //---------------------------- JDBC 4.0 -------------------------
 773 
 774     /**
 775      * Reads an SQL <code>NCLOB</code> value from the stream and returns it as a
 776      * <code>Clob</code> object in the Java programming language.
 777      *
 778      * @return a <code>NClob</code> object representing data of the SQL <code>NCLOB</code> value
 779      * at the head of the stream; <code>null</code> if the value read is
 780      * SQL <code>NULL</code>
 781      * @exception SQLException if a database access error occurs
 782      */
 783      public NClob readNClob() throws SQLException {
 784         throw new UnsupportedOperationException("Operation not supported");
 785     }
 786 
 787     /**
 788      * Reads the next attribute in the stream and returns it as a <code>String</code>
 789      * in the Java programming language. It is intended for use when
 790      * accessing  <code>NCHAR</code>,<code>NVARCHAR</code>
 791      * and <code>LONGNVARCHAR</code> columns.
 792      *
 793      * @return the attribute; if the value is SQL <code>NULL</code>, returns <code>null</code>
 794      * @exception SQLException if a database access error occurs
 795      */
 796     public String readNString() throws SQLException {
 797         throw new UnsupportedOperationException("Operation not supported");
 798     }
 799 
 800     /**
 801      * Reads an SQL <code>XML</code> value from the stream and returns it as a
 802      * <code>SQLXML</code> object in the Java programming language.
 803      *
 804      * @return a <code>SQLXML</code> object representing data of the SQL <code>XML</code> value
 805      * at the head of the stream; <code>null</code> if the value read is
 806      * SQL <code>NULL</code>
 807      * @exception SQLException if a database access error occurs
 808      */
 809     public SQLXML readSQLXML() throws SQLException {
 810         throw new UnsupportedOperationException("Operation not supported");
 811     }
 812 
 813      /**
 814      * Reads an SQL <code>ROWID</code> value from the stream and returns it as a
 815      * <code>RowId</code> object in the Java programming language.
 816      *
 817      * @return a <code>RowId</code> object representing data of the SQL <code>ROWID</code> value
 818      * at the head of the stream; <code>null</code> if the value read is
 819      * SQL <code>NULL</code>
 820      * @exception SQLException if a database access error occurs
 821      */
 822     public RowId readRowId() throws SQLException {
 823         throw new UnsupportedOperationException("Operation not supported");
 824     }
 825 
 826 
 827 }