package com.aggrepoint.winlet.spring; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.function.Function; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.TypeMismatchException; import org.springframework.core.MethodParameter; import org.springframework.util.StringUtils; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import com.aggrepoint.winlet.AccessRuleEngine; import com.aggrepoint.winlet.AuthorizationEngine; import com.aggrepoint.winlet.ConfigProvider; import com.aggrepoint.winlet.ContextUtils; import com.aggrepoint.winlet.ListProvider; import com.aggrepoint.winlet.PageStorage; import com.aggrepoint.winlet.PsnRuleEngine; import com.aggrepoint.winlet.ReqInfo; import com.aggrepoint.winlet.ReqInfoImpl; import com.aggrepoint.winlet.SharedPageStorage; import com.aggrepoint.winlet.UserEngine; import com.aggrepoint.winlet.UserProfile; import com.aggrepoint.winlet.form.Form; import com.aggrepoint.winlet.form.Validation; import com.aggrepoint.winlet.form.ValidationImpl; import com.aggrepoint.winlet.spring.annotation.AccessRule; import com.aggrepoint.winlet.spring.annotation.Cfg; import com.aggrepoint.winlet.spring.annotation.DateParameter; import com.aggrepoint.winlet.spring.annotation.IntegerParameter; import com.aggrepoint.winlet.spring.annotation.PageRefresh; import com.aggrepoint.winlet.spring.annotation.PageStorageAttr; import com.aggrepoint.winlet.spring.annotation.StringParameter; /** * * @author Jiangming Yang (yangjm@gmail.com) */ public class WinletHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { Class<?> clz = parameter.getParameterType(); if (clz.isAssignableFrom(Validation.class) || clz.isAssignableFrom(ReqInfoImpl.class) || clz.isAssignableFrom(PageStorage.class) || clz.isAssignableFrom(SharedPageStorage.class) || clz.isAssignableFrom(Form.class) || UserProfile.class.isAssignableFrom(clz) || UserEngine.class.isAssignableFrom(clz) || ConfigProvider.class.isAssignableFrom(clz) || PsnRuleEngine.class.isAssignableFrom(clz) || AuthorizationEngine.class.isAssignableFrom(clz) || AccessRuleEngine.class.isAssignableFrom(clz) || ListProvider.class.isAssignableFrom(clz) || parameter.getParameterAnnotation(Cfg.class) != null || parameter.getParameterAnnotation(PageStorageAttr.class) != null || parameter.getParameterAnnotation(StringParameter.class) != null || parameter.getParameterAnnotation(IntegerParameter.class) != null || parameter.getParameterAnnotation(DateParameter.class) != null) return true; if (clz == Boolean.class || clz == boolean.class) if (parameter.getParameterAnnotation(PageRefresh.class) != null || parameter.getParameterAnnotation(AccessRule.class) != null) return true; return false; } Object checkClass(Object val, Class<?> expected, Exception e) throws Exception { if (expected.isAssignableFrom(val.getClass())) return val; if (e == null) return null; throw e; } static final HashMap<String, SimpleDateFormat> SDFS = new HashMap<String, SimpleDateFormat>(); private SimpleDateFormat getSDF(String format) { if (StringUtils.isEmpty(format)) return null; SimpleDateFormat sdf = SDFS.get(format); if (sdf == null) { sdf = new SimpleDateFormat(format); SDFS.put(format, sdf); } return sdf; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Class<?> clz = parameter.getParameterType(); StringParameter rs = parameter .getParameterAnnotation(StringParameter.class); if (rs != null) { String val = webRequest.getParameter(rs.value()); if (val != null && rs.options() != null) for (String str : rs.options()) { if (rs.caseInsensitive() && val.equalsIgnoreCase(str) || !rs.caseInsensitive() && val.equals(str)) return str; } return StringUtils.isEmpty(rs.def()) && rs.options() != null && rs.options().length > 0 ? rs.options()[0] : rs.def(); } IntegerParameter ri = parameter .getParameterAnnotation(IntegerParameter.class); if (ri != null) { int val = 0; try { val = Integer.parseInt(webRequest.getParameter(ri.value())); } catch (Exception e) { return ri.def(); } if (val < ri.min() || val > ri.max()) return ri.def(); return val; } DateParameter rd = parameter .getParameterAnnotation(DateParameter.class); if (rd != null) { Date val = null; try { val = getSDF(rd.format()).parse( webRequest.getParameter(rd.value())); } catch (Exception e) { return null; } return val; } // 转换为参数所需格式。参考了AbstractNamedValueMethodArgumentResolver.resolveArgument中的实现 Function<Object, Object> convert = (arg) -> { if (binderFactory != null) { Class<?> paramType = parameter.getParameterType(); try { WebDataBinder binder = binderFactory.createBinder( webRequest, null, parameter.getParameterName()); return binder.convertIfNecessary(arg, paramType, parameter); } catch (TypeMismatchException ex) { throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(), parameter.getParameterName(), parameter, ex.getCause()); } catch (Exception ex) { throw new MethodArgumentConversionNotSupportedException( arg, paramType, parameter.getParameterName(), parameter, ex.getCause()); } } return arg; }; PageStorageAttr attr = parameter .getParameterAnnotation(PageStorageAttr.class); if (attr != null) { Object arg = null; if (!StringUtils.isEmpty(attr.reqparam())) { // 有定义请求参数,从请求参数中取值 arg = webRequest.getParameter(attr.reqparam()); if (StringUtils.isEmpty(arg)) arg = null; } PageStorage ps = ContextUtils.getReqInfo().getPageStorage(); if (arg == null) // 没有定义请求参数,或请求参数中没有值,从PageStorage中取 arg = ps.getAttribute(attr.value()); arg = convert.apply(arg); if (arg == null && attr.createIfNotExist()) arg = clz.newInstance(); ps.setAttribute(attr.value(), arg); return arg; } HttpServletRequest req = ContextUtils.getRequest(); Cfg cfg = parameter.getParameterAnnotation(Cfg.class); if (cfg != null) { String value = ContextUtils.getConfigProvider(req).getStr( cfg.value()); if (value == null) value = cfg.def(); return convert.apply(value); } if (clz == Boolean.class || clz == boolean.class) { if (parameter.getParameterAnnotation(PageRefresh.class) != null) return ContextUtils.getReqInfo().isPageRefresh(); AccessRule rule = parameter .getParameterAnnotation(AccessRule.class); if (rule != null) return ContextUtils.getAccessRuleEngine( ContextUtils.getRequest()).eval(rule.value()); } if (clz.isAssignableFrom(Validation.class)) return new ValidationImpl(ContextUtils.getReqInfo()); if (clz.isAssignableFrom(ReqInfoImpl.class)) return ContextUtils.getReqInfo(); if (clz.isAssignableFrom(Form.class)) return ContextUtils.getReqInfo().getForm(); if (clz.isAssignableFrom(PageStorage.class)) { ReqInfo reqInfo = ContextUtils.getReqInfo(); PageStorage ps = reqInfo.getPageStorage(); if (reqInfo.isPageRefresh()) ps.refresh(); return ps; } if (clz.isAssignableFrom(SharedPageStorage.class)) { ReqInfo reqInfo = ContextUtils.getReqInfo(); SharedPageStorage sps = reqInfo.getSharedPageStorage(); if (reqInfo.isPageRefresh()) sps.refresh(); return sps; } if (UserProfile.class.isAssignableFrom(clz)) return checkClass(ContextUtils.getUserEngine(req).getUser(req), clz, null); if (UserEngine.class.isAssignableFrom(clz)) return checkClass(ContextUtils.getUserEngine(req), clz, null); if (ConfigProvider.class.isAssignableFrom(clz)) return checkClass(ContextUtils.getConfigProvider(req), clz, null); if (AuthorizationEngine.class.isAssignableFrom(clz)) return checkClass(ContextUtils.getAuthorizationEngine(req), clz, null); if (AccessRuleEngine.class.isAssignableFrom(clz)) return checkClass(ContextUtils.getAccessRuleEngine(req), clz, null); if (PsnRuleEngine.class.isAssignableFrom(clz)) return checkClass(ContextUtils.getPsnRuleEngine(req), clz, null); if (ListProvider.class.isAssignableFrom(clz)) return checkClass(ContextUtils.getListProvider(req), clz, null); return null; } }