1 /*
2  * Copyright (c) 1996, 2015, 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 java.io;
27
28 import java.lang.reflect.Field;
29 import jdk.internal.reflect.CallerSensitive;
30 import jdk.internal.reflect.Reflection;
31 import sun.reflect.misc.ReflectUtil;
32
33 /**
34  * A description of a Serializable field from a Serializable class.  An array
35  * of ObjectStreamFields is used to declare the Serializable fields of a class.
36  *
37  * @author      Mike Warres
38  * @author      Roger Riggs
39  * @see ObjectStreamClass
40  * @since 1.2
41  */

42 public class ObjectStreamField
43     implements Comparable<Object>
44 {
45
46     /** field name */
47     private final String name;
48     /** canonical JVM signature of field type, if given */
49     private final String signature;
50     /** field type (Object.class if unknown non-primitive type) */
51     private final Class<?> type;
52     /** lazily constructed signature for the type, if no explicit signature */
53     private String typeSignature;
54     /** whether or not to (de)serialize field values as unshared */
55     private final boolean unshared;
56     /** corresponding reflective field object, if any */
57     private final Field field;
58     /** offset of field value in enclosing field group */
59     private int offset;
60
61     /**
62      * Create a Serializable field with the specified type.  This field should
63      * be documented with a <code>serialField</code> tag.
64      *
65      * @param   name the name of the serializable field
66      * @param   type the <code>Class</code> object of the serializable field
67      */

68     public ObjectStreamField(String name, Class<?> type) {
69         this(name, type, false);
70     }
71
72     /**
73      * Creates an ObjectStreamField representing a serializable field with the
74      * given name and type.  If unshared is false, values of the represented
75      * field are serialized and deserialized in the default manner--if the
76      * field is non-primitive, object values are serialized and deserialized as
77      * if they had been written and read by calls to writeObject and
78      * readObject.  If unshared is true, values of the represented field are
79      * serialized and deserialized as if they had been written and read by
80      * calls to writeUnshared and readUnshared.
81      *
82      * @param   name field name
83      * @param   type field type
84      * @param   unshared if false, write/read field values in the same manner
85      *          as writeObject/readObject; if true, write/read in the same
86      *          manner as writeUnshared/readUnshared
87      * @since   1.4
88      */

89     public ObjectStreamField(String name, Class<?> type, boolean unshared) {
90         if (name == null) {
91             throw new NullPointerException();
92         }
93         this.name = name;
94         this.type = type;
95         this.unshared = unshared;
96         this.field = null;
97         this.signature = null;
98     }
99
100     /**
101      * Creates an ObjectStreamField representing a field with the given name,
102      * signature and unshared setting.
103      */

104     ObjectStreamField(String name, String signature, boolean unshared) {
105         if (name == null) {
106             throw new NullPointerException();
107         }
108         this.name = name;
109         this.signature = signature.intern();
110         this.unshared = unshared;
111         this.field = null;
112
113         switch (signature.charAt(0)) {
114             case 'Z': type = Boolean.TYPE; break;
115             case 'B': type = Byte.TYPE; break;
116             case 'C': type = Character.TYPE; break;
117             case 'S': type = Short.TYPE; break;
118             case 'I': type = Integer.TYPE; break;
119             case 'J': type = Long.TYPE; break;
120             case 'F': type = Float.TYPE; break;
121             case 'D': type = Double.TYPE; break;
122             case 'L':
123             case '[': type = Object.classbreak;
124             defaultthrow new IllegalArgumentException("illegal signature");
125         }
126     }
127
128     /**
129      * Returns JVM type signature for given primitive.
130      */

131     private static String getPrimitiveSignature(Class<?> cl) {
132         if (cl == Integer.TYPE)
133             return "I";
134         else if (cl == Byte.TYPE)
135             return "B";
136         else if (cl == Long.TYPE)
137             return "J";
138         else if (cl == Float.TYPE)
139             return "F";
140         else if (cl == Double.TYPE)
141             return "D";
142         else if (cl == Short.TYPE)
143             return "S";
144         else if (cl == Character.TYPE)
145             return "C";
146         else if (cl == Boolean.TYPE)
147             return "Z";
148         else if (cl == Void.TYPE)
149             return "V";
150         else
151             throw new InternalError();
152     }
153
154     /**
155      * Returns JVM type signature for given class.
156      */

157     static String getClassSignature(Class<?> cl) {
158         if (cl.isPrimitive()) {
159             return getPrimitiveSignature(cl);
160         } else {
161             return appendClassSignature(new StringBuilder(), cl).toString();
162         }
163     }
164
165     static StringBuilder appendClassSignature(StringBuilder sbuf, Class<?> cl) {
166         while (cl.isArray()) {
167             sbuf.append('[');
168             cl = cl.getComponentType();
169         }
170
171         if (cl.isPrimitive()) {
172             sbuf.append(getPrimitiveSignature(cl));
173         } else {
174             sbuf.append('L').append(cl.getName().replace('.', '/')).append(';');
175         }
176
177         return sbuf;
178     }
179
180     /**
181      * Creates an ObjectStreamField representing the given field with the
182      * specified unshared setting.  For compatibility with the behavior of
183      * earlier serialization implementations, a "showType" parameter is
184      * necessary to govern whether or not a getType() call on this
185      * ObjectStreamField (if non-primitive) will return Object.class (as
186      * opposed to a more specific reference type).
187      */

188     ObjectStreamField(Field field, boolean unshared, boolean showType) {
189         this.field = field;
190         this.unshared = unshared;
191         name = field.getName();
192         Class<?> ftype = field.getType();
193         type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
194         signature = getClassSignature(ftype).intern();
195     }
196
197     /**
198      * Get the name of this field.
199      *
200      * @return  a <code>String</code> representing the name of the serializable
201      *          field
202      */

203     public String getName() {
204         return name;
205     }
206
207     /**
208      * Get the type of the field.  If the type is non-primitive and this
209      * <code>ObjectStreamField</code> was obtained from a deserialized {@link
210      * ObjectStreamClass} instance, then <code>Object.class</code> is returned.
211      * Otherwise, the <code>Class</code> object for the type of the field is
212      * returned.
213      *
214      * @return  a <code>Class</code> object representing the type of the
215      *          serializable field
216      */

217     @CallerSensitive
218     public Class<?> getType() {
219         if (System.getSecurityManager() != null) {
220             Class<?> caller = Reflection.getCallerClass();
221             if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), type.getClassLoader())) {
222                 ReflectUtil.checkPackageAccess(type);
223             }
224         }
225         return type;
226     }
227
228     /**
229      * Returns character encoding of field type.  The encoding is as follows:
230      * <blockquote><pre>
231      * B            byte
232      * C            char
233      * D            double
234      * F            float
235      * I            int
236      * J            long
237      * L            class or interface
238      * S            short
239      * Z            boolean
240      * [            array
241      * </pre></blockquote>
242      *
243      * @return  the typecode of the serializable field
244      */

