View Javadoc

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.tags;
17  
18  import static java.lang.Boolean.TRUE;
19  import static javax.servlet.jsp.PageContext.REQUEST_SCOPE;
20  import static org.seasar.cubby.CubbyConstants.ATTR_CONTEXT_PATH;
21  import static org.seasar.cubby.CubbyConstants.ATTR_ERRORS;
22  import static org.seasar.cubby.CubbyConstants.ATTR_PARAMS;
23  import static org.seasar.cubby.CubbyConstants.ATTR_VALIDATION_FAIL;
24  import static org.seasar.cubby.internal.util.LogMessages.format;
25  
26  import java.util.Collection;
27  import java.util.Map;
28  import java.util.Map.Entry;
29  
30  import javax.servlet.jsp.JspContext;
31  import javax.servlet.jsp.PageContext;
32  import javax.servlet.jsp.tagext.SimpleTag;
33  import javax.servlet.jsp.tagext.SimpleTagSupport;
34  
35  import org.seasar.cubby.CubbyConstants;
36  import org.seasar.cubby.action.ActionErrors;
37  import org.seasar.cubby.controller.FormWrapper;
38  import org.seasar.cubby.internal.util.StringUtils;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  /**
43   * カスタムタグで使用するユーティリティクラスです。
44   * 
45   * @author baba
46   */
47  class TagUtils {
48  
49  	/** ロガー。 */
50  	private static final Logger logger = LoggerFactory
51  			.getLogger(TagUtils.class);
52  
53  	/**
54  	 * 指定されたJSPコンテキストから{@link ActionErrors}を取得します。
55  	 * 
56  	 * @param context
57  	 *            JSPコンテキスト
58  	 * @return アクションで発生したエラー
59  	 */
60  	public static ActionErrors errors(final JspContext context) {
61  		return (ActionErrors) context.getAttribute(ATTR_ERRORS, REQUEST_SCOPE);
62  	}
63  
64  	/**
65  	 * 指定されたJSPコンテキストから指定されたパラメータ名に対応する要求パラメータを取得します。
66  	 * 
67  	 * @param context
68  	 *            JSPコンテキスト
69  	 * @param name
70  	 *            パラメータ名
71  	 * @return 要求パラメータ
72  	 */
73  	@SuppressWarnings("unchecked")
74  	private static Object[] paramValues(final JspContext context,
75  			final String name) {
76  		final Map<String, Object[]> valuesMap = Map.class.cast(context
77  				.getAttribute(ATTR_PARAMS, REQUEST_SCOPE));
78  		final Object[] values;
79  		if (valuesMap == null || !valuesMap.containsKey(name)) {
80  			values = new Object[0];
81  		} else {
82  			values = valuesMap.get(name);
83  		}
84  		return values;
85  	}
86  
87  	/**
88  	 * 指定されたフィールド名に対応するフォームのフィールドへの出力値を取得します。
89  	 * 
90  	 * @param context
91  	 *            JSPコンテキスト
92  	 * @param formWrapper
93  	 *            フォームオブジェクトのラッパー
94  	 * @param name
95  	 *            フィールド名
96  	 * @return フォームのフィールドへの出力値
97  	 */
98  	public static Object[] multipleFormValues(final JspContext context,
99  			final FormWrapper formWrapper, final String name) {
100 		return multipleFormValues(context, formWrapper, name, null);
101 	}
102 
103 	/**
104 	 * 指定されたフィールド名に対応するフォームのフィールドへの出力値を取得します。
105 	 * 
106 	 * @param context
107 	 *            JSPコンテキスト
108 	 * @param formWrapper
109 	 *            フォームオブジェクトのラッパー
110 	 * @param name
111 	 *            フィールド名
112 	 * @param checkedValue
113 	 *            チェック済みにする値
114 	 * @return フォームのフィールドへの出力値
115 	 */
116 	public static Object[] multipleFormValues(final JspContext context,
117 			final FormWrapper formWrapper, final String name,
118 			final String checkedValue) {
119 		final Object[] values;
120 		if (isValidationFail(context)) {
121 			values = paramValues(context, name);
122 		} else {
123 			if (checkedValue != null) {
124 				values = new Object[] { checkedValue };
125 			} else {
126 				if (!formWrapper.hasValues(name)) {
127 					if (logger.isDebugEnabled()) {
128 						logger.debug(format("DCUB0023", name));
129 					}
130 					return null;
131 				}
132 				values = formWrapper.getValues(name);
133 			}
134 		}
135 		return values;
136 	}
137 
138 	/**
139 	 * 指定されたフィールド名に対応するフォームのフィールドへの出力値を取得します。
140 	 * 
141 	 * @param context
142 	 *            JSPコンテキスト
143 	 * @param formWrapper
144 	 *            フォームオブジェクトのラッパー
145 	 * @param name
146 	 *            フィールド名
147 	 * @param index
148 	 *            インデックス
149 	 * @param specifiedValue
150 	 *            エラーがない場合に設定する値
151 	 * @return フォームのフィールドへの出力値
152 	 */
153 	public static Object formValue(final JspContext context,
154 			final FormWrapper formWrapper, final String name,
155 			final Integer index, final Object specifiedValue) {
156 		final Object value;
157 
158 		if (isValidationFail(context)) {
159 			if (specifiedValue == null) {
160 				final Object[] values = paramValues(context, name);
161 				value = value(values, index);
162 			} else {
163 				final Object[] values = paramValues(context, name);
164 				if (values.length == 0) {
165 					value = specifiedValue;
166 				} else {
167 					value = value(values, index);
168 				}
169 			}
170 		} else {
171 			if (specifiedValue != null) {
172 				value = specifiedValue;
173 			} else {
174 				if (!formWrapper.hasValues(name)) {
175 					logger.debug(format("DCUB0023", name));
176 					return null;
177 				}
178 				value = value(formWrapper.getValues(name), index);
179 			}
180 		}
181 
182 		return value;
183 	}
184 
185 	/**
186 	 * オブジェクトの配列から指定されたインデックスの値を取得します。
187 	 * <p>
188 	 * values が <code>null</code> の場合や index が要素数を越えていた場合は空文字を返します。index が
189 	 * <code>null</code> の場合は配列の最初の要素を返します。
190 	 * </p>
191 	 * 
192 	 * @param values
193 	 *            オブジェクトの配列
194 	 * @param index
195 	 *            インデックス
196 	 * @return 指定されたインデックスの要素
197 	 */
198 	private static Object value(final Object[] values, final Integer index) {
199 		final Object value;
200 		if (values == null) {
201 			value = "";
202 		} else {
203 			if (index == null) {
204 				value = getElement(values, 0);
205 			} else {
206 				value = getElement(values, index);
207 			}
208 		}
209 		return value;
210 	}
211 
212 	/**
213 	 * オブジェクトの配列から指定されたインデックスの要素を取得します。
214 	 * <p>
215 	 * index が要素数を越えていた場合は空文字を返します。
216 	 * </p>
217 	 * 
218 	 * @param values
219 	 *            オブジェクトの配列
220 	 * @param index
221 	 *            インデックス
222 	 * @return 指定されたインデックスの要素
223 	 */
224 	private static Object getElement(final Object[] values, final Integer index) {
225 		final Object value;
226 		if (values.length <= index) {
227 			value = "";
228 		} else {
229 			value = values[index];
230 		}
231 		return value;
232 	}
233 
234 	/**
235 	 * 指定されたJSPコンテキストのアクションが入力検証に失敗したかどうかを示します。
236 	 * 
237 	 * @param context
238 	 *            JSPコンテキスト
239 	 * @return アクションが入力検証に失敗した場合は <code>true</code>、そうでない場合は <code>false</code>
240 	 * @see CubbyConstants#ATTR_VALIDATION_FAIL
241 	 */
242 	private static boolean isValidationFail(final JspContext context) {
243 		return TRUE.equals(context.getAttribute(ATTR_VALIDATION_FAIL,
244 				REQUEST_SCOPE));
245 	}
246 
247 	public static final Object REMOVE_ATTRIBUTE = new Object();
248 
249 	/**
250 	 * 指定された {@link Map} を HTML タグの属性へ変換します。
251 	 * <p>
252 	 * map 中の値が属性を出力しないことを示すオブジェクトの場合、その属性は結果から除外します。
253 	 * </p>
254 	 * 
255 	 * @param map
256 	 *            属性のマップ
257 	 * @return HTML タグの属性
258 	 */
259 	public static String toAttr(final Map<String, Object> map) {
260 		final StringBuilder builder = new StringBuilder();
261 		for (final Entry<String, Object> entry : map.entrySet()) {
262 			final String key = entry.getKey();
263 			if (entry.getValue() == REMOVE_ATTRIBUTE) {
264 				continue;
265 			}
266 			builder.append(key);
267 			builder.append("=\"");
268 			builder.append(escapeHtml(entry.getValue()));
269 			builder.append("\" ");
270 		}
271 		return builder.toString();
272 	}
273 
274 	/**
275 	 * 指定されたオブジェクトが特定の文字列を含むかを示します。
276 	 * <p>
277 	 * 指定されたオブジェクトが配列や{@link Collection}の場合は、その要素の文字列表現が指定された文字列と同値かを示します。
278 	 * 指定されたオブジェクトが配列や{@link Collection}でない場合は、そのオブジェクトの文字列表現が指定された文字列と同値かを示します。
279 	 * </p>
280 	 * 
281 	 * @param obj
282 	 *            オブジェクト
283 	 * @param str
284 	 *            文字列
285 	 * @return 指定されたオブジェクトが特定の文字列を含む場合は <code>true</code>、そうでない場合は
286 	 *         <code>false</code>
287 	 */
288 	public static boolean contains(final Object obj, final String str) {
289 		if (obj instanceof Collection<?>) {
290 			return ((Collection<?>) obj).contains(str);
291 		} else if (obj.getClass().isArray()) {
292 			for (final Object value : (Object[]) obj) {
293 				if (equalsAsString(value, str)) {
294 					return true;
295 				}
296 			}
297 			return false;
298 		} else {
299 			return equalsAsString(obj, str);
300 		}
301 	}
302 
303 	/**
304 	 * 指定された値が文字列として同値かを示します。
305 	 * 
306 	 * @param obj1
307 	 *            比較するオブジェクト1
308 	 * @param obj2
309 	 *            比較するオブジェクト2
310 	 * @return obj1とobj2が文字列として同値の場合は <code>true</code>、そうでない場合は
311 	 *         <code>false</code>
312 	 */
313 	private static boolean equalsAsString(final Object obj1, final Object obj2) {
314 		if (obj1 == obj2) {
315 			return true;
316 		} else if (obj1 == null) {
317 			return false;
318 		} else {
319 			return obj1.toString().equals(obj2.toString());
320 		}
321 	}
322 
323 	/**
324 	 * 動的な属性の {@link Map} に、指定された <code>class</code> 属性を追加します。
325 	 * 
326 	 * @param dynamicAttributes
327 	 *            動的な属性の {@link Map}
328 	 * @param className
329 	 *            <code>class</code> 属性の名前
330 	 */
331 	public static void addCSSClassName(
332 			final Map<String, Object> dynamicAttributes, final String className) {
333 		String classValue = (String) dynamicAttributes.get("class");
334 		if (StringUtils.isEmpty(classValue)) {
335 			classValue = className;
336 		} else {
337 			classValue = classValue + " " + className;
338 		}
339 		dynamicAttributes.put("class", classValue);
340 	}
341 
342 	/**
343 	 * 指定されたタグの親の {@link FormTag} を検索し、そこからフォームオブジェクトのラッパーを取得します。
344 	 * 
345 	 * @param tag
346 	 *            タグ
347 	 * @return フォームオブジェクトのラッパー
348 	 */
349 	public static FormWrapper getFormWrapper(final SimpleTag tag) {
350 		final FormTag formTag = (FormTag) SimpleTagSupport
351 				.findAncestorWithClass(tag, FormTag.class);
352 		if (formTag == null) {
353 			return null;
354 		}
355 		return formTag.getFormWrapper();
356 	}
357 
358 	/**
359 	 * 指定された文字列をHTMLとしてエスケープします。
360 	 * <p>
361 	 * <table>
362 	 * <thead>
363 	 * <tr>
364 	 * <th>変換前</th>
365 	 * <th>変換後</th>
366 	 * </tr>
367 	 * </thead> <tbody>
368 	 * <tr>
369 	 * <td>&amp;</td>
370 	 * <td>&amp;amp;</td>
371 	 * </tr>
372 	 * <tr>
373 	 * <td>&lt;</td>
374 	 * <td>&amp;lt;</td>
375 	 * </tr>
376 	 * <tr>
377 	 * <td>&gt;</td>
378 	 * <td>&amp;gt;</td>
379 	 * </tr>
380 	 * <tr>
381 	 * <td>&quot;</td>
382 	 * <td>&amp;quot;</td>
383 	 * </tr>
384 	 * <tr>
385 	 * <td>&#39</td>
386 	 * <td>&amp;#39</td>
387 	 * </tr>
388 	 * </tbody>
389 	 * </table>
390 	 * </p>
391 	 * 
392 	 * @param str
393 	 * @return エスケープされた文字列
394 	 */
395 	public static String escapeHtml(final Object str) {
396 		if (str == null) {
397 			return "";
398 		}
399 		String text = str.toString();
400 		text = StringUtils.replace(text, "&", "&amp;");
401 		text = StringUtils.replace(text, "<", "&lt;");
402 		text = StringUtils.replace(text, ">", "&gt;");
403 		text = StringUtils.replace(text, "\"", "&quot;");
404 		text = StringUtils.replace(text, "'", "&#39;");
405 		return text;
406 	}
407 
408 	/**
409 	 * オブジェクトを文字列に変換します。 オブジェクトが<code>null</code>の場合、空文字を返します。
410 	 * 
411 	 * @param object
412 	 *            対象のオブジェクト
413 	 * @return オブジェクトのtoString結果。
414 	 */
415 	public static String toString(final Object object) {
416 		return object == null ? "" : object.toString();
417 	}
418 
419 	/**
420 	 * コンテキストパスを取得します。
421 	 * 
422 	 * @param jspContext
423 	 *            JSP コンテキスト
424 	 * @return コンテキストパス
425 	 */
426 	public static String getContextPath(final JspContext jspContext) {
427 		final String contextPath = (String) jspContext.getAttribute(
428 				ATTR_CONTEXT_PATH, PageContext.REQUEST_SCOPE);
429 		if ("/".equals(contextPath)) {
430 			return "";
431 		}
432 		return contextPath;
433 	}
434 
435 }