View Javadoc
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         if (System.getSecurityManager() != null) {
166             Class<?> caller = Reflection.getCallerClass();
167             if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), type.getClassLoader())) {
168                 ReflectUtil.checkPackageAccess(type);
169             }
170         }
171         return type;
172     }
173 
174     /**
175      * Returns character encoding of field type.  The encoding is as follows:
176      * <blockquote><pre>
177      * B            byte
178      * C            char
179      * D            double
180      * F            float
181      * I            int
182      * J            long
183      * L            class or interface
184      * S            short
185      * Z            boolean
186      * [            array
187      * </pre></blockquote>
188      *
189      * @return  the typecode of the serializable field
190      */
191     // REMIND: deprecate?
192     public char getTypeCode() {
193         return signature.charAt(0);
194     }
195 
196     /**
197      * Return the JVM type signature.
198      *
199      * @return  null if this field has a primitive type.
200      */
201     // REMIND: deprecate?
202     public String getTypeString() {
203         return isPrimitive() ? null : signature;
204     }
205 
206     /**
207      * Offset of field within instance data.
208      *
209      * @return  the offset of this field
210      * @see #setOffset
211      */
212     // REMIND: deprecate?
213     public int getOffset() {
214         return offset;
215     }
216 
217     /**
218      * Offset within instance data.
219      *
220      * @param   offset the offset of the field
221      * @see #getOffset
222      */
223     // REMIND: deprecate?
224     protected void setOffset(int offset) {
225         this.offset = offset;
226     }
227 
228     /**
229      * Return true if this field has a primitive type.
230      *
231      * @return  true if and only if this field corresponds to a primitive type
232      */
233     // REMIND: deprecate?
234     public boolean isPrimitive() {
235         char tcode = signature.charAt(0);
236         return ((tcode != 'L') && (tcode != '['));
237     }
238 
239     /**
240      * Returns boolean value indicating whether or not the serializable field
241      * represented by this ObjectStreamField instance is unshared.
242      *
243      * @return {@code true} if this field is unshared
244      *
245      * @since 1.4
246      */
247     public boolean isUnshared() {
248         return unshared;
249     }
250 
251     /**
252      * Compare this field with another <code>ObjectStreamField</code>.  Return
253      * -1 if this is smaller, 0 if equal, 1 if greater.  Types that are
254      * primitives are "smaller" than object types.  If equal, the field names
255      * are compared.
256      */
257     // REMIND: deprecate?
258     public int compareTo(Object obj) {
259         ObjectStreamField other = (ObjectStreamField) obj;
260         boolean isPrim = isPrimitive();
261         if (isPrim != other.isPrimitive()) {
262             return isPrim ? -1 : 1;
263         }
264         return name.compareTo(other.name);
265     }
266 
267     /**
268      * Return a string that describes this field.
269      */
270     public String toString() {
271         return signature + ' ' + name;
272     }
273 
274     /**
275      * Returns field represented by this ObjectStreamField, or null if
276      * ObjectStreamField is not associated with an actual field.
277      */
278     Field getField() {
279         return field;
280     }
281 
282     /**
283      * Returns JVM type signature of field (similar to getTypeString, except
284      * that signature strings are returned for primitive fields as well).
285      */
286     String getSignature() {
287         return signature;
288     }
289 
290     /**
291      * Returns JVM type signature for given class.
292      */
293     private static String getClassSignature(Class<?> cl) {
294         StringBuilder sbuf = new StringBuilder();
295         while (cl.isArray()) {
296             sbuf.append('[');
297             cl = cl.getComponentType();
298         }
299         if (cl.isPrimitive()) {
300             if (cl == Integer.TYPE) {
301                 sbuf.append('I');
302             } else if (cl == Byte.TYPE) {
303                 sbuf.append('B');
304             } else if (cl == Long.TYPE) {
305                 sbuf.append('J');
306             } else if (cl == Float.TYPE) {
307                 sbuf.append('F');
308             } else if (cl == Double.TYPE) {
309                 sbuf.append('D');
310             } else if (cl == Short.TYPE) {
311                 sbuf.append('S');
312             } else if (cl == Character.TYPE) {
313                 sbuf.append('C');
314             } else if (cl == Boolean.TYPE) {
315                 sbuf.append('Z');
316             } else if (cl == Void.TYPE) {
317                 sbuf.append('V');
318             } else {
319                 throw new InternalError();
320             }
321         } else {
322             sbuf.append('L' + cl.getName().replace('.', '/') + ';');
323         }
324         return sbuf.toString();
325     }
326 }