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.class; break;
124 default: throw 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 true} if 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