1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.io; 19 20 import java.lang.ref.WeakReference; 21 import java.util.Arrays; 22 import java.util.Comparator; 23 24 /** 25 * Describes a field for the purpose of serialization. Classes can define the 26 * collection of fields that are serialized, which may be different from the set 27 * of all declared fields. 28 * 29 * @see ObjectOutputStream#writeFields() 30 * @see ObjectInputStream#readFields() 31 */ 32 public class ObjectStreamField implements Comparable<Object> { 33 34 // Declared name of the field 35 private String name; 36 37 // Declared type of the field 38 private Object type; 39 40 // offset of this field in the object 41 int offset; 42 43 // Cached version of intern'ed type String 44 private String typeString; 45 46 private boolean unshared; 47 48 private boolean isDeserialized; 49 50 /** 51 * Constructs an ObjectStreamField with the specified name and type. 52 * 53 * @param name 54 * the name of the field. 55 * @param cl 56 * the type of the field. 57 * @throws NullPointerException 58 * if {@code name} or {@code cl} is {@code null}. 59 */ ObjectStreamField(String name, Class<?> cl)60 public ObjectStreamField(String name, Class<?> cl) { 61 if (name == null) { 62 throw new NullPointerException("name == null"); 63 } else if (cl == null) { 64 throw new NullPointerException("cl == null"); 65 } 66 this.name = name; 67 this.type = new WeakReference<Class<?>>(cl); 68 } 69 70 /** 71 * Constructs an ObjectStreamField with the specified name, type and the 72 * indication if it is unshared. 73 * 74 * @param name 75 * the name of the field. 76 * @param cl 77 * the type of the field. 78 * @param unshared 79 * {@code true} if the field is written and read unshared; 80 * {@code false} otherwise. 81 * @throws NullPointerException 82 * if {@code name} or {@code cl} is {@code null}. 83 * @see ObjectOutputStream#writeUnshared(Object) 84 */ ObjectStreamField(String name, Class<?> cl, boolean unshared)85 public ObjectStreamField(String name, Class<?> cl, boolean unshared) { 86 if (name == null) { 87 throw new NullPointerException("name == null"); 88 } else if (cl == null) { 89 throw new NullPointerException("cl == null"); 90 } 91 this.name = name; 92 this.type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl); 93 this.unshared = unshared; 94 } 95 96 /** 97 * Constructs an ObjectStreamField with the given name and the given type. 98 * The type may be null. 99 * 100 * @param signature 101 * A String representing the type of the field 102 * @param name 103 * a String, the name of the field, or null 104 */ ObjectStreamField(String signature, String name)105 ObjectStreamField(String signature, String name) { 106 if (name == null) { 107 throw new NullPointerException("name == null"); 108 } 109 this.name = name; 110 this.typeString = signature.replace('.', '/').intern(); 111 defaultResolve(); 112 this.isDeserialized = true; 113 } 114 115 /** 116 * Compares this field descriptor to the specified one. Checks first if one 117 * of the compared fields has a primitive type and the other one not. If so, 118 * the field with the primitive type is considered to be "smaller". If both 119 * fields are equal, their names are compared. 120 * 121 * @param o 122 * the object to compare with. 123 * @return -1 if this field is "smaller" than field {@code o}, 0 if both 124 * fields are equal; 1 if this field is "greater" than field {@code 125 * o}. 126 */ compareTo(Object o)127 public int compareTo(Object o) { 128 ObjectStreamField f = (ObjectStreamField) o; 129 boolean thisPrimitive = this.isPrimitive(); 130 boolean fPrimitive = f.isPrimitive(); 131 132 // If one is primitive and the other isn't, we have enough info to 133 // compare 134 if (thisPrimitive != fPrimitive) { 135 return thisPrimitive ? -1 : 1; 136 } 137 138 // Either both primitives or both not primitives. Compare based on name. 139 return this.getName().compareTo(f.getName()); 140 } 141 142 /** 143 * Gets the name of this field. 144 * 145 * @return the field's name. 146 */ getName()147 public String getName() { 148 return name; 149 } 150 151 /** 152 * Gets the offset of this field in the object. 153 * 154 * @return this field's offset. 155 */ getOffset()156 public int getOffset() { 157 return offset; 158 } 159 160 /** 161 * Return the type of the field the receiver represents, this is an internal 162 * method 163 * 164 * @return A Class object representing the type of the field 165 */ 166 // Changed from private to default visibility for usage in ObjectStreamClass getTypeInternal()167 /* package */ Class<?> getTypeInternal() { 168 if (type instanceof WeakReference) { 169 return (Class<?>) ((WeakReference<?>) type).get(); 170 } 171 return (Class<?>) type; 172 } 173 174 /** 175 * Gets the type of this field. 176 * 177 * @return a {@code Class} object representing the type of the field. 178 */ getType()179 public Class<?> getType() { 180 Class<?> cl = getTypeInternal(); 181 if (isDeserialized && !cl.isPrimitive()) { 182 return Object.class; 183 } 184 return cl; 185 } 186 187 /** 188 * Gets a character code for the type of this field. The following codes are 189 * used: 190 * 191 * <pre> 192 * B byte 193 * C char 194 * D double 195 * F float 196 * I int 197 * J long 198 * L class or interface 199 * S short 200 * Z boolean 201 * [ array 202 * </pre> 203 * 204 * @return the field's type code. 205 */ getTypeCode()206 public char getTypeCode() { 207 return typeCodeOf(getTypeInternal()); 208 } 209 typeCodeOf(Class<?> type)210 private char typeCodeOf(Class<?> type) { 211 if (type == int.class) { 212 return 'I'; 213 } else if (type == byte.class) { 214 return 'B'; 215 } else if (type == char.class) { 216 return 'C'; 217 } else if (type == short.class) { 218 return 'S'; 219 } else if (type == boolean.class) { 220 return 'Z'; 221 } else if (type == long.class) { 222 return 'J'; 223 } else if (type == float.class) { 224 return 'F'; 225 } else if (type == double.class) { 226 return 'D'; 227 } else if (type.isArray()) { 228 return '['; 229 } else { 230 return 'L'; 231 } 232 } 233 234 /** 235 * Gets the type signature used by the VM to represent the type of this 236 * field. 237 * 238 * @return the signature of this field's class or {@code null} if this 239 * field's type is primitive. 240 */ getTypeString()241 public String getTypeString() { 242 if (isPrimitive()) { 243 return null; 244 } 245 if (typeString == null) { 246 Class<?> t = getTypeInternal(); 247 String typeName = t.getName().replace('.', '/'); 248 String str = (t.isArray()) ? typeName : ("L" + typeName + ';'); 249 typeString = str.intern(); 250 } 251 return typeString; 252 } 253 254 /** 255 * Indicates whether this field's type is a primitive type. 256 * 257 * @return {@code true} if this field's type is primitive; {@code false} if 258 * the type of this field is a regular class. 259 */ isPrimitive()260 public boolean isPrimitive() { 261 Class<?> t = getTypeInternal(); 262 return t != null && t.isPrimitive(); 263 } 264 writeField(DataOutputStream out)265 boolean writeField(DataOutputStream out) throws IOException { 266 Class<?> t = getTypeInternal(); 267 out.writeByte(typeCodeOf(t)); 268 out.writeUTF(name); 269 return (t != null && t.isPrimitive()); 270 } 271 272 /** 273 * Sets this field's offset in the object. 274 * 275 * @param newValue 276 * the field's new offset. 277 */ setOffset(int newValue)278 protected void setOffset(int newValue) { 279 this.offset = newValue; 280 } 281 282 /** 283 * Returns a string containing a concise, human-readable description of this 284 * field descriptor. 285 * 286 * @return a printable representation of this descriptor. 287 */ 288 @Override toString()289 public String toString() { 290 return this.getClass().getName() + '(' + getName() + ':' + getTypeInternal() + ')'; 291 } 292 resolve(ClassLoader loader)293 void resolve(ClassLoader loader) { 294 if (typeString == null && isPrimitive()) { 295 // primitive type declared in a serializable class 296 typeString = String.valueOf(getTypeCode()); 297 } 298 299 if (typeString.length() == 1) { 300 if (defaultResolve()) { 301 return; 302 } 303 } 304 305 String className = typeString.replace('/', '.'); 306 if (className.charAt(0) == 'L') { 307 // remove L and ; 308 className = className.substring(1, className.length() - 1); 309 } 310 try { 311 Class<?> cl = Class.forName(className, false, loader); 312 type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl); 313 } catch (ClassNotFoundException e) { 314 // Ignored 315 } 316 } 317 318 /** 319 * Indicates whether this field is unshared. 320 * 321 * @return {@code true} if this field is unshared, {@code false} otherwise. 322 */ isUnshared()323 public boolean isUnshared() { 324 return unshared; 325 } 326 setUnshared(boolean unshared)327 void setUnshared(boolean unshared) { 328 this.unshared = unshared; 329 } 330 331 /** 332 * Resolves typeString into type. Returns true if the type is primitive 333 * and false otherwise. 334 */ defaultResolve()335 private boolean defaultResolve() { 336 switch (typeString.charAt(0)) { 337 case 'I': 338 type = int.class; 339 return true; 340 case 'B': 341 type = byte.class; 342 return true; 343 case 'C': 344 type = char.class; 345 return true; 346 case 'S': 347 type = short.class; 348 return true; 349 case 'Z': 350 type = boolean.class; 351 return true; 352 case 'J': 353 type = long.class; 354 return true; 355 case 'F': 356 type = float.class; 357 return true; 358 case 'D': 359 type = double.class; 360 return true; 361 default: 362 type = Object.class; 363 return false; 364 } 365 } 366 } 367