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