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
58 /**
59 * Create a Serializable field with the specified type. This field should
60 * be documented with a {@code serialField} tag.
61 *
62 * @param name the name of the serializable field
63 * @param type the {@code Class} object of the serializable field
64 */
65 public ObjectStreamField(String name, Class<?> type) {
66 this(name, type, false);
67 }
68
69 /**
70 * Creates an ObjectStreamField representing a serializable field with the
71 * given name and type. If unshared is false, values of the represented
72 * field are serialized and deserialized in the default manner--if the
73 * field is non-primitive, object values are serialized and deserialized as
74 * if they had been written and read by calls to writeObject and
75 * readObject. If unshared is true, values of the represented field are
76 * serialized and deserialized as if they had been written and read by
77 * calls to writeUnshared and readUnshared.
78 *
79 * @param name field name
80 * @param type field type
81 * @param unshared if false, write/read field values in the same manner
82 * as writeObject/readObject; if true, write/read in the same
83 * manner as writeUnshared/readUnshared
84 * @since 1.4
85 */
86 public ObjectStreamField(String name, Class<?> type, boolean unshared) {
87 if (name == null) {
88 throw new NullPointerException();
89 }
90 this.name = name;
91 this.type = type;
92 this.unshared = unshared;
93 this.field = null;
94 this.signature = null;
95 }
96
97 /**
98 * Creates an ObjectStreamField representing a field with the given name,
99 * signature and unshared setting.
100 */
101 ObjectStreamField(String name, String signature, boolean unshared) {
102 if (name == null) {
103 throw new NullPointerException();
104 }
105 this.name = name;
106 this.signature = signature.intern();
107 this.unshared = unshared;
108 this.field = null;
109
110 type = switch (signature.charAt(0)) {
111 case 'Z' -> Boolean.TYPE;
112 case 'B' -> Byte.TYPE;
113 case 'C' -> Character.TYPE;
114 case 'S' -> Short.TYPE;
115 case 'I' -> Integer.TYPE;
116 case 'J' -> Long.TYPE;
117 case 'F' -> Float.TYPE;
118 case 'D' -> Double.TYPE;
119 case 'L', '[' -> Object.class;
120 default -> throw new IllegalArgumentException("illegal signature");
121 };
122 }
123
124 /**
125 * Creates an ObjectStreamField representing the given field with the
126 * specified unshared setting. For compatibility with the behavior of
127 * earlier serialization implementations, a "showType" parameter is
128 * necessary to govern whether or not a getType() call on this
129 * ObjectStreamField (if non-primitive) will return Object.class (as
130 * opposed to a more specific reference type).
131 */
132 ObjectStreamField(Field field, boolean unshared, boolean showType) {
133 this.field = field;
134 this.unshared = unshared;
135 name = field.getName();
136 Class<?> ftype = field.getType();
137 type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
138 signature = ftype.descriptorString().intern();
139 }
140
141 /**
142 * Get the name of this field.
143 *
144 * @return a {@code String} representing the name of the serializable
145 * field
146 */
147 public String getName() {
148 return name;
149 }
150
151 /**
152 * Get the type of the field. If the type is non-primitive and this
153 * {@code ObjectStreamField} was obtained from a deserialized {@link
154 * ObjectStreamClass} instance, then {@code Object.class} is returned.
155 * Otherwise, the {@code Class} object for the type of the field is
156 * returned.
157 *
158 * @return a {@code Class} object representing the type of the
159 * serializable field
160 */
161 public Class<?> getType() {
162 return type;
163 }
164
165 /**
166 * Returns character encoding of field type. The encoding is as follows:
167 * <blockquote><pre>
168 * B byte
169 * C char
170 * D double
171 * F float
172 * I int
173 * J long
174 * L class or interface
175 * S short
176 * Z boolean
177 * [ array
178 * </pre></blockquote>
179 *
180 * @return the typecode of the serializable field
181 */
182 // REMIND: deprecate?
183 public char getTypeCode() {
184 return getSignature().charAt(0);
185 }
186
187 /**
188 * Return the JVM type signature.
189 *
190 * @return null if this field has a primitive type.
191 */
192 // REMIND: deprecate?
193 public String getTypeString() {
194 return isPrimitive() ? null : getSignature();
195 }
196
197 /**
198 * Offset of field within instance data.
199 *
200 * @return the offset of this field
201 * @see #setOffset
202 */
203 // REMIND: deprecate?
204 public int getOffset() {
205 return offset;
206 }
207
208 /**
209 * Offset within instance data.
210 *
211 * @param offset the offset of the field
212 * @see #getOffset
213 */
214 // REMIND: deprecate?
215 protected void setOffset(int offset) {
216 this.offset = offset;
217 }
218
219 /**
220 * Return true if this field has a primitive type.
221 *
222 * @return true if and only if this field corresponds to a primitive type
223 */
224 // REMIND: deprecate?
225 public boolean isPrimitive() {
226 char tcode = getTypeCode();
227 return ((tcode != 'L') && (tcode != '['));
228 }
229
230 /**
231 * Returns boolean value indicating whether or not the serializable field
232 * represented by this ObjectStreamField instance is unshared.
233 *
234 * @return {@code true} if this field is unshared
235 *
236 * @since 1.4
237 */
238 public boolean isUnshared() {
239 return unshared;
240 }
241
242 /**
243 * Compare this field with another {@code ObjectStreamField}. Return
244 * -1 if this is smaller, 0 if equal, 1 if greater. Types that are
245 * primitives are "smaller" than object types. If equal, the field names
246 * are compared.
247 */
248 // REMIND: deprecate?
249 public int compareTo(Object obj) {
250 ObjectStreamField other = (ObjectStreamField) obj;
251 boolean isPrim = isPrimitive();
252 if (isPrim != other.isPrimitive()) {
253 return isPrim ? -1 : 1;
254 }
255 return name.compareTo(other.name);
256 }
257
258 /**
259 * Return a string that describes this field.
260 */
261 public String toString() {
262 return getSignature() + ' ' + name;
263 }
264
265 /**
266 * Returns field represented by this ObjectStreamField, or null if
267 * ObjectStreamField is not associated with an actual field.
268 */
269 Field getField() {
270 return field;
271 }
272
273 /**
274 * Returns JVM type signature of field (similar to getTypeString, except
275 * that signature strings are returned for primitive fields as well).
276 */
277 String getSignature() {
278 if (signature != null) {
279 return signature;
280 }
281
282 String sig = typeSignature;
283 // This lazy calculation is safe since signature can be null iff one
284 // of the public constructors are used, in which case type is always
285 // initialized to the exact type we want the signature to represent.
286 if (sig == null) {
287 typeSignature = sig = type.descriptorString().intern();
288 }
289 return sig;
290 }
291 }