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