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