1 /*
   2  * Copyright (c) 2003, 2010, 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 javax.sql.*;
  30 import java.io.*;
  31 import java.lang.String;
  32 import java.math.*;
  33 import java.util.Map;
  34 import java.util.Vector;
  35 
  36 /**
  37  * The output stream for writing the attributes of a
  38  * custom-mapped user-defined type (UDT) back to the database.
  39  * The driver uses this interface internally, and its
  40  * methods are never directly invoked by an application programmer.
  41  * <p>
  42  * When an application calls the
  43  * method <code>PreparedStatement.setObject</code>, the driver
  44  * checks to see whether the value to be written is a UDT with
  45  * a custom mapping.  If it is, there will be an entry in a
  46  * type map containing the <code>Class</code> object for the
  47  * class that implements <code>SQLData</code> for this UDT.
  48  * If the value to be written is an instance of <code>SQLData</code>,
  49  * the driver will create an instance of <code>SQLOutputImpl</code>
  50  * and pass it to the method <code>SQLData.writeSQL</code>.
  51  * The method <code>writeSQL</code> in turn calls the
  52  * appropriate <code>SQLOutputImpl.writeXXX</code> methods
  53  * to write data from the <code>SQLData</code> object to
  54  * the <code>SQLOutputImpl</code> output stream as the
  55  * representation of an SQL user-defined type.
  56  */
  57 public class SQLOutputImpl implements SQLOutput {
  58 
  59     /**
  60      * A reference to an existing vector that
  61      * contains the attributes of a <code>Struct</code> object.
  62      */
  63     private Vector<Object> attribs;
  64 
  65     /**
  66      * The type map the driver supplies to a newly created
  67      * <code>SQLOutputImpl</code> object.  This type map
  68      * indicates the <code>SQLData</code> class whose
  69      * <code>writeSQL</code> method will be called.  This
  70      * method will in turn call the appropriate
  71      * <code>SQLOutputImpl</code> writer methods.
  72      */
  73     private Map<String,Class<?>> map;
  74 
  75     /**
  76      * Creates a new <code>SQLOutputImpl</code> object
  77      * initialized with the given vector of attributes and
  78      * type map.  The driver will use the type map to determine
  79      * which <code>SQLData.writeSQL</code> method to invoke.
  80      * This method will then call the appropriate
  81      * <code>SQLOutputImpl</code> writer methods in order and
  82      * thereby write the attributes to the new output stream.
  83      *
  84      * @param attributes a <code>Vector</code> object containing the attributes of
  85      *        the UDT to be mapped to one or more objects in the Java
  86      *        programming language
  87      *
  88      * @param map a <code>java.util.Map</code> object containing zero or
  89      *        more entries, with each entry consisting of 1) a <code>String</code>
  90      *        giving the fully qualified name of a UDT and 2) the
  91      *        <code>Class</code> object for the <code>SQLData</code> implementation
  92      *        that defines how the UDT is to be mapped
  93      * @throws SQLException if the <code>attributes</code> or the <code>map</code>
  94      *        is a <code>null</code> value
  95      */
  96     public SQLOutputImpl(Vector<Object> attributes, Map<String,Class<?>> map)
  97         throws SQLException
  98     {
  99         if ((attributes == null) || (map == null)) {
 100             throw new SQLException("Cannot instantiate a SQLOutputImpl " +
 101             "instance with null parameters");
 102         }
 103         this.attribs = attributes;
 104         this.map = map;
 105     }
 106 
 107     //================================================================
 108     // Methods for writing attributes to the stream of SQL data.
 109     // These methods correspond to the column-accessor methods of
 110     // java.sql.ResultSet.
 111     //================================================================
 112 
 113     /**
 114      * Writes a <code>String</code> in the Java programming language
 115      * to this <code>SQLOutputImpl</code> object. The driver converts
 116      * it to an SQL <code>CHAR</code>, <code>VARCHAR</code>, or
 117      * <code>LONGVARCHAR</code> before returning it to the database.
 118      *
 119      * @param x the value to pass to the database
 120      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 121      *        use by a <code>SQLData</code> object attempting to write the attribute
 122      *        values of a UDT to the database.
 123      */
 124     public void writeString(String x) throws SQLException {
 125         //System.out.println("Adding :"+x);
 126         attribs.add(x);
 127     }
 128 
 129     /**
 130      * Writes a <code>boolean</code> in the Java programming language
 131      * to this <code>SQLOutputImpl</code> object. The driver converts
 132      * it to an SQL <code>BIT</code> before returning it to the database.
 133      *
 134      * @param x the value to pass to the database
 135      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 136      *        use by a <code>SQLData</code> object attempting to write the attribute
 137      *        values of a UDT to the database.
 138      */
 139     public void writeBoolean(boolean x) throws SQLException {
 140         attribs.add(Boolean.valueOf(x));
 141     }
 142 
 143     /**
 144      * Writes a <code>byte</code> in the Java programming language
 145      * to this <code>SQLOutputImpl</code> object. The driver converts
 146      * it to an SQL <code>BIT</code> before returning it to the database.
 147      *
 148      * @param x the value to pass to the database
 149      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 150      *        use by a <code>SQLData</code> object attempting to write the attribute
 151      *        values of a UDT to the database.
 152      */
 153     public void writeByte(byte x) throws SQLException {
 154         attribs.add(Byte.valueOf(x));
 155     }
 156 
 157     /**
 158      * Writes a <code>short</code> in the Java programming language
 159      * to this <code>SQLOutputImpl</code> object. The driver converts
 160      * it to an SQL <code>SMALLINT</code> before returning it to the database.
 161      *
 162      * @param x the value to pass to the database
 163      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 164      *        use by a <code>SQLData</code> object attempting to write the attribute
 165      *        values of a UDT to the database.
 166      */
 167     public void writeShort(short x) throws SQLException {
 168         attribs.add(Short.valueOf(x));
 169     }
 170 
 171     /**
 172      * Writes an <code>int</code> in the Java programming language
 173      * to this <code>SQLOutputImpl</code> object. The driver converts
 174      * it to an SQL <code>INTEGER</code> before returning it to the database.
 175      *
 176      * @param x the value to pass to the database
 177      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 178      *        use by a <code>SQLData</code> object attempting to write the attribute
 179      *        values of a UDT to the database.
 180      */
 181     public void writeInt(int x) throws SQLException {
 182         attribs.add(Integer.valueOf(x));
 183     }
 184 
 185     /**
 186      * Writes a <code>long</code> in the Java programming language
 187      * to this <code>SQLOutputImpl</code> object. The driver converts
 188      * it to an SQL <code>BIGINT</code> before returning it to the database.
 189      *
 190      * @param x the value to pass to the database
 191      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 192      *        use by a <code>SQLData</code> object attempting to write the attribute
 193      *        values of a UDT to the database.
 194      */
 195     public void writeLong(long x) throws SQLException {
 196         attribs.add(Long.valueOf(x));
 197     }
 198 
 199     /**
 200      * Writes a <code>float</code> in the Java programming language
 201      * to this <code>SQLOutputImpl</code> object. The driver converts
 202      * it to an SQL <code>REAL</code> before returning it to the database.
 203      *
 204      * @param x the value to pass to the database
 205      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 206      *        use by a <code>SQLData</code> object attempting to write the attribute
 207      *        values of a UDT to the database.
 208      */
 209     public void writeFloat(float x) throws SQLException {
 210         attribs.add(new Float(x));
 211     }
 212 
 213     /**
 214      * Writes a <code>double</code> in the Java programming language
 215      * to this <code>SQLOutputImpl</code> object. The driver converts
 216      * it to an SQL <code>DOUBLE</code> before returning it to the database.
 217      *
 218      * @param x the value to pass to the database
 219      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 220      *        use by a <code>SQLData</code> object attempting to write the attribute
 221      *        values of a UDT to the database.
 222      */
 223     public void writeDouble(double x) throws SQLException{
 224         attribs.add(new Double(x));
 225     }
 226 
 227     /**
 228      * Writes a <code>java.math.BigDecimal</code> object in the Java programming
 229      * language to this <code>SQLOutputImpl</code> object. The driver converts
 230      * it to an SQL <code>NUMERIC</code> before returning it to the database.
 231      *
 232      * @param x the value to pass to the database
 233      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 234      *        use by a <code>SQLData</code> object attempting to write the attribute
 235      *        values of a UDT to the database.
 236      */
 237     public void writeBigDecimal(java.math.BigDecimal x) throws SQLException{
 238         attribs.add(x);
 239     }
 240 
 241     /**
 242      * Writes an array of <code>bytes</code> in the Java programming language
 243      * to this <code>SQLOutputImpl</code> object. The driver converts
 244      * it to an SQL <code>VARBINARY</code> or <code>LONGVARBINARY</code>
 245      * before returning it to the database.
 246      *
 247      * @param x the value to pass to the database
 248      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 249      *        use by a <code>SQLData</code> object attempting to write the attribute
 250      *        values of a UDT to the database.
 251      */
 252     public void writeBytes(byte[] x) throws SQLException {
 253         attribs.add(x);
 254     }
 255 
 256     /**
 257      * Writes a <code>java.sql.Date</code> object in the Java programming
 258      * language to this <code>SQLOutputImpl</code> object. The driver converts
 259      * it to an SQL <code>DATE</code> before returning it to the database.
 260      *
 261      * @param x the value to pass to the database
 262      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 263      *        use by a <code>SQLData</code> object attempting to write the attribute
 264      *        values of a UDT to the database.
 265      */
 266     public void writeDate(java.sql.Date x) throws SQLException {
 267         attribs.add(x);
 268     }
 269 
 270     /**
 271      * Writes a <code>java.sql.Time</code> object in the Java programming
 272      * language to this <code>SQLOutputImpl</code> object. The driver converts
 273      * it to an SQL <code>TIME</code> before returning it to the database.
 274      *
 275      * @param x the value to pass to the database
 276      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 277      *        use by a <code>SQLData</code> object attempting to write the attribute
 278      *        values of a UDT to the database.
 279      */
 280     public void writeTime(java.sql.Time x) throws SQLException {
 281         attribs.add(x);
 282     }
 283 
 284     /**
 285      * Writes a <code>java.sql.Timestamp</code> object in the Java programming
 286      * language to this <code>SQLOutputImpl</code> object. The driver converts
 287      * it to an SQL <code>TIMESTAMP</code> before returning it to the database.
 288      *
 289      * @param x the value to pass to the database
 290      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 291      *        use by a <code>SQLData</code> object attempting to write the attribute
 292      *        values of a UDT to the database.
 293      */
 294     public void writeTimestamp(java.sql.Timestamp x) throws SQLException {
 295         attribs.add(x);
 296     }
 297 
 298     /**
 299      * Writes a stream of Unicode characters to this
 300      * <code>SQLOutputImpl</code> object. The driver will do any necessary
 301      * conversion from Unicode to the database <code>CHAR</code> format.
 302      *
 303      * @param x the value to pass to the database
 304      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 305      *        use by a <code>SQLData</code> object attempting to write the attribute
 306      *        values of a UDT to the database.
 307      */
 308     public void writeCharacterStream(java.io.Reader x) throws SQLException {
 309          BufferedReader bufReader = new BufferedReader(x);
 310          try {
 311              int i;
 312              while( (i = bufReader.read()) != -1 ) {
 313                 char ch = (char)i;
 314                 StringBuffer strBuf = new StringBuffer();
 315                 strBuf.append(ch);
 316 
 317                 String str = new String(strBuf);
 318                 String strLine = bufReader.readLine();
 319 
 320                 writeString(str.concat(strLine));
 321              }
 322          } catch(IOException ioe) {
 323 
 324          }
 325     }
 326 
 327     /**
 328      * Writes a stream of ASCII characters to this
 329      * <code>SQLOutputImpl</code> object. The driver will do any necessary
 330      * conversion from ASCII to the database <code>CHAR</code> format.
 331      *
 332      * @param x the value to pass to the database
 333      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 334      *        use by a <code>SQLData</code> object attempting to write the attribute
 335      *        values of a UDT to the database.
 336      */
 337     public void writeAsciiStream(java.io.InputStream x) throws SQLException {
 338          BufferedReader bufReader = new BufferedReader(new InputStreamReader(x));
 339          try {
 340                int i;
 341                while( (i=bufReader.read()) != -1 ) {
 342                 char ch = (char)i;
 343 
 344                 StringBuffer strBuf = new StringBuffer();
 345                 strBuf.append(ch);
 346 
 347                 String str = new String(strBuf);
 348                 String strLine = bufReader.readLine();
 349 
 350                 writeString(str.concat(strLine));
 351             }
 352           }catch(IOException ioe) {
 353             throw new SQLException(ioe.getMessage());
 354         }
 355     }
 356 
 357     /**
 358      * Writes a stream of uninterpreted bytes to this <code>SQLOutputImpl</code>
 359      * object.
 360      *
 361      * @param x the value to pass to the database
 362      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 363      *        use by a <code>SQLData</code> object attempting to write the attribute
 364      *        values of a UDT to the database.
 365      */
 366     public void writeBinaryStream(java.io.InputStream x) throws SQLException {
 367          BufferedReader bufReader = new BufferedReader(new InputStreamReader(x));
 368          try {
 369                int i;
 370              while( (i=bufReader.read()) != -1 ) {
 371                 char ch = (char)i;
 372 
 373                 StringBuffer strBuf = new StringBuffer();
 374                 strBuf.append(ch);
 375 
 376                 String str = new String(strBuf);
 377                 String strLine = bufReader.readLine();
 378 
 379                 writeString(str.concat(strLine));
 380              }
 381         } catch(IOException ioe) {
 382             throw new SQLException(ioe.getMessage());
 383         }
 384     }
 385 
 386     //================================================================
 387     // Methods for writing items of SQL user-defined types to the stream.
 388     // These methods pass objects to the database as values of SQL
 389     // Structured Types, Distinct Types, Constructed Types, and Locator
 390     // Types.  They decompose the Java object(s) and write leaf data
 391     // items using the methods above.
 392     //================================================================
 393 
 394     /**
 395      * Writes to the stream the data contained in the given
 396      * <code>SQLData</code> object.
 397      * When the <code>SQLData</code> object is <code>null</code>, this
 398      * method writes an SQL <code>NULL</code> to the stream.
 399      * Otherwise, it calls the <code>SQLData.writeSQL</code>
 400      * method of the given object, which
 401      * writes the object's attributes to the stream.
 402      * <P>
 403      * The implementation of the method <code>SQLData.writeSQ</code>
 404      * calls the appropriate <code>SQLOutputImpl.writeXXX</code> method(s)
 405      * for writing each of the object's attributes in order.
 406      * The attributes must be read from an <code>SQLInput</code>
 407      * input stream and written to an <code>SQLOutputImpl</code>
 408      * output stream in the same order in which they were
 409      * listed in the SQL definition of the user-defined type.
 410      *
 411      * @param x the object representing data of an SQL structured or
 412      *          distinct type
 413      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 414      *        use by a <code>SQLData</code> object attempting to write the attribute
 415      *        values of a UDT to the database.
 416      */
 417     public void writeObject(SQLData x) throws SQLException {
 418 
 419         /*
 420          * Except for the types that are passed as objects
 421          * this seems to be the only way for an object to
 422          * get a null value for a field in a structure.
 423          *
 424          * Note: this means that the class defining SQLData
 425          * will need to track if a field is SQL null for itself
 426          */
 427         if (x == null) {
 428             attribs.add(x);
 429             return;
 430         }
 431 
 432         /*
 433          * We have to write out a SerialStruct that contains
 434          * the name of this class otherwise we don't know
 435          * what to re-instantiate during readSQL()
 436          */
 437         attribs.add(new SerialStruct((SQLData)x, map));
 438     }
 439 
 440     /**
 441      * Writes a <code>Ref</code> object in the Java programming language
 442      * to this <code>SQLOutputImpl</code> object.  The driver converts
 443      * it to a serializable <code>SerialRef</code> SQL <code>REF</code> value
 444      * before returning it to the database.
 445      *
 446      * @param x an object representing an SQL <code>REF</code> value
 447      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 448      *        use by a <code>SQLData</code> object attempting to write the attribute
 449      *        values of a UDT to the database.
 450      */
 451     public void writeRef(Ref x) throws SQLException {
 452         if (x == null) {
 453             attribs.add(x);
 454             return;
 455         }
 456         attribs.add(new SerialRef(x));
 457     }
 458 
 459     /**
 460      * Writes a <code>Blob</code> object in the Java programming language
 461      * to this <code>SQLOutputImpl</code> object.  The driver converts
 462      * it to a serializable <code>SerialBlob</code> SQL <code>BLOB</code> value
 463      * before returning it to the database.
 464      *
 465      * @param x an object representing an SQL <code>BLOB</code> value
 466      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 467      *        use by a <code>SQLData</code> object attempting to write the attribute
 468      *        values of a UDT to the database.
 469      */
 470     public void writeBlob(Blob x) throws SQLException {
 471         if (x == null) {
 472             attribs.add(x);
 473             return;
 474         }
 475         attribs.add(new SerialBlob(x));
 476     }
 477 
 478     /**
 479      * Writes a <code>Clob</code> object in the Java programming language
 480      * to this <code>SQLOutputImpl</code> object.  The driver converts
 481      * it to a serializable <code>SerialClob</code> SQL <code>CLOB</code> value
 482      * before returning it to the database.
 483      *
 484      * @param x an object representing an SQL <code>CLOB</code> value
 485      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 486      *        use by a <code>SQLData</code> object attempting to write the attribute
 487      *        values of a UDT to the database.
 488      */
 489     public void writeClob(Clob x) throws SQLException {
 490         if (x == null) {
 491             attribs.add(x);
 492             return;
 493         }
 494         attribs.add(new SerialClob(x));
 495     }
 496 
 497     /**
 498      * Writes a <code>Struct</code> object in the Java
 499      * programming language to this <code>SQLOutputImpl</code>
 500      * object. The driver converts this value to an SQL structured type
 501      * before returning it to the database.
 502      * <P>
 503      * This method should be used when an SQL structured type has been
 504      * mapped to a <code>Struct</code> object in the Java programming
 505      * language (the standard mapping).  The method
 506      * <code>writeObject</code> should be used if an SQL structured type
 507      * has been custom mapped to a class in the Java programming language.
 508      *
 509      * @param x an object representing the attributes of an SQL structured type
 510      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 511      *        use by a <code>SQLData</code> object attempting to write the attribute
 512      *        values of a UDT to the database.
 513      */
 514     public void writeStruct(Struct x) throws SQLException {
 515         SerialStruct s = new SerialStruct(x,map);;
 516         attribs.add(s);
 517     }
 518 
 519     /**
 520      * Writes an <code>Array</code> object in the Java
 521      * programming language to this <code>SQLOutputImpl</code>
 522      * object. The driver converts this value to a serializable
 523      * <code>SerialArray</code> SQL <code>ARRAY</code>
 524      * value before returning it to the database.
 525      *
 526      * @param x an object representing an SQL <code>ARRAY</code> value
 527      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 528      *        use by a <code>SQLData</code> object attempting to write the attribute
 529      *        values of a UDT to the database.
 530      */
 531     public void writeArray(Array x) throws SQLException {
 532         if (x == null) {
 533             attribs.add(x);
 534             return;
 535         }
 536         attribs.add(new SerialArray(x, map));
 537     }
 538 
 539     /**
 540      * Writes an <code>java.sql.Type.DATALINK</code> object in the Java
 541      * programming language to this <code>SQLOutputImpl</code> object. The
 542      * driver converts this value to a serializable <code>SerialDatalink</code>
 543      * SQL <code>DATALINK</code> value before return it to the database.
 544      *
 545      * @param url an object representing a SQL <code>DATALINK</code> value
 546      * @throws SQLException if the <code>SQLOutputImpl</code> object is in
 547      *        use by a <code>SQLData</code> object attempting to write the attribute
 548      *        values of a UDT to the database.
 549      */
 550     public void writeURL(java.net.URL url) throws SQLException {
 551         if (url == null) {
 552             attribs.add(url);
 553             return;
 554         }
 555         attribs.add(new SerialDatalink(url));
 556 
 557     }
 558 
 559 
 560     /**
 561    * Writes the next attribute to the stream as a <code>String</code>
 562    * in the Java programming language. The driver converts this to a
 563    * SQL <code>NCHAR</code> or
 564    * <code>NVARCHAR</code> or <code>LONGNVARCHAR</code> value
 565    * (depending on the argument's
 566    * size relative to the driver's limits on <code>NVARCHAR</code> values)
 567    * when it sends it to the stream.
 568    *
 569    * @param x the value to pass to the database
 570    * @exception SQLException if a database access error occurs
 571    * @since 1.6
 572    */
 573    public void writeNString(String x) throws SQLException {
 574         throw new UnsupportedOperationException("Operation not supported");
 575     }
 576 
 577   /**
 578    * Writes an SQL <code>NCLOB</code> value to the stream.
 579    *
 580    * @param x a <code>NClob</code> object representing data of an SQL
 581    * <code>NCLOB</code> value
 582    *
 583    * @exception SQLException if a database access error occurs
 584    * @since 1.6
 585    */
 586    public void writeNClob(NClob x) throws SQLException {
 587         throw new UnsupportedOperationException("Operation not supported");
 588     }
 589 
 590 
 591   /**
 592    * Writes an SQL <code>ROWID</code> value to the stream.
 593    *
 594    * @param x a <code>RowId</code> object representing data of an SQL
 595    * <code>ROWID</code> value
 596    *
 597    * @exception SQLException if a database access error occurs
 598    * @since 1.6
 599    */
 600    public void writeRowId(RowId x) throws SQLException {
 601         throw new UnsupportedOperationException("Operation not supported");
 602     }
 603 
 604 
 605   /**
 606    * Writes an SQL <code>XML</code> value to the stream.
 607    *
 608    * @param x a <code>SQLXML</code> object representing data of an SQL
 609    * <code>XML</code> value
 610    *
 611    * @exception SQLException if a database access error occurs
 612    * @since 1.6
 613    */
 614    public void writeSQLXML(SQLXML x) throws SQLException {
 615         throw new UnsupportedOperationException("Operation not supported");
 616     }
 617 
 618 }