##### Coverage Report - org.seasar.cubby.action.Forward

Classes in this File | Line Coverage | Branch Coverage | Complexity
Forward |
88% | 61/69
67% | 19/28
| 1.6521739130434783;1.652
Forward$1 |
N/A | N/A
N/A | N/A
| 1.6521739130434783;1.652
Forward$ForwardRouting |
53% | 8/15
N/A | N/A
| 1.6521739130434783;1.652

1 |  |
     /*
2 |  |
      * Copyright 2004-2009 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.action;
17 |  |
18 |  |
     import static org.seasar.cubby.CubbyConstants.ATTR_ROUTING;
19 |  |
     import static org.seasar.cubby.internal.util.LogMessages.format;
20 |  |
21 |  |
     import java.io.IOException;
22 |  |
     import java.lang.reflect.Method;
23 |  |
     import java.util.Collections;
24 |  |
     import java.util.HashMap;
25 |  |
     import java.util.List;
26 |  |
     import java.util.Map;
27 |  |
     import java.util.regex.Pattern;
28 |  |
29 |  |
     import javax.servlet.RequestDispatcher;
30 |  |
     import javax.servlet.ServletException;
31 |  |
     import javax.servlet.http.HttpServletRequest;
32 |  |
     import javax.servlet.http.HttpServletResponse;
33 |  |
34 |  |
     import org.seasar.cubby.internal.util.MetaUtils;
35 |  |
     import org.seasar.cubby.internal.util.QueryStringBuilder;
36 |  |
     import org.seasar.cubby.internal.util.StringUtils;
37 |  |
     import org.seasar.cubby.routing.PathResolver;
38 |  |
     import org.seasar.cubby.routing.Routing;
39 |  |
     import org.seasar.cubby.spi.PathResolverProvider;
40 |  |
     import org.seasar.cubby.spi.ProviderFactory;
41 |  |
     import org.slf4j.Logger;
42 |  |
     import org.slf4j.LoggerFactory;
43 |  |
44 |  |
     /**
45 |  |
      * 指定されたパスにフォワードする {@link ActionResult} です。
46 |  |
      * <p>
47 |  |
      * アクションメソッドの戻り値としてこのインスタンスを指定することで、指定されたパスにフォワードします。
48 |  |
      * </p>
49 |  |
      * <p>
50 |  |
      * 使用例1 : フォワード先を相対パスで指定
51 |  |
      *
52 |  |
      * <pre>
53 |  |
      * return new Forward(&quot;list.jsp&quot;);
54 |  |
      * </pre>
55 |  |
      *
56 |  |
      * </p>
57 |  |
      * <p>
58 |  |
      * 使用例2 : フォワード先を絶対パスで指定
59 |  |
      *
60 |  |
      * <pre>
61 |  |
      * return new Forward(&quot;/todo/list.jsp&quot;);
62 |  |
      * </pre>
63 |  |
      *
64 |  |
      * </p>
65 |  |
      * <p>
66 |  |
      * 使用例2 : フォワード先をクラスとメソッド名で指定
67 |  |
      *
68 |  |
      * <pre>
69 |  |
      * return new Forward(TodoListAction.class, &quot;show&quot;);
70 |  |
      * </pre>
71 |  |
      *
72 |  |
      * <p>
73 |  |
      * 使用例3 : フォワード先をクラスとメソッド名で指定(paramメソッドによるパラメータつき)
74 |  |
      *
75 |  |
      * <pre>
76 |  |
      * return new Forward(TodoListAction.class, &quot;show&quot;).param(&quot;value1&quot;, &quot;12345&quot;);
77 |  |
      * </pre>
78 |  |
      *
79 |  |
      * </p>
80 |  |
      * <p>
81 |  |
      * 使用例3 : フォワード先をクラスとメソッド名で指定(Mapによるパラメータつき)
82 |  |
      *
83 |  |
      * <pre>
84 |  |
      * Map&lt;String, String[]&gt; parameters = new HashMap();
85 |  |
      * parameters.put(&quot;value1&quot;, new String[] { &quot;12345&quot; });
86 |  |
      * return new Forward(TodoListAction.class, &quot;show&quot;, parameters);
87 |  |
      * </pre>
88 |  |
      *
89 |  |
      * </p>
90 |  |
      * <p>
91 |  |
      * フォワード前には {@link Action#invokePreRenderMethod(Method)} を実行します。 フォワード後には
92 |  |
      * {@link Action#invokePostRenderMethod(Method)} を実行し、フラッシュメッセージをクリアします。
93 |  |
      * </p>
94 |  |
      *
95 |  |
      * @author baba
96 |  |
      */
