package com.salama.android.webcore;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import MetoXML.XmlDeserializer;
import MetoXML.Base.XmlParseException;
import MetoXML.Cast.BaseTypesMapping;
import MetoXML.Util.PropertyDescriptor;
import android.util.Log;
import com.salama.android.support.ServiceSupportApplication;
import com.salama.android.util.SSLog;
public class NativeService {
protected static final String JS_PREFIX_NATIVE_SERVICE = "nativeService://";
protected static final String JS_PREFIX_NATIVE_SERVICE_LOWER = "nativeservice://";
protected static final String SPECIAL_SERVICE_THIS_VIEW = "thisView";
private HashMap<String, Object> _targetDict = new HashMap<String, Object>();
/**
* 解析指令
* @param cmd 指令内容
* @return InvokeMsg或List<InvokeMsg>
*/
public static Object parseNativeServiceCmd(String cmd) {
if(cmd.startsWith(JS_PREFIX_NATIVE_SERVICE)
|| cmd.startsWith(JS_PREFIX_NATIVE_SERVICE_LOWER)) {
return InvokeMsg.invokeMsgWithXml(InvokeMsg.decodeURLString(cmd.substring(16)));
} else {
return null;
}
}
/**
* 注册service
* @param serviceName service名称
* @param service service实例
*/
public void registerService(String serviceName, Object service) {
_targetDict.put(serviceName, service);
}
/**
* 调用
* @param targetName 目标对象
* @param methodName 方法名
* @param params 参数列表
* @param thisView thisView
* @return 调用返回值
* @throws InvocationTargetException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws XmlParseException
* @throws IOException
* @throws ParseException
*/
public Object invoke(String targetName, String methodName,
List<String> params, Object thisView) throws InvocationTargetException,
NoSuchMethodException, IllegalAccessException, InstantiationException,
XmlParseException, IOException, ParseException {
return invokeImp(targetName, methodName, params, thisView);
}
private Object invokeImp(String targetName, String methodName,
List<String> params, Object thisView) throws InvocationTargetException,
NoSuchMethodException, IllegalAccessException, InstantiationException,
XmlParseException, IOException, ParseException {
Object target = null;
if(SPECIAL_SERVICE_THIS_VIEW.equals(targetName)) {
target = thisView;
} else {
target = findTarget(targetName, thisView);
}
int paramsCount = 0;
if(params != null) {
paramsCount = params.size();
}
if(target == null) {
SSLog.e("NativeService", "invokeImp() Not found target:" + targetName);
return "";
} else {
SSLog.d("NativeService", "invokeImp() target:" + targetName + " targetType:" + target.getClass().getSimpleName());
}
Method method = findMethod(target, methodName, paramsCount);
if(method == null) {
Log.e("NativeService", "invoke() Not found method:" + methodName);
return "";
}
Class<?>[] paramTypes = method.getParameterTypes();
Object[] paramValues = new Object[paramTypes.length];
Class<?> paramType;
String paramXml;
String param;
String xmlTagName;
int indexFirstTag;
int index2FirstTag;
for(int i = 0; i < paramTypes.length; i++) {
paramType = paramTypes[i];
paramXml = params.get(i);
SSLog.d("NativeService()", "paramType[" + i + "]:" + paramType + " paramXml:" + paramXml);
if(paramXml.length() == 0) {
paramValues[i] = null;
continue;
}
indexFirstTag = paramXml.indexOf('<');
index2FirstTag = paramXml.indexOf('>', indexFirstTag);
xmlTagName = paramXml.substring(indexFirstTag + 1, index2FirstTag);
if(xmlTagName.equals("String")) {
param = (String) XmlDeserializer.stringToObject(paramXml, String.class,
ServiceSupportApplication.singleton());
if(param.startsWith("$$")) {
//decode: "$$" -> "$"
paramValues[i] = param.substring(1);
} else if (param.startsWith("$")) {
//value stack
paramValues[i] = findValueFromWebVariableStack(
param.substring(1), (WebVariableStack)thisView);
} else {
paramValues[i] = BaseTypesMapping.Convert(paramType, param);
}
} else if (xmlTagName.equals("Object")) {
paramValues[i] = XmlDeserializer.stringToObject(
paramXml, paramType, ServiceSupportApplication.singleton());
} else if (xmlTagName.equals("List")) {
paramValues[i] = XmlDeserializer.stringToObject(
paramXml, ArrayList.class, ServiceSupportApplication.singleton());
} else if (xmlTagName.equals("double")) {
param = (String) XmlDeserializer.stringToObject(paramXml, String.class,
ServiceSupportApplication.singleton());
paramValues[i] = BaseTypesMapping.Convert(paramType, param);
} else {
paramValues[i] = XmlDeserializer.stringToObject(
paramXml, paramType, ServiceSupportApplication.singleton());
}
}
//invoke
if(method.getReturnType() == void.class) {
method.invoke(target, paramValues);
return null;
} else {
Object returnVal = method.invoke(target, paramValues);
return returnVal;
}
}
private Method findMethod(Object target, String methodName, int paramsCount) {
Method[] methods = target.getClass().getMethods();
Method method;
for(int i = 0; i < methods.length; i++) {
method = methods[i];
if(method.getName().equals(methodName)
&& method.getParameterTypes().length == paramsCount) {
return method;
}
}
return null;
}
private Object findTarget(String targetName, Object thisView) throws InvocationTargetException,
NoSuchMethodException, IllegalAccessException {
int index = targetName.indexOf('.');
Object targetObj = null;
String targetObjName = null;
if(index < 0) {
targetObjName = targetName;
} else {
targetObjName = targetName.substring(0, index);
}
if(SPECIAL_SERVICE_THIS_VIEW.equals(targetObjName)) {
targetObj = thisView;
} else if (targetObjName.startsWith("$")) {
//value stack
if(isInstanceAssignableToClass(thisView, WebVariableStack.class)) {
String varName = targetObjName.substring(1);
targetObj = findValueFromWebVariableStack(varName, (WebVariableStack)thisView);
if(targetObj == null) {
return null;
}
} else {
//not support variable stack
return null;
}
} else {
//dict
targetObj = _targetDict.get(targetObjName);
if(targetObj == null) {
return null;
}
}
if(index < 0) {
return targetObj;
} else {
String propertyNameTmp = null;
int previousIndex = index;
int indexTmp;
PropertyDescriptor propDesc = null;
while(true) {
indexTmp = targetName.indexOf('.', previousIndex + 1);
if(indexTmp < 0) {
propertyNameTmp = targetName.substring(previousIndex + 1);
propDesc = new PropertyDescriptor(propertyNameTmp, targetObj.getClass());
targetObj = propDesc.getReadMethod().invoke(targetObj, (Object[])null);
return targetObj;
} else {
propertyNameTmp = targetName.substring(previousIndex + 1, indexTmp);
propDesc = new PropertyDescriptor(propertyNameTmp, targetObj.getClass());
targetObj = propDesc.getReadMethod().invoke(targetObj, (Object[])null);
}
previousIndex = indexTmp;
}
}
}
private Object findValueFromWebVariableStack(String varName, WebVariableStack thisView) {
Object targetObj = null;
targetObj = thisView.getVariable(varName, WebVariableStack.WebVariableStackScopeTemp);
if(targetObj == null) {
targetObj = thisView.getVariable(varName, WebVariableStack.WebVariableStackScopePage);
}
if(targetObj == null && varName.equals(SPECIAL_SERVICE_THIS_VIEW)) {
return thisView;
} else {
return targetObj;
}
}
/**
* 检测对象实例是否可转换为指定类型
* @param obj 对象实例
* @param toClass 类型
* @return
*/
public static boolean isInstanceAssignableToClass(Object obj, Class<?> toClass) {
try {
Object obj2 = toClass.cast(obj);
return (obj2 == obj);
} catch(Exception e) {
return false;
}
}
}