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
26package java.io;
27
28import java.lang.reflect.Field;
29import sun.reflect.CallerSensitive;
30import sun.reflect.Reflection;
31import sun.reflect.misc.ReflectUtil;
32import 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 */
43public 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     */
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     */
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     */
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     */
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     */
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
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?
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?
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?
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?
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?
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     */
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?
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     */
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     */
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     */
281    String getSignature() {
282        return signature;
283    }
284
285    /**
286     * Returns JVM type signature for given class.
287     */
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