Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package java.io;
     27 
     28 import java.lang.reflect.Field;
     29 import sun.reflect.CallerSensitive;
     30 import sun.reflect.Reflection;
     31 import sun.reflect.misc.ReflectUtil;
     32 
     33 /**
     34  * A description of a Serializable field from a Serializable class.  An array
     35  * of ObjectStreamFields is used to declare the Serializable fields of a class.
     36  *
     37  * @author      Mike Warres
     38  * @author      Roger Riggs
     39  * @see ObjectStreamClass
     40  * @since 1.2
     41  */
     42 public class ObjectStreamField
     43     implements Comparable<Object>
     44 {
     45 
     46     /** field name */
     47     private final String name;
     48     /** canonical JVM signature of field type */
     49     private final String signature;
     50     /** field type (Object.class if unknown non-primitive type) */
     51     private final Class<?> type;
     52     /** whether or not to (de)serialize field values as unshared */
     53     private final boolean unshared;
     54     /** corresponding reflective field object, if any */
     55     private final Field field;
     56     /** offset of field value in enclosing field group */
     57     private int offset = 0;
     58 
     59     /**
     60      * Create a Serializable field with the specified type.  This field should
     61      * be documented with a <code>serialField</code> tag.
     62      *
     63      * @param   name the name of the serializable field
     64      * @param   type the <code>Class</code> object of the serializable field
     65      */
     66     public ObjectStreamField(String name, Class<?> type) {
     67         this(name, type, false);
     68     }
     69 
     70     /**
     71      * Creates an ObjectStreamField representing a serializable field with the
     72      * given name and type.  If unshared is false, values of the represented
     73      * field are serialized and deserialized in the default manner--if the
     74      * field is non-primitive, object values are serialized and deserialized as
     75      * if they had been written and read by calls to writeObject and
     76      * readObject.  If unshared is true, values of the represented field are
     77      * serialized and deserialized as if they had been written and read by
     78      * calls to writeUnshared and readUnshared.
     79      *
     80      * @param   name field name
     81      * @param   type field type
     82      * @param   unshared if false, write/read field values in the same manner
     83      *          as writeObject/readObject; if true, write/read in the same
     84      *          manner as writeUnshared/readUnshared
     85      * @since   1.4
     86      */
     87     public ObjectStreamField(String name, Class<?> type, boolean unshared) {
     88         if (name == null) {
     89             throw new NullPointerException();
     90         }
     91         this.name = name;
     92         this.type = type;
     93         this.unshared = unshared;
     94         signature = getClassSignature(type).intern();
     95         field = null;
     96     }
     97 
     98     /**
     99      * Creates an ObjectStreamField representing a field with the given name,
    100      * signature and unshared setting.
    101      */
    102     ObjectStreamField(String name, String signature, boolean unshared) {
    103         if (name == null) {
    104             throw new NullPointerException();
    105         }
    106         this.name = name;
    107         this.signature = signature.intern();
    108         this.unshared = unshared;
    109         field = null;
    110 
    111         switch (signature.charAt(0)) {
    112             case 'Z': type = Boolean.TYPE; break;
    113             case 'B': type = Byte.TYPE; break;
    114             case 'C': type = Character.TYPE; break;
    115             case 'S': type = Short.TYPE; break;
    116             case 'I': type = Integer.TYPE; break;
    117             case 'J': type = Long.TYPE; break;
    118             case 'F': type = Float.TYPE; break;
    119             case 'D': type = Double.TYPE; break;
    120             case 'L':
    121             case '[': type = Object.class; break;
    122             default: throw new IllegalArgumentException("illegal signature");
    123         }
    124     }
    125 
    126     /**
    127      * Creates an ObjectStreamField representing the given field with the
    128      * specified unshared setting.  For compatibility with the behavior of
    129      * earlier serialization implementations, a "showType" parameter is
    130      * necessary to govern whether or not a getType() call on this
    131      * ObjectStreamField (if non-primitive) will return Object.class (as
    132      * opposed to a more specific reference type).
    133      */
    134     ObjectStreamField(Field field, boolean unshared, boolean showType) {
    135         this.field = field;
    136         this.unshared = unshared;
    137         name = field.getName();
    138         Class<?> ftype = field.getType();
    139         type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
    140         signature = getClassSignature(ftype).intern();
    141     }
    142 
    143     /**
    144      * Get the name of this field.
    145      *
    146      * @return  a <code>String</code> representing the name of the serializable
    147      *          field
    148      */
    149     public String getName() {
    150         return name;
    151     }
    152 
    153     /**
    154      * Get the type of the field.  If the type is non-primitive and this
    155      * <code>ObjectStreamField</code> was obtained from a deserialized {@link
    156      * ObjectStreamClass} instance, then <code>Object.class</code> is returned.
    157      * Otherwise, the <code>Class</code> object for the type of the field is
    158      * returned.
    159      *
    160      * @return  a <code>Class</code> object representing the type of the
    161      *          serializable field
    162      */
    163     @CallerSensitive
    164     public Class<?> getType() {
    165         /* BEGIN Android-removed: Security manager is always null on Android.
    166         if (System.getSecurityManager() != null) {
    167              Class<?> caller = Reflection.getCallerClass();
    168             if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), type.getClassLoader())) {
    169                 ReflectUtil.checkPackageAccess(type);
    170             }
    171         }
    172         END Android-removed: Security manager is always null on Android. */
    173         return type;
    174     }
    175 
    176     /**
    177      * Returns character encoding of field type.  The encoding is as follows:
    178      * <blockquote><pre>
    179      * B            byte
    180      * C            char
    181      * D            double
    182      * F            float
    183      * I            int
    184      * J            long
    185      * L            class or interface
    186      * S            short
    187      * Z            boolean
    188      * [            array
    189      * </pre></blockquote>
    190      *
    191      * @return  the typecode of the serializable field
    192      */
    193     // REMIND: deprecate?
    194     public char getTypeCode() {
    195         return signature.charAt(0);
    196     }
    197 
    198     /**
    199      * Return the JVM type signature.
    200      *
    201      * @return  null if this field has a primitive type.
    202      */
    203     // REMIND: deprecate?
    204     public String getTypeString() {
    205         return isPrimitive() ? null : signature;
    206     }
    207 
    208     /**
    209      * Offset of field within instance data.
    210      *
    211      * @return  the offset of this field
    212      * @see #setOffset
    213      */
    214     // REMIND: deprecate?
    215     public int getOffset() {
    216         return offset;
    217     }
    218 
    219     /**
    220      * Offset within instance data.
    221      *
    222      * @param   offset the offset of the field
    223      * @see #getOffset
    224      */
    225     // REMIND: deprecate?
    226     protected void setOffset(int offset) {
    227         this.offset = offset;
    228     }
    229 
    230     /**
    231      * Return true if this field has a primitive type.
    232      *
    233      * @return  true if and only if this field corresponds to a primitive type
    234      */
    235     // REMIND: deprecate?
    236     public boolean isPrimitive() {
    237         char tcode = signature.charAt(0);
    238         return ((tcode != 'L') && (tcode != '['));
    239     }
    240 
    241     /**
    242      * Returns boolean value indicating whether or not the serializable field
    243      * represented by this ObjectStreamField instance is unshared.
    244      *
    245      * @return {@code true} if this field is unshared
    246      *
    247      * @since 1.4
    248      */
    249     public boolean isUnshared() {
    250         return unshared;
    251     }
    252 
    253     /**
    254      * Compare this field with another <code>ObjectStreamField</code>.  Return
    255      * -1 if this is smaller, 0 if equal, 1 if greater.  Types that are
    256      * primitives are "smaller" than object types.  If equal, the field names
    257      * are compared.
    258      */
    259     // REMIND: deprecate?
    260     public int compareTo(Object obj) {
    261         ObjectStreamField other = (ObjectStreamField) obj;
    262         boolean isPrim = isPrimitive();
    263         if (isPrim != other.isPrimitive()) {
    264             return isPrim ? -1 : 1;
    265         }
    266         return name.compareTo(other.name);
    267     }
    268 
    269     /**
    270      * Return a string that describes this field.
    271      */
    272     public String toString() {
    273         return signature + ' ' + name;
    274     }
    275 
    276     /**
    277      * Returns field represented by this ObjectStreamField, or null if
    278      * ObjectStreamField is not associated with an actual field.
    279      */
    280     Field getField() {
    281         return field;
    282     }
    283 
    284     /**
    285      * Returns JVM type signature of field (similar to getTypeString, except
    286      * that signature strings are returned for primitive fields as well).
    287      */
    288     String getSignature() {
    289         return signature;
    290     }
    291 
    292     /**
    293      * Returns JVM type signature for given class.
    294      */
    295     private static String getClassSignature(Class<?> cl) {
    296         StringBuilder sbuf = new StringBuilder();
    297         while (cl.isArray()) {
    298             sbuf.append('[');
    299             cl = cl.getComponentType();
    300         }
    301         if (cl.isPrimitive()) {
    302             if (cl == Integer.TYPE) {
    303                 sbuf.append('I');
    304             } else if (cl == Byte.TYPE) {
    305                 sbuf.append('B');
    306             } else if (cl == Long.TYPE) {
    307                 sbuf.append('J');
    308             } else if (cl == Float.TYPE) {
    309                 sbuf.append('F');
    310             } else if (cl == Double.TYPE) {
    311                 sbuf.append('D');
    312             } else if (cl == Short.TYPE) {
    313                 sbuf.append('S');
    314             } else if (cl == Character.TYPE) {
    315                 sbuf.append('C');
    316             } else if (cl == Boolean.TYPE) {
    317                 sbuf.append('Z');
    318             } else if (cl == Void.TYPE) {
    319                 sbuf.append('V');
    320             } else {
    321                 throw new InternalError();
    322             }
    323         } else {
    324             sbuf.append('L' + cl.getName().replace('.', '/') + ';');
    325         }
    326         return sbuf.toString();
    327     }
    328 }
    329