/*
* Copyright 2015 Shashank Tulsyan.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package neembuu.rus.type;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import neembuu.rus.DefaultValue;
import neembuu.rus.MapRus;
import neembuu.rus.Rus;
import neembuu.rus.Rusila;
import neembuu.rus.Util;
import neembuu.rus.V;
import neembuu.rus.VImpl;
import neembuu.rus.VObj;
/**
*
* @author Shashank
*/
public final class TypeImplementor<E> implements InvocationHandler{
private final Rus r; private final TypeHandlerProvider thp;
private final boolean cachingEnabled;
private final HashMap cache = new HashMap();//reading each time from disk
// is more expensive, un-necessary and weird HOWEVER
// caching introduces clear possibility of not having the latest value
// if two separate objects of this are being used in different thread
// at the same time.
public TypeImplementor(Rus r, TypeHandlerProvider thp) {
this(r, thp, true);
}
public TypeImplementor(Rus r, TypeHandlerProvider thp,boolean cachingEnabled) {
this.r = r;
this.cachingEnabled = cachingEnabled;
this.thp = thp;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(!cachingEnabled)return invoke2(proxy, method, args);
Class retType = method.getReturnType();
synchronized (cache){
if(Util.isGetter(method, args)){
Object result = cache.get(method.getName());
if(result==null){
result = handleGettters(proxy, method, retType);
cache.put(method.getName(),result);
}return result;
}
if(Util.isSetter(method, args)){
handleSettters(proxy, method, args[0]);
cache.put(method.getName(),args[0]);
}
}
return null;
}
private Object invoke2(Object proxy, Method method, Object[] args) throws Throwable {
Class retType = method.getReturnType();
if(Util.isGetter(method, args)){
return handleGettters(proxy, method, retType);
}
if(Util.isSetter(method, args)){
handleSettters(proxy, method, args[0]);
}return null;
}
private Object handleGettters(Object proxy, Method m,Class retType){
DefaultValue dv = (DefaultValue)m.getAnnotation(DefaultValue.class);
if(r.isDirectory(m.getName())){
TypeHandler th = thp.provideFor(retType);
if(th!=null)return th.handle(r.r(m.getName()), dv);
else {
Rusila r1 = Rusila.newInstance();
r1.r(r.r(m.getName()));
r1.thp(thp);
return r1.I(m.getReturnType());
}
}
V v;
if(r instanceof MapRus){
Object val = ((MapRus)r).get(m.getName());
v= new VObj(val);
}else{
v = Rusila.get(r, m.getName(),dv==null?4*1024:dv.maximumDataSize());
}
if(retType.isAssignableFrom(Double.TYPE)){
return v.d(dv==null?0d:dv.d());
}else if(retType.isAssignableFrom(Integer.TYPE)){
return v.i(dv==null?0:dv.i());
}else if(retType.isAssignableFrom(Long.TYPE)){
return v.l(dv==null?0:dv.l());
}else if(retType.isAssignableFrom(Boolean.TYPE)){
return v.b(dv==null?false:dv.b());
}else if(retType.isAssignableFrom(String.class)){
return v.s(dv==null?"":dv.s());
}else if(retType.isArray()){
return handleGettersArrays(proxy, m, retType,dv,v);
}
ValueHandler vh = thp.provideFor(retType,dv);
String tempValue; String dval = dv==null?"":dv.s();
if(v==null){
return null;
}
tempValue = v.s(dval);
return vh.handle(tempValue, r, m.getName(), dv);
}
static Object handleGettersArrays(Object proxy, Method m,Class retType,
DefaultValue dv,V v){
String values = v.s("");
String[]valA = values.replaceAll("\r", "").split("\n");
if(retType.isAssignableFrom(double[].class)){
if(v.isNull())return dv==null?new double[0]:dv.da();
double[]array = (double[]) Array.newInstance(double.class, valA.length);
for (int i = 0; i < valA.length; i++) {
array[i] = Double.parseDouble(valA[i]);
}
return array;
}else if(retType.isAssignableFrom(int[].class)){
if(v.isNull())return dv==null?new int[0]:dv.ia();
int[]array = (int[]) Array.newInstance(int.class, valA.length);
for (int i = 0; i < valA.length; i++) {
array[i] = Integer.parseInt(valA[i]);
}
return array;
}else if(retType.isAssignableFrom(long[].class)){
if(v.isNull())return dv==null?new long[0]:dv.la();
long[]array = (long[]) Array.newInstance(long.class, valA.length);
for (int i = 0; i < valA.length; i++) {
array[i] = Long.parseLong(valA[i]);
}
return array;
}else if(retType.isAssignableFrom(boolean[].class)){
if(v.isNull())return dv==null?new boolean[0]:dv.ba();
boolean[]array = (boolean []) Array.newInstance(boolean.class, valA.length);
for (int i = 0; i < valA.length; i++) {
array[i] = Boolean.parseBoolean(valA[i]);
}
return array;
}else if(retType.isAssignableFrom(String[].class)){
if(v.isNull())return dv==null?new String[0]:dv.sa();
return valA;
}
return null;
}
private void handleSettters(Object proxy, Method m, Object arg)throws Exception{
if(r instanceof MapRus){
((MapRus)r).put(m.getName(),arg);
return;
}
Rusila.set(r, m.getName(), arg);
}
}