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