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