org.zkoss.bind.impl.ParamCall Maven / Gradle / Ivy
package org.zkoss.bind.impl;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.bind.BindContext;
import org.zkoss.bind.Binder;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.SelectorParam;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.CookieParam;
import org.zkoss.bind.annotation.Default;
import org.zkoss.bind.annotation.ExecutionArgParam;
import org.zkoss.bind.annotation.ExecutionParam;
import org.zkoss.bind.annotation.HeaderParam;
import org.zkoss.bind.annotation.QueryParam;
import org.zkoss.bind.annotation.Scope;
import org.zkoss.bind.annotation.ScopeParam;
import org.zkoss.bind.sys.BindEvaluatorX;
import org.zkoss.bind.sys.ReferenceBinding;
import org.zkoss.lang.Classes;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Components;
import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.select.Selectors;
/**
* To help invoke a method with {@link BindingParam} etc.. features.
* @author dennis
* @since 6.0.0
*/
public class ParamCall {
private static final Logger _log = LoggerFactory.getLogger(ParamCall.class);
private Map, ParamResolver> _paramResolvers;
private List _types;//to map class type directly, regardless the annotation
private boolean _mappingType;//to enable the map class type without annotation, it is for compatible to rc2, only support BindeContext and Binder
private ContextObjects _contextObjects;
private static final String COOKIE_CACHE = "$PARAM_COOKIES$";
private Component _root = null;
private Component _component = null;
private Execution _execution = null;
private Binder _binder = null;
private BindContext _bindContext = null;
public ParamCall(){
this(true);
}
public ParamCall(boolean mappingType){
_paramResolvers = new HashMap, ParamResolver>();
_contextObjects = new ContextObjects();
_types = new ArrayList();
_mappingType = mappingType;
_paramResolvers.put(ContextParam.class, new ParamResolver() {
public Object resolveParameter(Annotation anno,Class> returnType) {
Object val = _contextObjects.get(((ContextParam) anno).value());
return val==null?null:Classes.coerce(returnType, val);
}
});
}
public void setBindContext(BindContext ctx){
_bindContext = ctx;
_types.add(new Type(ctx.getClass(),_bindContext));
}
public void setBinder(Binder binder){
_binder = binder;
_types.add(new Type(binder.getClass(),_binder));
_root = binder.getView();
}
public void setBindingArgs(final Map bindingArgs){
_paramResolvers.put(BindingParam.class, new ParamResolver() {
public Object resolveParameter(Annotation anno,Class> returnType) {
Object val = bindingArgs.get(((BindingParam) anno).value());
return val==null?null:Classes.coerce(returnType, val);
}
});
}
public void call(Object base, Method method) {
Class>[] paramTypes = method.getParameterTypes();
java.lang.annotation.Annotation[][] parmAnnos = method.getParameterAnnotations();
Object[] params = new Object[paramTypes.length];
try {
for (int i = 0; i < paramTypes.length; i++) {
params[i] = resolveParameter(parmAnnos[i],paramTypes[i]);
}
method.invoke(base, params);
} catch(InvocationTargetException invokEx){
//Ian YT Tsai (2012.06.20), while InvocationTargetException,
//using original exception is much meaningful.
Throwable c = invokEx.getCause();
if(c == null) c = invokEx;
_log.error("", c);
throw UiException.Aide.wrap(c);
} catch (Exception e) {
_log.error("", e);
throw UiException.Aide.wrap(e);
}
}
private Object resolveParameter(java.lang.annotation.Annotation[] parmAnnos, Class> paramType){
Object val = null;
boolean hitResolver = false;
Default defAnno = null;
for(Annotation anno:parmAnnos){
Class> annotype = anno.annotationType();
if(defAnno==null && annotype.equals(Default.class)){
defAnno = (Default)anno;
continue;
}
ParamResolver resolver = _paramResolvers.get(annotype);
if(resolver==null) continue;
hitResolver = true;
val = resolver.resolveParameter(anno,paramType);
if(val!=null) {
break;
}
//don't break until get a value
}
if(val == null && defAnno != null){
val = Classes.coerce(paramType, defAnno.value());
}
//to compatible to rc2, do we have to?
if(_mappingType && val==null && !hitResolver && _types!=null){
for (Type type : _types) {
if (type != null && paramType.isAssignableFrom(type.clz)) {
val = type.value;
break;
}
}
}
return val;
}
//utility to hold implicit class and runtime value
private static class Type{
final Class> clz;
final Object value;
public Type(Class> clz,Object value){
this.clz = clz;
this.value = value;
}
}
private interface ParamResolver {
public Object resolveParameter(T anno,Class> returnType);
}
public void setComponent(Component comp) {
_component = comp;
//scope param
_paramResolvers.put(ScopeParam.class, new ParamResolver() {
public Object resolveParameter(Annotation anno,Class> returnType) {
final String name = ((ScopeParam)anno).value();
final Scope[] ss = ((ScopeParam)anno).scopes();
Object val = null;
for(Scope s:ss){
switch(s){
case AUTO:
if(ss.length==1){
val = _component.getAttribute(name,true);
}else{
throw new UiException("don't use "+s+" with other scopes "+Arrays.toString(ss));
}
}
}
if(val==null){
for(Scope scope:ss){
final String scopeName = scope.getName();
Object scopeObj = Components.getImplicit(_component, scopeName);
if(scopeObj instanceof Map){
val = ((Map,?>)scopeObj).get(name);
if(val!=null) break;
}else if(scopeObj !=null){
_log.error("the scope of "+scopeName+" is not a Map, is "+scopeObj);
}
}
}
//zk-1469,
if(val instanceof ReferenceBinding){
val = resolveReferenceBinding(name,(ReferenceBinding)val,returnType);
}
return val==null?null:Classes.coerce(returnType, val);
}
});
//component
_paramResolvers.put(SelectorParam.class, new ParamResolver() {
public Object resolveParameter(Annotation anno,Class> returnType) {
final String selector = ((SelectorParam) anno).value();
final List result = Selectors.find(_root, selector);
Object val;
if(!Collection.class.isAssignableFrom(returnType)){
val = result.size()>0?Classes.coerce(returnType, result.get(0)):null;
}else{
val = Classes.coerce(returnType, result);
}
return val;
}
});
}
private Object resolveReferenceBinding(String name, ReferenceBinding rbinding,Class> returnType){
BindEvaluatorX evalx = rbinding.getBinder().getEvaluatorX();
//resolve by name or by rbinding.propertyString directly?
Object val = BindEvaluatorXUtil.eval(evalx, rbinding.getComponent(), name, returnType, null);
//following is quick but not safe because of the null arg
// val = ((ReferenceBinding)val).getValue(null);
return val;
}
public void setExecution(Execution exec) {
_execution = exec;
//http param
_paramResolvers.put(QueryParam.class, new ParamResolver() {
public Object resolveParameter(Annotation anno,Class> returnType) {
Object val = _execution.getParameter(((QueryParam) anno).value());
return val==null?null:Classes.coerce(returnType, val);
}
});
_paramResolvers.put(HeaderParam.class, new ParamResolver() {
public Object resolveParameter(Annotation anno,Class> returnType) {
Object val = _execution.getHeader(((HeaderParam) anno).value());
return val==null?null:Classes.coerce(returnType, val);
}
});
_paramResolvers.put(CookieParam.class, new ParamResolver() {
@SuppressWarnings("unchecked")
public Object resolveParameter(Annotation anno,Class> returnType) {
Map m = (Map)_execution.getAttribute(COOKIE_CACHE);
if(m==null){
final Object req = _execution.getNativeRequest();
m = new HashMap();
_execution.setAttribute(COOKIE_CACHE, m);
if(req instanceof HttpServletRequest){
final Cookie[] cks = ((HttpServletRequest)req).getCookies();
if(cks != null){
for(Cookie ck:cks){
m.put(ck.getName().toLowerCase(java.util.Locale.ENGLISH), ck.getValue());
}
}
}else/* if(req instanceof PortletRequest)*/{
//no cookie in protlet 1.0
}
}
Object val = m==null?null:m.get(((CookieParam) anno).value().toLowerCase(java.util.Locale.ENGLISH));
return val==null?null:Classes.coerce(returnType, val);
}
});
//execution
_paramResolvers.put(ExecutionParam.class, new ParamResolver() {
public Object resolveParameter(Annotation anno,Class> returnType) {
Object val = _execution.getAttribute(((ExecutionParam) anno).value());
return val==null?null:Classes.coerce(returnType, val);
}
});
_paramResolvers.put(ExecutionArgParam.class, new ParamResolver() {
public Object resolveParameter(Annotation anno,Class> returnType) {
Object val = _execution.getArg().get(((ExecutionArgParam) anno).value());
return val==null?null:Classes.coerce(returnType, val);
}
});
}
class ContextObjects {
public Object get(ContextType type){
switch(type){
//bind contexts
case BIND_CONTEXT:
return _bindContext;
case BINDER:
return _binder;
case COMMAND_NAME:
return _bindContext==null?null:_bindContext.getCommandName();
case TRIGGER_EVENT:
return _bindContext==null?null:_bindContext.getTriggerEvent();
//zk execution contexts
case EXECUTION:
return _execution;
case COMPONENT:
return _component;
case SPACE_OWNER:
return _component==null?null:_component.getSpaceOwner();
case VIEW:
return _binder==null?null:_binder.getView();
case PAGE:
return _component==null?null:_component.getPage();
case DESKTOP:
return _component==null?null:_component.getDesktop();
case SESSION:
return _component==null?null:Components.getImplicit(_component, "session");
case APPLICATION:
return _component==null?null:Components.getImplicit(_component, "application");
}
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy