| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.io; |
| |
| import java.lang.ref.WeakReference; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| |
| import org.apache.harmony.misc.accessors.ObjectAccessor; |
| |
| /** |
| * Describes a field for the purpose of serialization. Classes can define the |
| * collection of fields that are serialized, which may be different from the set |
| * of all declared fields. |
| * |
| * @see ObjectOutputStream#writeFields() |
| * @see ObjectInputStream#readFields() |
| */ |
| public class ObjectStreamField implements Comparable<Object> { |
| |
| static final int FIELD_IS_NOT_RESOLVED = -1; |
| static final int FIELD_IS_ABSENT = -2; |
| |
| // Declared name of the field |
| private String name; |
| |
| // Declared type of the field |
| private Object type; |
| |
| // offset of this field in the object |
| int offset; |
| |
| // Cached version of intern'ed type String |
| private String typeString; |
| |
| private boolean unshared; |
| |
| private boolean isDeserialized; |
| |
| private long assocFieldID = FIELD_IS_NOT_RESOLVED; |
| |
| long getFieldID(ObjectAccessor accessor, Class<?> declaringClass) { |
| if (assocFieldID != FIELD_IS_NOT_RESOLVED) { |
| return assocFieldID; |
| } else { |
| try { |
| assocFieldID = accessor.getFieldID(declaringClass, name); |
| } catch(NoSuchFieldError e) { |
| assocFieldID = FIELD_IS_ABSENT; |
| } |
| return assocFieldID; |
| } |
| } |
| |
| /** |
| * Constructs an ObjectStreamField with the specified name and type. |
| * |
| * @param name |
| * the name of the field. |
| * @param cl |
| * the type of the field. |
| * @throws NullPointerException |
| * if {@code name} or {@code cl} is {@code null}. |
| */ |
| public ObjectStreamField(String name, Class<?> cl) { |
| if (name == null || cl == null) { |
| throw new NullPointerException(); |
| } |
| this.name = name; |
| this.type = new WeakReference<Class<?>>(cl); |
| } |
| |
| /** |
| * Constructs an ObjectStreamField with the specified name, type and the |
| * indication if it is unshared. |
| * |
| * @param name |
| * the name of the field. |
| * @param cl |
| * the type of the field. |
| * @param unshared |
| * {@code true} if the field is written and read unshared; |
| * {@code false} otherwise. |
| * @throws NullPointerException |
| * if {@code name} or {@code cl} is {@code null}. |
| * @see ObjectOutputStream#writeUnshared(Object) |
| */ |
| public ObjectStreamField(String name, Class<?> cl, boolean unshared) { |
| if (name == null || cl == null) { |
| throw new NullPointerException(); |
| } |
| this.name = name; |
| this.type = (cl.getClassLoader() == null) ? cl |
| : new WeakReference<Class<?>>(cl); |
| this.unshared = unshared; |
| } |
| |
| /** |
| * Constructs an ObjectStreamField with the given name and the given type. |
| * The type may be null. |
| * |
| * @param signature |
| * A String representing the type of the field |
| * @param name |
| * a String, the name of the field, or null |
| */ |
| ObjectStreamField(String signature, String name) { |
| if (name == null) { |
| throw new NullPointerException(); |
| } |
| this.name = name; |
| this.typeString = signature.replace('.', '/').intern(); |
| this.isDeserialized = true; |
| } |
| |
| /** |
| * Compares this field descriptor to the specified one. Checks first if one |
| * of the compared fields has a primitive type and the other one not. If so, |
| * the field with the primitive type is considered to be "smaller". If both |
| * fields are equal, their names are compared. |
| * |
| * @param o |
| * the object to compare with. |
| * @return -1 if this field is "smaller" than field {@code o}, 0 if both |
| * fields are equal; 1 if this field is "greater" than field {@code |
| * o}. |
| */ |
| public int compareTo(Object o) { |
| ObjectStreamField f = (ObjectStreamField) o; |
| boolean thisPrimitive = this.isPrimitive(); |
| boolean fPrimitive = f.isPrimitive(); |
| |
| // If one is primitive and the other isn't, we have enough info to |
| // compare |
| if (thisPrimitive != fPrimitive) { |
| return thisPrimitive ? -1 : 1; |
| } |
| |
| // Either both primitives or both not primitives. Compare based on name. |
| return this.getName().compareTo(f.getName()); |
| } |
| |
| @Override |
| public boolean equals(Object arg0) { |
| return (arg0 instanceof ObjectStreamField) && (compareTo(arg0) == 0); |
| } |
| |
| @Override |
| public int hashCode() { |
| return getName().hashCode(); |
| } |
| |
| /** |
| * Gets the name of this field. |
| * |
| * @return the field's name. |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Gets the offset of this field in the object. |
| * |
| * @return this field's offset. |
| */ |
| public int getOffset() { |
| return offset; |
| } |
| |
| /** |
| * Return the type of the field the receiver represents, this is an internal |
| * method |
| * |
| * @return A Class object representing the type of the field |
| */ |
| private Class<?> getTypeInternal() { |
| if (type instanceof WeakReference) { |
| return (Class<?>) ((WeakReference<?>) type).get(); |
| } |
| return (Class<?>) type; |
| } |
| |
| /** |
| * Gets the type of this field. |
| * |
| * @return a {@code Class} object representing the type of the field. |
| */ |
| public Class<?> getType() { |
| Class<?> cl = getTypeInternal(); |
| if (isDeserialized && !cl.isPrimitive()) { |
| return Object.class; |
| } |
| return cl; |
| } |
| |
| /** |
| * Gets a character code for the type of this field. The following codes are |
| * used: |
| * |
| * <pre> |
| * B byte |
| * C char |
| * D double |
| * F float |
| * I int |
| * J long |
| * L class or interface |
| * S short |
| * Z boolean |
| * [ array |
| * </pre> |
| * |
| * @return the field's type code. |
| */ |
| public char getTypeCode() { |
| Class<?> t = getTypeInternal(); |
| if (t == Integer.TYPE) { |
| return 'I'; |
| } |
| if (t == Byte.TYPE) { |
| return 'B'; |
| } |
| if (t == Character.TYPE) { |
| return 'C'; |
| } |
| if (t == Short.TYPE) { |
| return 'S'; |
| } |
| if (t == Boolean.TYPE) { |
| return 'Z'; |
| } |
| if (t == Long.TYPE) { |
| return 'J'; |
| } |
| if (t == Float.TYPE) { |
| return 'F'; |
| } |
| if (t == Double.TYPE) { |
| return 'D'; |
| } |
| if (t.isArray()) { |
| return '['; |
| } |
| return 'L'; |
| } |
| |
| /** |
| * Gets the type signature used by the VM to represent the type of this |
| * field. |
| * |
| * @return the signature of this field's class or {@code null} if this |
| * field's type is primitive. |
| */ |
| public String getTypeString() { |
| if (isPrimitive()) { |
| return null; |
| } |
| if (typeString == null) { |
| Class<?> t = getTypeInternal(); |
| String typeName = t.getName().replace('.', '/'); |
| String str = (t.isArray()) ? typeName : ("L" + typeName + ';'); //$NON-NLS-1$ |
| typeString = str.intern(); |
| } |
| return typeString; |
| } |
| |
| /** |
| * Indicates whether this field's type is a primitive type. |
| * |
| * @return {@code true} if this field's type is primitive; {@code false} if |
| * the type of this field is a regular class. |
| */ |
| public boolean isPrimitive() { |
| Class<?> t = getTypeInternal(); |
| return t != null && t.isPrimitive(); |
| } |
| |
| /** |
| * Sets this field's offset in the object. |
| * |
| * @param newValue |
| * the field's new offset. |
| */ |
| protected void setOffset(int newValue) { |
| this.offset = newValue; |
| } |
| |
| /** |
| * Returns a string containing a concise, human-readable description of this |
| * field descriptor. |
| * |
| * @return a printable representation of this descriptor. |
| */ |
| @Override |
| public String toString() { |
| return this.getClass().getName() + '(' + getName() + ':' |
| + getTypeInternal() + ')'; |
| } |
| |
| /** |
| * Sorts the fields for dumping. Primitive types come first, then regular |
| * types. |
| * |
| * @param fields |
| * ObjectStreamField[] fields to be sorted |
| */ |
| static void sortFields(ObjectStreamField[] fields) { |
| // Sort if necessary |
| if (fields.length > 1) { |
| Comparator<ObjectStreamField> fieldDescComparator = new Comparator<ObjectStreamField>() { |
| public int compare(ObjectStreamField f1, ObjectStreamField f2) { |
| return f1.compareTo(f2); |
| } |
| }; |
| Arrays.sort(fields, fieldDescComparator); |
| } |
| } |
| |
| void resolve(ClassLoader loader) { |
| if (typeString.length() == 1) { |
| switch (typeString.charAt(0)) { |
| case 'I': |
| type = Integer.TYPE; |
| return; |
| case 'B': |
| type = Byte.TYPE; |
| return; |
| case 'C': |
| type = Character.TYPE; |
| return; |
| case 'S': |
| type = Short.TYPE; |
| return; |
| case 'Z': |
| type = Boolean.TYPE; |
| return; |
| case 'J': |
| type = Long.TYPE; |
| return; |
| case 'F': |
| type = Float.TYPE; |
| return; |
| case 'D': |
| type = Double.TYPE; |
| return; |
| } |
| } |
| String className = typeString.replace('/', '.'); |
| if (className.charAt(0) == 'L') { |
| // remove L and ; |
| className = className.substring(1, className.length() - 1); |
| } |
| try { |
| Class<?> cl = Class.forName(className, false, loader); |
| type = (cl.getClassLoader() == null) ? cl |
| : new WeakReference<Class<?>>(cl); |
| } catch (ClassNotFoundException e) { |
| // Ignored |
| } |
| } |
| |
| /** |
| * Indicats whether this field is unshared. |
| * |
| * @return {@code true} if this field is unshared, {@code false} otherwise. |
| */ |
| public boolean isUnshared() { |
| return unshared; |
| } |
| |
| void setUnshared(boolean unshared) { |
| this.unshared = unshared; |
| } |
| } |