com.venky.swf.db.table.ModelInvocationHandler Maven / Gradle / Ivy
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.venky.swf.db.table;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.venky.cache.Cache;
import com.venky.core.collections.IgnoreCaseList;
import com.venky.core.collections.SequenceSet;
import com.venky.core.log.TimerStatistics.Timer;
import com.venky.core.string.StringUtil;
import com.venky.extension.Registry;
import com.venky.swf.db.Database;
import com.venky.swf.db.JdbcTypeHelper.TypeConverter;
import com.venky.swf.db.JdbcTypeHelper.TypeRef;
import com.venky.swf.db.annotations.column.COLUMN_DEF;
import com.venky.swf.db.annotations.column.defaulting.StandardDefaulter;
import com.venky.swf.db.annotations.column.relationship.CONNECTED_VIA;
import com.venky.swf.db.annotations.column.validations.processors.DateFormatValidator;
import com.venky.swf.db.annotations.column.validations.processors.EnumerationValidator;
import com.venky.swf.db.annotations.column.validations.processors.ExactLengthValidator;
import com.venky.swf.db.annotations.column.validations.processors.FieldValidator;
import com.venky.swf.db.annotations.column.validations.processors.MandatoryValidator;
import com.venky.swf.db.annotations.column.validations.processors.MaxLengthValidator;
import com.venky.swf.db.annotations.column.validations.processors.RegExValidator;
import com.venky.swf.db.model.Model;
import com.venky.swf.db.model.User;
import com.venky.swf.db.model.reflection.ModelReflector;
import com.venky.swf.db.table.Table.ColumnDescriptor;
import com.venky.swf.sql.Conjunction;
import com.venky.swf.sql.Delete;
import com.venky.swf.sql.Expression;
import com.venky.swf.sql.Insert;
import com.venky.swf.sql.Operator;
import com.venky.swf.sql.Select;
import com.venky.swf.sql.Update;
/**
*
* @author venky
*/
public class ModelInvocationHandler implements InvocationHandler {
private Record record = null;
private Model proxy = null;
private ModelReflector extends Model> reflector = null;
private List virtualFields = new IgnoreCaseList();
private String modelName = null;
public ModelReflector extends Model> getReflector() {
return reflector;
}
public String getModelName(){
return modelName;
}
public ModelInvocationHandler(Class extends Model> modelClass, Record record) {
this.record = record;
this.reflector = ModelReflector.instance(modelClass);
this.modelName = Table.getSimpleModelClassName(reflector.getTableName());
this.virtualFields = reflector.getVirtualFields();
record.startTracking();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Not Required. setProxy(getModelClass().cast(proxy));
String mName = method.getName();
Class> retType = method.getReturnType();
Class>[] parameters = method.getParameterTypes();
if (getReflector().getFieldGetters().contains(method)) {
String fieldName = getReflector().getFieldName(method);
if (!virtualFields.contains(fieldName)){
ColumnDescriptor cd = getReflector().getColumnDescriptor(method);
String columnName = cd.getName();
Object value = record.get(columnName);
TypeRef> ref =Database.getJdbcTypeHelper().getTypeRef(retType);
TypeConverter> converter = ref.getTypeConverter();
if (value == null) {
Object defaultValue = null;
COLUMN_DEF colDef = getReflector().getAnnotation(method,COLUMN_DEF.class);
if (colDef != null){
defaultValue = StandardDefaulter.getDefaultValue(colDef.value(),colDef.args());
}
return cd.isNullable() ? defaultValue : converter.valueOf(defaultValue);
} else if (retType.isInstance(value) && !ref.isLOB()) {
return value;
} else {
return converter.valueOf(value);
}
}
} else if (getReflector().getFieldSetters().contains(method) ) {
String fieldName = StringUtil.underscorize(mName.substring(3));
if (!virtualFields.contains(fieldName)){
String columnName = getReflector().getColumnDescriptor(fieldName).getName();
return record.put(columnName, args[0]);
}
} else if (getReflector().getReferredModelGetters().contains(method)) {
return getParent(method);
} else if (getReflector().getChildGetters().contains(method)) {
CONNECTED_VIA join = reflector.getAnnotation(method,CONNECTED_VIA.class);
if (join != null){
return getChildren(getReflector().getChildModelClass(method),join.value());
}else {
return getChildren(getReflector().getChildModelClass(method));
}
}
for (Object impl: modelImplObjects){
try {
Method inModelImplClass = impl.getClass().getMethod(mName, parameters);
if (retType.isAssignableFrom(inModelImplClass.getReturnType())){
Timer timer = Timer.startTimer(inModelImplClass.toString());
try {
return inModelImplClass.invoke(impl, args);
}finally{
timer.stop();
}
}
}catch(NoSuchMethodException ex){
//
}
}
Method inCurrentClass = this.getClass().getMethod(mName, parameters);
if (retType.isAssignableFrom(inCurrentClass.getReturnType())) {
return inCurrentClass.invoke(this, args);
} else {
throw new NoSuchMethodException("Donot know how to execute this method");
}
}
@SuppressWarnings("unchecked")
public P getParent(Method parentGetter) {
Class
parentClass = (Class
) parentGetter.getReturnType();
String parentIdFieldName = StringUtil.underscorize(parentGetter.getName().substring(3) +"Id");
Method parentIdGetter = this.reflector.getFieldGetter(parentIdFieldName);
Integer parentId;
try {
parentId = (Integer)parentIdGetter.invoke(proxy);
} catch (Exception e) {
throw new RuntimeException(parentIdFieldName,e);
}
P parent = null;
if (parentId != null) {
parent = Database.getTable(parentClass).get(parentId);
}
return parent;
}
public List getChildren(Class childClass){
List children = new ArrayList();
ModelReflector> childReflector = ModelReflector.instance(childClass);
for (String fieldName: childReflector.getFields()){
if (fieldName.endsWith(StringUtil.underscorize(getModelName() +"Id"))){
children.addAll(getChildren(childClass, fieldName));
}
}
return children;
}
public List getChildren(Class childClass, String parentIdFieldName){
int parentId = proxy.getId();
String parentIdColumnName = ModelReflector.instance(childClass).getColumnDescriptor(parentIdFieldName).getName();
Select q = new Select();
q.from(childClass);
q.where(new Expression(parentIdColumnName,Operator.EQ,new BindVariable(parentId)));
return q.execute(childClass);
}
public void setProxy(M proxy) {
this.proxy = proxy;
}
public M getProxy() {
return (M)proxy;
}
public boolean isAccessibleBy(User user){
return isAccessibleBy(user, getReflector().getModelClass());
}
public boolean isAccessibleBy(User user,Class extends Model> asModel){
Timer timer = Timer.startTimer();
try {
if (!getReflector().reflects(asModel)){
return false;
}
Map> fieldNameValues = user.getParticipationOptions(asModel);
if (fieldNameValues.isEmpty()){
return true;
}
for (String fieldName:fieldNameValues.keySet()){
List values = fieldNameValues.get(fieldName);
Object value = reflector.get(getProxy(), fieldName);
if (values.contains(value)) {
return true;
}
}
return false;
}finally{
timer.stop();
}
}
public Record getRawRecord(){
return record;
}
private static Map,List>> modelImplsMap = new HashMap, List>>();
@SuppressWarnings("unchecked")
public static M getProxy(Class modelClass, Record record) {
ModelReflector ref = ModelReflector.instance(modelClass);
List> modelImplClasses = modelImplsMap.get(modelClass) ;
if (modelImplClasses == null){
synchronized (modelImplsMap) {
modelImplClasses = modelImplsMap.get(modelClass);
if (modelImplClasses == null){
modelImplClasses = getModelImplClasses(modelClass);
modelImplsMap.put(modelClass, modelImplClasses);
}
}
}
try {
ModelInvocationHandler mImpl = new ModelInvocationHandler(modelClass, record);
M m = modelClass.cast(Proxy.newProxyInstance(modelClass.getClassLoader(), ref.getClassHierarchies().toArray(new Class>[]{}), mImpl));
mImpl.setProxy(m);
for (Class> implClass: modelImplClasses){
mImpl.addModelImplObject(constructImpl(implClass, m));
}
return m;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Object constructImpl(Class> implClass, Model m){
if (ModelImpl.class.isAssignableFrom(implClass)){
if (ModelImpl.class.equals(implClass)) {
return new ModelImpl(m);
}else {
ParameterizedType pt = (ParameterizedType)implClass.getGenericSuperclass();
Class extends Model> modelClass = (Class extends Model>) pt.getActualTypeArguments()[0];
try {
return implClass.getConstructor(modelClass).newInstance(m);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
throw new RuntimeException("Don't know how to instantiate " + implClass.getName());
}
private List