MicroSpaceAccessor.java

package org.microspace.specific;

import java.lang.reflect.Method;
import java.util.List;
import java.util.TreeMap;

import org.microspace.annotation.IndexType;
import org.microspace.annotation.Indexed;
import org.microspace.annotation.PartitionId;
import org.microspace.annotation.SpaceRecord;
import org.microspace.annotation.ThreadId;
import org.microspace.exception.IllegalOperationException;
import org.microspace.table.column.Accessor;
import org.microspace.table.column.GetSetPair;

/**
 * 
 * This ia a method based accessor.
 * 
 * @author Gaspar Sinai	
 * @version 2012-06-01
 *
 */
public class MicroSpaceAccessor<T> implements Accessor<T> {

	private GetSetPair<T> primaryKeyGetter;
	private GetSetPair<T> threadIdGetter;
	private GetSetPair<T> partitionIdGetter;
	
	private List<GetSetPair<T>> getSetPairs;
	private TreeMap<String, GetSetPair<T>> getSetPairsByName = new TreeMap<String, GetSetPair<T>> ();
	
	private Class<T> targetClass;
	
	private boolean localRecord;
	private boolean updatableRecord;
	
	public MicroSpaceAccessor (Class<T> targetClass) {
		load (targetClass, false);
	}
	public MicroSpaceAccessor (Class<T> targetClass, boolean ignoreAnnotations) {
		load (targetClass, ignoreAnnotations);
	}
		
	private void load (Class<T> targetClass, boolean ignoreAnnotations) {	
		this.targetClass = targetClass;
		
		Class<?> partitionIdType = null;
		
		NullValueExtractor extractor = new MicroSpaceNullValueExtractor();
		// Collect the public getXX methods.
		TreeMap<String, MethodBasedGetter<T>> getters = MethodUtils.scanGetters (targetClass, extractor);
		TreeMap<String, MethodBasedSetter<T>> setters = MethodUtils.scanSetters (targetClass);
		getSetPairs = MethodUtils.generateGetSetPairs (getters, setters);
		for (GetSetPair<T> pair : getSetPairs) {
			getSetPairsByName.put(pair.getName(), pair);
		}
		if (ignoreAnnotations) return;
		
		for (MethodBasedGetter<T> getter : getters.values()) {
			Indexed property = getter.getMethod().getAnnotation(Indexed.class);
			if (property != null) {
				getter.setIndexType(getIndexType(getter.getMethod()));
				if (property.primaryKey()) {
					if (primaryKeyGetter != null) {
						throw new IllegalArgumentException("Class "
								+ targetClass + " has multiple primary keys.");
					}
					primaryKeyGetter = getSetPairsByName.get(getter.getName());
				}
			}
			ThreadId threadId = getter.getMethod().getAnnotation(ThreadId.class);
			if (threadId != null) {
				if (threadIdGetter != null) {
					throw new IllegalArgumentException("Class "
							+ targetClass + " has multiple ThreadId.");
				}
				threadIdGetter = getSetPairsByName.get(getter.getName());
			}
			PartitionId partitionId = getter.getMethod().getAnnotation(PartitionId.class);
			if (partitionId != null) {
				if (partitionIdGetter != null) {
					throw new IllegalArgumentException("Class "
							+ targetClass + " has multiple partitionId.");
				}
				partitionIdGetter = getSetPairsByName.get(getter.getName());
				partitionIdType = getter.getReturnType();
			}
		}
		if (partitionIdType != null && !Integer.class.equals(partitionIdType)) {
			throw new IllegalArgumentException ("PartitionId is not Integer");
		}
		if (primaryKeyGetter == null) {
			throw new IllegalArgumentException ("No primary key defined for " + targetClass.getName());
		}
		localRecord = false;
		updatableRecord = false;
		if (targetClass.isAnnotationPresent(SpaceRecord.class)) {
			SpaceRecord annotation = targetClass.getAnnotation(SpaceRecord.class);
			localRecord = annotation.local();
			updatableRecord = annotation.updatable();
		}
	}
	
	public GetSetPair<T> getPrimaryKeyGetSetPair() {
		return primaryKeyGetter;
	}
	public GetSetPair<T> getThreadIdGetSetPair() {
		return threadIdGetter;
	}
	public GetSetPair<T> getPartitionIdGetSetPair() {
		return partitionIdGetter;
	}
	public List<GetSetPair<T>> getGetSetPairs() {
		return getSetPairs;
	}

	public GetSetPair<T> getGetSetPair (String name) {
		return getSetPairsByName.get(name);
	}


	public T getBlankObject () {
		return MethodUtils.getBlank (targetClass);
	}
	
	public Class<T> getTargetClass() {
		return targetClass;
	}
	
	protected IndexType getIndexType (Method method) {
		Indexed it = method.getAnnotation (Indexed.class);
		if (it != null && it.type() == IndexType.NONE) return null;
		if (it != null &&  it.type() != IndexType.AUTO) {
			if (it.type() == IndexType.SORTED && !Comparable.class.isAssignableFrom(method.getReturnType())){
				throw new IllegalOperationException ("SORTED index on " + method.getName() + " is not Comparable");
			}
			return it.type();
		}
		if (Comparable.class.isAssignableFrom(method.getReturnType())) {
			return IndexType.SORTED;
		} else {
			return IndexType.HASHED;
		}
	}
	
	@Override
	public boolean isLocalRecord() {
		return localRecord;
	}
	@Override
	public boolean isUpdatableRecord() {
		return updatableRecord;
	}

}