View Javadoc

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