View Javadoc

1   /*
2    * Copyright 2004-2008 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.util;
17  
18  import static org.seasar.cubby.CubbyConstants.INTERNAL_FORWARD_DIRECTORY;
19  
20  import java.lang.reflect.Method;
21  import java.util.Collection;
22  
23  import javax.servlet.http.HttpServletRequest;
24  
25  import org.seasar.cubby.action.Accept;
26  import org.seasar.cubby.action.Action;
27  import org.seasar.cubby.action.ActionResult;
28  import org.seasar.cubby.action.Path;
29  import org.seasar.cubby.action.RequestMethod;
30  import org.seasar.framework.util.StringUtil;
31  
32  /**
33   * Cubby内部で使用するユーティリティクラスです。
34   * 
35   * @author baba
36   * @since 1.0.0
37   */
38  public class CubbyUtils {
39  
40  	/** インデックスのメソッド名。 */
41  	private static final String INDEX_METHOD_NAME = "index";
42  
43  	/** デフォルトの{@link Accept}アノテーション。 */
44  	private static Accept DEFAULT_ACCEPT_ANNOTATION;
45  	static {
46  		@Accept
47  		class AcceptDummy {
48  		}
49  		DEFAULT_ACCEPT_ANNOTATION = AcceptDummy.class
50  				.getAnnotation(Accept.class);
51  	}
52  
53  	/**
54  	 * 指定されたアクションクラスに対応するディレクトリを取得します。
55  	 * 
56  	 * @param actionClass
57  	 *            アクションクラス
58  	 * @return アクションクラスに対応するディレクトリ
59  	 */
60  	public static String getActionDirectory(final Class<?> actionClass) {
61  		final String actionName;
62  		final Path path = actionClass.getAnnotation(Path.class);
63  		if (path != null && !StringUtil.isEmpty(path.value())) {
64  			actionName = path.value();
65  		} else {
66  			final String name = left(actionClass.getSimpleName(), "$");
67  			actionName = toFirstLower(name.replaceAll(
68  					"(.*[.])*([^.]+)(Action$)", "$2"));
69  		}
70  		return actionName;
71  	}
72  
73  	/**
74  	 * 指定された文字列をセパレータで区切った左側の文字列を返します。
75  	 * 
76  	 * @param text
77  	 *            文字列
78  	 * @param sep
79  	 *            セパレータ
80  	 * @return セパレータで区切った左側の文字列
81  	 */
82  	private static String left(final String text, final String sep) {
83  		final int pos = text.indexOf(sep);
84  		if (pos != -1) {
85  			return text.substring(0, pos);
86  		}
87  		return text;
88  	}
89  
90  	/**
91  	 * 指定された文字列の先頭1文字を小文字に変換します。
92  	 * 
93  	 * @param text
94  	 *            変換する文字列
95  	 * @return 先頭1文字を小文字にした文字列
96  	 */
97  	private static String toFirstLower(final String text) {
98  		if (StringUtil.isEmpty(text)) {
99  			throw new IllegalArgumentException("text is empty.");
100 		}
101 		final StringBuilder sb = new StringBuilder();
102 		sb.append(text.substring(0, 1).toLowerCase());
103 		if (text.length() > 1) {
104 			sb.append(text.substring(1));
105 		}
106 		return sb.toString();
107 	}
108 
109 	/**
110 	 * 指定されたアクションメソッドのパスを取得します。
111 	 * 
112 	 * @param actionClass
113 	 *            アクションクラス
114 	 * @param method
115 	 *            アクションメソッド
116 	 * @return アクションメソッドのパス
117 	 */
118 	public static String getActionPath(final Class<?> actionClass,
119 			final Method method) {
120 		final String path;
121 		final String actionMethodName = getActionMethodName(method);
122 		if (actionMethodName.startsWith("/")) {
123 			return path = actionMethodName;
124 		} else {
125 			final String actionDirectory = CubbyUtils
126 					.getActionDirectory(actionClass);
127 			if ("/".equals(actionDirectory)) {
128 				path = "/" + actionMethodName;
129 			} else {
130 				path = "/" + actionDirectory + "/" + actionMethodName;
131 			}
132 		}
133 		return path;
134 	}
135 
136 	/**
137 	 * 指定されたアクションメソッドのアクションメソッド名を取得します。
138 	 * 
139 	 * @param method
140 	 *            アクションメソッド
141 	 * @return アクションメソッド名
142 	 */
143 	private static String getActionMethodName(final Method method) {
144 		final String actionName;
145 		final Path path = method.getAnnotation(Path.class);
146 		if (path != null && !StringUtil.isEmpty(path.value())) {
147 			actionName = path.value();
148 		} else {
149 			final String methodName = method.getName();
150 			if (INDEX_METHOD_NAME.equals(methodName)) {
151 				actionName = "";
152 			} else {
153 				actionName = methodName;
154 			}
155 		}
156 		return actionName;
157 	}
158 
159 	/**
160 	 * 指定されたアクションメソッドが受付可能なリクエストメソッドを取得します。
161 	 * 
162 	 * @param actionClass
163 	 *            アクションクラス
164 	 * @param method
165 	 *            アクションメソッド
166 	 * @return 受付可能なリクエストメソッド
167 	 */
168 	public static RequestMethod[] getAcceptableRequestMethods(
169 			final Class<?> actionClass, final Method method) {
170 		Accept accept = method.getAnnotation(Accept.class);
171 		if (accept == null) {
172 			accept = actionClass.getAnnotation(Accept.class);
173 			if (accept == null) {
174 				accept = DEFAULT_ACCEPT_ANNOTATION;
175 			}
176 		}
177 		return accept.value();
178 	}
179 
180 	/**
181 	 * 指定されたオブジェクトのサイズを取得します。
182 	 * 
183 	 * @param value
184 	 *            オブジェクト
185 	 * @return オブジェクトのサイズ
186 	 */
187 	public static int getObjectSize(final Object value) {
188 		final int size;
189 		if (value == null) {
190 			size = 0;
191 		} else if (value.getClass().isArray()) {
192 			final Object[] array = (Object[]) value;
193 			size = array.length;
194 		} else if (value instanceof Collection) {
195 			final Collection<?> collection = (Collection<?>) value;
196 			size = collection.size();
197 		} else {
198 			size = 1;
199 		}
200 		return size;
201 	}
202 
203 	/**
204 	 * リクエストのURIからコンテキストパスを除いたパスを返します。
205 	 * 
206 	 * @param request
207 	 *            リクエスト
208 	 * @return コンテキストパスを除いたパス
209 	 */
210 	public static String getPath(final HttpServletRequest request) {
211 		final String uri = request.getRequestURI();
212 		final String contextPath = request.getContextPath();
213 		final String path;
214 		if ("/".equals(contextPath)) {
215 			path = uri;
216 		} else {
217 			path = uri.substring(contextPath.length());
218 		}
219 		return path;
220 	}
221 
222 	/**
223 	 * アクションクラスとメソッド名から内部フォワードのパスへ変換します。
224 	 * 
225 	 * @param actionClass
226 	 *            アクションクラス
227 	 * @param methodName
228 	 *            メソッド名
229 	 * @return 内部フォワードパス
230 	 */
231 	public static String getInternalForwardPath(
232 			final Class<? extends Action> actionClass, final String methodName) {
233 		return "/" + INTERNAL_FORWARD_DIRECTORY + "/"
234 				+ actionClass.getCanonicalName() + "/" + methodName;
235 	}
236 
237 	/**
238 	 * 指定されたクラスがアクションメソッドかを示します。
239 	 * 
240 	 * @param clazz
241 	 *            クラス
242 	 * @return 指定されたクラスがアクションクラスの場合は <code>true</code>、そうでない場合は
243 	 *         <code>false</code>
244 	 */
245 	public static boolean isActionClass(final Class<?> clazz) {
246 		return Action.class.isAssignableFrom(clazz);
247 	}
248 
249 	/**
250 	 * 指定されたメソッドがアクションメソッドかを示します。
251 	 * 
252 	 * @param method
253 	 *            メソッド
254 	 * @return 指定されたメソッドがアクションメソッドの場合は <code>true</code>、そうでない場合は
255 	 *         <code>false</code>
256 	 */
257 	public static boolean isActionMethod(final Method method) {
258 		return method.getReturnType().isAssignableFrom(ActionResult.class)
259 				&& method.getParameterTypes().length == 0;
260 	}
261 
262 	/**
263 	 * 指定された文字列のなかで、最初に出現した置換対象を置換文字列で置き換えます。
264 	 * 
265 	 * @param text
266 	 *            対象の文字列
267 	 * @param replace
268 	 *            置換対象
269 	 * @param with
270 	 *            置換文字列
271 	 * @return 最初に出現した置換対象を置換文字列で置き換えた文字列
272 	 */
273 	public static String replaceFirst(final String text, final String replace,
274 			final String with) {
275 		if (text == null || replace == null || with == null) {
276 			return text;
277 		}
278 		final int index = text.indexOf(replace);
279 		if (index == -1) {
280 			return text;
281 		}
282 		final StringBuilder builder = new StringBuilder(100);
283 		builder.append(text.substring(0, index));
284 		builder.append(with);
285 		builder.append(text.substring(index + replace.length()));
286 		return builder.toString();
287 	}
288 
289 	/**
290 	 * 指定された文字列を区切り文字で区切った文字列の配列に変換します。
291 	 * 
292 	 * @param text
293 	 *            対象の文字列
294 	 * @param delim
295 	 *            区切り文字
296 	 * @return 指定された文字列を区切り文字で区切った文字列の配列
297 	 */
298 	public static String[] split2(final String text, final char delim) {
299 		if (text == null) {
300 			return null;
301 		}
302 		int index = text.indexOf(delim);
303 		if (index == -1) {
304 			return new String[] { text };
305 		}
306 		String[] tokens = new String[2];
307 		tokens[0] = text.substring(0, index);
308 		tokens[1] = text.substring(index + 1);
309 		return tokens;
310 	}
311 
312 	/**
313 	 * 指定された文字列をHTMLとしてエスケープします。
314 	 * <p>
315 	 * <table> <thead>
316 	 * <tr>
317 	 * <th>変換前</th>
318 	 * <th>変換後</th>
319 	 * </tr>
320 	 * </thead> <tbody>
321 	 * <tr>
322 	 * <td>&amp;</td>
323 	 * <td>&amp;amp;</td>
324 	 * </tr>
325 	 * <tr>
326 	 * <td>&lt;</td>
327 	 * <td>&amp;lt;</td>
328 	 * </tr>
329 	 * <tr>
330 	 * <td>&gt;</td>
331 	 * <td>&amp;gt;</td>
332 	 * </tr>
333 	 * <tr>
334 	 * <td>&quot;</td>
335 	 * <td>&amp;quot;</td>
336 	 * </tr>
337 	 * <tr>
338 	 * <td>&#39</td>
339 	 * <td>&amp;#39</td>
340 	 * </tr>
341 	 * </tbody> </table>
342 	 * </p>
343 	 * 
344 	 * @param str
345 	 * @return エスケープされた文字列
346 	 */
347 	public static String escapeHtml(final Object str) {
348 		if (str == null) {
349 			return "";
350 		}
351 		String text;
352 		if (str instanceof String) {
353 			text = (String) str;
354 		} else {
355 			text = str.toString();
356 		}
357 		text = StringUtil.replace(text, "&", "&amp;");
358 		text = StringUtil.replace(text, "<", "&lt;");
359 		text = StringUtil.replace(text, ">", "&gt;");
360 		text = StringUtil.replace(text, "\"", "&quot;");
361 		text = StringUtil.replace(text, "'", "&#39;");
362 		return text;
363 	}
364 
365 }