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 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      */
     60     public ObjectStreamField(String name, Class<?> cl) {
     61         if (name == null || cl == null) {
     62             throw new NullPointerException();
     63         }
     64         this.name = name;
     65         this.type = new WeakReference<Class<?>>(cl);
     66     }
     67 
     68     /**
     69      * Constructs an ObjectStreamField with the specified name, type and the
     70      * indication if it is unshared.
     71      *
     72      * @param name
     73      *            the name of the field.
     74      * @param cl
     75      *            the type of the field.
     76      * @param unshared
     77      *            {@code true} if the field is written and read unshared;
     78      *            {@code false} otherwise.
     79      * @throws NullPointerException
     80      *             if {@code name} or {@code cl} is {@code null}.
     81      * @see ObjectOutputStream#writeUnshared(Object)
     82      */
     83     public ObjectStreamField(String name, Class<?> cl, boolean unshared) {
     84         if (name == null || cl == null) {
     85             throw new NullPointerException();
     86         }
     87         this.name = name;
     88         this.type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl);
     89         this.unshared = unshared;
     90     }
     91 
     92     /**
     93      * Constructs an ObjectStreamField with the given name and the given type.
     94      * The type may be null.
     95      *
     96      * @param signature
     97      *            A String representing the type of the field
     98      * @param name
     99      *            a String, the name of the field, or null
    100      */
    101     ObjectStreamField(String signature, String name) {
    102         if (name == null) {
    103             throw new NullPointerException();
    104         }
    105         this.name = name;
    106         this.typeString = signature.replace('.', '/').intern();
    107         defaultResolve();
    108         this.isDeserialized = true;
    109     }
    110 
    111     /**
    112      * Compares this field descriptor to the specified one. Checks first if one
    113      * of the compared fields has a primitive type and the other one not. If so,
    114      * the field with the primitive type is considered to be "smaller". If both
    115      * fields are equal, their names are compared.
    116      *
    117      * @param o
    118      *            the object to compare with.
    119      * @return -1 if this field is "smaller" than field {@code o}, 0 if both
    120      *         fields are equal; 1 if this field is "greater" than field {@code
    121      *         o}.
    122      */
    123     public int compareTo(Object o) {
    124         ObjectStreamField f = (ObjectStreamField) o;
    125         boolean thisPrimitive = this.isPrimitive();
    126         boolean fPrimitive = f.isPrimitive();
    127 
    128         // If one is primitive and the other isn't, we have enough info to
    129         // compare
    130         if (thisPrimitive != fPrimitive) {
    131             return thisPrimitive ? -1 : 1;
    132         }
    133 
    134         // Either both primitives or both not primitives. Compare based on name.
    135         return this.getName().compareTo(f.getName());
    136     }
    137 
    138     /**
    139      * Gets the name of this field.
    140      *
    141      * @return the field's name.
    142      */
    143     public String getName() {
    144         return name;
    145     }
    146 
    147     /**
    148      * Gets the offset of this field in the object.
    149      *
    150      * @return this field's offset.
    151      */
    152     public int getOffset() {
    153         return offset;
    154     }
    155 
    156     /**
    157      * Return the type of the field the receiver represents, this is an internal
    158      * method
    159      *
    160      * @return A Class object representing the type of the field
    161      */
    162     // Changed from private to default visibility for usage in ObjectStreamClass
    163     /* package */ Class<?> getTypeInternal() {
    164         if (type instanceof WeakReference) {
    165             return (Class<?>) ((WeakReference<?>) type).get();
    166         }
    167         return (Class<?>) type;
    168     }
    169 
    170     /**
    171      * Gets the type of this field.
    172      *
    173      * @return a {@code Class} object representing the type of the field.
    174      */
    175     public Class<?> getType() {
    176         Class<?> cl = getTypeInternal();
    177         if (isDeserialized && !cl.isPrimitive()) {
    178             return Object.class;
    179         }
    180         return cl;
    181     }
    182 
    183     /**
    184      * Gets a character code for the type of this field. The following codes are
    185      * used:
    186      *
    187      * <pre>
    188      * B     byte
    189      * C     char
    190      * D     double
    191      * F     float
    192      * I     int
    193      * J     long
    194      * L     class or interface
    195      * S     short
    196      * Z     boolean
    197      * [     array
    198      * </pre>
    199      *
    200      * @return the field's type code.
    201      */
    202     public char getTypeCode() {
    203         return typeCodeOf(getTypeInternal());
    204     }
    205 
    206     private char typeCodeOf(Class<?> type) {
    207         if (type == int.class) {
    208             return 'I';
    209         } else if (type == byte.class) {
    210             return 'B';
    211         } else if (type == char.class) {
    212             return 'C';
    213         } else if (type == short.class) {
    214             return 'S';
    215         } else if (type == boolean.class) {
    216             return 'Z';
    217         } else if (type == long.class) {
    218             return 'J';
    219         } else if (type == float.class) {
    220             return 'F';
    221         } else if (type == double.class) {
    222             return 'D';
    223         } else if (type.isArray()) {
    224             return '[';
    225         } else {
    226             return 'L';
    227         }
    228     }
    229 
    230     /**
    231      * Gets the type signature used by the VM to represent the type of this
    232      * field.
    233      *
    234      * @return the signature of this field's class or {@code null} if this
    235      *         field's type is primitive.
    236      */
    237     public String getTypeString() {
    238         if (isPrimitive()) {
    239             return null;
    240         }
    241         if (typeString == null) {
    242             Class<?> t = getTypeInternal();
    243             String typeName = t.getName().replace('.', '/');
    244             String str = (t.isArray()) ? typeName : ("L" + typeName + ';');
    245             typeString = str.intern();
    246         }
    247         return typeString;
    248     }
    249 
    250     /**
    251      * Indicates whether this field's type is a primitive type.
    252      *
    253      * @return {@code true} if this field's type is primitive; {@code false} if
    254      *         the type of this field is a regular class.
    255      */
    256     public boolean isPrimitive() {
    257         Class<?> t = getTypeInternal();
    258         return t != null && t.isPrimitive();
    259     }
    260 
    261     boolean writeField(DataOutputStream out) throws IOException {
    262         Class<?> t = getTypeInternal();
    263         out.writeByte(typeCodeOf(t));
    264         out.writeUTF(name);
    265         return (t != null && t.isPrimitive());
    266     }
    267 
    268     /**
    269      * Sets this field's offset in the object.
    270      *
    271      * @param newValue
    272      *            the field's new offset.
    273      */
    274     protected void setOffset(int newValue) {
    275         this.offset = newValue;
    276     }
    277 
    278     /**
    279      * Returns a string containing a concise, human-readable description of this
    280      * field descriptor.
    281      *
    282      * @return a printable representation of this descriptor.
    283      */
    284     @Override
    285     public String toString() {
    286         return this.getClass().getName() + '(' + getName() + ':' + getTypeInternal() + ')';
    287     }
    288 
    289     void resolve(ClassLoader loader) {
    290         if (typeString == null && isPrimitive()) {
    291             // primitive type declared in a serializable class
    292             typeString = String.valueOf(getTypeCode());
    293         }
    294 
    295         if (typeString.length() == 1) {
    296             if (defaultResolve()) {
    297                 return;
    298             }
    299         }
    300 
    301         String className = typeString.replace('/', '.');
    302         if (className.charAt(0) == 'L') {
    303             // remove L and ;
    304             className = className.substring(1, className.length() - 1);
    305         }
    306         try {
    307             Class<?> cl = Class.forName(className, false, loader);
    308             type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl);
    309         } catch (ClassNotFoundException e) {
    310             // Ignored
    311         }
    312     }
    313 
    314     /**
    315      * Indicates whether this field is unshared.
    316      *
    317      * @return {@code true} if this field is unshared, {@code false} otherwise.
    318      */
    319     public boolean isUnshared() {
    320         return unshared;
    321     }
    322 
    323     void setUnshared(boolean unshared) {
    324         this.unshared = unshared;
    325     }
    326 
    327     /**
    328      * Resolves typeString into type. Returns true if the type is primitive
    329      * and false otherwise.
    330      */
    331     private boolean defaultResolve() {
    332         switch (typeString.charAt(0)) {
    333         case 'I':
    334             type = int.class;
    335             return true;
    336         case 'B':
    337             type = byte.class;
    338             return true;
    339         case 'C':
    340             type = char.class;
    341             return true;
    342         case 'S':
    343             type = short.class;
    344             return true;
    345         case 'Z':
    346             type = boolean.class;
    347             return true;
    348         case 'J':
    349             type = long.class;
    350             return true;
    351         case 'F':
    352             type = float.class;
    353             return true;
    354         case 'D':
    355             type = double.class;
    356             return true;
    357         default:
    358             type = Object.class;
    359             return false;
    360         }
    361     }
    362 }
    363