97 |  |
     public class Forward implements ActionResult {
98 |  |
99 |  |
             /** ロガー。 */
100 | 1 |
             private static final Logger logger = LoggerFactory.getLogger(Forward.class);
101 |  |
102 |  |
             /** 空のパラメータ。 */
103 | 1 |
             private static final Map<String, String[]> EMPTY_PARAMETERS = Collections
104 |  |
                             .emptyMap();
105 |  |
106 |  |
             /** {@link PathResolver} のプロバイダ。 */
107 |  |
             private final PathResolverProvider pathResolverProvider;
108 |  |
109 |  |
             /** フォワード先のパス。 */
110 |  |
             private String path;
111 |  |
112 |  |
             /** ルーティング。 */
113 |  |
             private final Routing routing;
114 |  |
115 |  |
             /** フォワード先のアクションクラス */
116 |  |
             private Class<?> actionClass;
117 |  |
118 |  |
             /** フォワード先のアクションクラスのメソッド名 */
119 |  |
             private String methodName;
120 |  |
121 |  |
             /** フォワード時のパラメータ */
122 |  |
             private Map<String, String[]> parameters;
123 |  |
124 |  |
             /**
125 |  |
              * インスタンスを生成します。
126 |  |
              *
127 |  |
              * @param path
128 |  |
              *            フォワード先のパス
129 |  |
              */
130 | 6 |
             public Forward(final String path) {
131 | 6 |
                     this.pathResolverProvider = null;
132 | 6 |
                     this.path = path;
133 | 6 |
                     this.routing = null;
134 | 6 |
             }
135 |  |
136 |  |
             /**
137 |  |
              * インスタンスを生成します。
138 |  |
              *
139 |  |
              * @param actionClass
140 |  |
              *            アクションクラス
141 |  |
              * @param methodName
142 |  |
              *            アクションメソッド名
143 |  |
              * @param parameters
144 |  |
              *            パラメータ
145 |  |
              */
146 |  |
             public Forward(final Class<?> actionClass, final String methodName,
147 | 3 |
                             final Map<String, String[]> parameters) {
148 | 3 |
                     this.pathResolverProvider = ProviderFactory
149 |  |
                                     .get(PathResolverProvider.class);
150 | 3 |
                     this.actionClass = actionClass;
151 | 3 |
                     this.methodName = methodName;
152 | 3 |
                     this.parameters = parameters;
153 |  |
                     try {
154 | 3 |
                             final Method method = actionClass.getMethod(methodName);
155 | 3 |
                             this.routing = new ForwardRouting(actionClass, method);
156 | 0 |
                     } catch (final NoSuchMethodException e) {
157 | 0 |
                             throw new IllegalArgumentException(e);
158 | 3 |
                     }
159 | 3 |
             }
160 |  |
161 |  |
             /**
162 |  |
              * 指定されたアクションクラスのindexメソッドへフォワードするインスタンスを生成します。
163 |  |
              *
164 |  |
              * @param actionClass
165 |  |
              *            アクションクラス
166 |  |
              */
167 |  |
             public Forward(final Class<?> actionClass) {
168 | 1 |
                     this(actionClass, "index");
169 | 1 |
             }
170 |  |
171 |  |
             /**
172 |  |
              * 指定されたアクションメソッドへフォワードするインスタンスを生成します。
173 |  |
              *
174 |  |
              * @param actionClass
175 |  |
              *            アクションクラス
176 |  |
              * @param methodName
177 |  |
              *            アクションメソッド名
178 |  |
              */
179 |  |
             public Forward(final Class<?> actionClass, final String methodName) {
180 | 3 |
                     this(actionClass, methodName, EMPTY_PARAMETERS);
181 | 3 |
             }
182 |  |
183 |  |
             /**
184 |  |
              * パスを取得します。
185 |  |
              *
186 |  |
              * @param characterEncoding
187 |  |
              *            URI のエンコーディング
188 |  |
              * @return パス
189 |  |
              */
190 |  |
             public String getPath(final String characterEncoding) {
191 | 17 |
                     if (isReverseLookupRedirect()) {
192 | 8 |
                             final PathResolver pathResolver = this.pathResolverProvider
193 |  |
                                             .getPathResolver();
194 | 8 |
                             final String forwardPath = pathResolver.reverseLookup(actionClass,
195 |  |
                                             methodName, parameters, characterEncoding);
196 | 8 |
                             this.path = forwardPath;
197 |  |
                     }
198 | 17 |
                     return this.path;
199 |  |
             }
200 |  |
201 |  |
             /**
202 |  |
              * アクションクラスを指定したフォワードかどうかを判定します。
203 |  |
              *
204 |  |
              * @return アクションクラスを指定したフォワードならtrue
205 |  |
              */
206 |  |
             private boolean isReverseLookupRedirect() {
207 | 23 |
                     return this.actionClass != null && this.methodName != null
208 |  |
                                     && this.parameters != null;
209 |  |
             }
210 |  |
211 |  |
             /**
212 |  |
              * {@inheritDoc}
213 |  |
              */
214 |  |
             public void execute(final ActionContext actionContext,
215 |  |
                             final HttpServletRequest request, final HttpServletResponse response)
216 |  |
                             throws ServletException, IOException {
217 | 6 |
                     actionContext.invokePreRenderMethod();
218 |  |
219 | 6 |
                     final String forwardPath = calculateForwardPath(getPath(request
220 |  |
                                     .getCharacterEncoding()), actionContext.getActionClass(),
221 |  |
                                     request.getCharacterEncoding());
222 | 6 |
                     if (this.routing != null) {
223 | 3 |
                             request.setAttribute(ATTR_ROUTING, this.routing);
224 |  |
                     }
225 | 6 |
                     if (logger.isDebugEnabled()) {
226 | 6 |
                             logger.debug(format("DCUB0001", forwardPath, routing));
227 |  |
                     }
228 | 6 |
                     final RequestDispatcher dispatcher = request
229 |  |
                                     .getRequestDispatcher(forwardPath);
230 | 6 |
                     dispatcher.forward(request, response);
231 | 6 |
                     if (logger.isDebugEnabled()) {
232 | 6 |
                             logger.debug(format("DCUB0002", forwardPath));
233 |  |
                     }
234 |  |
235 | 6 |
                     actionContext.invokePostRenderMethod();
236 | 6 |
                     actionContext.clearFlash();
237 | 6 |
             }
238 |  |
239 |  |
             /**
240 |  |
              * フォワードするパスを計算します。
241 |  |
              *
242 |  |
              * @param actionClass
243 |  |
              *            アクションクラス
244 |  |
              * @param characterEncoding
245 |  |
              *            URI のエンコーディング
246 |  |
              * @return フォワードするパス
247 |  |
              */
248 |  |
             protected String calculateForwardPath(final String path,
249 |  |
                             final Class<?> actionClass, final String characterEncoding) {
250 |  |
                     final String absolutePath;
251 | 6 |
                     if (getPath(characterEncoding).startsWith("/")) {
252 | 4 |
                             absolutePath = path;
253 |  |
                     } else {
254 | 2 |
                             final String actionDirectory = MetaUtils
255 |  |
                                             .getActionDirectory(actionClass);
256 | 2 |
                             if (StringUtils.isEmpty(actionDirectory)) {
257 | 0 |
                                     absolutePath = "/" + path;
258 |  |
                             } else {
259 | 2 |
                                     final StringBuilder builder = new StringBuilder();
260 | 2 |
                                     if (!actionDirectory.startsWith("/")) {
261 | 2 |
                                             builder.append("/");
262 |  |
                                     }
263 | 2 |
                                     builder.append(actionDirectory);
264 | 2 |
                                     if (!actionDirectory.endsWith("/")) {
265 | 2 |
                                             builder.append("/");
266 |  |
                                     }
267 | 2 |
                                     builder.append(path);
268 | 2 |
                                     absolutePath = builder.toString();
269 |  |
                             }
270 |  |
                     }
271 | 6 |
                     return absolutePath;
272 |  |
             }
273 |  |
274 |  |
             /**
275 |  |
              * パラメータを追加します。
276 |  |
              *
277 |  |
              * @param paramName
278 |  |
              *            パラメータ名
279 |  |
              * @param paramValue
280 |  |
              *            パラメータの値。{@code Object#toString()}の結果が値として使用されます。
281 |  |
              * @return フォワードする URL
282 |  |
              */
283 |  |
             public Forward param(final String paramName, final Object paramValue) {
284 | 6 |
                     return param(paramName, new String[] { paramValue.toString() });
285 |  |
             }
286 |  |
287 |  |
             /**
288 |  |
              * パラメータを追加します。
289 |  |
              *
290 |  |
              * @param paramName
291 |  |
              *            パラメータ名
292 |  |
              * @param paramValues
293 |  |
              *            パラメータの値の配列。配列の要素の{@code Object#toString()}の結果がそれぞれの値として使用されます。
294 |  |
              * @return フォワードする URL
295 |  |
              */
296 |  |
             public Forward param(final String paramName, final Object[] paramValues) {
297 | 0 |
                     return param(paramName, toStringArray(paramValues));
298 |  |
             }
299 |  |
300 |  |
             /**
301 |  |
              * パラメータを追加します。
302 |  |
              *
303 |  |
              * @param paramName
304 |  |
              *            パラメータ名
305 |  |
              * @param paramValues
306 |  |
              *            パラメータの値
307 |  |
              * @return フォワードする URL
308 |  |
              */
309 |  |
             public Forward param(final String paramName, final String[] paramValues) {
310 | 6 |
                     if (isReverseLookupRedirect()) {
311 | 4 |
                             if (this.parameters == EMPTY_PARAMETERS) {
312 | 2 |
                                     this.parameters = new HashMap<String, String[]>();
313 |  |
                             }
314 | 4 |
                             this.parameters.put(paramName, paramValues);
315 |  |
                     } else {
316 | 2 |
                             final QueryStringBuilder builder = new QueryStringBuilder(this.path);
317 | 2 |
                             builder.addParam(paramName, paramValues);
318 | 2 |
                             this.path = builder.toString();
319 |  |
                     }
320 | 6 |
                     return this;
321 |  |
             }
322 |  |
323 |  |
             /**
324 |  |
              * {@code Object#toString()}型の配列を{@code Object#toString()}型の配列に変換します。
325 |  |
              * <p>
326 |  |
              * 配列のそれぞれの要素に対して{@code Object#toString()}を使用して変換します。
327 |  |
              * </p>
328 |  |
              *
329 |  |
              * @param paramValues
330 |  |
              *            {@code Object#toString()}型の配列
331 |  |
              * @return {@code Object#toString()}型の配列。
332 |  |
              */
333 |  |
             private String[] toStringArray(final Object[] paramValues) {
334 | 0 |
                     final String[] values = new String[paramValues.length];
335 | 0 |
                     for (int i = 0; i < paramValues.length; i++) {
336 | 0 |
                             values[i] = paramValues[i].toString();
337 |  |
                     }
338 | 0 |
                     return values;
339 |  |
             }
340 |  |
341 |  |
             /**
342 |  |
              * アクションメソッドへフォワードするためのルーティングです。
343 |  |
              *
344 |  |
              * @author baba
345 |  |
              */
346 | 3 |
             private static class ForwardRouting implements Routing {
347 |  |
348 |  |
                     /** アクションクラス。 */
349 |  |
                     private final Class<?> actionClass;
350 |  |
351 |  |
                     /** アクションメソッド。 */
352 |  |
                     private final Method actionMethod;
353 |  |
354 |  |
                     /**
355 |  |
                      * 指定されたアクションメソッドを実行する新規ルーティングを生成します。
356 |  |
                      */
357 |  |
                     private ForwardRouting(final Class<?> actionClass,
358 | 3 |
                                     final Method actionMethod) {
359 | 3 |
                             this.actionClass = actionClass;
360 | 3 |
                             this.actionMethod = actionMethod;
361 | 3 |
                     }
362 |  |
363 |  |
                     /**
364 |  |
                      * {@inheritDoc}
365 |  |
                      */
366 |  |
                     public Class<?> getActionClass() {
367 | 3 |
                             return actionClass;
368 |  |
                     }
369 |  |
370 |  |
                     /**
371 |  |
                      * {@inheritDoc}
372 |  |
                      */
373 |  |
                     public Method getActionMethod() {
374 | 3 |
                             return actionMethod;
375 |  |
                     }
376 |  |
377 |  |
                     /**
378 |  |
                      * {@inheritDoc}
379 |  |
                      */
380 |  |
                     public String getActionPath() {
381 | 0 |
                             return null;
382 |  |
                     }
383 |  |
384 |  |
                     /**
385 |  |
                      * {@inheritDoc}
386 |  |
                      */
387 |  |
                     public List<String> getUriParameterNames() {
388 | 0 |
                             return null;
389 |  |
                     }
390 |  |
391 |  |
                     /**
392 |  |
                      * {@inheritDoc}
393 |  |
                      */
394 |  |
                     public Pattern getPattern() {
395 | 0 |
                             return null;
396 |  |
                     }
397 |  |
398 |  |
                     /**
399 |  |
                      * {@inheritDoc}
400 |  |
                      */
401 |  |
                     public RequestMethod getRequestMethod() {
402 | 0 |
                             return null;
403 |  |
                     }
404 |  |
405 |  |
                     /**
406 |  |
                      * {@inheritDoc}
407 |  |
                      */
408 |  |
                     public String getOnSubmit() {
409 | 0 |
                             return null;
410 |  |
                     }
411 |  |
412 |  |
                     /**
413 |  |
                      * {@inheritDoc}
414 |  |
                      */
415 |  |
                     public int getPriority() {
416 | 0 |
                             return 0;
417 |  |
                     }
418 |  |
419 |  |
                     /**
420 |  |
                      * {@inheritDoc}
421 |  |
                      */
422 |  |
                     public boolean isAcceptable(final String requestMethod) {
423 | 0 |
                             return true;
424 |  |
                     }
425 |  |
426 |  |
                     /**
427 |  |
                      * {@inheritDoc}
428 |  |
                      */
429 |  |
                     @Override
430 |  |
                     public String toString() {
431 | 3 |
                             return new StringBuilder().append("[").append(actionMethod).append(
432 |  |
                                             "]").toString();
433 |  |
                     }
434 |  |
435 |  |
             }
436 |  |
437 |  |
     }