package se.unlogic.standardutils.dao;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import se.unlogic.standardutils.annotations.UnsupportedFieldTypeException;
import se.unlogic.standardutils.dao.annotations.DAOPopulate;
import se.unlogic.standardutils.dao.annotations.ManyToOne;
import se.unlogic.standardutils.populators.QueryParameterPopulator;
import se.unlogic.standardutils.reflection.ReflectionUtils;
import se.unlogic.standardutils.string.StringUtils;
public class DefaultManyToOneRelation<LocalType,RemoteType, RemoteKeyType> implements ManyToOneRelation<LocalType, RemoteType, RemoteKeyType>{
private final String columnName;
private final Field field;
private QueryParameterPopulator<RemoteKeyType> queryParameterPopulator;
private Method queryMethod;
private final Method resultSetMethod;
private final Field remoteKeyField;
private final AnnotatedDAOFactory daoFactory;
private AnnotatedDAO<RemoteType> annotatedDAO;
private QueryParameterFactory<RemoteType,RemoteKeyType> queryParameterFactory;
private final Class<RemoteType> remoteClass;
private final Class<RemoteKeyType> remoteRemoteKeyClass;
private final boolean autoGenerated;
private boolean initialized;
public DefaultManyToOneRelation(Class<LocalType> beanClass, Class<RemoteType> remoteClass, Class<RemoteKeyType> remoteKeyClass, Field field, Field remoteKeyField, DAOPopulate daoManaged, AnnotatedDAOFactory daoFactory, boolean autoGenerated) {
this.remoteClass = remoteClass;
this.remoteRemoteKeyClass = remoteKeyClass;
this.field = field;
this.remoteKeyField = remoteKeyField;
this.daoFactory = daoFactory;
this.autoGenerated = autoGenerated;
if(!StringUtils.isEmpty(daoManaged.columnName())){
this.columnName = daoManaged.columnName();
}else{
this.columnName = field.getName();
}
ReflectionUtils.fixFieldAccess(remoteKeyField);
//TODO use column instead!
resultSetMethod = ResultSetField.RESULTSET_METHODS.get(remoteKeyClass);
if(resultSetMethod == null){
throw new UnsupportedFieldTypeException("Unable to find resultset method for field " + remoteKeyField.getName() + " in " + remoteClass + " when creating many to one relation for field " + field.getName() + " in " + beanClass, field, ManyToOne.class, beanClass);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getColumnName()
*/
public String getColumnName(){
return columnName;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getQueryParameterPopulator()
*/
public QueryParameterPopulator<RemoteKeyType> getQueryParameterPopulator(){
if(queryParameterPopulator == null && queryMethod == null){
if(!initialized){
this.init();
}
this.queryParameterPopulator = annotatedDAO.getQueryParameterPopulator(remoteRemoteKeyClass);
}
return queryParameterPopulator;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getQueryMethod()
*/
public Method getQueryMethod(){
if(this.queryMethod == null){
this.queryMethod = PreparedStatementQueryMethods.getObjectQueryMethod();
}
return queryMethod;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getBeanValue(LocalType)
*/
@SuppressWarnings("unchecked")
public RemoteKeyType getBeanValue(LocalType bean){
try {
RemoteType subBean = (RemoteType) field.get(bean);
if(subBean == null){
return null;
}
return (RemoteKeyType)remoteKeyField.get(subBean);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getParamValue(java.lang.Object)
*/
@SuppressWarnings("unchecked")
public RemoteKeyType getParamValue(Object bean) {
try {
if(bean == null){
return null;
}
return (RemoteKeyType) remoteKeyField.get(bean);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#setValue(LocalType, java.sql.ResultSet, java.sql.Connection, java.lang.reflect.Field[])
*/
@SuppressWarnings("unchecked")
public void setValue(LocalType bean, ResultSet resultSet, Connection connection, RelationQuery relationQuery) throws SQLException{
try {
if(!initialized){
this.init();
}
RemoteKeyType keyValue = (RemoteKeyType)this.resultSetMethod.invoke(resultSet, this.columnName);
if(keyValue != null){
HighLevelQuery<RemoteType> query = new HighLevelQuery<RemoteType>();
query.addParameter(queryParameterFactory.getParameter(keyValue));
query.addRelations(relationQuery);
RemoteType remoteBeanInstance = annotatedDAO.get(query ,connection);
this.getField().set(bean, remoteBeanInstance);
}
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#add(LocalType, java.sql.Connection, java.lang.reflect.Field)
*/
@SuppressWarnings("unchecked")
public void add(LocalType bean, Connection connection, RelationQuery relationQuery) throws SQLException{
if(!initialized){
this.init();
}
try {
RemoteType remoteBean = (RemoteType) field.get(bean);
if(remoteBean != null){
this.annotatedDAO.add(remoteBean, connection, relationQuery);
}
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#update(LocalType, java.sql.Connection, java.lang.reflect.Field)
*/
@SuppressWarnings("unchecked")
public void update(LocalType bean, Connection connection, RelationQuery relationQuery) throws SQLException{
if(!initialized){
this.init();
}
try {
RemoteType remoteBean = (RemoteType) field.get(bean);
if(remoteBean != null){
this.annotatedDAO.addOrUpdate(remoteBean, connection, relationQuery);
}
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private void init() {
this.annotatedDAO = this.daoFactory.getDAO(remoteClass);
this.queryParameterFactory = annotatedDAO.getParamFactory(remoteKeyField, remoteRemoteKeyClass);
this.initialized = true;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getField()
*/
public Field getField() {
return field;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getBeanField()
*/
public Field getBeanField() {
return this.field;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getParamType()
*/
public Class<RemoteType> getParamType() {
return remoteClass;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#isAutoGenerated()
*/
public boolean isAutoGenerated() {
return autoGenerated;
}
public static <LT,RT,RKT> DefaultManyToOneRelation<LT, RT, RKT> getGenericInstance(Class<LT> beanClass, Class<RT> remoteClass, Class<RKT> remoteKeyClass, Field field, Field remoteField, DAOPopulate daoManaged, AnnotatedDAOFactory daoFactory, boolean autoGenerated){
return new DefaultManyToOneRelation<LT, RT, RKT>(beanClass, remoteClass, remoteKeyClass, field, remoteField, daoManaged, daoFactory, autoGenerated);
}
}