001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.io;
019:
020: import java.lang.ref.WeakReference;
021: import java.util.Arrays;
022: import java.util.Comparator;
023:
024: /**
025: * This class represents object fields that are saved to the stream, by
026: * serialization. Classes can define the collection of fields to be dumped,
027: * which can differ from the actual object's declared fields.
028: *
029: * @see ObjectOutputStream#writeFields()
030: * @see ObjectInputStream#readFields()
031: */
032: public class ObjectStreamField implements Comparable<Object> {
033:
034: // Declared name of the field
035: private String name;
036:
037: // Declared type of the field
038: private Object type;
039:
040: // offset of this field in the object
041: int offset;
042:
043: // Cached version of intern'ed type String
044: private String typeString;
045:
046: private boolean unshared;
047:
048: private boolean isDeserialized;
049:
050: /**
051: * Constructs an ObjectStreamField with the given name and the given type
052: *
053: * @param name
054: * a String, the name of the field
055: * @param cl
056: * A Class object representing the type of the field
057: */
058: public ObjectStreamField(String name, Class<?> cl) {
059: if (name == null || cl == null) {
060: throw new NullPointerException();
061: }
062: this .name = name;
063: this .type = new WeakReference<Class<?>>(cl);
064: }
065:
066: /**
067: * Constructs an ObjectStreamField with the given name and the given type
068: *
069: * @param name
070: * a String, the name of the field
071: * @param cl
072: * A Class object representing the type of the field
073: * @param unshared
074: * write and read the field unshared
075: */
076: public ObjectStreamField(String name, Class<?> cl, boolean unshared) {
077: if (name == null || cl == null) {
078: throw new NullPointerException();
079: }
080: this .name = name;
081: this .type = (cl.getClassLoader() == null) ? cl
082: : new WeakReference<Class<?>>(cl);
083: this .unshared = unshared;
084: }
085:
086: /**
087: * Constructs an ObjectStreamField with the given name and the given type.
088: * The type may be null.
089: *
090: * @param signature
091: * A String representing the type of the field
092: * @param name
093: * a String, the name of the field, or null
094: */
095: ObjectStreamField(String signature, String name) {
096: if (name == null) {
097: throw new NullPointerException();
098: }
099: this .name = name;
100: this .typeString = signature.replace('.', '/').intern();
101: this .isDeserialized = true;
102: }
103:
104: /**
105: * Comparing the receiver to the parameter, according to the Comparable
106: * interface.
107: *
108: * @param o
109: * The object to compare against
110: *
111: * @return -1 if the receiver is "smaller" than the parameter. 0 if the
112: * receiver is "equal" to the parameter. 1 if the receiver is
113: * "greater" than the parameter.
114: */
115: public int compareTo(Object o) {
116: ObjectStreamField f = (ObjectStreamField) o;
117: boolean this Primitive = this .isPrimitive();
118: boolean fPrimitive = f.isPrimitive();
119:
120: // If one is primitive and the other isn't, we have enough info to
121: // compare
122: if (this Primitive != fPrimitive) {
123: return this Primitive ? -1 : 1;
124: }
125:
126: // Either both primitives or both not primitives. Compare based on name.
127: return this .getName().compareTo(f.getName());
128: }
129:
130: @Override
131: public boolean equals(Object arg0) {
132: return (arg0 instanceof ObjectStreamField)
133: && (compareTo(arg0) == 0);
134: }
135:
136: @Override
137: public int hashCode() {
138: return getName().hashCode();
139: }
140:
141: /**
142: * Return the name of the field the receiver represents
143: *
144: * @return a String, the name of the field
145: */
146: public String getName() {
147: return name;
148: }
149:
150: /**
151: * Return the offset of this field in the object
152: *
153: * @return an int, the offset
154: */
155: public int getOffset() {
156: return offset;
157: }
158:
159: /**
160: * Return the type of the field the receiver represents, this is an internal
161: * method
162: *
163: * @return A Class object representing the type of the field
164: */
165: private Class<?> getTypeInternal() {
166: if (type instanceof WeakReference) {
167: return (Class<?>) ((WeakReference<?>) type).get();
168: }
169: return (Class<?>) type;
170: }
171:
172: /**
173: * Return the type of the field the receiver represents
174: *
175: * @return A Class object representing the type of the field
176: */
177: public Class<?> getType() {
178: Class<?> cl = getTypeInternal();
179: if (isDeserialized && !cl.isPrimitive()) {
180: return Object.class;
181: }
182: return cl;
183: }
184:
185: /**
186: * Return the type code that corresponds to the class the receiver
187: * represents
188: *
189: * @return A char, the typecode of the class
190: */
191: public char getTypeCode() {
192: Class<?> t = getTypeInternal();
193: if (t == Integer.TYPE) {
194: return 'I';
195: }
196: if (t == Byte.TYPE) {
197: return 'B';
198: }
199: if (t == Character.TYPE) {
200: return 'C';
201: }
202: if (t == Short.TYPE) {
203: return 'S';
204: }
205: if (t == Boolean.TYPE) {
206: return 'Z';
207: }
208: if (t == Long.TYPE) {
209: return 'J';
210: }
211: if (t == Float.TYPE) {
212: return 'F';
213: }
214: if (t == Double.TYPE) {
215: return 'D';
216: }
217: if (t.isArray()) {
218: return '[';
219: }
220: return 'L';
221: }
222:
223: /**
224: * Return the type signature used by the VM to represent the type for this
225: * field.
226: *
227: * @return A String, the signature for the class of this field.
228: */
229: public String getTypeString() {
230: if (isPrimitive()) {
231: return null;
232: }
233: if (typeString == null) {
234: Class<?> t = getTypeInternal();
235: String typeName = t.getName().replace('.', '/');
236: String str = (t.isArray()) ? typeName
237: : ("L" + typeName + ';'); //$NON-NLS-1$
238: typeString = str.intern();
239: }
240: return typeString;
241: }
242:
243: /**
244: * Return a boolean indicating whether the class of this field is a
245: * primitive type or not
246: *
247: * @return true if the type of this field is a primitive type false if the
248: * type of this field is a regular class.
249: */
250: public boolean isPrimitive() {
251: Class<?> t = getTypeInternal();
252: return t != null && t.isPrimitive();
253: }
254:
255: /**
256: * Set the offset this field represents in the object
257: *
258: * @param newValue
259: * an int, the offset
260: */
261: protected void setOffset(int newValue) {
262: this .offset = newValue;
263: }
264:
265: /**
266: * Answers a string containing a concise, human-readable description of the
267: * receiver.
268: *
269: * @return a printable representation for the receiver.
270: */
271: @Override
272: public String toString() {
273: return this .getClass().getName() + '(' + getName() + ':'
274: + getTypeInternal() + ')';
275: }
276:
277: /**
278: * Sorts the fields for dumping. Primitive types come first, then regular
279: * types.
280: *
281: * @param fields
282: * ObjectStreamField[] fields to be sorted
283: */
284: static void sortFields(ObjectStreamField[] fields) {
285: // Sort if necessary
286: if (fields.length > 1) {
287: Comparator<ObjectStreamField> fieldDescComparator = new Comparator<ObjectStreamField>() {
288: public int compare(ObjectStreamField f1,
289: ObjectStreamField f2) {
290: return f1.compareTo(f2);
291: }
292: };
293: Arrays.sort(fields, fieldDescComparator);
294: }
295: }
296:
297: void resolve(ClassLoader loader) {
298: if (typeString.length() == 1) {
299: switch (typeString.charAt(0)) {
300: case 'I':
301: type = Integer.TYPE;
302: return;
303: case 'B':
304: type = Byte.TYPE;
305: return;
306: case 'C':
307: type = Character.TYPE;
308: return;
309: case 'S':
310: type = Short.TYPE;
311: return;
312: case 'Z':
313: type = Boolean.TYPE;
314: return;
315: case 'J':
316: type = Long.TYPE;
317: return;
318: case 'F':
319: type = Float.TYPE;
320: return;
321: case 'D':
322: type = Double.TYPE;
323: return;
324: }
325: }
326: String className = typeString.replace('/', '.');
327: if (className.charAt(0) == 'L') {
328: // remove L and ;
329: className = className.substring(1, className.length() - 1);
330: }
331: try {
332: Class<?> cl = Class.forName(className, false, loader);
333: type = (cl.getClassLoader() == null) ? cl
334: : new WeakReference<Class<?>>(cl);
335: } catch (ClassNotFoundException e) {
336: // Ignored
337: }
338: }
339:
340: /**
341: * Answers whether this serialized field is unshared.
342: *
343: * @return true if the field is unshared, false otherwise.
344: */
345: public boolean isUnshared() {
346: return unshared;
347: }
348:
349: void setUnshared(boolean unshared) {
350: this.unshared = unshared;
351: }
352: }
|