/*
 
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 
*
 
* This code is free software; you can redistribute it and/or modify it
 
* under the terms of the GNU General Public License version 2 only, as
 
* published by the Free Software Foundation.
  
Oracle designates this
 
* particular file as subject to the "Classpath" exception as provided
 
* by Oracle in the LICENSE file that accompanied this code.
 
*
 
* This code is distributed in the hope that it will be useful, but WITHOUT
 
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
* FITNESS FOR A PARTICULAR PURPOSE.
  
See the GNU General Public License
 
* version 2 for more details (a copy is included in the LICENSE file that
 
* accompanied this code).
 
*
 
* You should have received a copy of the GNU General Public License version
 
* 2 along with this work; if not, write to the Free Software Foundation,
 
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
*
 
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 
* or visit www.oracle.com if you need additional information or have any
 
* questions.
 
*/

package java.io;

import java.lang.reflect.Field;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.reflect.misc.ReflectUtil;

/**
 
* A description of a Serializable field from a Serializable class.
  
An array
 
* of ObjectStreamFields is used to declare the Serializable fields of a class.
 
*
 
* @author
      
Mike Warres
 
* @author
      
Roger Riggs
 
* @see ObjectStreamClass
 
* @since 1.2
 
*/

public class ObjectStreamField
    
implements Comparable<Object>
{

    
/** field name */
    
private final String name;
    
/** canonical JVM signature of field type */
    
private final String signature;
    
/** field type (Object.class if unknown non-primitive type) */
    
private final Class<?> type;
    
/** whether or not to (de)serialize field values as unshared */
    
private final boolean unshared;
    
/** corresponding reflective field object, if any */
    
private final Field field;
    
/** offset of field value in enclosing field group */
    
private int offset = 0;

    
/**
     
* Create a Serializable field with the specified type.
  
This field should
     
* be documented with a <code>serialField</code> tag.
     
*
     
* @param
   
name the name of the serializable field
     
* @param
   
type the <code>Class</code> object of the serializable field
     
*/

    
public ObjectStreamField(String name, Class<?> type) {
        
this(name, type, false);
    
}

    
/**
     
* Creates an ObjectStreamField representing a serializable field with the
     
* given name and type.
  
If unshared is false, values of the represented
     
* field are serialized and deserialized in the default manner--if the
     
* field is non-primitive, object values are serialized and deserialized as
     
* if they had been written and read by calls to writeObject and
     
* readObject.
  
If unshared is true, values of the represented field are
     
* serialized and deserialized as if they had been written and read by
     
* calls to writeUnshared and readUnshared.
     
*
     
* @param
   
name field name
     
* @param
   
type field type
     
* @param
   
unshared if false, write/read field values in the same manner
     
*
          
as writeObject/readObject; if true, write/read in the same
     
*
          
manner as writeUnshared/readUnshared
     
* @since
   
1.4
     
*/

    
public ObjectStreamField(String name, Class<?> type, boolean unshared) {
        
if (name == null) {
            
throw new NullPointerException();
        
}
        
this.name = name;
        
this.type = type;
        
this.unshared = unshared;
        
signature = getClassSignature(type).intern();
        
field = null;
    
}

    
/**
     
* Creates an ObjectStreamField representing a field with the given name,
     
* signature and unshared setting.
     
*/

    
ObjectStreamField(String name, String signature, boolean unshared) {
        
if (name == null) {
            
throw new NullPointerException();
        
}
        
this.name = name;
        
this.signature = signature.intern();
        
this.unshared = unshared;
        
field = null;

        
switch (signature.charAt(0)) {
            
case 'Z': type = Boolean.TYPE; break;
            
case 'B': type = Byte.TYPE; break;
            
case 'C': type = Character.TYPE; break;
            
case 'S': type = Short.TYPE; break;
            
case 'I': type = Integer.TYPE; break;
            
case 'J': type = Long.TYPE; break;
            
case 'F': type = Float.TYPE; break;
            
case 'D': type = Double.TYPE; break;
            
case 'L':
            
case '[': type = Object.class; break;
            
default: throw new IllegalArgumentException("illegal signature");
        
}
    
}

    
/**
     
* Creates an ObjectStreamField representing the given field with the
     
* specified unshared setting.
  
For compatibility with the behavior of
     
* earlier serialization implementations, a "showType" parameter is
     
* necessary to govern whether or not a getType() call on this
     
* ObjectStreamField (if non-primitive) will return Object.class (as
     
* opposed to a more specific reference type).
     
*/

    
ObjectStreamField(Field field, boolean unshared, boolean showType) {
        
this.field = field;
        
this.unshared = unshared;
        
name = field.getName();
        
Class<?> ftype = field.getType();
        
type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
        
signature = getClassSignature(ftype).intern();
    
}

    
/**
     
* Get the name of this field.
     
*
     
* @return
  
a <code>String</code> representing the name of the serializable
     
*
          
field
     
*/

    
public String getName() {
        
return name;
    
}

    
/**
     
* Get the type of the field.
  
If the type is non-primitive and this
     
* <code>ObjectStreamField</code> was obtained from a deserialized {@link
     
* ObjectStreamClass} instance, then <code>Object.class</code> is returned.
     
* Otherwise, the <code>Class</code> object for the type of the field is
     
* returned.
     
*
     
* @return
  
a <code>Class</code> object representing the type of the
     
*
          
serializable field
     
*/

    
@CallerSensitive
    
public Class<?> getType() {
        
if (System.getSecurityManager() != null) {
            
Class<?> caller = Reflection.getCallerClass();
            
if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), type.getClassLoader())) {
                
ReflectUtil.checkPackageAccess(type);
            
}
        
}
        
