package org.openejb.util.io;


import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectOutput;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamConstants;
import java.io.ObjectStreamField;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;

public class FieldDescriptor implements java.io.Serializable, Comparable {


    //================================================================
    //  Methods and fields to suppport the basic properties that will
    //  be written to or dread from the stream.
    //  this FieldDescriptor belongs to.
    //
    //====

    public FieldDescriptor(Field field){
        this.field = field;
        this.name = field.getName();
        this.type = field.getType();

       	if (type.isPrimitive()) {
    	    if      (type == Integer.TYPE)   typeCode = 'I';
  	    else if (type == Byte.TYPE)      typeCode = 'B';
    	    else if (type == Long.TYPE)      typeCode = 'J';
    	    else if (type == Float.TYPE)     typeCode = 'F';
    	    else if (type == Double.TYPE)    typeCode = 'D';
    	    else if (type == Short.TYPE)     typeCode = 'S';
    	    else if (type == Character.TYPE) typeCode = 'C';
    	    else if (type == Boolean.TYPE)   typeCode = 'Z';
    	    else if (type == Void.TYPE)      typeCode = 'V';
    	}
    	else if (type.isArray()) {
    	    typeCode = '[';
            typeString = ClassDescriptor.getSignature(type).toString().intern();
    	}
    	else {
    	    typeCode = 'L';
    	    StringBuffer buf = new StringBuffer();
    	    buf.append(typeCode);
    	    buf.append(type.getName().replace('.', '/'));
    	    buf.append(';');
    	    typeString = buf.toString().intern();
    	}

    }

    public FieldDescriptor(String name, Class type){

    }

    protected String typeString;

    public String getTypeString(){
        return typeString;
    }

    /*
     * The name of this field
     */
    protected String name;

    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }

    protected Field field;

    public Field getField(){
        return field;
    }

    public void setField(Field field){
        this.field = field;
    }

    protected char typeCode;

    public char getTypeCode(){
        return typeCode;
    }

    public void setTypeCode(char typeCode){
        this.typeCode = typeCode;
    }

    protected Class type;

    /**
     * Compare this field with another <code>FieldDescriptor</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.
     */
    // This method is called quite often. Declaring
    // field2 outside of the method prevents us from
    // burning trough these objects for every comparison
    // necessary to sort a list of these fields.
    public int compareTo(Object o) {
    	FieldDescriptor f2 = (FieldDescriptor)o;
    	boolean thisprim = (this.typeString == null);
    	boolean otherprim = (f2.typeString == null);

    	if (thisprim != otherprim) {
    	    return (thisprim ? -1 : 1);
    	}
    	return this.name.compareTo(f2.name);
    }

    //================================================================
    //  Methods and fields to suppport tracking of the ClassDescriptor
    //  this FieldDescriptor belongs to.
    //
    //====

    protected ClassDescriptor classDesc;

    public ClassDescriptor getClassDescriptor(){
        return classDesc;
    }

    public void setClassDescriptor(ClassDescriptor classDesc){
        this.classDesc = classDesc;
    }

    public void writeDesc(ObjectOutputStream out) throws IOException {
            out.writeByte((int)typeCode);
            out.writeUTF(name);
            if (!type.isPrimitive()) out.writeString(typeString);
    }

    public void write(Object o, ObjectOutputStream out) throws IOException, InvalidClassException {

	    if (field == null) throw new InvalidClassException(classDesc.forClass().getName(), "Nonexistent field " + name);
	    try {
		switch (typeCode) {
    		case 'B': out.writeByte(field.getByte(o));        break;
    		case 'C': out.writeChar(field.getChar(o));        break;
    		case 'I': out.writeInt(field.getInt(o));          break;
    		case 'Z': out.writeBoolean(field.getBoolean(o));  break;
    		case 'J': out.writeLong(field.getLong(o));	      break;
    		case 'F': out.writeFloat(field.getFloat(o));      break;
    		case 'D': out.writeDouble(field.getDouble(o));    break;
    		case 'S': out.writeShort(field.getShort(o)); 	  break;
    		case '[':
    		case 'L': out.writeObject(field.get(o));          break;
    		default: throw new InvalidClassException(classDesc.forClass().getName());
		}
	    }
	    catch (IllegalAccessException e) {
		throw new InvalidClassException(classDesc.forClass().getName(), e.getMessage());
	    }
	}
}