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,
     80             ObjectStreamField[] declared) {
     81         super();
     82         // We assume the slots are already sorted in the right shape for dumping
     83         buildSlots(fields);
     84         declaredFields = declared;
     85     }
     86 
     87     /**
     88      * Build emulated slots that correspond to emulated fields. A slot is a
     89      * field descriptor (ObjectStreamField) plus the actual value it holds.
     90      *
     91      * @param fields
     92      *            an array of ObjectStreamField, which describe the fields to be
     93      *            emulated (names, types, etc).
     94      */
     95     private void buildSlots(ObjectStreamField[] fields) {
     96         slotsToSerialize = new ObjectSlot[fields.length];
     97         for (int i = 0; i < fields.length; i++) {
     98             ObjectSlot s = new ObjectSlot();
     99             slotsToSerialize[i] = s;
    100             s.field = fields[i];
    101         }
    102         // We assume the slots are already sorted in the right shape for dumping
    103     }
    104 
    105     /**
    106      * Returns {@code true} indicating the field called {@code name} has not had
    107      * a value explicitly assigned and that it still holds a default value for
    108      * its type, or {@code false} indicating that the field named has been
    109      * assigned a value explicitly.
    110      *
    111      * @param name
    112      *            the name of the field to test.
    113      * @return {@code true} if {@code name} still holds its default value,
    114      *         {@code false} otherwise
    115      *
    116      * @throws IllegalArgumentException
    117      *             if {@code name} is {@code null}
    118      */
    119     public boolean defaulted(String name) throws IllegalArgumentException {
    120         ObjectSlot slot = findSlot(name, null);
    121         if (slot == null) {
    122             throw new IllegalArgumentException("no field '" + name + "'");
    123         }
    124         return slot.defaulted;
    125     }
    126 
    127     /**
    128      * Finds and returns an ObjectSlot that corresponds to a field named {@code
    129      * fieldName} and type {@code fieldType}. If the field type {@code
    130      * fieldType} corresponds to a primitive type, the field type has to match
    131      * exactly or {@code null} is returned. If the field type {@code fieldType}
    132      * corresponds to an object type, the field type has to be compatible in
    133      * terms of assignment, or null is returned. If {@code fieldType} is {@code
    134      * null}, no such compatibility checking is performed and the slot is
    135      * returned.
    136      *
    137      * @param fieldName
    138      *            the name of the field to find
    139      * @param fieldType
    140      *            the type of the field. This will be used to test
    141      *            compatibility. If {@code null}, no testing is done, the
    142      *            corresponding slot is returned.
    143      * @return the object slot, or {@code null} if there is no field with that
    144      *         name, or no compatible field (relative to {@code fieldType})
    145      */
    146     private ObjectSlot findSlot(String fieldName, Class<?> fieldType) {
    147         boolean isPrimitive = fieldType != null && fieldType.isPrimitive();
    148 
    149         for (int i = 0; i < slotsToSerialize.length; i++) {
    150             ObjectSlot slot = slotsToSerialize[i];
    151             if (slot.field.getName().equals(fieldName)) {
    152                 if (isPrimitive) {
    153                     // Looking for a primitive type field. Types must match
    154                     // *exactly*
    155                     if (slot.field.getType() == fieldType) {
    156                         return slot;
    157                     }
    158                 } else {
    159                     // Looking for a non-primitive type field.
    160                     if (fieldType == null) {
    161                         return slot; // Null means we take anything
    162                     }
    163                     // Types must be compatible (assignment)
    164                     if (slot.field.getType().isAssignableFrom(fieldType)) {
    165                         return slot;
    166                     }
    167                 }
    168             }
    169         }
    170 
    171         if (declaredFields != null) {
    172             for (int i = 0; i < declaredFields.length; i++) {
    173                 ObjectStreamField field = declaredFields[i];
    174                 if (field.getName().equals(fieldName)) {
    175                     if (isPrimitive ? field.getType() == fieldType
    176                             : fieldType == null
    177                                     || field.getType().isAssignableFrom(
    178                                             fieldType)) {
    179                         ObjectSlot slot = new ObjectSlot();
    180                         slot.field = field;
    181                         slot.defaulted = true;
    182                         return slot;
    183                     }
    184                 }
    185             }
    186         }
    187         return null;
    188     }
    189 
    190     /**
    191      * Finds and returns the byte value of a given field named {@code name}
    192      * in the receiver. If the field has not been assigned any value yet, the
    193      * default value {@code defaultValue} is returned instead.
    194      *
    195      * @param name
    196      *            the name of the field to find.
    197      * @param defaultValue
    198      *            return value in case the field has not been assigned to yet.
    199      * @return the value of the given field if it has been assigned, the default
    200      *         value otherwise.
    201      *
    202      * @throws IllegalArgumentException
    203      *             if the corresponding field can not be found.
    204      */
    205     public byte get(String name, byte defaultValue)
    206             throws IllegalArgumentException {
    207         ObjectSlot slot = findSlot(name, Byte.TYPE);
    208         // if not initialized yet, we give the default value
    209         if (slot == null) {
    210             throw new IllegalArgumentException("no byte field '" + name + "'");
    211         }
    212         return slot.defaulted ? defaultValue : ((Byte) slot.fieldValue)
    213                 .byteValue();
    214     }
    215 
    216     /**
    217      * Finds and returns the char value of a given field named {@code name} in the
    218      * receiver. If the field has not been assigned any value yet, the default
    219      * value {@code defaultValue} is returned instead.
    220      *
    221      * @param name
    222      *            the name of the field to find.
    223      * @param defaultValue
    224      *            return value in case the field has not been assigned to yet.
    225      * @return the value of the given field if it has been assigned, the default
    226      *         value otherwise.
    227      *
    228      * @throws IllegalArgumentException
    229      *             if the corresponding field can not be found.
    230      */
    231     public char get(String name, char defaultValue)
    232             throws IllegalArgumentException {
    233         ObjectSlot slot = findSlot(name, Character.TYPE);
    234         // if not initialized yet, we give the default value
    235         if (slot == null) {
    236             throw new IllegalArgumentException("no char field '" + name + "'");
    237         }
    238         return slot.defaulted ? defaultValue : ((Character) slot.fieldValue)
    239                 .charValue();
    240     }
    241 
    242     /**
    243      * Finds and returns the double value of a given field named {@code name}
    244      * in the receiver. If the field has not been assigned any value yet, the
    245      * default value {@code defaultValue} is returned instead.
    246      *
    247      * @param name
    248      *            the name of the field to find.
    249      * @param defaultValue
    250      *            return value in case the field has not been assigned to yet.
    251      * @return the value of the given field if it has been assigned, the default
    252      *         value otherwise.
    253      *
    254      * @throws IllegalArgumentException
    255      *             if the corresponding field can not be found.
    256      */
    257     public double get(String name, double defaultValue)
    258             throws IllegalArgumentException {
    259         ObjectSlot slot = findSlot(name, Double.TYPE);
    260         // if not initialized yet, we give the default value
    261         if (slot == null) {
    262             throw new IllegalArgumentException("no double field '" + name + "'");
    263         }
    264         return slot.defaulted ? defaultValue : ((Double) slot.fieldValue)
    265                 .doubleValue();
    266     }
    267 
    268     /**
    269      * Finds and returns the float value of a given field named {@code name} in
    270      * the receiver. If the field has not been assigned any value yet, the
    271      * default value {@code defaultValue} is returned instead.
    272      *
    273      * @param name
    274      *            the name of the field to find.
    275      * @param defaultValue
    276      *            return value in case the field has not been assigned to yet.
    277      * @return the value of the given field if it has been assigned, the default
    278      *         value otherwise.
    279      *
    280      * @throws IllegalArgumentException
    281      *             if the corresponding field can not be found.
    282      */
    283     public float get(String name, float defaultValue)
    284             throws IllegalArgumentException {
    285         ObjectSlot slot = findSlot(name, Float.TYPE);
    286         // if not initialized yet, we give the default value
    287         if (slot == null) {
    288             throw new IllegalArgumentException("no float field '" + name + "'");
    289         }
    290         return slot.defaulted ? defaultValue : ((Float) slot.fieldValue)
    291                 .floatValue();
    292     }
    293 
    294     /**
    295      * Finds and returns the int value of a given field named {@code name} in the
    296      * receiver. If the field has not been assigned any value yet, the default
    297      * value {@code defaultValue} is returned instead.
    298      *
    299      * @param name
    300      *            the name of the field to find.
    301      * @param defaultValue
    302      *            return value in case the field has not been assigned to yet.
    303      * @return the value of the given field if it has been assigned, the default
    304      *         value otherwise.
    305      *
    306      * @throws IllegalArgumentException
    307      *             if the corresponding field can not be found.
    308      */
    309     public int get(String name, int defaultValue)
    310             throws IllegalArgumentException {
    311         ObjectSlot slot = findSlot(name, Integer.TYPE);
    312         // if not initialized yet, we give the default value
    313         if (slot == null) {
    314             throw new IllegalArgumentException("no int field '" + name + "'");
    315         }
    316         return slot.defaulted ? defaultValue : ((Integer) slot.fieldValue)
    317                 .intValue();
    318     }
    319 
    320     /**
    321      * Finds and returns the long value of a given field named {@code name} in the
    322      * receiver. If the field has not been assigned any value yet, the default
    323      * value {@code defaultValue} is returned instead.
    324      *
    325      * @param name
    326      *            the name of the field to find.
    327      * @param defaultValue
    328      *            return value in case the field has not been assigned to yet.
    329      * @return the value of the given field if it has been assigned, the default
    330      *         value otherwise.
    331      *
    332      * @throws IllegalArgumentException
    333      *             if the corresponding field can not be found.
    334      */
    335     public long get(String name, long defaultValue)
    336             throws IllegalArgumentException {
    337         ObjectSlot slot = findSlot(name, Long.TYPE);
    338         // if not initialized yet, we give the default value
    339         if (slot == null) {
    340             throw new IllegalArgumentException("no long field '" + name + "'");
    341         }
    342         return slot.defaulted ? defaultValue : ((Long) slot.fieldValue)
    343                 .longValue();
    344     }
    345 
    346     /**
    347      * Finds and returns the Object value of a given field named {@code name} in
    348      * the receiver. If the field has not been assigned any value yet, the
    349      * default value {@code defaultValue} is returned instead.
    350      *
    351      * @param name
    352      *            the name of the field to find.
    353      * @param defaultValue
    354      *            return value in case the field has not been assigned to yet.
    355      * @return the value of the given field if it has been assigned, the default
    356      *         value otherwise.
    357      *
    358      * @throws IllegalArgumentException
    359      *             if the corresponding field can not be found.
    360      */
    361     public Object get(String name, Object defaultValue)
    362             throws IllegalArgumentException {
    363         ObjectSlot slot = findSlot(name, null);
    364         // if not initialized yet, we give the default value
    365         if (slot == null || slot.field.getType().isPrimitive()) {
    366             throw new IllegalArgumentException("no Object field '" + name + "'");
    367         }
    368         return slot.defaulted ? defaultValue : slot.fieldValue;
    369     }
    370 
    371     /**
    372      * Finds and returns the short value of a given field named {@code name} in
    373      * the receiver. If the field has not been assigned any value yet, the
    374      * default value {@code defaultValue} is returned instead.
    375      *
    376      * @param name
    377      *            the name of the field to find.
    378      * @param defaultValue
    379      *            return value in case the field has not been assigned to yet.
    380      * @return the value of the given field if it has been assigned, the default
    381      *         value otherwise.
    382      *
    383      * @throws IllegalArgumentException
    384      *             if the corresponding field can not be found.
    385      */
    386     public short get(String name, short defaultValue)
    387             throws IllegalArgumentException {
    388         ObjectSlot slot = findSlot(name, Short.TYPE);
    389         // if not initialized yet, we give the default value
    390         if (slot == null) {
    391             throw new IllegalArgumentException("no short field '" + name + "'");
    392         }
    393         return slot.defaulted ? defaultValue : ((Short) slot.fieldValue)
    394                 .shortValue();
    395     }
    396 
    397     /**
    398      * Finds and returns the boolean value of a given field named {@code name} in
    399      * the receiver. If the field has not been assigned any value yet, the
    400      * default value {@code defaultValue} is returned instead.
    401      *
    402      * @param name
    403      *            the name of the field to find.
    404      * @param defaultValue
    405      *            return value in case the field has not been assigned to yet.
    406      * @return the value of the given field if it has been assigned, the default
    407      *         value otherwise.
    408      *
    409      * @throws IllegalArgumentException
    410      *             if the corresponding field can not be found.
    411      */
    412     public boolean get(String name, boolean defaultValue)
    413             throws IllegalArgumentException {
    414         ObjectSlot slot = findSlot(name, Boolean.TYPE);
    415         // if not initialized yet, we give the default value
    416         if (slot == null) {
    417             throw new IllegalArgumentException("no boolean field '" + name + "'");
    418         }
    419         return slot.defaulted ? defaultValue : ((Boolean) slot.fieldValue)
    420                 .booleanValue();
    421     }
    422 
    423     /**
    424      * Find and set the byte value of a given field named {@code name} in the
    425      * receiver.
    426      *
    427      * @param name
    428      *            the name of the field to set.
    429      * @param value
    430      *            new value for the field.
    431      *
    432      * @throws IllegalArgumentException
    433      *             if the corresponding field can not be found.
    434      */
    435     public void put(String name, byte value) throws IllegalArgumentException {
    436         ObjectSlot slot = findSlot(name, Byte.TYPE);
    437         if (slot == null) {
    438             throw new IllegalArgumentException("no byte field '" + name + "'");
    439         }
    440         slot.fieldValue = Byte.valueOf(value);
    441         slot.defaulted = false; // No longer default value
    442     }
    443 
    444     /**
    445      * Find and set the char value of a given field named {@code name} in the
    446      * receiver.
    447      *
    448      * @param name
    449      *            the name of the field to set.
    450      * @param value
    451      *            new value for the field.
    452      *
    453      * @throws IllegalArgumentException
    454      *             if the corresponding field can not be found.
    455      */
    456     public void put(String name, char value) throws IllegalArgumentException {
    457         ObjectSlot slot = findSlot(name, Character.TYPE);
    458         if (slot == null) {
    459             throw new IllegalArgumentException("no char field '" + name + "'");
    460         }
    461         slot.fieldValue = Character.valueOf(value);
    462         slot.defaulted = false; // No longer default value
    463     }
    464 
    465     /**
    466      * Find and set the double value of a given field named {@code name} in the
    467      * receiver.
    468      *
    469      * @param name
    470      *            the name of the field to set.
    471      * @param value
    472      *            new value for the field.
    473      *
    474      * @throws IllegalArgumentException
    475      *             if the corresponding field can not be found.
    476      */
    477     public void put(String name, double value) throws IllegalArgumentException {
    478         ObjectSlot slot = findSlot(name, Double.TYPE);
    479         if (slot == null) {
    480             throw new IllegalArgumentException("no double field '" + name + "'");
    481         }
    482         slot.fieldValue = Double.valueOf(value);
    483         slot.defaulted = false; // No longer default value
    484     }
    485 
    486     /**
    487      * Find and set the float value of a given field named {@code name} in the
    488      * receiver.
    489      *
    490      * @param name
    491      *            the name of the field to set.
    492      * @param value
    493      *            new value for the field.
    494      *
    495      * @throws IllegalArgumentException
    496      *             if the corresponding field can not be found.
    497      */
    498     public void put(String name, float value) throws IllegalArgumentException {
    499         ObjectSlot slot = findSlot(name, Float.TYPE);
    500         if (slot == null) {
    501             throw new IllegalArgumentException("no float field '" + name + "'");
    502         }
    503         slot.fieldValue = Float.valueOf(value);
    504         slot.defaulted = false; // No longer default value
    505     }
    506 
    507     /**
    508      * Find and set the int value of a given field named {@code name} in the
    509      * receiver.
    510      *
    511      * @param name
    512      *            the name of the field to set.
    513      * @param value
    514      *            new value for the field.
    515      *
    516      * @throws IllegalArgumentException
    517      *             if the corresponding field can not be found.
    518      */
    519     public void put(String name, int value) throws IllegalArgumentException {
    520         ObjectSlot slot = findSlot(name, Integer.TYPE);
    521         if (slot == null) {
    522             throw new IllegalArgumentException("no integer field '" + name + "'");
    523         }
    524         slot.fieldValue = Integer.valueOf(value);
    525         slot.defaulted = false; // No longer default value
    526     }
    527 
    528     /**
    529      * Find and set the long value of a given field named {@code name} in the
    530      * receiver.
    531      *
    532      * @param name
    533      *            the name of the field to set.
    534      * @param value
    535      *            new value for the field.
    536      *
    537      * @throws IllegalArgumentException
    538      *             if the corresponding field can not be found.
    539      */
    540     public void put(String name, long value) throws IllegalArgumentException {
    541         ObjectSlot slot = findSlot(name, Long.TYPE);
    542         if (slot == null) {
    543             throw new IllegalArgumentException("no long field '" + name + "'");
    544         }
    545         slot.fieldValue = Long.valueOf(value);
    546         slot.defaulted = false; // No longer default value
    547     }
    548 
    549     /**
    550      * Find and set the Object value of a given field named {@code name} in the
    551      * receiver.
    552      *
    553      * @param name
    554      *            the name of the field to set.
    555      * @param value
    556      *            new value for the field.
    557      *
    558      * @throws IllegalArgumentException
    559      *             if the corresponding field can not be found.
    560      */
    561     public void put(String name, Object value) throws IllegalArgumentException {
    562         Class<?> valueClass = null;
    563         if (value != null) {
    564             valueClass = value.getClass();
    565         }
    566         ObjectSlot slot = findSlot(name, valueClass);
    567         if (slot == null) {
    568             throw new IllegalArgumentException("no Object field '" + name + "'");
    569         }
    570         slot.fieldValue = value;
    571         slot.defaulted = false; // No longer default value
    572     }
    573 
    574     /**
    575      * Find and set the short value of a given field named {@code name} in the
    576      * receiver.
    577      *
    578      * @param name
    579      *            the name of the field to set.
    580      * @param value
    581      *            new value for the field.
    582      *
    583      * @throws IllegalArgumentException
    584      *             if the corresponding field can not be found.
    585      */
    586     public void put(String name, short value) throws IllegalArgumentException {
    587         ObjectSlot slot = findSlot(name, Short.TYPE);
    588         if (slot == null) {
    589             throw new IllegalArgumentException("no short field '" + name + "'");
    590         }
    591         slot.fieldValue = Short.valueOf(value);
    592         slot.defaulted = false; // No longer default value
    593     }
    594 
    595     /**
    596      * Find and set the boolean value of a given field named {@code name} in the
    597      * receiver.
    598      *
    599      * @param name
    600      *            the name of the field to set.
    601      * @param value
    602      *            new value for the field.
    603      *
    604      * @throws IllegalArgumentException
    605      *             if the corresponding field can not be found.
    606      */
    607     public void put(String name, boolean value) throws IllegalArgumentException {
    608         ObjectSlot slot = findSlot(name, Boolean.TYPE);
    609         if (slot == null) {
    610             throw new IllegalArgumentException("no boolean field '" + name + "'");
    611         }
    612         slot.fieldValue = Boolean.valueOf(value);
    613         slot.defaulted = false; // No longer default value
    614     }
    615 
    616     /**
    617      * Return the array of ObjectSlot the receiver represents.
    618      *
    619      * @return array of ObjectSlot the receiver represents.
    620      */
    621     public ObjectSlot[] slots() {
    622         return slotsToSerialize;
    623     }
    624 }
    625