/* * Copyright 2008-2010 the T2 Project ant the Others. * * 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 org.t2framework.confeito.action.impl; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.t2framework.confeito.action.ActionContext; import org.t2framework.confeito.action.ActionContextBuilder; import org.t2framework.confeito.action.ActionInvokingContext; import org.t2framework.confeito.action.ActionStatus; import org.t2framework.confeito.exception.NoActionMethodFoundRuntimeException; import org.t2framework.confeito.exception.NoDefaultActionMethodFoundRuntimeException; import org.t2framework.confeito.internal.PathUtil; import org.t2framework.confeito.method.ActionMethodResolver; import org.t2framework.confeito.model.ActionMethod; import org.t2framework.confeito.model.PageComponent; import org.t2framework.confeito.util.Assertion; /** * <#if locale="en"> * <p> * ActionContextBuilderImpl is an implementation class of * {@link ActionContextBuilder}. * </p> * <#else> * <p> * ActionContextBuilderImplは、{@link ActionContextBuilder}の実装クラスです. * </p> * </#if> * * @author shot */ public class ActionContextBuilderImpl implements ActionContextBuilder { protected Map<Class<? extends Annotation>, ActionMethodResolver> methodResolvers = new HashMap<Class<? extends Annotation>, ActionMethodResolver>(); protected ActionMethodResolver defaultResolver; /** * * <#if locale="en"> * <p> * Construct this object. The both parameters, {@link ActionMethodResolver} * map and default resolver must not be null. * </p> * <#else> * <p> * このオブジェクトを構築します. {@link ActionMethodResolver}のMapと defaultResolver * はnullであってはいけません. * </p> * </#if> * * @param methodResolvers * @param defaultResolver */ public ActionContextBuilderImpl( Map<Class<? extends Annotation>, ActionMethodResolver> methodResolvers, ActionMethodResolver defaultResolver) { this.methodResolvers = Assertion.notNull(methodResolvers); this.defaultResolver = Assertion.notNull(defaultResolver); } /** * <#if locale="en"> * <p> * Build an {@link ActionContext} using {@link ActionMethodResolver}.The * building process is: * </p> * <ul> * <li>set the {@link PageComponent} that comes from * {@link ActionInvokingContext} and iterate each {@link ActionMethod}.</li> * <li>Find and correct taget candidate {@link MethodDesc} using * {@link ActionMethodResolver#isMatch(ActionContext, Annotation, MethodDesc) * .}</li> * <li>Find the target {@link MethodDesc} and set up page variables which is * resolved by * {@link ActionMethodResolver#resolve(ActionContext, Annotation, MethodDesc) * .}</li> * <li>?resolve again?</li> * </ul> * <#else> * <p> * * </p> * </#if> * * @see org.t2framework.confeito.action.ActionContextBuilder#build(org.t2framework.confeito.action.ActionInvokingContext) * @param invokingActionContext * @return build action context * @throws NoActionMethodFoundRuntimeException */ @Override public ActionContext build(final ActionInvokingContext invokingActionContext) throws NoActionMethodFoundRuntimeException { Assertion.notNull(invokingActionContext); final PageComponent current = invokingActionContext .getCurrentPageDesc(); final ActionContext actionContext = invokingActionContext .getActionContext(); actionContext.setTargetPageDesc(current); List<Method> matchList = null; int maxMatchCount = 0; search: for (Method methodDesc : current.getActionMethod()) { actionContext.clearMatchCount(); final boolean continueToNext = buildActionMethod(actionContext, methodDesc); if (continueToNext) { continue search; } final int matchCount = actionContext.getMatchCount(); if (maxMatchCount < matchCount) { maxMatchCount = matchCount; matchList = new ArrayList<Method>(); matchList.add(methodDesc); } else if (matchCount == maxMatchCount) { if (matchList == null) { continue search; } matchList.add(methodDesc); } } buildActionContext(actionContext, current, matchList); invokingActionContext.setActionStatus(ActionStatus.FOUND_ACTION_METHOD); return actionContext; } protected void buildActionContext(ActionContext actionContext, PageComponent current, List<Method> matchList) { Method methodDesc = findMethodDescFromMatchList(actionContext, current, matchList); resolvePageVariables(actionContext); resolveActionMethod(actionContext, methodDesc); actionContext.setTargetMethodDesc(methodDesc); } protected Method findMethodDescFromMatchList(ActionContext actionContext, PageComponent current, List<Method> matchList) throws NoActionMethodFoundRuntimeException { Method ret = null; if (matchList != null) { if (0 < matchList.size()) { ret = matchList.get(0); } else { throw new NoActionMethodFoundRuntimeException(current .getActionMethod().getMethodNames()); } } else { ret = current.getDefaultMethod(); setupForDefaultMethodDesc(actionContext, current, ret); } return ret; } protected boolean buildActionMethod(final ActionContext actionContext, final Method methodDesc) { boolean continueToNext = false; for (Annotation annotation : methodDesc.getAnnotations()) { final ActionMethodResolver resolver = findAnnotationResolver(annotation); if (resolver == null) { continue; } boolean match = buildActionMethodByResolver(resolver, actionContext, annotation, methodDesc); if (match == false) { continueToNext = true; break; } } return continueToNext; } protected boolean buildActionMethodByResolver( ActionMethodResolver resolver, ActionContext actionContext, Annotation annotation, Method methodDesc) { boolean resolved = false; if (resolver.isMatch(actionContext, annotation, methodDesc)) { actionContext.incrementMatchCount(); resolved = true; } return resolved; } protected void resolvePageVariables(ActionContext actionContext) { String pagePath = PathUtil.getPagePath(actionContext.getRequest()); PageComponent pd = actionContext.getTargetPageDesc(); Map<String, String> varMap = pd.getUrlTemplate().parseUrl(pagePath); if (varMap != null) { for (Entry<String, String> entry : varMap.entrySet()) { actionContext.addVariables(entry.getKey(), entry.getValue()); } } } protected void resolveActionMethod(ActionContext actionContext, Method methodDesc) { for (Annotation annotation : methodDesc.getAnnotations()) { final ActionMethodResolver resolver = findAnnotationResolver(annotation); if (resolver == null) { continue; } resolver.preResolve(actionContext, annotation, methodDesc); try { resolver.resolve(actionContext, annotation, methodDesc); } finally { resolver.postResolve(actionContext, annotation, methodDesc); } } } protected void setupForDefaultMethodDesc(final ActionContext actionContext, final PageComponent current, final Method methodDesc) { if (current.hasDefaultMethod() == false) { throw new NoDefaultActionMethodFoundRuntimeException(); } defaultResolver.resolve(actionContext, current.getDefaultAnnotation(), methodDesc); } protected ActionMethodResolver findAnnotationResolver(Annotation annotation) { Assertion.notNull(annotation); return methodResolvers.get(annotation.annotationType()); } /** * <#if locale="en"> * <p> * Set {@link ActionMethodResolver} as default resolver. * </p> * <#else> * <p> * * </p> * </#if> */ public void setDefaultActionAnnotationResolver( final ActionMethodResolver defaultResolver) { this.defaultResolver = Assertion.notNull(defaultResolver); } /** * <#if locale="en"> * <p> * Add {@link ActionMethodResolver} for target annotation class. * </p> * <#else> * <p> * * </p> * </#if> */ public void addActionAnnotationResolver( Class<? extends Annotation> annotationClass, ActionMethodResolver resolver) { methodResolvers.put(annotationClass, resolver); } }