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 }