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 /** 21 * An EmulatedFields is an object that represents a set of emulated fields for 22 * an object being dumped or loaded. It allows objects to be dumped with a shape 23 * different than the fields they were declared to have. 24 * 25 * @see ObjectInputStream.GetField 26 * @see ObjectOutputStream.PutField 27 * @see EmulatedFieldsForLoading 28 * @see EmulatedFieldsForDumping 29 */ 30 class EmulatedFields { 31 32 // A slot is a field plus its value 33 static class ObjectSlot { 34 35 // Field descriptor 36 ObjectStreamField field; 37 38 // Actual value this emulated field holds 39 Object fieldValue; 40 41 // If this field has a default value (true) or something has been 42 // assigned (false) 43 boolean defaulted = true; 44 45 /** 46 * Returns the descriptor for this emulated field. 47 * 48 * @return the field descriptor 49 */ getField()50 public ObjectStreamField getField() { 51 return field; 52 } 53 54 /** 55 * Returns the value held by this emulated field. 56 * 57 * @return the field value 58 */ getFieldValue()59 public Object getFieldValue() { 60 return fieldValue; 61 } 62 } 63 64 // The collection of slots the receiver represents 65 private ObjectSlot[] slotsToSerialize; 66 67 private ObjectStreamField[] declaredFields; 68 69 /** 70 * Constructs a new instance of EmulatedFields. 71 * 72 * @param fields 73 * an array of ObjectStreamFields, which describe the fields to 74 * be emulated (names, types, etc). 75 * @param declared 76 * an array of ObjectStreamFields, which describe the declared 77 * fields. 78 */ EmulatedFields(ObjectStreamField[] fields, ObjectStreamField[] declared)79 public EmulatedFields(ObjectStreamField[] fields, ObjectStreamField[] declared) { 80 // We assume the slots are already sorted in the right shape for dumping 81 buildSlots(fields); 82 declaredFields = declared; 83 } 84 85 /** 86 * Build emulated slots that correspond to emulated fields. A slot is a 87 * field descriptor (ObjectStreamField) plus the actual value it holds. 88 * 89 * @param fields 90 * an array of ObjectStreamField, which describe the fields to be 91 * emulated (names, types, etc). 92 */ buildSlots(ObjectStreamField[] fields)93 private void buildSlots(ObjectStreamField[] fields) { 94 slotsToSerialize = new ObjectSlot[fields.length]; 95 for (int i = 0; i < fields.length; i++) { 96 ObjectSlot s = new ObjectSlot(); 97 slotsToSerialize[i] = s; 98 s.field = fields[i]; 99 } 100 // We assume the slots are already sorted in the right shape for dumping 101 } 102 103 /** 104 * Returns {@code true} indicating the field called {@code name} has not had 105 * a value explicitly assigned and that it still holds a default value for 106 * its type, or {@code false} indicating that the field named has been 107 * assigned a value explicitly. 108 * 109 * @param name 110 * the name of the field to test. 111 * @return {@code true} if {@code name} still holds its default value, 112 * {@code false} otherwise 113 * 114 * @throws IllegalArgumentException 115 * if {@code name} is {@code null} 116 */ defaulted(String name)117 public boolean defaulted(String name) throws IllegalArgumentException { 118 ObjectSlot slot = findSlot(name, null); 119 if (slot == null) { 120 throw new IllegalArgumentException("no field '" + name + "'"); 121 } 122 return slot.defaulted; 123 } 124 125 /** 126 * Finds and returns an ObjectSlot that corresponds to a field named {@code 127 * fieldName} and type {@code fieldType}. If the field type {@code 128 * fieldType} corresponds to a primitive type, the field type has to match 129 * exactly or {@code null} is returned. If the field type {@code fieldType} 130 * corresponds to an object type, the field type has to be compatible in 131 * terms of assignment, or null is returned. If {@code fieldType} is {@code 132 * null}, no such compatibility checking is performed and the slot is 133 * returned. 134 * 135 * @param fieldName 136 * the name of the field to find 137 * @param fieldType 138 * the type of the field. This will be used to test 139 * compatibility. If {@code null}, no testing is done, the 140 * corresponding slot is returned. 141 * @return the object slot, or {@code null} if there is no field with that 142 * name, or no compatible field (relative to {@code fieldType}) 143 */ findSlot(String fieldName, Class<?> fieldType)144 private ObjectSlot findSlot(String fieldName, Class<?> fieldType) { 145 boolean isPrimitive = fieldType != null && fieldType.isPrimitive(); 146 for (int i = 0; i < slotsToSerialize.length; i++) { 147 ObjectSlot slot = slotsToSerialize[i]; 148 if (slot.field.getName().equals(fieldName)) { 149 if (isPrimitive) { 150 // Looking for a primitive type field. Types must match 151 // *exactly* 152 if (slot.field.getType() == fieldType) { 153 return slot; 154 } 155 } else { 156 // Looking for a non-primitive type field. 157 if (fieldType == null) { 158 return slot; // Null means we take anything 159 } 160 // Types must be compatible (assignment) 161 if (slot.field.getType().isAssignableFrom(fieldType)) { 162 return slot; 163 } 164 } 165 } 166 } 167 168 if (declaredFields != null) { 169 for (int i = 0; i < declaredFields.length; i++) { 170 ObjectStreamField field = declaredFields[i]; 171 if (field.getName().equals(fieldName)) { 172 if (isPrimitive ? fieldType == field.getType() : fieldType == null || 173 field.getType().isAssignableFrom(fieldType)) { 174 ObjectSlot slot = new ObjectSlot(); 175 slot.field = field; 176 slot.defaulted = true; 177 return slot; 178 } 179 } 180 } 181 } 182 return null; 183 } 184 findMandatorySlot(String name, Class<?> type)185 private ObjectSlot findMandatorySlot(String name, Class<?> type) { 186 ObjectSlot slot = findSlot(name, type); 187 if (slot == null || (type == null && slot.field.getType().isPrimitive())) { 188 throw new IllegalArgumentException("no field '" + name + "' of type " + type); 189 } 190 return slot; 191 } 192 193 /** 194 * Finds and returns the byte value of a given field named {@code name} 195 * in the receiver. If the field has not been assigned any value yet, the 196 * default value {@code defaultValue} is returned instead. 197 * 198 * @param name 199 * the name of the field to find. 200 * @param defaultValue 201 * return value in case the field has not been assigned to yet. 202 * @return the value of the given field if it has been assigned, the default 203 * value otherwise. 204 * 205 * @throws IllegalArgumentException 206 * if the corresponding field can not be found. 207 */ get(String name, byte defaultValue)208 public byte get(String name, byte defaultValue) throws IllegalArgumentException { 209 ObjectSlot slot = findMandatorySlot(name, byte.class); 210 return slot.defaulted ? defaultValue : ((Byte) slot.fieldValue).byteValue(); 211 } 212 213 /** 214 * Finds and returns the char value of a given field named {@code name} in the 215 * receiver. If the field has not been assigned any value yet, the default 216 * value {@code defaultValue} is returned instead. 217 * 218 * @param name 219 * the name of the field to find. 220 * @param defaultValue 221 * return value in case the field has not been assigned to yet. 222 * @return the value of the given field if it has been assigned, the default 223 * value otherwise. 224 * 225 * @throws IllegalArgumentException 226 * if the corresponding field can not be found. 227 */ get(String name, char defaultValue)228 public char get(String name, char defaultValue) throws IllegalArgumentException { 229 ObjectSlot slot = findMandatorySlot(name, char.class); 230 return slot.defaulted ? defaultValue : ((Character) slot.fieldValue).charValue(); 231 } 232 233 /** 234 * Finds and returns the double value of a given field named {@code name} 235 * in the receiver. If the field has not been assigned any value yet, the 236 * default value {@code defaultValue} is returned instead. 237 * 238 * @param name 239 * the name of the field to find. 240 * @param defaultValue 241 * return value in case the field has not been assigned to yet. 242 * @return the value of the given field if it has been assigned, the default 243 * value otherwise. 244 * 245 * @throws IllegalArgumentException 246 * if the corresponding field can not be found. 247 */ get(String name, double defaultValue)248 public double get(String name, double defaultValue) throws IllegalArgumentException { 249 ObjectSlot slot = findMandatorySlot(name, double.class); 250 return slot.defaulted ? defaultValue : ((Double) slot.fieldValue).doubleValue(); 251 } 252 253 /** 254 * Finds and returns the float value of a given field named {@code name} in 255 * the receiver. If the field has not been assigned any value yet, the 256 * default value {@code defaultValue} is returned instead. 257 * 258 * @param name 259 * the name of the field to find. 260 * @param defaultValue 261 * return value in case the field has not been assigned to yet. 262 * @return the value of the given field if it has been assigned, the default 263 * value otherwise. 264 * 265 * @throws IllegalArgumentException 266 * if the corresponding field can not be found. 267 */ get(String name, float defaultValue)268 public float get(String name, float defaultValue) throws IllegalArgumentException { 269 ObjectSlot slot = findMandatorySlot(name, float.class); 270 return slot.defaulted ? defaultValue : ((Float) slot.fieldValue).floatValue(); 271 } 272 273 /** 274 * Finds and returns the int value of a given field named {@code name} in the 275 * receiver. If the field has not been assigned any value yet, the default 276 * value {@code defaultValue} is returned instead. 277 * 278 * @param name 279 * the name of the field to find. 280 * @param defaultValue 281 * return value in case the field has not been assigned to yet. 282 * @return the value of the given field if it has been assigned, the default 283 * value otherwise. 284 * 285 * @throws IllegalArgumentException 286 * if the corresponding field can not be found. 287 */ get(String name, int defaultValue)288 public int get(String name, int defaultValue) throws IllegalArgumentException { 289 ObjectSlot slot = findMandatorySlot(name, int.class); 290 return slot.defaulted ? defaultValue : ((Integer) slot.fieldValue).intValue(); 291 } 292 293 /** 294 * Finds and returns the long value of a given field named {@code name} in the 295 * receiver. If the field has not been assigned any value yet, the default 296 * value {@code defaultValue} is returned instead. 297 * 298 * @param name 299 * the name of the field to find. 300 * @param defaultValue 301 * return value in case the field has not been assigned to yet. 302 * @return the value of the given field if it has been assigned, the default 303 * value otherwise. 304 * 305 * @throws IllegalArgumentException 306 * if the corresponding field can not be found. 307 */ get(String name, long defaultValue)308 public long get(String name, long defaultValue) throws IllegalArgumentException { 309 ObjectSlot slot = findMandatorySlot(name, long.class); 310 return slot.defaulted ? defaultValue : ((Long) slot.fieldValue).longValue(); 311 } 312 313 /** 314 * Finds and returns the Object value of a given field named {@code name} in 315 * the receiver. If the field has not been assigned any value yet, the 316 * default value {@code defaultValue} is returned instead. 317 * 318 * @param name 319 * the name of the field to find. 320 * @param defaultValue 321 * return value in case the field has not been assigned to yet. 322 * @return the value of the given field if it has been assigned, the default 323 * value otherwise. 324 * 325 * @throws IllegalArgumentException 326 * if the corresponding field can not be found. 327 */ get(String name, Object defaultValue)328 public Object get(String name, Object defaultValue) throws IllegalArgumentException { 329 ObjectSlot slot = findMandatorySlot(name, null); 330 return slot.defaulted ? defaultValue : slot.fieldValue; 331 } 332 333 /** 334 * Finds and returns the short value of a given field named {@code name} in 335 * the receiver. If the field has not been assigned any value yet, the 336 * default value {@code defaultValue} is returned instead. 337 * 338 * @param name 339 * the name of the field to find. 340 * @param defaultValue 341 * return value in case the field has not been assigned to yet. 342 * @return the value of the given field if it has been assigned, the default 343 * value otherwise. 344 * 345 * @throws IllegalArgumentException 346 * if the corresponding field can not be found. 347 */ get(String name, short defaultValue)348 public short get(String name, short defaultValue) throws IllegalArgumentException { 349 ObjectSlot slot = findMandatorySlot(name, short.class); 350 return slot.defaulted ? defaultValue : ((Short) slot.fieldValue).shortValue(); 351 } 352 353 /** 354 * Finds and returns the boolean value of a given field named {@code name} in 355 * the receiver. If the field has not been assigned any value yet, the 356 * default value {@code defaultValue} is returned instead. 357 * 358 * @param name 359 * the name of the field to find. 360 * @param defaultValue 361 * return value in case the field has not been assigned to yet. 362 * @return the value of the given field if it has been assigned, the default 363 * value otherwise. 364 * 365 * @throws IllegalArgumentException 366 * if the corresponding field can not be found. 367 */ get(String name, boolean defaultValue)368 public boolean get(String name, boolean defaultValue) throws IllegalArgumentException { 369 ObjectSlot slot = findMandatorySlot(name, boolean.class); 370 return slot.defaulted ? defaultValue : ((Boolean) slot.fieldValue).booleanValue(); 371 } 372 373 /** 374 * Find and set the byte value of a given field named {@code name} in the 375 * receiver. 376 * 377 * @param name 378 * the name of the field to set. 379 * @param value 380 * new value for the field. 381 * 382 * @throws IllegalArgumentException 383 * if the corresponding field can not be found. 384 */ put(String name, byte value)385 public void put(String name, byte value) throws IllegalArgumentException { 386 ObjectSlot slot = findMandatorySlot(name, byte.class); 387 slot.fieldValue = Byte.valueOf(value); 388 slot.defaulted = false; // No longer default value 389 } 390 391 /** 392 * Find and set the char value of a given field named {@code name} in the 393 * receiver. 394 * 395 * @param name 396 * the name of the field to set. 397 * @param value 398 * new value for the field. 399 * 400 * @throws IllegalArgumentException 401 * if the corresponding field can not be found. 402 */ put(String name, char value)403 public void put(String name, char value) throws IllegalArgumentException { 404 ObjectSlot slot = findMandatorySlot(name, char.class); 405 slot.fieldValue = Character.valueOf(value); 406 slot.defaulted = false; // No longer default value 407 } 408 409 /** 410 * Find and set the double value of a given field named {@code name} in the 411 * receiver. 412 * 413 * @param name 414 * the name of the field to set. 415 * @param value 416 * new value for the field. 417 * 418 * @throws IllegalArgumentException 419 * if the corresponding field can not be found. 420 */ put(String name, double value)421 public void put(String name, double value) throws IllegalArgumentException { 422 ObjectSlot slot = findMandatorySlot(name, double.class); 423 slot.fieldValue = Double.valueOf(value); 424 slot.defaulted = false; // No longer default value 425 } 426 427 /** 428 * Find and set the float value of a given field named {@code name} in the 429 * receiver. 430 * 431 * @param name 432 * the name of the field to set. 433 * @param value 434 * new value for the field. 435 * 436 * @throws IllegalArgumentException 437 * if the corresponding field can not be found. 438 */ put(String name, float value)439 public void put(String name, float value) throws IllegalArgumentException { 440 ObjectSlot slot = findMandatorySlot(name, float.class); 441 slot.fieldValue = Float.valueOf(value); 442 slot.defaulted = false; // No longer default value 443 } 444 445 /** 446 * Find and set the int value of a given field named {@code name} in the 447 * receiver. 448 * 449 * @param name 450 * the name of the field to set. 451 * @param value 452 * new value for the field. 453 * 454 * @throws IllegalArgumentException 455 * if the corresponding field can not be found. 456 */ put(String name, int value)457 public void put(String name, int value) throws IllegalArgumentException { 458 ObjectSlot slot = findMandatorySlot(name, int.class); 459 slot.fieldValue = Integer.valueOf(value); 460 slot.defaulted = false; // No longer default value 461 } 462 463 /** 464 * Find and set the long value of a given field named {@code name} in the 465 * receiver. 466 * 467 * @param name 468 * the name of the field to set. 469 * @param value 470 * new value for the field. 471 * 472 * @throws IllegalArgumentException 473 * if the corresponding field can not be found. 474 */ put(String name, long value)475 public void put(String name, long value) throws IllegalArgumentException { 476 ObjectSlot slot = findMandatorySlot(name, long.class); 477 slot.fieldValue = Long.valueOf(value); 478 slot.defaulted = false; // No longer default value 479 } 480 481 /** 482 * Find and set the Object value of a given field named {@code name} in the 483 * receiver. 484 * 485 * @param name 486 * the name of the field to set. 487 * @param value 488 * new value for the field. 489 * 490 * @throws IllegalArgumentException 491 * if the corresponding field can not be found. 492 */ put(String name, Object value)493 public void put(String name, Object value) throws IllegalArgumentException { 494 Class<?> valueClass = null; 495 if (value != null) { 496 valueClass = value.getClass(); 497 } 498 ObjectSlot slot = findMandatorySlot(name, valueClass); 499 slot.fieldValue = value; 500 slot.defaulted = false; // No longer default value 501 } 502 503 /** 504 * Find and set the short value of a given field named {@code name} in the 505 * receiver. 506 * 507 * @param name 508 * the name of the field to set. 509 * @param value 510 * new value for the field. 511 * 512 * @throws IllegalArgumentException 513 * if the corresponding field can not be found. 514 */ put(String name, short value)515 public void put(String name, short value) throws IllegalArgumentException { 516 ObjectSlot slot = findMandatorySlot(name, short.class); 517 slot.fieldValue = Short.valueOf(value); 518 slot.defaulted = false; // No longer default value 519 } 520 521 /** 522 * Find and set the boolean value of a given field named {@code name} in the 523 * receiver. 524 * 525 * @param name 526 * the name of the field to set. 527 * @param value 528 * new value for the field. 529 * 530 * @throws IllegalArgumentException 531 * if the corresponding field can not be found. 532 */ put(String name, boolean value)533 public void put(String name, boolean value) throws IllegalArgumentException { 534 ObjectSlot slot = findMandatorySlot(name, boolean.class); 535 slot.fieldValue = Boolean.valueOf(value); 536 slot.defaulted = false; // No longer default value 537 } 538 539 /** 540 * Return the array of ObjectSlot the receiver represents. 541 * 542 * @return array of ObjectSlot the receiver represents. 543 */ slots()544 public ObjectSlot[] slots() { 545 return slotsToSerialize; 546 } 547 } 548