Coverage Report - org.seasar.cubby.routing.impl.PathResolverImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PathResolverImpl
93%
154/165
86%
77/90
0
PathResolverImpl$RoutingComparator
76%
16/21
64%
9/14
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.routing.impl;
 17  
 
 18  
 import static org.seasar.cubby.CubbyConstants.INTERNAL_FORWARD_DIRECTORY;
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.io.UnsupportedEncodingException;
 22  
 import java.lang.reflect.Method;
 23  
 import java.net.URLDecoder;
 24  
 import java.net.URLEncoder;
 25  
 import java.util.ArrayList;
 26  
 import java.util.Collections;
 27  
 import java.util.Comparator;
 28  
 import java.util.HashMap;
 29  
 import java.util.Iterator;
 30  
 import java.util.List;
 31  
 import java.util.Map;
 32  
 import java.util.TreeMap;
 33  
 import java.util.Map.Entry;
 34  
 import java.util.regex.Matcher;
 35  
 import java.util.regex.Pattern;
 36  
 
 37  
 import org.seasar.cubby.action.Action;
 38  
 import org.seasar.cubby.action.Path;
 39  
 import org.seasar.cubby.action.RequestMethod;
 40  
 import org.seasar.cubby.controller.ClassDetector;
 41  
 import org.seasar.cubby.controller.DetectClassProcessor;
 42  
 import org.seasar.cubby.exception.ActionRuntimeException;
 43  
 import org.seasar.cubby.exception.DuplicateRoutingRuntimeException;
 44  
 import org.seasar.cubby.routing.InternalForwardInfo;
 45  
 import org.seasar.cubby.routing.PathResolver;
 46  
 import org.seasar.cubby.routing.Routing;
 47  
 import org.seasar.cubby.util.CubbyUtils;
 48  
 import org.seasar.cubby.util.QueryStringBuilder;
 49  
 import org.seasar.framework.convention.NamingConvention;
 50  
 import org.seasar.framework.exception.IORuntimeException;
 51  
 import org.seasar.framework.log.Logger;
 52  
 import org.seasar.framework.util.ClassUtil;
 53  
 import org.seasar.framework.util.Disposable;
 54  
 import org.seasar.framework.util.DisposableUtil;
 55  
 import org.seasar.framework.util.StringUtil;
 56  
 
 57  
 /**
 58  
  * クラスパスから {@link Action} を検索し、クラス名やメソッド名、そのクラスやメソッドに指定された
 59  
  * {@link org.seasar.cubby.action.Path}
 60  
  * の情報からアクションのパスを抽出し、リクエストされたパスをどのメソッドに振り分けるかを決定します。
 61  
  * 
 62  
  * @author baba
 63  
  * @since 1.0.0
 64  
  */
 65  
 public class PathResolverImpl implements PathResolver, DetectClassProcessor,
 66  
                 Disposable {
 67  
 
 68  
         /** ロガー */
 69  1
         private static final Logger logger = Logger
 70  
                         .getLogger(PathResolverImpl.class);
 71  
 
 72  
         /** デフォルトの URI エンコーディング */
 73  
         private static final String DEFAULT_URI_ENCODING = "UTF-8";
 74  
 
 75  
         /** アクションのパスからパラメータを抽出するための正規表現パターン */
 76  1
         private static Pattern URI_PARAMETER_MATCHING_PATTERN = Pattern
 77  
                         .compile("([{]([^}]+)[}])([^{]*)");
 78  
 
 79  
         /** デフォルトの URI パラメータ正規表現 */
 80  
         private static final String DEFAULT_URI_PARAMETER_REGEX = "[a-zA-Z0-9]+";
 81  
 
 82  
         /** インスタンスが初期化済みであることを示します。 */
 83  
         private boolean initialized;
 84  
 
 85  
         /** 命名規約。 */
 86  
         private NamingConvention namingConvention;
 87  
 
 88  
         /** ルーティングのコンパレータ。 */
 89  54
         private final Comparator<Routing> routingComparator = new RoutingComparator();
 90  
 
 91  
         /** 登録されたルーティングのマップ。 */
 92  54
         private final Map<Routing, Routing> routings = new TreeMap<Routing, Routing>(
 93  
                         routingComparator);
 94  
 
 95  
         /** クラスパスを走査してクラスを検出するクラス。 */
 96  
         private ClassDetector classDetector;
 97  
 
 98  
         /** URI のエンコーディング。 */
 99  54
         private String uriEncoding = DEFAULT_URI_ENCODING;
 100  
 
 101  
         /** 手動登録用のプライオリティカウンタ。 */
 102  54
         private int priorityCounter = 0;
 103  
 
 104  
         /**
 105  
          * インスタンス化します。
 106  
          */
 107  54
         public PathResolverImpl() {
 108  54
         }
 109  
 
 110  
         /**
 111  
          * ルーティング情報を取得します。
 112  
          * 
 113  
          * @return ルーティング情報
 114  
          */
 115  
         public List<Routing> getRoutings() {
 116  2
                 initialize();
 117  2
                 return Collections.unmodifiableList(new ArrayList<Routing>(routings
 118  
                                 .values()));
 119  
         }
 120  
 
 121  
         /**
 122  
          * クラスパスを走査してクラスを検出するクラスを設定します。
 123  
          * 
 124  
          * @param classDetector
 125  
          *            クラスパスを走査してクラスを設定します。
 126  
          */
 127  
         public void setClassDetector(final ClassDetector classDetector) {
 128  54
                 this.classDetector = classDetector;
 129  54
         }
 130  
 
 131  
         /**
 132  
          * URI エンコーディングを設定します。
 133  
          * 
 134  
          * @param uriEncoding
 135  
          *            URI エンコーディング
 136  
          */
 137  
         public void setUriEncoding(final String uriEncoding) {
 138  54
                 this.uriEncoding = uriEncoding;
 139  54
         }
 140  
 
 141  
         /**
 142  
          * 初期化します。
 143  
          */
 144  
         public void initialize() {
 145  15
                 if (initialized) {
 146  0
                         return;
 147  
                 }
 148  15
                 classDetector.detect();
 149  15
                 DisposableUtil.add(this);
 150  15
                 initialized = true;
 151  15
         }
 152  
 
 153  
         /**
 154  
          * {@inheritDoc}
 155  
          */
 156  
         public void dispose() {
 157  15
                 final List<Routing> removes = new ArrayList<Routing>();
 158  15
                 for (final Routing routing : routings.keySet()) {
 159  358
                         if (routing.isAuto()) {
 160  354
                                 removes.add(routing);
 161  
                         }
 162  
                 }
 163  15
                 for (final Routing routing : removes) {
 164  354
                         routings.remove(routing);
 165  
                 }
 166  15
                 initialized = false;
 167  15
         }
 168  
 
 169  
         /**
 170  
          * ルーティング情報を登録します。
 171  
          * <p>
 172  
          * クラスパスを検索して自動登録されるルーティング情報以外にも、このメソッドによって手動でルーティング情報を登録できます。
 173  
          * </p>
 174  
          * 
 175  
          * @param actionPath
 176  
          *            アクションのパス
 177  
          * @param actionClass
 178  
          *            アクションクラス
 179  
          * @param methodName
 180  
          *            アクションメソッド名
 181  
          */
 182  
         public void add(final String actionPath,
 183  
                         final Class<? extends Action> actionClass, final String methodName) {
 184  83
                 this.add(actionPath, actionClass, methodName, new RequestMethod[0]);
 185  83
         }
 186  
 
 187  
         /**
 188  
          * ルーティング情報を登録します。
 189  
          * <p>
 190  
          * クラスパスを検索して自動登録されるルーティング情報以外にも、このメソッドによって手動でルーティング情報を登録できます。
 191  
          * </p>
 192  
          * 
 193  
          * @param actionPath
 194  
          *            アクションのパス
 195  
          * @param actionClass
 196  
          *            アクションクラス
 197  
          * @param methodName
 198  
          *            アクションメソッド名
 199  
          * @param requestMethods
 200  
          *            リクエストメソッド
 201  
          */
 202  
         public void add(final String actionPath,
 203  
                         final Class<? extends Action> actionClass, final String methodName,
 204  
                         final RequestMethod... requestMethods) {
 205  
 
 206  86
                 final Method method = ClassUtil.getMethod(actionClass, methodName,
 207  
                                 new Class<?>[0]);
 208  86
                 if (requestMethods == null || requestMethods.length == 0) {
 209  252
                         for (final RequestMethod requestMethod : CubbyUtils.DEFAULT_ACCEPT_ANNOTATION
 210  
                                         .value()) {
 211  168
                                 this.add(actionPath, actionClass, method, requestMethod, false);
 212  
                         }
 213  
                 } else {
 214  4
                         for (final RequestMethod requestMethod : requestMethods) {
 215  2
                                 this.add(actionPath, actionClass, method, requestMethod, false);
 216  
                         }
 217  
                 }
 218  86
         }
 219  
 
 220  
         /**
 221  
          * ルーティング情報を登録します。
 222  
          * 
 223  
          * @param actionPath
 224  
          *            アクションのパス
 225  
          * @param actionClass
 226  
          *            アクションクラス
 227  
          * @param method
 228  
          *            アクションメソッド
 229  
          * @param requestMethods
 230  
          *            リクエストメソッド
 231  
          * @param auto
 232  
          *            自動登録かどうか
 233  
          */
 234  
         private void add(final String actionPath,
 235  
                         final Class<? extends Action> actionClass, final Method method,
 236  
                         final RequestMethod requestMethod, final boolean auto) {
 237  
 
 238  524
                 final Matcher matcher = URI_PARAMETER_MATCHING_PATTERN
 239  
                                 .matcher(actionPath);
 240  524
                 String uriRegex = actionPath;
 241  524
                 final List<String> uriParameterNames = new ArrayList<String>();
 242  870
                 while (matcher.find()) {
 243  346
                         final String holder = matcher.group(2);
 244  346
                         final String[] tokens = CubbyUtils.split2(holder, ',');
 245  346
                         uriParameterNames.add(tokens[0]);
 246  
                         final String uriParameterRegex;
 247  346
                         if (tokens.length == 1) {
 248  244
                                 uriParameterRegex = DEFAULT_URI_PARAMETER_REGEX;
 249  
                         } else {
 250  102
                                 uriParameterRegex = tokens[1];
 251  
                         }
 252  346
                         uriRegex = StringUtil.replace(uriRegex, matcher.group(1),
 253  
                                         regexGroup(uriParameterRegex));
 254  346
                 }
 255  524
                 uriRegex = "^" + uriRegex + "$";
 256  524
                 final Pattern pattern = Pattern.compile(uriRegex);
 257  
 
 258  524
                 final String onSubmit = CubbyUtils.getOnSubmit(method);
 259  
 
 260  524
                 final int priority = auto ? CubbyUtils.getPriority(method)
 261  
                                 : priorityCounter++;
 262  
 
 263  524
                 final Routing routing = new RoutingImpl(actionClass, method,
 264  
                                 actionPath, uriParameterNames, pattern, requestMethod,
 265  
                                 onSubmit, priority, auto);
 266  
 
 267  524
                 if (routings.containsKey(routing)) {
 268  0
                         final Routing duplication = routings.get(routing);
 269  0
                         if (!routing.getActionClass().equals(duplication.getActionClass())
 270  
                                         || !routing.getMethod().equals(duplication.getMethod())) {
 271  0
                                 throw new DuplicateRoutingRuntimeException("ECUB0001",
 272  
                                                 new Object[] { routing, duplication });
 273  
                         }
 274  0
                 } else {
 275  524
                         routings.put(routing, routing);
 276  524
                         if (logger.isDebugEnabled()) {
 277  524
                                 logger.log("DCUB0007", new Object[] { routing });
 278  
                         }
 279  
                 }
 280  524
         }
 281  
 
 282  
         /**
 283  
          * {@inheritDoc}
 284  
          */
 285  
         public InternalForwardInfo getInternalForwardInfo(final String path,
 286  
                         final String requestMethod) {
 287  13
                 initialize();
 288  
 
 289  
                 final String decodedPath;
 290  
                 try {
 291  13
                         decodedPath = URLDecoder.decode(path, uriEncoding);
 292  0
                 } catch (final IOException e) {
 293  0
                         throw new IORuntimeException(e);
 294  13
                 }
 295  
 
 296  13
                 final InternalForwardInfo internalForwardInfo = findInternalForwardInfo(
 297  
                                 decodedPath, requestMethod);
 298  13
                 return internalForwardInfo;
 299  
         }
 300  
 
 301  
         /**
 302  
          * 指定されたパス、メソッドに対応する内部フォワード情報を検索します。
 303  
          * 
 304  
          * @param path
 305  
          *            リクエストのパス
 306  
          * @param requestMethod
 307  
          *            リクエストのメソッド
 308  
          * @return 内部フォワード情報、対応する内部フォワード情報が登録されていない場合は <code>null</code>
 309  
          */
 310  
         private InternalForwardInfo findInternalForwardInfo(final String path,
 311  
                         final String requestMethod) {
 312  13
                 final Iterator<Routing> iterator = routings.values().iterator();
 313  190
                 while (iterator.hasNext()) {
 314  188
                         final Routing routing = iterator.next();
 315  188
                         final Matcher matcher = routing.getPattern().matcher(path);
 316  188
                         if (matcher.find()) {
 317  12
                                 if (routing.isAcceptable(requestMethod)) {
 318  11
                                         final Map<String, Routing> onSubmitRoutings = new HashMap<String, Routing>();
 319  11
                                         onSubmitRoutings.put(routing.getOnSubmit(), routing);
 320  127
                                         while (iterator.hasNext()) {
 321  116
                                                 final Routing anotherRouting = iterator.next();
 322  116
                                                 if (routing.getPattern().pattern().equals(
 323  
                                                                 anotherRouting.getPattern().pattern())
 324  
                                                                 && routing.getRequestMethod().equals(
 325  
                                                                                 anotherRouting.getRequestMethod())) {
 326  0
                                                         onSubmitRoutings.put(anotherRouting.getOnSubmit(),
 327  
                                                                         anotherRouting);
 328  
                                                 }
 329  116
                                         }
 330  
 
 331  11
                                         final Map<String, String[]> uriParameters = new HashMap<String, String[]>();
 332  19
                                         for (int i = 0; i < matcher.groupCount(); i++) {
 333  8
                                                 final String name = routing.getUriParameterNames().get(
 334  
                                                                 i);
 335  8
                                                 final String value = matcher.group(i + 1);
 336  8
                                                 uriParameters.put(name, new String[] { value });
 337  
                                         }
 338  11
                                         final String inernalFowardPath = buildInternalForwardPath(uriParameters);
 339  
 
 340  11
                                         final InternalForwardInfo internalForwardInfo = new InternalForwardInfoImpl(
 341  
                                                         inernalFowardPath, onSubmitRoutings);
 342  
 
 343  11
                                         return internalForwardInfo;
 344  
                                 }
 345  
                         }
 346  177
                 }
 347  
 
 348  2
                 return null;
 349  
         }
 350  
 
 351  
         /**
 352  
          * {@inheritDoc}
 353  
          */
 354  
         public String buildInternalForwardPath(
 355  
                         final Map<String, String[]> parameters) {
 356  17
                 final StringBuilder builder = new StringBuilder(100);
 357  17
                 builder.append(INTERNAL_FORWARD_DIRECTORY);
 358  17
                 if (parameters != null && !parameters.isEmpty()) {
 359  8
                         builder.append("?");
 360  8
                         final QueryStringBuilder query = new QueryStringBuilder();
 361  8
                         if (!StringUtil.isEmpty(uriEncoding)) {
 362  8
                                 query.setEncode(uriEncoding);
 363  
                         }
 364  8
                         for (final Entry<String, String[]> entry : parameters.entrySet()) {
 365  24
                                 for (final String parameter : entry.getValue()) {
 366  12
                                         query.addParam(entry.getKey(), parameter);
 367  
                                 }
 368  
                         }
 369  8
                         builder.append(query.toString());
 370  
                 }
 371  17
                 return builder.toString();
 372  
         }
 373  
 
 374  
         /**
 375  
          * 命名規約を設定します。
 376  
          * 
 377  
          * @param namingConvention
 378  
          *            命名規約
 379  
          */
 380  
         public void setNamingConvention(final NamingConvention namingConvention) {
 381  54
                 this.namingConvention = namingConvention;
 382  54
         }
 383  
 
 384  
         /**
 385  
          * 指定された正規表現を括弧「()」で囲んで正規表現のグループにします。
 386  
          * 
 387  
          * @param regex
 388  
          *            正規表現
 389  
          * @return 正規表現のグループ
 390  
          */
 391  
         private static String regexGroup(final String regex) {
 392  346
                 return "(" + regex + ")";
 393  
         }
 394  
 
 395  
         /**
 396  
          * ルーティングのコンパレータ。
 397  
          * 
 398  
          * @author baba
 399  
          */
 400  4515
         static class RoutingComparator implements Comparator<Routing> {
 401  
 
 402  
                 /**
 403  
                  * routing1 と routing2 を比較します。
 404  
                  * <p>
 405  
                  * 正規表現パターンと HTTP メソッドが同じ場合は同値とみなします。
 406  
                  * </p>
 407  
                  * <p>
 408  
                  * また、大小関係は以下のようになります。
 409  
                  * <ul>
 410  
                  * <li>優先度(@link {@link Path#priority()})が小さい順</li>
 411  
                  * <li>URI 埋め込みパラメータが少ない順</li>
 412  
                  * <li>正規表現の順(@link {@link String#compareTo(String)})</li>
 413  
                  * </ul>
 414  
                  * </p>
 415  
                  * 
 416  
                  * @param routing1
 417  
                  *            比較対象1
 418  
                  * @param routing2
 419  
                  *            比較対象2
 420  
                  * @return 比較結果
 421  
                  */
 422  
                 public int compare(final Routing routing1, final Routing routing2) {
 423  4460
                         int compare = routing1.getPriority() - routing2.getPriority();
 424  4460
                         if (compare != 0) {
 425  800
                                 return compare;
 426  
                         }
 427  3660
                         compare = routing1.getUriParameterNames().size()
 428  
                                         - routing2.getUriParameterNames().size();
 429  3660
                         if (compare != 0) {
 430  966
                                 return compare;
 431  
                         }
 432  2694
                         compare = routing1.getPattern().pattern().compareTo(
 433  
                                         routing2.getPattern().pattern());
 434  2694
                         if (compare != 0) {
 435  1720
                                 return compare;
 436  
                         }
 437  974
                         compare = routing1.getRequestMethod().compareTo(
 438  
                                         routing2.getRequestMethod());
 439  974
                         if (compare != 0) {
 440  618
                                 return compare;
 441  
                         }
 442  356
                         if (routing1.getOnSubmit() == routing2.getOnSubmit()) {
 443  356
                                 compare = 0;
 444  0
                         } else if (routing1.getOnSubmit() == null) {
 445  0
                                 compare = -1;
 446  0
                         } else if (routing2.getOnSubmit() == null) {
 447  0
                                 compare = 1;
 448  
                         } else {
 449  0
                                 compare = routing1.getOnSubmit().compareTo(
 450  
                                                 routing2.getOnSubmit());
 451  
                         }
 452  356
                         return compare;
 453  
                 }
 454  
         }
 455  
 
 456  
         /**
 457  
          * {@inheritDoc}
 458  
          */
 459  
         public String reverseLookup(final Class<? extends Action> actionClass,
 460  
                         final String methodName, final Map<String, String[]> parameters) {
 461  18
                 final Routing routing = findRouting(actionClass, methodName);
 462  17
                 final String actionPath = routing.getActionPath();
 463  
 
 464  17
                 final Matcher matcher = URI_PARAMETER_MATCHING_PATTERN
 465  
                                 .matcher(actionPath);
 466  17
                 final Map<String, String[]> copyOfParameters = new HashMap<String, String[]>(
 467  
                                 parameters);
 468  17
                 String redirectPath = actionPath;
 469  25
                 while (matcher.find()) {
 470  10
                         final String holder = matcher.group(2);
 471  10
                         final String[] tokens = CubbyUtils.split2(holder, ',');
 472  10
                         final String uriParameterName = tokens[0];
 473  10
                         if (!copyOfParameters.containsKey(uriParameterName)) {
 474  1
                                 throw new ActionRuntimeException("ECUB0104", new Object[] {
 475  
                                                 actionPath, uriParameterName });
 476  
                         }
 477  9
                         final String value = copyOfParameters.remove(uriParameterName)[0];
 478  
                         final String uriParameterRegex;
 479  9
                         if (tokens.length == 1) {
 480  6
                                 uriParameterRegex = DEFAULT_URI_PARAMETER_REGEX;
 481  
                         } else {
 482  3
                                 uriParameterRegex = tokens[1];
 483  
                         }
 484  9
                         if (!value.matches(uriParameterRegex)) {
 485  1
                                 throw new ActionRuntimeException("ECUB0105",
 486  
                                                 new Object[] { actionPath, uriParameterName, value,
 487  
                                                                 uriParameterRegex });
 488  
                         }
 489  
                         try {
 490  8
                                 final String encodedValue = URLEncoder.encode(value,
 491  
                                                 uriEncoding);
 492  8
                                 redirectPath = StringUtil.replace(redirectPath, matcher
 493  
                                                 .group(1), encodedValue);
 494  0
                         } catch (final UnsupportedEncodingException e) {
 495  0
                                 throw new IORuntimeException(e);
 496  8
                         }
 497  8
                 }
 498  15
                 if (!copyOfParameters.isEmpty()) {
 499  8
                         final QueryStringBuilder builder = new QueryStringBuilder();
 500  8
                         builder.setEncode(uriEncoding);
 501  8
                         for (final Entry<String, String[]> entry : copyOfParameters
 502  
                                         .entrySet()) {
 503  22
                                 for (final String value : entry.getValue()) {
 504  11
                                         builder.addParam(entry.getKey(), value);
 505  
                                 }
 506  
                         }
 507  8
                         redirectPath += "?" + builder.toString();
 508  
                 }
 509  
 
 510  15
                 return redirectPath;
 511  
         }
 512  
 
 513  
         /**
 514  
          * 指定されたクラス、メソッドに対応するルーティング情報を検索します。
 515  
          * 
 516  
          * @param actionClass
 517  
          *            クラス
 518  
          * @param methodName
 519  
          *            メソッド
 520  
          * @return ルーティング情報
 521  
          * @throws ActionRuntimeException
 522  
          *             ルーティング情報が見つからなかった場合
 523  
          */
 524  
         private Routing findRouting(final Class<? extends Action> actionClass,
 525  
                         final String methodName) {
 526  18
                 for (final Routing routing : routings.values()) {
 527  47
                         if (actionClass.getCanonicalName().equals(
 528  
                                         routing.getActionClass().getCanonicalName())) {
 529  47
                                 if (methodName.equals(routing.getMethod().getName())) {
 530  17
                                         return routing;
 531  
                                 }
 532  
                         }
 533  
                 }
 534  1
                 throw new ActionRuntimeException("ECUB0103", new Object[] {
 535  
                                 actionClass, methodName });
 536  
         }
 537  
 
 538  
         /**
 539  
          * {@inheritDoc}
 540  
          * <p>
 541  
          * 指定されたパッケージ名、クラス名から導出されるクラスがアクションクラスだった場合はルーティングを登録します。
 542  
          * </p>
 543  
          */
 544  
         public void processClass(final String packageName,
 545  
                         final String shortClassName) {
 546  5287
                 if (shortClassName.indexOf('$') != -1) {
 547  1496
                         return;
 548  
                 }
 549  3791
                 final String className = ClassUtil.concatName(packageName,
 550  
                                 shortClassName);
 551  3791
                 if (!namingConvention.isTargetClassName(className)) {
 552  3568
                         return;
 553  
                 }
 554  223
                 if (!className.endsWith(namingConvention.getActionSuffix())) {
 555  150
                         return;
 556  
                 }
 557  73
                 final Class<?> clazz = ClassUtil.forName(className);
 558  73
                 if (namingConvention.isSkipClass(clazz)) {
 559  0
                         return;
 560  
                 }
 561  73
                 if (!CubbyUtils.isActionClass(clazz)) {
 562  16
                         return;
 563  
                 }
 564  57
                 final Class<? extends Action> actionClass = cast(clazz);
 565  
 
 566  1167
                 for (final Method method : clazz.getMethods()) {
 567  1110
                         if (CubbyUtils.isActionMethod(method)) {
 568  198
                                 final String actionPath = CubbyUtils.getActionPath(actionClass,
 569  
                                                 method);
 570  198
                                 final RequestMethod[] acceptableRequestMethods = CubbyUtils
 571  
                                                 .getAcceptableRequestMethods(clazz, method);
 572  552
                                 for (final RequestMethod requestMethod : acceptableRequestMethods) {
 573  354
                                         add(actionPath, actionClass, method, requestMethod, true);
 574  
                                 }
 575  
                         }
 576  
                 }
 577  57
         }
 578  
 
 579  
         /**
 580  
          * 指定されたクラスを <code>Class&lt;? extends Action&gt;</code> にキャストします。
 581  
          * 
 582  
          * @param clazz
 583  
          *            クラス
 584  
          * @return キャストされたクラス
 585  
          */
 586  
         @SuppressWarnings("unchecked")
 587  
         private static Class<? extends Action> cast(final Class<?> clazz) {
 588  57
                 return (Class<? extends Action>) clazz;
 589  
         }
 590  
 
 591  
 }