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