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