245     // REMIND: deprecate?
246     public char getTypeCode() {
247         return getSignature().charAt(0);
248     }
249
250     /**
251      * Return the JVM type signature.
252      *
253      * @return  null if this field has a primitive type.
254      */

255     // REMIND: deprecate?
256     public String getTypeString() {
257         return isPrimitive() ? null : getSignature();
258     }
259
260     /**
261      * Offset of field within instance data.
262      *
263      * @return  the offset of this field
264      * @see #setOffset
265      */

266     // REMIND: deprecate?
267     public int getOffset() {
268         return offset;
269     }
270
271     /**
272      * Offset within instance data.
273      *
274      * @param   offset the offset of the field
275      * @see #getOffset
276      */

277     // REMIND: deprecate?
278     protected void setOffset(int offset) {
279         this.offset = offset;
280     }
281
282     /**
283      * Return true if this field has a primitive type.
284      *
285      * @return  true if and only if this field corresponds to a primitive type
286      */

287     // REMIND: deprecate?
288     public boolean isPrimitive() {
289         char tcode = getTypeCode();
290         return ((tcode != 'L') && (tcode != '['));
291     }
292
293     /**
294      * Returns boolean value indicating whether or not the serializable field
295      * represented by this ObjectStreamField instance is unshared.
296      *
297      * @return {@code trueif this field is unshared
298      *
299      * @since 1.4
300      */

301     public boolean isUnshared() {
302         return unshared;
303     }
304
305     /**
306      * Compare this field with another <code>ObjectStreamField</code>.  Return
307      * -1 if this is smaller, 0 if equal, 1 if greater.  Types that are
308      * primitives are "smaller" than object types.  If equal, the field names
309      * are compared.
310      */

311     // REMIND: deprecate?
312     public int compareTo(Object obj) {
313         ObjectStreamField other = (ObjectStreamField) obj;
314         boolean isPrim = isPrimitive();
315         if (isPrim != other.isPrimitive()) {
316             return isPrim ? -1 : 1;
317         }
318         return name.compareTo(other.name);
319     }
320
321     /**
322      * Return a string that describes this field.
323      */

324     public String toString() {
325         return getSignature() + ' ' + name;
326     }
327
328     /**
329      * Returns field represented by this ObjectStreamField, or null if
330      * ObjectStreamField is not associated with an actual field.
331      */

332     Field getField() {
333         return field;
334     }
335
336     /**
337      * Returns JVM type signature of field (similar to getTypeString, except
338      * that signature strings are returned for primitive fields as well).
339      */

340     String getSignature() {
341         if (signature != null) {
342             return signature;
343         }
344
345         String sig = typeSignature;
346         // This lazy calculation is safe since signature can be null iff one
347         // of the public constructors are used, in which case type is always
348         // initialized to the exact type we want the signature to represent.
349         if (sig == null) {
350             typeSignature = sig = getClassSignature(type).intern();
351         }
352         return sig;
353     }
354 }
355