Coverage Report - org.seasar.cubby.convention.impl.PathResolverImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PathResolverImpl
79%
61/77
60%
12/20
0
PathResolverImpl$ActionClassCollector
89%
17/19
83%
10/12
0
PathResolverImpl$RoutingInfo
59%
17/29
40%
4/10
0
 
 1  
 /*
 2  
  * Copyright 2004-2008 the Seasar Foundation and the Others.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 13  
  * either express or implied. See the License for the specific language
 14  
  * governing permissions and limitations under the License.
 15  
  */
 16  
 package org.seasar.cubby.convention.impl;
 17  
 
 18  
 import java.io.IOException;
 19  
 import java.lang.reflect.Method;
 20  
 import java.net.URLDecoder;
 21  
 import java.util.ArrayList;
 22  
 import java.util.HashMap;
 23  
 import java.util.LinkedHashMap;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 import java.util.Map.Entry;
 27  
 import java.util.regex.Matcher;
 28  
 import java.util.regex.Pattern;
 29  
 
 30  
 import javax.servlet.http.HttpServletRequest;
 31  
 
 32  
 import org.seasar.cubby.action.Action;
 33  
 import org.seasar.cubby.action.RequestMethod;
 34  
 import org.seasar.cubby.convention.ForwardInfo;
 35  
 import org.seasar.cubby.convention.PathResolver;
 36  
 import org.seasar.cubby.util.CubbyUtils;
 37  
 import org.seasar.cubby.util.QueryStringBuilder;
 38  
 import org.seasar.framework.container.SingletonS2Container;
 39  
 import org.seasar.framework.convention.NamingConvention;
 40  
 import org.seasar.framework.exception.IORuntimeException;
 41  
 import org.seasar.framework.log.Logger;
 42  
 import org.seasar.framework.util.ClassUtil;
 43  
 import org.seasar.framework.util.Disposable;
 44  
 import org.seasar.framework.util.DisposableUtil;
 45  
 import org.seasar.framework.util.StringUtil;
 46  
 
 47  
 /**
 48  
  * クラスパスから {@link Action} を検索し、そのメソッドに指定された {@link org.seasar.cubby.action.Path}
 49  
  * の情報によって、リクエストされたパスをどのメソッドに振り分けるかを決定します。
 50  
  * 
 51  
  * @author baba
 52  
  */
 53  337
 public class PathResolverImpl implements PathResolver, Disposable {
 54  
 
 55  1
         private static Pattern routingPattern = Pattern
 56  
                         .compile("([{]([^}]+)[}])([^{]*)");
 57  
 
 58  1
         private static final Logger logger = Logger
 59  
                         .getLogger(PathResolverImpl.class);
 60  
 
 61  
         private boolean initialized;
 62  
 
 63  
         private NamingConvention namingConvention;
 64  
 
 65  1
         private final Map<Pattern, RoutingInfo> routingPatterns = new LinkedHashMap<Pattern, RoutingInfo>();
 66  
 
 67  1
         private final Map<Pattern, RoutingInfo> customRoutingPatterns = new LinkedHashMap<Pattern, RoutingInfo>();
 68  
 
 69  
         private String uriEncoding;
 70  
 
 71  1
         public PathResolverImpl() {
 72  1
         }
 73  
 
 74  
         public void setUriEncoding(final String uriEncoding) {
 75  1
                 this.uriEncoding = uriEncoding;
 76  1
         }
 77  
 
 78  
         public void initialize() {
 79  2
                 if (!initialized) {
 80  1
                         routingPatterns.clear();
 81  1
                         final ClassCollector classCollector = new ActionClassCollector();
 82  1
                         classCollector.collect();
 83  
 
 84  1
                         DisposableUtil.add(this);
 85  1
                         initialized = true;
 86  
                 }
 87  2
         }
 88  
 
 89  
         public void dispose() {
 90  1
                 routingPatterns.clear();
 91  1
                 initialized = false;
 92  1
         }
 93  
 
 94  
         public void add(final String regexp,
 95  
                         final Class<? extends Action> actionClass, final String methodName,
 96  
                         final RequestMethod... requestMethods) {
 97  
 
 98  0
                 final Method method = ClassUtil.getMethod(actionClass, methodName,
 99  
                                 new Class<?>[0]);
 100  0
                 this.add(regexp, actionClass, method, customRoutingPatterns,
 101  
                                 requestMethods);
 102  0
         }
 103  
 
 104  
         private void add(final String regexp,
 105  
                         final Class<? extends Action> actionClass, final Method method,
 106  
                         final Map<Pattern, RoutingInfo> patternToRoutingInfoMap,
 107  
                         final RequestMethod... requestMethods) {
 108  
 
 109  12
                 String actionFullName = regexp;
 110  12
                 final List<String> uriParameterNames = new ArrayList<String>();
 111  12
                 final Matcher matcher = routingPattern.matcher(actionFullName);
 112  12
                 while (matcher.find()) {
 113  0
                         final String name = matcher.group(2);
 114  0
                         final String[] names = name.split(",", 2);
 115  0
                         if (names.length == 1) {
 116  0
                                 actionFullName = StringUtil.replace(actionFullName, matcher
 117  
                                                 .group(1), "([a-zA-Z0-9]+)");
 118  0
                                 uriParameterNames.add(matcher.group(2));
 119  
                         } else {
 120  0
                                 actionFullName = StringUtil.replace(actionFullName, matcher
 121  
                                                 .group(1), "(" + names[1] + ")");
 122  0
                                 uriParameterNames.add(names[0]);
 123  
                         }
 124  0
                 }
 125  
 
 126  12
                 final String forwardPath = this.fromActionClassToPath(actionClass,
 127  
                                 method);
 128  
 
 129  12
                 final RoutingInfo routingInfo = new RoutingInfo(actionClass, method,
 130  
                                 uriParameterNames, forwardPath, requestMethods);
 131  12
                 final Pattern pattern = Pattern.compile("^" + actionFullName + "$");
 132  
 
 133  12
                 patternToRoutingInfoMap.put(pattern, routingInfo);
 134  12
                 if (logger.isDebugEnabled()) {
 135  12
                         logger.log("DCUB0007", new Object[] { actionFullName, method,
 136  
                                         uriParameterNames });
 137  
                 }
 138  12
         }
 139  
 
 140  
         private String fromActionClassToPath(
 141  
                         final Class<? extends Action> actionClass, final Method method) {
 142  12
                 final String componentName = namingConvention
 143  
                                 .fromClassNameToComponentName(actionClass.getCanonicalName());
 144  12
                 final StringBuilder builder = new StringBuilder(100);
 145  12
                 builder.append('/');
 146  12
                 builder.append(componentName.substring(
 147  
                                 0,
 148  
                                 componentName.length()
 149  
                                                 - namingConvention.getActionSuffix().length())
 150  
                                 .replaceAll("_", "/"));
 151  12
                 builder.append('/');
 152  12
                 builder.append(method.getName());
 153  12
                 return builder.toString();
 154  
         }
 155  
 
 156  
         public ForwardInfo getForwardInfo(final String path) {
 157  2
                 if (logger.isDebugEnabled()) {
 158  2
                         logger.log("DCUB0006", new Object[] { path });
 159  
                 }
 160  
 
 161  2
                 initialize();
 162  
 
 163  
                 final String decodedPath;
 164  
                 try {
 165  2
                         decodedPath = URLDecoder.decode(path, uriEncoding);
 166  0
                 } catch (final IOException e) {
 167  0
                         throw new IORuntimeException(e);
 168  2
                 }
 169  
 
 170  2
                 ForwardInfo forwardInfo = findForwardInfo(decodedPath,
 171  
                                 customRoutingPatterns);
 172  2
                 if (forwardInfo == null) {
 173  2
                         forwardInfo = findForwardInfo(decodedPath, routingPatterns);
 174  
                 }
 175  2
                 return forwardInfo;
 176  
         }
 177  
 
 178  
         private ForwardInfo findForwardInfo(final String path,
 179  
                         final Map<Pattern, RoutingInfo> routingPatterns) {
 180  4
                 final Map<String, String> uriParams = new HashMap<String, String>();
 181  
                 for (final Entry<Pattern, RoutingInfo> entry : routingPatterns
 182  4
                                 .entrySet()) {
 183  7
                         final Matcher matcher = entry.getKey().matcher(path);
 184  7
                         if (matcher.find()) {
 185  2
                                 final RoutingInfo routingInfo = entry.getValue();
 186  2
                                 final HttpServletRequest request = SingletonS2Container
 187  
                                                 .getComponent(HttpServletRequest.class);
 188  2
                                 if (routingInfo.isAcceptable(request)) {
 189  2
                                         for (int i = 1; i < matcher.groupCount() + 1; i++) {
 190  0
                                                 final String name = routingInfo.getUriParameterNames()
 191  
                                                                 .get(i - 1);
 192  0
                                                 final String value = matcher.group(i);
 193  0
                                                 uriParams.put(name, value);
 194  
                                         }
 195  2
                                         final ForwardInfoImpl forwardInfo = new ForwardInfoImpl(
 196  
                                                         routingInfo, uriParams);
 197  2
                                         return forwardInfo;
 198  
                                 }
 199  
                         }
 200  5
                 }
 201  2
                 return null;
 202  
         }
 203  
 
 204  
         public void setNamingConvention(final NamingConvention namingConvention) {
 205  1
                 this.namingConvention = namingConvention;
 206  1
         }
 207  
 
 208  
         class RoutingInfo {
 209  
 
 210  
                 private final Class<? extends Action> actionClass;
 211  
 
 212  
                 private final Method method;
 213  
 
 214  
                 private final List<String> uriParameterNames;
 215  
 
 216  
                 private final String forwardPath;
 217  
 
 218  
                 private final RequestMethod[] requestMethods;
 219  
 
 220  
                 public RoutingInfo(final Class<? extends Action> actionClass,
 221  
                                 final Method method, final List<String> uriParameterNames,
 222  
                                 final String forwardPath,
 223  12
                                 final RequestMethod[] requestMethods) {
 224  12
                         this.actionClass = actionClass;
 225  12
                         this.method = method;
 226  12
                         this.uriParameterNames = uriParameterNames;
 227  12
                         this.forwardPath = forwardPath;
 228  12
                         this.requestMethods = requestMethods;
 229  12
                 }
 230  
 
 231  
                 public String buildForwardPath(final Map<String, String> uriParams) {
 232  2
                         final StringBuilder builder = new StringBuilder(100);
 233  2
                         builder.append(forwardPath);
 234  2
                         if (!uriParams.isEmpty()) {
 235  0
                                 builder.append("?");
 236  0
                                 final QueryStringBuilder query = new QueryStringBuilder();
 237  0
                                 final String encoding = PathResolverImpl.this.uriEncoding;
 238  0
                                 if (!StringUtil.isEmpty(encoding)) {
 239  0
                                         query.setEncode(encoding);
 240  
                                 }
 241  0
                                 for (final Entry<String, String> entry : uriParams.entrySet()) {
 242  0
                                         query.addParam(entry.getKey(), entry.getValue());
 243  
                                 }
 244  0
                                 builder.append(query.toString());
 245  
                         }
 246  2
                         return builder.toString();
 247  
                 }
 248  
 
 249  
                 public Class<? extends Action> getActionClass() {
 250  2
                         return actionClass;
 251  
                 }
 252  
 
 253  
                 public Method getMethod() {
 254  2
                         return method;
 255  
                 }
 256  
 
 257  
                 public List<String> getUriParameterNames() {
 258  0
                         return uriParameterNames;
 259  
                 }
 260  
 
 261  
                 public String getForwardPath() {
 262  0
                         return forwardPath;
 263  
                 }
 264  
 
 265  
                 public RequestMethod[] getRequestMethods() {
 266  0
                         return requestMethods;
 267  
                 }
 268  
 
 269  
                 public boolean isAcceptable(final HttpServletRequest request) {
 270  2
                         final String requestMethod = request.getMethod();
 271  4
                         for (final RequestMethod acceptableRequestMethod : requestMethods) {
 272  4
                                 if (StringUtil.equalsIgnoreCase(acceptableRequestMethod.name(),
 273  
                                                 requestMethod)) {
 274  2
                                         return true;
 275  
                                 }
 276  
                         }
 277  0
                         return false;
 278  
                 }
 279  
 
 280  
         }
 281  
 
 282  
         class ActionClassCollector extends ClassCollector {
 283  
 
 284  1
                 public ActionClassCollector() {
 285  1
                         super(namingConvention);
 286  1
                 }
 287  
 
 288  
                 public void processClass(final String packageName,
 289  
                                 final String shortClassName) {
 290  212
                         if (shortClassName.indexOf('$') != -1) {
 291  61
                                 return;
 292  
                         }
 293  151
                         final String className = ClassUtil.concatName(packageName,
 294  
                                         shortClassName);
 295  151
                         if (!namingConvention.isTargetClassName(className)) {
 296  0
                                 return;
 297  
                         }
 298  151
                         if (!className.endsWith(namingConvention.getActionSuffix())) {
 299  146
                                 return;
 300  
                         }
 301  5
                         final Class<? extends Action> clazz = classForName(className);
 302  5
                         if (namingConvention.isSkipClass(clazz)) {
 303  0
                                 return;
 304  
                         }
 305  
 
 306  97
                         for (final Method method : clazz.getMethods()) {
 307  92
                                 if (CubbyUtils.isActionMethod(method)) {
 308  12
                                         final String actionFullName = CubbyUtils.getActionUrl(
 309  
                                                         clazz, method);
 310  12
                                         final RequestMethod[] acceptableRequestMethods = CubbyUtils
 311  
                                                         .getAcceptableRequestMethods(clazz, method);
 312  12
                                         add(actionFullName, clazz, method, routingPatterns,
 313  
                                                         acceptableRequestMethods);
 314  
                                 }
 315  
                         }
 316  5
                 }
 317  
 
 318  
         }
 319  
 
 320  
         @SuppressWarnings("unchecked")
 321  
         private static <T> Class<T> classForName(final String className) {
 322  5
                 return ClassUtil.forName(className);
 323  
         }
 324  
 
 325  
 }