1 /* 2 * Copyright (c) 1996, 2019, 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 */ ObjectStreamField(String name, Class<?> type)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 */ ObjectStreamField(String name, Class<?> type, boolean unshared)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 */ ObjectStreamField(String name, String signature, boolean unshared)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 */ getPrimitiveSignature(Class<?> cl)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 */ getClassSignature(Class<?> cl)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 appendClassSignature(StringBuilder sbuf, Class<?> cl)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 */ ObjectStreamField(Field field, boolean unshared, boolean showType)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} representing the name of the serializable 201 * field 202 */ getName()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} was obtained from a deserialized {@link 210 * ObjectStreamClass} instance, then {@code Object.class} is returned. 211 * Otherwise, the {@code Class} object for the type of the field is 212 * returned. 213 * 214 * @return a {@code Class} object representing the type of the 215 * serializable field 216 */ 217 @CallerSensitive getType()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? getTypeCode()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? getTypeString()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? getOffset()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? setOffset(int offset)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? isPrimitive()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 */ isUnshared()301 public boolean isUnshared() { 302 return unshared; 303 } 304 305 /** 306 * Compare this field with another {@code ObjectStreamField}. 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? compareTo(Object obj)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 */ toString()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 */ getField()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 */ getSignature()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