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 }