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