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