Home | History | Annotate | Download | only in io
      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          */
     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          */
     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      */
     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      */
     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      */
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    544     public ObjectSlot[] slots() {
    545         return slotsToSerialize;
    546     }
    547 }
    548