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 18package java.io; 19 20import java.lang.ref.WeakReference; 21import java.util.Arrays; 22import 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 * @since Android 1.0 33 */ 34public class ObjectStreamField implements Comparable<Object> { 35 36 // Declared name of the field 37 private String name; 38 39 // Declared type of the field 40 private Object type; 41 42 // offset of this field in the object 43 int offset; 44 45 // Cached version of intern'ed type String 46 private String typeString; 47 48 private boolean unshared; 49 50 private boolean isDeserialized; 51 52 /** 53 * Constructs an ObjectStreamField with the specified name and type. 54 * 55 * @param name 56 * the name of the field. 57 * @param cl 58 * the type of the field. 59 * @throws NullPointerException 60 * if {@code name} or {@code cl} is {@code null}. 61 * @since Android 1.0 62 */ 63 public ObjectStreamField(String name, Class<?> cl) { 64 if (name == null || cl == null) { 65 throw new NullPointerException(); 66 } 67 this.name = name; 68 this.type = new WeakReference<Class<?>>(cl); 69 } 70 71 /** 72 * Constructs an ObjectStreamField with the specified name, type and the 73 * indication if it is unshared. 74 * 75 * @param name 76 * the name of the field. 77 * @param cl 78 * the type of the field. 79 * @param unshared 80 * {@code true} if the field is written and read unshared; 81 * {@code false} otherwise. 82 * @throws NullPointerException 83 * if {@code name} or {@code cl} is {@code null}. 84 * @see ObjectOutputStream#writeUnshared(Object) 85 * @since Android 1.0 86 */ 87 public ObjectStreamField(String name, Class<?> cl, boolean unshared) { 88 if (name == null || cl == null) { 89 throw new NullPointerException(); 90 } 91 this.name = name; 92 this.type = (cl.getClassLoader() == null) ? cl 93 : new WeakReference<Class<?>>(cl); 94 this.unshared = unshared; 95 } 96 97 /** 98 * Constructs an ObjectStreamField with the given name and the given type. 99 * The type may be null. 100 * 101 * @param signature 102 * A String representing the type of the field 103 * @param name 104 * a String, the name of the field, or null 105 */ 106 ObjectStreamField(String signature, String name) { 107 if (name == null) { 108 throw new NullPointerException(); 109 } 110 this.name = name; 111 this.typeString = signature.replace('.', '/').intern(); 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 * @since Android 1.0 127 */ 128 public int compareTo(Object o) { 129 ObjectStreamField f = (ObjectStreamField) o; 130 boolean thisPrimitive = this.isPrimitive(); 131 boolean fPrimitive = f.isPrimitive(); 132 133 // If one is primitive and the other isn't, we have enough info to 134 // compare 135 if (thisPrimitive != fPrimitive) { 136 return thisPrimitive ? -1 : 1; 137 } 138 139 // Either both primitives or both not primitives. Compare based on name. 140 return this.getName().compareTo(f.getName()); 141 } 142 143 // BEGIN android-removed 144 // There shouldn't be an implementation of these methods. 145 // /** 146 // * Indicates if this field descriptor is equal to {@code arg0}. Field 147 // * descriptors are equal if their name is equal. 148 // * 149 // * @param arg0 150 // * the object to check equality with. 151 // * @return {@code true} if the name of this field descriptor is equal to the 152 // * name of {@code arg0}, {@code false} otherwise. 153 // * @since Android 1.0 154 // */ 155 // @Override 156 // public boolean equals(Object arg0) { 157 // // BEGIN android-changed 158 // // copied from newer harmony version 159 // return (arg0 instanceof ObjectStreamField) && compareTo(arg0) == 0; 160 // // END android-changed 161 // } 162 // 163 // /** 164 // * Returns a hash code for this field descriptor. The hash code of this 165 // * field's name is returned. 166 // * 167 // * @return the field's hash code. 168 // * @since Android 1.0 169 // */ 170 // @Override 171 // public int hashCode() { 172 // return getName().hashCode(); 173 // } 174 // END android-removed 175 176 /** 177 * Gets the name of this field. 178 * 179 * @return the field's name. 180 * @since Android 1.0 181 */ 182 public String getName() { 183 return name; 184 } 185 186 /** 187 * Gets the offset of this field in the object. 188 * 189 * @return this field's offset. 190 * @since Android 1.0 191 */ 192 public int getOffset() { 193 return offset; 194 } 195 196 /** 197 * Return the type of the field the receiver represents, this is an internal 198 * method 199 * 200 * @return A Class object representing the type of the field 201 */ 202 // BEGIN android-note 203 // Changed from private to default visibility for usage in ObjectStreamClass 204 // END android-note 205 /* package */ Class<?> getTypeInternal() { 206 if (type instanceof WeakReference) { 207 return (Class<?>) ((WeakReference<?>) type).get(); 208 } 209 return (Class<?>) type; 210 } 211 212 /** 213 * Gets the type of this field. 214 * 215 * @return a {@code Class} object representing the type of the field. 216 * @since Android 1.0 217 */ 218 public Class<?> getType() { 219 Class<?> cl = getTypeInternal(); 220 if (isDeserialized && !cl.isPrimitive()) { 221 return Object.class; 222 } 223 return cl; 224 } 225 226 /** 227 * Gets a character code for the type of this field. The following codes are 228 * used: 229 * 230 * <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> 242 * 243 * @return the field's type code. 244 * @since Android 1.0 245 */ 246 public char getTypeCode() { 247 Class<?> t = getTypeInternal(); 248 if (t == Integer.TYPE) { 249 return 'I'; 250 } 251 if (t == Byte.TYPE) { 252 return 'B'; 253 } 254 if (t == Character.TYPE) { 255 return 'C'; 256 } 257 if (t == Short.TYPE) { 258 return 'S'; 259 } 260 if (t == Boolean.TYPE) { 261 return 'Z'; 262 } 263 if (t == Long.TYPE) { 264 return 'J'; 265 } 266 if (t == Float.TYPE) { 267 return 'F'; 268 } 269 if (t == Double.TYPE) { 270 return 'D'; 271 } 272 if (t.isArray()) { 273 return '['; 274 } 275 return 'L'; 276 } 277 278 /** 279 * Gets the type signature used by the VM to represent the type of this 280 * field. 281 * 282 * @return the signature of this field's class or {@code null} if this 283 * field's type is primitive. 284 * @since Android 1.0 285 */ 286 public String getTypeString() { 287 if (isPrimitive()) { 288 return null; 289 } 290 if (typeString == null) { 291 Class<?> t = getTypeInternal(); 292 String typeName = t.getName().replace('.', '/'); 293 String str = (t.isArray()) ? typeName : ("L" + typeName + ';'); //$NON-NLS-1$ 294 typeString = str.intern(); 295 } 296 return typeString; 297 } 298 299 /** 300 * Indicates whether this field's type is a primitive type. 301 * 302 * @return {@code true} if this field's type is primitive; {@code false} if 303 * the type of this field is a regular class. 304 * @since Android 1.0 305 */ 306 public boolean isPrimitive() { 307 Class<?> t = getTypeInternal(); 308 return t != null && t.isPrimitive(); 309 } 310 311 /** 312 * Sets this field's offset in the object. 313 * 314 * @param newValue 315 * the field's new offset. 316 * @since Android 1.0 317 */ 318 protected void setOffset(int newValue) { 319 this.offset = newValue; 320 } 321 322 /** 323 * Returns a string containing a concise, human-readable description of this 324 * field descriptor. 325 * 326 * @return a printable representation of this descriptor. 327 * @since Android 1.0 328 */ 329 @Override 330 public String toString() { 331 return this.getClass().getName() + '(' + getName() + ':' 332 + getTypeInternal() + ')'; 333 } 334 335 /** 336 * Sorts the fields for dumping. Primitive types come first, then regular 337 * types. 338 * 339 * @param fields 340 * ObjectStreamField[] fields to be sorted 341 */ 342 static void sortFields(ObjectStreamField[] fields) { 343 // Sort if necessary 344 if (fields.length > 1) { 345 Comparator<ObjectStreamField> fieldDescComparator = new Comparator<ObjectStreamField>() { 346 public int compare(ObjectStreamField f1, ObjectStreamField f2) { 347 return f1.compareTo(f2); 348 } 349 }; 350 Arrays.sort(fields, fieldDescComparator); 351 } 352 } 353 354 void resolve(ClassLoader loader) { 355 if (typeString.length() == 1) { 356 switch (typeString.charAt(0)) { 357 case 'I': 358 type = Integer.TYPE; 359 return; 360 case 'B': 361 type = Byte.TYPE; 362 return; 363 case 'C': 364 type = Character.TYPE; 365 return; 366 case 'S': 367 type = Short.TYPE; 368 return; 369 case 'Z': 370 type = Boolean.TYPE; 371 return; 372 case 'J': 373 type = Long.TYPE; 374 return; 375 case 'F': 376 type = Float.TYPE; 377 return; 378 case 'D': 379 type = Double.TYPE; 380 return; 381 } 382 } 383 String className = typeString.replace('/', '.'); 384 if (className.charAt(0) == 'L') { 385 // remove L and ; 386 className = className.substring(1, className.length() - 1); 387 } 388 try { 389 Class<?> cl = Class.forName(className, false, loader); 390 type = (cl.getClassLoader() == null) ? cl 391 : new WeakReference<Class<?>>(cl); 392 } catch (ClassNotFoundException e) { 393 // Ignored 394 } 395 } 396 397 /** 398 * Indicats whether this field is unshared. 399 * 400 * @return {@code true} if this field is unshared, {@code false} otherwise. 401 * @since Android 1.0 402 */ 403 public boolean isUnshared() { 404 return unshared; 405 } 406 407 void setUnshared(boolean unshared) { 408 this.unshared = unshared; 409 } 410} 411