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.invoke.util.BytecodeDescriptor;
  30 import sun.reflect.CallerSensitive;
  31 import sun.reflect.Reflection;
  32 import sun.reflect.misc.ReflectUtil;
  33 
  34 /**
  35  * A description of a Serializable field from a Serializable class.  An array
  36  * of ObjectStreamFields is used to declare the Serializable fields of a class.
  37  *
  38  * @author      Mike Warres
  39  * @author      Roger Riggs
  40  * @see ObjectStreamClass
  41  * @since 1.2
  42  */
  43 public class ObjectStreamField
  44     implements Comparable<Object>
  45 {
  46 
  47     /** field name */
  48     private final String name;
  49     /** canonical JVM signature of field type */
  50     private final String signature;
  51     /** field type (Object.class if unknown non-primitive type) */
  52     private final Class<?> type;
  53     /** whether or not to (de)serialize field values as unshared */
  54     private final boolean unshared;
  55     /** corresponding reflective field object, if any */
  56     private final Field field;
  57     /** offset of field value in enclosing field group */
  58     private int offset = 0;
  59 
  60     /**
  61      * Create a Serializable field with the specified type.  This field should
  62      * be documented with a <code>serialField</code> tag.
  63      *
  64      * @param   name the name of the serializable field
  65      * @param   type the <code>Class</code> object of the serializable field
  66      */
  67     public ObjectStreamField(String name, Class<?> type) {
  68         this(name, type, false);
  69     }
  70 
  71     /**
  72      * Creates an ObjectStreamField representing a serializable field with the
  73      * given name and type.  If unshared is false, values of the represented
  74      * field are serialized and deserialized in the default manner--if the
  75      * field is non-primitive, object values are serialized and deserialized as
  76      * if they had been written and read by calls to writeObject and
  77      * readObject.  If unshared is true, values of the represented field are
  78      * serialized and deserialized as if they had been written and read by
  79      * calls to writeUnshared and readUnshared.
  80      *
  81      * @param   name field name
  82      * @param   type field type
  83      * @param   unshared if false, write/read field values in the same manner
  84      *          as writeObject/readObject; if true, write/read in the same
  85      *          manner as writeUnshared/readUnshared
  86      * @since   1.4
  87      */
  88     public ObjectStreamField(String name, Class<?> type, boolean unshared) {
  89         if (name == null) {
  90             throw new NullPointerException();
  91         }
  92         this.name = name;
  93         this.type = type;
  94         this.unshared = unshared;
  95         signature = BytecodeDescriptor.unparse(type).intern();
  96         field = null;
  97     }
  98 
  99     /**
 100      * Creates an ObjectStreamField representing a field with the given name,
 101      * signature and unshared setting.
 102      */
 103     ObjectStreamField(String name, String signature, boolean unshared) {
 104         if (name == null) {
 105             throw new NullPointerException();
 106         }
 107         this.name = name;
 108         this.signature = signature.intern();
 109         this.unshared = unshared;
 110         field = null;
 111 
 112         switch (signature.charAt(0)) {
 113             case 'Z': type = Boolean.TYPE; break;
 114             case 'B': type = Byte.TYPE; break;
 115             case 'C': type = Character.TYPE; break;
 116             case 'S': type = Short.TYPE; break;
 117             case 'I': type = Integer.TYPE; break;
 118             case 'J': type = Long.TYPE; break;
 119             case 'F': type = Float.TYPE; break;
 120             case 'D': type = Double.TYPE; break;
 121             case 'L':
 122             case '[': type = Object.class; break;
 123             default: throw new IllegalArgumentException("illegal signature");
 124         }
 125     }
 126 
 127     /**
 128      * Creates an ObjectStreamField representing the given field with the
 129      * specified unshared setting.  For compatibility with the behavior of
 130      * earlier serialization implementations, a "showType" parameter is
 131      * necessary to govern whether or not a getType() call on this
 132      * ObjectStreamField (if non-primitive) will return Object.class (as
 133      * opposed to a more specific reference type).
 134      */
 135     ObjectStreamField(Field field, boolean unshared, boolean showType) {
 136         this.field = field;
 137         this.unshared = unshared;
 138         name = field.getName();
 139         Class<?> ftype = field.getType();
 140         type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
 141         signature = BytecodeDescriptor.unparse(ftype).intern();
 142     }
 143 
 144     /**
 145      * Get the name of this field.
 146      *
 147      * @return  a <code>String</code> representing the name of the serializable
 148      *          field
 149      */
 150     public String getName() {
 151         return name;
 152     }
 153 
 154     /**
 155      * Get the type of the field.  If the type is non-primitive and this
 156      * <code>ObjectStreamField</code> was obtained from a deserialized {@link
 157      * ObjectStreamClass} instance, then <code>Object.class</code> is returned.
 158      * Otherwise, the <code>Class</code> object for the type of the field is
 159      * returned.
 160      *
 161      * @return  a <code>Class</code> object representing the type of the
 162      *          serializable field
 163      */
 164     @CallerSensitive
 165     public Class<?> getType() {
 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         return type;
 173     }
 174 
 175     /**
 176      * Returns character encoding of field type.  The encoding is as follows:
 177      * <blockquote><pre>
 178      * B            byte
 179      * C            char
 180      * D            double
 181      * F            float
 182      * I            int
 183      * J            long
 184      * L            class or interface
 185      * S            short
 186      * Z            boolean
 187      * [            array
 188      * </pre></blockquote>
 189      *
 190      * @return  the typecode of the serializable field
 191      */
 192     // REMIND: deprecate?
 193     public char getTypeCode() {
 194         return signature.charAt(0);
 195     }
 196 
 197     /**
 198      * Return the JVM type signature.
 199      *
 200      * @return  null if this field has a primitive type.
 201      */
 202     // REMIND: deprecate?
 203     public String getTypeString() {
 204         return isPrimitive() ? null : signature;
 205     }
 206 
 207     /**
 208      * Offset of field within instance data.
 209      *
 210      * @return  the offset of this field
 211      * @see #setOffset
 212      */
 213     // REMIND: deprecate?
 214     public int getOffset() {
 215         return offset;
 216     }
 217 
 218     /**
 219      * Offset within instance data.
 220      *
 221      * @param   offset the offset of the field
 222      * @see #getOffset
 223      */
 224     // REMIND: deprecate?
 225     protected void setOffset(int offset) {
 226         this.offset = offset;
 227     }
 228 
 229     /**
 230      * Return true if this field has a primitive type.
 231      *
 232      * @return  true if and only if this field corresponds to a primitive type
 233      */
 234     // REMIND: deprecate?
 235     public boolean isPrimitive() {
 236         char tcode = signature.charAt(0);
 237         return ((tcode != 'L') && (tcode != '['));
 238     }
 239 
 240     /**
 241      * Returns boolean value indicating whether or not the serializable field
 242      * represented by this ObjectStreamField instance is unshared.
 243      *
 244      * @return {@code true} if this field is unshared
 245      *
 246      * @since 1.4
 247      */
 248     public boolean isUnshared() {
 249         return unshared;
 250     }
 251 
 252     /**
 253      * Compare this field with another <code>ObjectStreamField</code>.  Return
 254      * -1 if this is smaller, 0 if equal, 1 if greater.  Types that are
 255      * primitives are "smaller" than object types.  If equal, the field names
 256      * are compared.
 257      */
 258     // REMIND: deprecate?
 259     public int compareTo(Object obj) {
 260         ObjectStreamField other = (ObjectStreamField) obj;
 261         boolean isPrim = isPrimitive();
 262         if (isPrim != other.isPrimitive()) {
 263             return isPrim ? -1 : 1;
 264         }
 265         return name.compareTo(other.name);
 266     }
 267 
 268     /**
 269      * Return a string that describes this field.
 270      */
 271     public String toString() {
 272         return signature + ' ' + name;
 273     }
 274 
 275     /**
 276      * Returns field represented by this ObjectStreamField, or null if
 277      * ObjectStreamField is not associated with an actual field.
 278      */
 279     Field getField() {
 280         return field;
 281     }
 282 
 283     /**
 284      * Returns JVM type signature of field (similar to getTypeString, except
 285      * that signature strings are returned for primitive fields as well).
 286      */
 287     String getSignature() {
 288         return signature;
 289     }
 290 }