return type;
    
}

    
/**
     
* Returns character encoding of field type.
  
The encoding is as follows:
     
* <blockquote><pre>
     
* B
            
byte
     
* C
            
char
     
* D
            
double
     
* F
            
float
     
* I
            
int
     
* J
            
long
     
* L
            
class or interface
     
* S
            
short
     
* Z
            
boolean
     
* [
            
array
     
* </pre></blockquote>
     
*
     
* @return
  
the typecode of the serializable field
     
*/

    
// REMIND: deprecate?
    
public char getTypeCode() {
        
return signature.charAt(0);
    
}

    
/**
     
* Return the JVM type signature.
     
*
     
* @return
  
null if this field has a primitive type.
     
*/

    
// REMIND: deprecate?
    
public String getTypeString() {
        
return isPrimitive() ? null : signature;
    
}

    
/**
     
* Offset of field within instance data.
     
*
     
* @return
  
the offset of this field
     
* @see #setOffset
     
*/

    
// REMIND: deprecate?
    
public int getOffset() {
        
return offset;
    
}

    
/**
     
* Offset within instance data.
     
*
     
* @param
   
offset the offset of the field
     
* @see #getOffset
     
*/

    
// REMIND: deprecate?
    
protected void setOffset(int offset) {
        
this.offset = offset;
    
}

    
/**
     
* Return true if this field has a primitive type.
     
*
     
* @return
  
true if and only if this field corresponds to a primitive type
     
*/

    
// REMIND: deprecate?
    
public boolean isPrimitive() {
        
char tcode = signature.charAt(0);
        
return ((tcode != 'L') && (tcode != '['));
    
}

    
/**
     
* Returns boolean value indicating whether or not the serializable field
     
* represented by this ObjectStreamField instance is unshared.
     
*
     
* @return {@code true} if this field is unshared
     
*
     
* @since 1.4
     
*/

    
public boolean isUnshared() {
        
return unshared;
    
}

    
/**
     
* Compare this field with another <code>ObjectStreamField</code>.
  
Return
     
* -1 if this is smaller, 0 if equal, 1 if greater.
  
Types that are
     
* primitives are "smaller" than object types.
  
If equal, the field names
     
* are compared.
     
*/

    
// REMIND: deprecate?
    
public int compareTo(Object obj) {
        
ObjectStreamField other = (ObjectStreamField) obj;
        
boolean isPrim = isPrimitive();
        
if (isPrim != other.isPrimitive()) {
            
return isPrim ? -1 : 1;
        
}
        
return name.compareTo(other.name);
    
}

    
/**
     
* Return a string that describes this field.
     
*/
    
public String toString() {
        
return signature + ' ' + name;
    
}

    
/**
     
* Returns field represented by this ObjectStreamField, or null if
     
* ObjectStreamField is not associated with an actual field.
     
*/

    
Field getField() {
        
return field;
    
}

    
/**
     
* Returns JVM type signature of field (similar to getTypeString, except
     
* that signature strings are returned for primitive fields as well).
     
*/

    
String getSignature() {
        
return signature;
    
}

    
/**
     
* Returns JVM type signature for given class.
     
*/
    
private static String getClassSignature(Class<?> cl) {
        
StringBuilder sbuf = new StringBuilder();
        
while (cl.isArray()) {
            
sbuf.append('[');
            
cl = cl.getComponentType();
        
}
        
if (cl.isPrimitive()) {
            
if (cl == Integer.TYPE) {
                
sbuf.append('I');
            
} else if (cl == Byte.TYPE) {
                
sbuf.append('B');
            
} else if (cl == Long.TYPE) {
                
sbuf.append('J');
            
} else if (cl == Float.TYPE) {
                
sbuf.append('F');
            
} else if (cl == Double.TYPE) {
                
sbuf.append('D');
            
} else if (cl == Short.TYPE) {
                
sbuf.append('S');
            
} else if (cl == Character.TYPE) {
                
sbuf.append('C');
            
} else if (cl == Boolean.TYPE) {
                
sbuf.append('Z');
            
} else if (cl == Void.TYPE) {
                
sbuf.append('V');
            
} else {
                
throw new InternalError();
            
}
        
} else {
            
sbuf.append('L' + cl.getName().replace('.', '/') + ';');
        
}
        
return sbuf.toString();
    
}
}