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