1 /*
2  * Copyright (c) 1996, 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 java.io;
27 
28 import java.lang.reflect.Field;
29 import sun.reflect.CallerSensitive;
30 import sun.reflect.Reflection;
31 import sun.reflect.misc.ReflectUtil;
32 import dalvik.system.VMStack;
33 
34 /**
35  * A description of a Serializable field from a Serializable class.  An array
36  * of ObjectStreamFields is used to declare the Serializable fields of a class.
37  *
38  * @author      Mike Warres
39  * @author      Roger Riggs
40  * @see ObjectStreamClass
41  * @since 1.2
42  */
43 public class ObjectStreamField
44     implements Comparable<Object>
45 {
46 
47     /** field name */
48     private final String name;
49     /** canonical JVM signature of field type */
50     private final String signature;
51     /** field type (Object.class if unknown non-primitive type) */
52     private final Class<?> type;
53     /** whether or not to (de)serialize field values as unshared */
54     private final boolean unshared;
55     /** corresponding reflective field object, if any */
56     private final Field field;
57     /** offset of field value in enclosing field group */
58     private int offset = 0;
59 
60     /**
61      * Create a Serializable field with the specified type.  This field should
62      * be documented with a <code>serialField</code> tag.
63      *
64      * @param   name the name of the serializable field
65      * @param   type the <code>Class</code> object of the serializable field
66      */
ObjectStreamField(String name, Class<?> type)67     public ObjectStreamField(String name, Class<?> type) {
68         this(name, type, false);
69     }
70 
71     /**
72      * Creates an ObjectStreamField representing a serializable field with the
73      * given name and type.  If unshared is false, values of the represented
74      * field are serialized and deserialized in the default manner--if the
75      * field is non-primitive, object values are serialized and deserialized as
76      * if they had been written and read by calls to writeObject and
77      * readObject.  If unshared is true, values of the represented field are
78      * serialized and deserialized as if they had been written and read by
79      * calls to writeUnshared and readUnshared.
80      *
81      * @param   name field name
82      * @param   type field type
83      * @param   unshared if false, write/read field values in the same manner
84      *          as writeObject/readObject; if true, write/read in the same
85      *          manner as writeUnshared/readUnshared
86      * @since   1.4
87      */
ObjectStreamField(String name, Class<?> type, boolean unshared)88     public ObjectStreamField(String name, Class<?> type, boolean unshared) {
89         if (name == null) {
90             throw new NullPointerException();
91         }
92         this.name = name;
93         this.type = type;
94         this.unshared = unshared;
95         signature = getClassSignature(type).intern();
96         field = null;
97     }
98 
99     /**
100      * Creates an ObjectStreamField representing a field with the given name,
101      * signature and unshared setting.
102      */
ObjectStreamField(String name, String signature, boolean unshared)103     ObjectStreamField(String name, String signature, boolean unshared) {
104         if (name == null) {
105             throw new NullPointerException();
106         }
107         this.name = name;
108         this.signature = signature.intern();
109         this.unshared = unshared;
110         field = null;
111 
112         switch (signature.charAt(0)) {
113             case 'Z': type = Boolean.TYPE; break;
114             case 'B': type = Byte.TYPE; break;
115             case 'C': type = Character.TYPE; break;
116             case 'S': type = Short.TYPE; break;
117             case 'I': type = Integer.TYPE; break;
118             case 'J': type = Long.TYPE; break;
119             case 'F': type = Float.TYPE; break;
120             case 'D': type = Double.TYPE; break;
121             case 'L':
122             case '[': type = Object.class; break;
123             default: throw new IllegalArgumentException("illegal signature");
124         }
125     }
126 
127     /**
128      * Creates an ObjectStreamField representing the given field with the
129      * specified unshared setting.  For compatibility with the behavior of
130      * earlier serialization implementations, a "showType" parameter is
131      * necessary to govern whether or not a getType() call on this
132      * ObjectStreamField (if non-primitive) will return Object.class (as
133      * opposed to a more specific reference type).
134      */
ObjectStreamField(Field field, boolean unshared, boolean showType)135     ObjectStreamField(Field field, boolean unshared, boolean showType) {
136         this.field = field;
137         this.unshared = unshared;
138         name = field.getName();
139         Class<?> ftype = field.getType();
140         type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
141         signature = getClassSignature(ftype).intern();
142     }
143 
144     /**
145      * Get the name of this field.
146      *
147      * @return  a <code>String</code> representing the name of the serializable
148      *          field
149      */
getName()150     public String getName() {
151         return name;
152     }
153 
154     /**
155      * Get the type of the field.  If the type is non-primitive and this
156      * <code>ObjectStreamField</code> was obtained from a deserialized {@link
157      * ObjectStreamClass} instance, then <code>Object.class</code> is returned.
158      * Otherwise, the <code>Class</code> object for the type of the field is
159      * returned.
160      *
161      * @return  a <code>Class</code> object representing the type of the
162      *          serializable field
163      */
164     @CallerSensitive
getType()165     public Class<?> getType() {
166         return type;
167     }
168 
169     /**
170      * Returns character encoding of field type.  The encoding is as follows:
171      * <blockquote><pre>
172      * B            byte
173      * C            char
174      * D            double
175      * F            float
176      * I            int
177      * J            long
178      * L            class or interface
179      * S            short
180      * Z            boolean
181      * [            array
182      * </pre></blockquote>
183      *
184      * @return  the typecode of the serializable field
185      */
186     // REMIND: deprecate?
getTypeCode()187     public char getTypeCode() {
188         return signature.charAt(0);
189     }
190 
191     /**
192      * Return the JVM type signature.
193      *
194      * @return  null if this field has a primitive type.
195      */
196     // REMIND: deprecate?
getTypeString()197     public String getTypeString() {
198         return isPrimitive() ? null : signature;
199     }
200 
201     /**
202      * Offset of field within instance data.
203      *
204      * @return  the offset of this field
205      * @see #setOffset
206      */
207     // REMIND: deprecate?
getOffset()208     public int getOffset() {
209         return offset;
210     }
211 
212     /**
213      * Offset within instance data.
214      *
215      * @param   offset the offset of the field
216      * @see #getOffset
217      */
218     // REMIND: deprecate?
setOffset(int offset)219     protected void setOffset(int offset) {
220         this.offset = offset;
221     }
222 
223     /**
224      * Return true if this field has a primitive type.
225      *
226      * @return  true if and only if this field corresponds to a primitive type
227      */
228     // REMIND: deprecate?
isPrimitive()229     public boolean isPrimitive() {
230         char tcode = signature.charAt(0);
231         return ((tcode != 'L') && (tcode != '['));
232     }
233 
234     /**
235      * Returns boolean value indicating whether or not the serializable field
236      * represented by this ObjectStreamField instance is unshared.
237      *
238      * @return {@code true} if this field is unshared
239      *
240      * @since 1.4
241      */
isUnshared()242     public boolean isUnshared() {
243         return unshared;
244     }
245 
246     /**
247      * Compare this field with another <code>ObjectStreamField</code>.  Return
248      * -1 if this is smaller, 0 if equal, 1 if greater.  Types that are
249      * primitives are "smaller" than object types.  If equal, the field names
250      * are compared.
251      */
252     // REMIND: deprecate?
compareTo(Object obj)253     public int compareTo(Object obj) {
254         ObjectStreamField other = (ObjectStreamField) obj;
255         boolean isPrim = isPrimitive();
256         if (isPrim != other.isPrimitive()) {
257             return isPrim ? -1 : 1;
258         }
259         return name.compareTo(other.name);
260     }
261 
262     /**
263      * Return a string that describes this field.
264      */
toString()265     public String toString() {
266         return signature + ' ' + name;
267     }
268 
269     /**
270      * Returns field represented by this ObjectStreamField, or null if
271      * ObjectStreamField is not associated with an actual field.
272      */
getField()273     Field getField() {
274         return field;
275     }
276 
277     /**
278      * Returns JVM type signature of field (similar to getTypeString, except
279      * that signature strings are returned for primitive fields as well).
280      */
getSignature()281     String getSignature() {
282         return signature;
283     }
284 
285     /**
286      * Returns JVM type signature for given class.
287      */
getClassSignature(Class<?> cl)288     private static String getClassSignature(Class<?> cl) {
289         StringBuilder sbuf = new StringBuilder();
290         while (cl.isArray()) {
291             sbuf.append('[');
292             cl = cl.getComponentType();
293         }
294         if (cl.isPrimitive()) {
295             if (cl == Integer.TYPE) {
296                 sbuf.append('I');
297             } else if (cl == Byte.TYPE) {
298                 sbuf.append('B');
299             } else if (cl == Long.TYPE) {
300                 sbuf.append('J');
301             } else if (cl == Float.TYPE) {
302                 sbuf.append('F');
303             } else if (cl == Double.TYPE) {
304                 sbuf.append('D');
305             } else if (cl == Short.TYPE) {
306                 sbuf.append('S');
307             } else if (cl == Character.TYPE) {
308                 sbuf.append('C');
309             } else if (cl == Boolean.TYPE) {
310                 sbuf.append('Z');
311             } else if (cl == Void.TYPE) {
312                 sbuf.append('V');
313             } else {
314                 throw new InternalError();
315             }
316         } else {
317             sbuf.append('L' + cl.getName().replace('.', '/') + ';');
318         }
319         return sbuf.toString();
320     }
321 }
322