1 /*
   2  * Copyright (c) 2003, 2013, 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.math.*;
  32 import java.util.Arrays;
  33 import java.util.Map;
  34 import java.util.Vector;
  35 
  36 import javax.sql.rowset.*;
  37 
  38 /**
  39  * A serialized mapping in the Java programming language of an SQL
  40  * structured type. Each attribute that is not already serialized
  41  * is mapped to a serialized form, and if an attribute is itself
  42  * a structured type, each of its attributes that is not already
  43  * serialized is mapped to a serialized form.
  44  * <P>
  45  * In addition, the structured type is custom mapped to a class in the
  46  * Java programming language if there is such a mapping, as are
  47  * its attributes, if appropriate.
  48  * <P>
  49  * The <code>SerialStruct</code> class provides a constructor for creating
  50  * an instance from a <code>Struct</code> object, a method for retrieving
  51  * the SQL type name of the SQL structured type in the database, and methods
  52  * for retrieving its attribute values.
  53  *
  54  * <h3> Thread safety </h3>
  55  *
  56  * A SerialStruct is not safe for use by multiple concurrent threads.  If a
  57  * SerialStruct is to be used by more than one thread then access to the
  58  * SerialStruct should be controlled by appropriate synchronization.
  59  *
  60  * @since 1.5
  61  */
  62 public class SerialStruct implements Struct, Serializable, Cloneable {
  63 
  64 
  65     /**
  66      * The SQL type name for the structured type that this
  67      * <code>SerialStruct</code> object represents.  This is the name
  68      * used in the SQL definition of the SQL structured type.
  69      *
  70      * @serial
  71      */
  72     private String SQLTypeName;
  73 
  74     /**
  75      * An array of <code>Object</code> instances in  which each
  76      * element is an attribute of the SQL structured type that this
  77      * <code>SerialStruct</code> object represents.  The attributes are
  78      * ordered according to their order in the definition of the
  79      * SQL structured type.
  80      *
  81      * @serial
  82      */
  83     private Object attribs[];
  84 
  85     /**
  86      * Constructs a <code>SerialStruct</code> object from the given
  87      * <code>Struct</code> object, using the given <code>java.util.Map</code>
  88      * object for custom mapping the SQL structured type or any of its
  89      * attributes that are SQL structured types.
  90      *
  91      * @param in an instance of {@code Struct}
  92      * @param map a <code>java.util.Map</code> object in which
  93      *        each entry consists of 1) a <code>String</code> object
  94      *        giving the fully qualified name of a UDT and 2) the
  95      *        <code>Class</code> object for the <code>SQLData</code> implementation
  96      *        that defines how the UDT is to be mapped
  97      * @throws SerialException if an error occurs
  98      * @see java.sql.Struct
  99      */
 100      public SerialStruct(Struct in, Map<String,Class<?>> map)
 101          throws SerialException
 102      {
 103 
 104         try {
 105 
 106         // get the type name
 107         SQLTypeName = in.getSQLTypeName();
 108         System.out.println("SQLTypeName: " + SQLTypeName);
 109 
 110         // get the attributes of the struct
 111         attribs = in.getAttributes(map);
 112 
 113         /*
 114          * the array may contain further Structs
 115          * and/or classes that have been mapped,
 116          * other types that we have to serialize
 117          */
 118         mapToSerial(map);
 119 
 120         } catch (SQLException e) {
 121             throw new SerialException(e.getMessage());
 122         }
 123     }
 124 
 125      /**
 126       * Constructs a <code>SerialStruct</code> object from the
 127       * given <code>SQLData</code> object, using the given type
 128       * map to custom map it to a class in the Java programming
 129       * language.  The type map gives the SQL type and the class
 130       * to which it is mapped.  The <code>SQLData</code> object
 131       * defines the class to which the SQL type will be mapped.
 132       *
 133       * @param in an instance of the <code>SQLData</code> class
 134       *           that defines the mapping of the SQL structured
 135       *           type to one or more objects in the Java programming language
 136       * @param map a <code>java.util.Map</code> object in which
 137       *        each entry consists of 1) a <code>String</code> object
 138       *        giving the fully qualified name of a UDT and 2) the
 139       *        <code>Class</code> object for the <code>SQLData</code> implementation
 140       *        that defines how the UDT is to be mapped
 141       * @throws SerialException if an error occurs
 142       */
 143     public SerialStruct(SQLData in, Map<String,Class<?>> map)
 144         throws SerialException
 145     {
 146 
 147         try {
 148 
 149         //set the type name
 150         SQLTypeName = in.getSQLTypeName();
 151 
 152         Vector<Object> tmp = new Vector<>();
 153         in.writeSQL(new SQLOutputImpl(tmp, map));
 154         attribs = tmp.toArray();
 155 
 156         } catch (SQLException e) {
 157             throw new SerialException(e.getMessage());
 158         }
 159     }
 160 
 161 
 162     /**
 163      * Retrieves the SQL type name for this <code>SerialStruct</code>
 164      * object. This is the name used in the SQL definition of the
 165      * structured type
 166      *
 167      * @return a <code>String</code> object representing the SQL
 168      *         type name for the SQL structured type that this
 169      *         <code>SerialStruct</code> object represents
 170      * @throws SerialException if an error occurs
 171      */
 172     public String getSQLTypeName() throws SerialException {
 173         return SQLTypeName;
 174     }
 175 
 176     /**
 177      * Retrieves an array of <code>Object</code> values containing the
 178      * attributes of the SQL structured type that this
 179      * <code>SerialStruct</code> object represents.
 180      *
 181      * @return an array of <code>Object</code> values, with each
 182      *         element being an attribute of the SQL structured type
 183      *         that this <code>SerialStruct</code> object represents
 184      * @throws SerialException if an error occurs
 185      */
 186     public Object[]  getAttributes() throws SerialException {
 187         Object[] val = this.attribs;
 188         return (val == null) ? null : Arrays.copyOf(val, val.length);
 189     }
 190 
 191     /**
 192      * Retrieves the attributes for the SQL structured type that
 193      * this <code>SerialStruct</code> represents as an array of
 194      * <code>Object</code> values, using the given type map for
 195      * custom mapping if appropriate.
 196      *
 197      * @param map a <code>java.util.Map</code> object in which
 198      *        each entry consists of 1) a <code>String</code> object
 199      *        giving the fully qualified name of a UDT and 2) the
 200      *        <code>Class</code> object for the <code>SQLData</code> implementation
 201      *        that defines how the UDT is to be mapped
 202      * @return an array of <code>Object</code> values, with each
 203      *         element being an attribute of the SQL structured
 204      *         type that this <code>SerialStruct</code> object
 205      *         represents
 206      * @throws SerialException if an error occurs
 207      */
 208     public Object[] getAttributes(Map<String,Class<?>> map)
 209         throws SerialException
 210     {
 211         Object[] val = this.attribs;
 212         return (val == null) ? null : Arrays.copyOf(val, val.length);
 213     }
 214 
 215 
 216     /**
 217      * Maps attributes of an SQL structured type that are not
 218      * serialized to a serialized form, using the given type map
 219      * for custom mapping when appropriate.  The following types
 220      * in the Java programming language are mapped to their
 221      * serialized forms:  <code>Struct</code>, <code>SQLData</code>,
 222      * <code>Ref</code>, <code>Blob</code>, <code>Clob</code>, and
 223      * <code>Array</code>.
 224      * <P>
 225      * This method is called internally and is not used by an
 226      * application programmer.
 227      *
 228      * @param map a <code>java.util.Map</code> object in which
 229      *        each entry consists of 1) a <code>String</code> object
 230      *        giving the fully qualified name of a UDT and 2) the
 231      *        <code>Class</code> object for the <code>SQLData</code> implementation
 232      *        that defines how the UDT is to be mapped
 233      * @throws SerialException if an error occurs
 234      */
 235     private void mapToSerial(Map<String,Class<?>> map) throws SerialException {
 236 
 237         try {
 238 
 239         for (int i = 0; i < attribs.length; i++) {
 240             if (attribs[i] instanceof Struct) {
 241                 attribs[i] = new SerialStruct((Struct)attribs[i], map);
 242             } else if (attribs[i] instanceof SQLData) {
 243                 attribs[i] = new SerialStruct((SQLData)attribs[i], map);
 244             } else if (attribs[i] instanceof Blob) {
 245                 attribs[i] = new SerialBlob((Blob)attribs[i]);
 246             } else if (attribs[i] instanceof Clob) {
 247                 attribs[i] = new SerialClob((Clob)attribs[i]);
 248             } else if (attribs[i] instanceof Ref) {
 249                 attribs[i] = new SerialRef((Ref)attribs[i]);
 250             } else if (attribs[i] instanceof java.sql.Array) {
 251                 attribs[i] = new SerialArray((java.sql.Array)attribs[i], map);
 252             }
 253         }
 254 
 255         } catch (SQLException e) {
 256             throw new SerialException(e.getMessage());
 257         }
 258         return;
 259     }
 260 
 261     /**
 262      * Compares this SerialStruct to the specified object.  The result is
 263      * {@code true} if and only if the argument is not {@code null} and is a
 264      * {@code SerialStruct} object whose attributes are identical to this
 265      * object's attributes
 266      *
 267      * @param  obj The object to compare this {@code SerialStruct} against
 268      *
 269      * @return {@code true} if the given object represents a {@code SerialStruct}
 270      *          equivalent to this SerialStruct, {@code false} otherwise
 271      *
 272      */
 273     public boolean equals(Object obj) {
 274         if (this == obj) {
 275             return true;
 276         }
 277         if (obj instanceof SerialStruct) {
 278             SerialStruct ss = (SerialStruct)obj;
 279             return SQLTypeName.equals(ss.SQLTypeName) &&
 280                     Arrays.equals(attribs, ss.attribs);
 281         }
 282         return false;
 283     }
 284 
 285     /**
 286      * Returns a hash code for this {@code SerialStruct}. The hash code for a
 287      * {@code SerialStruct} object is computed using the hash codes
 288      * of the attributes of the {@code SerialStruct} object and its
 289      * {@code SQLTypeName}
 290      *
 291      * @return  a hash code value for this object.
 292      */
 293     public int hashCode() {
 294         return ((31 + Arrays.hashCode(attribs)) * 31) * 31
 295                 + SQLTypeName.hashCode();
 296     }
 297 
 298     /**
 299      * Returns a clone of this {@code SerialStruct}. The copy will contain a
 300      * reference to a clone of the underlying attribs array, not a reference
 301      * to the original underlying attribs array of this {@code SerialStruct} object.
 302      *
 303      * @return  a clone of this SerialStruct
 304      */
 305     public Object clone() {
 306         try {
 307             SerialStruct ss = (SerialStruct) super.clone();
 308             ss.attribs = Arrays.copyOf(attribs, attribs.length);
 309             return ss;
 310         } catch (CloneNotSupportedException ex) {
 311             // this shouldn't happen, since we are Cloneable
 312             throw new InternalError();
 313         }
 314 
 315     }
 316 
 317     /**
 318      * readObject is called to restore the state of the {@code SerialStruct} from
 319      * a stream.
 320      */
 321     private void readObject(ObjectInputStream s)
 322             throws IOException, ClassNotFoundException {
 323 
 324        ObjectInputStream.GetField fields = s.readFields();
 325        Object[] tmp = (Object[])fields.get("attribs", null);
 326        attribs = tmp == null ? null : tmp.clone();
 327        SQLTypeName = (String)fields.get("SQLTypeName", null);
 328     }
 329 
 330     /**
 331      * writeObject is called to save the state of the {@code SerialStruct}
 332      * to a stream.
 333      */
 334     private void writeObject(ObjectOutputStream s)
 335             throws IOException, ClassNotFoundException {
 336 
 337         ObjectOutputStream.PutField fields = s.putFields();
 338         fields.put("attribs", attribs);
 339         fields.put("SQLTypeName", SQLTypeName);
 340         s.writeFields();
 341     }
 342 
 343     /**
 344      * The identifier that assists in the serialization of this
 345      * <code>SerialStruct</code> object.
 346      */
 347     static final long serialVersionUID = -8322445504027483372L;
 348 }