package relational;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Hashtable;
import Tools.*;

/**
 * An object that identifies a field of a ``table'' (class).
 * These come in a few varieties.
 */
public abstract class FieldDescriptor
	implements Serializable {

	// Doesn't work if f.getDeclaringClass() is abstract -- that's
	// not a relational table.
	// TODO: InternalDatabase will "fail" silently (bad indexes) when
	// trying to play around with subclasses of nonabstract classes
	// used as tables.
	public static FieldDescriptor get(Field f) {
		return get(f, f.getDeclaringClass());
	}

	/**
	 * Field descriptor for field f in class c (even if f is a member
	 * of a superclass of c)
	 */
	public static FieldDescriptor get(Field f, Class c) {
		Tools.Assert.assert((c.getModifiers() & Modifier.ABSTRACT)==0);
		HashKey hk = new HashKey();
		hk.addElement(f);
		hk.addElement(c);
		FieldDescriptor fd = (FieldDescriptor) cache.get(hk);
		if (fd==null) {
			fd = FieldDescriptorField.get(f, c);
			cache.put(hk, fd);
		}
		return fd;
	}

	/**
	 * A field that refers to the object defining the row
	 */
	public static FieldDescriptor get(Class c) {
		FieldDescriptor fd = (FieldDescriptor) cache.get(c);
		if (fd==null) {
			fd = FieldDescriptorReference.get(c);
			cache.put(c, fd);
		}
		return fd;
	}

	public static FieldDescriptor getPrimaryKey(Class c) {
		FieldDescriptor fd = null;
		try {
			Field f = c.getField("primaryKey");
			fd = get(f, c);
		} catch (NoSuchFieldException ex) {
			throw new RuntimeException("Can't find primary key of class "+c);
		}
		return fd;
	}

	public abstract Object get(Row source);

	/**
	 * which table declares this field
	 */
	public abstract Class getDeclaringClass();

	/**
	 * which table (Class) this field's value belongs to
	 */
	public abstract Class getType();

	protected static Hashtable cache = new Hashtable();
}
