• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
ObjectStreamField(String name, Class<?> cl)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      */
ObjectStreamField(String name, Class<?> cl, boolean unshared)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      */
ObjectStreamField(String signature, String name)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      */
compareTo(Object o)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      */
getName()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      */
getOffset()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
getTypeInternal()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      */
getType()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      */
getTypeCode()202     public char getTypeCode() {
203         return typeCodeOf(getTypeInternal());
204     }
205 
typeCodeOf(Class<?> type)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      */
getTypeString()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      */
isPrimitive()256     public boolean isPrimitive() {
257         Class<?> t = getTypeInternal();
258         return t != null && t.isPrimitive();
259     }
260 
writeField(DataOutputStream out)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      */
setOffset(int newValue)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
toString()285     public String toString() {
286         return this.getClass().getName() + '(' + getName() + ':' + getTypeInternal() + ')';
287     }
288 
resolve(ClassLoader loader)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      */
isUnshared()319     public boolean isUnshared() {
320         return unshared;
321     }
322 
setUnshared(boolean unshared)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      */
defaultResolve()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