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.internal.controller.impl;
17  
18  import static org.seasar.cubby.CubbyConstants.*;
19  import static org.seasar.cubby.CubbyConstants.ATTR_CONTEXT_PATH;
20  import static org.seasar.cubby.CubbyConstants.ATTR_MESSAGES;
21  
22  import java.util.ArrayList;
23  import java.util.Enumeration;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.Map.Entry;
30  
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletRequestWrapper;
33  
34  import org.seasar.cubby.CubbyConstants;
35  import org.seasar.cubby.controller.FormWrapperFactory;
36  import org.seasar.cubby.internal.controller.ThreadContext;
37  import org.seasar.cubby.internal.util.IteratorEnumeration;
38  import org.seasar.cubby.spi.beans.Attribute;
39  import org.seasar.cubby.spi.beans.BeanDesc;
40  import org.seasar.cubby.spi.beans.BeanDescFactory;
41  
42  /**
43   * 特別な属性を取得するためにサーブレットへの要求をラップします。
44   * <p>
45   * <ul>
46   * <li>{@link #getAttribute(String)}</li>
47   * <li>{@link #getAttributeNames()}</li>
48   * </ul>
49   * 上記メソッドでは、ラップされた要求の属性に加えて以下のような属性を使用することができます。
50   * <table>
51   * <thead>
52   * <tr>
53   * <th>属性名</th>
54   * <th>値</th>
55   * <th>型</th>
56   * </tr>
57   * </thead><tbody>
58   * <tr>
59   * <td>{@link CubbyConstants#ATTR_CONTEXT_PATH}</td>
60   * <td>コンテキストパス</td>
61   * <td>{@link String}</td>
62   * </tr>
63   * <tr>
64   * <td>{@link CubbyConstants#ATTR_ACTION}</td>
65   * <td>アクション</td>
66   * <td>{@link org.seasar.cubby.action.Action}</td>
67   * </tr>
68   * <tr>
69   * <td>{@link CubbyConstants#ATTR_MESSAGES}</td>
70   * <td>メッセージリソース</td>
71   * <td>{@link java.util.Map}</td>
72   * </tr>
73   * <tr>
74   * <td>{@link CubbyConstants#ATTR_FORM_WRAPPER_FACTORY}</td>
75   * <td>フォームオブジェクトのラッパーファクトリ</td>
76   * <td>{@link FormWrapperFactory}</td>
77   * </tr>
78   * <tr>
79   * <td>アクションのプロパティ名</td>
80   * <td>アクションのプロパティ値</td>
81   * <td>任意</td>
82   * </tr>
83   * </table>
84   * これらの属性は通常の属性よりも優先されるのでご注意ください。
85   * </p>
86   * <p>
87   * また、以下のリクエストパラメータに関するメソッドは、通常のリクエストパラメータに加え、URI パラメータも対象として処理します。
88   * <ul>
89   * <li>{@link #getParameter(String)}</li>
90   * <li>{@link #getParameterMap()}</li>
91   * <li>{@link #getParameterNames()}</li>
92   * <li>{@link #getParameterValues(String)}</li>
93   * </ul>
94   * </p>
95   * 
96   * @author baba
97   */
98  class CubbyHttpServletRequestWrapper extends HttpServletRequestWrapper {
99  
100 	/** URI パラメータの {@link Map} です。 */
101 	private final Map<String, String[]> uriParameters;
102 
103 	/** フォームオブジェクトのラッパーファクトリです。 */
104 	private FormWrapperFactory formWrapperFactory;
105 
106 	/**
107 	 * 指定された要求をラップした要求オブジェクトを構築します。
108 	 * 
109 	 * @param request
110 	 *            要求
111 	 * @param uriParameters
112 	 *            URI パラメータの {@link Map}
113 	 */
114 	public CubbyHttpServletRequestWrapper(final HttpServletRequest request,
115 			final Map<String, String[]> uriParameters) {
116 		super(request);
117 		this.uriParameters = uriParameters;
118 	}
119 
120 	/**
121 	 * 指定された属性の値を <code>Object</code> として返します。指定された名前の属性が存在しない場合は、
122 	 * <code>null</code> を返します。
123 	 * 
124 	 * @param name
125 	 *            属性の名前を指定する <code>String</code>
126 	 * @return 属性の値を含む <code>Object</code>。属性が存在しない場合は <code>null</code>
127 	 */
128 	@Override
129 	public Object getAttribute(final String name) {
130 		final Object value;
131 		if (ATTR_CONTEXT_PATH.equals(name)) {
132 			value = this.getContextPath();
133 		} else if (ATTR_MESSAGES.equals(name)) {
134 			value = ThreadContext.getMessagesMap();
135 		} else if (ATTR_FORM_WRAPPER_FACTORY.equals(name)) {
136 			if (this.formWrapperFactory == null) {
137 				this.formWrapperFactory = new FormWrapperFactoryImpl();
138 			}
139 			value = this.formWrapperFactory;
140 		} else {
141 			final Object action = super.getAttribute(ATTR_ACTION);
142 			if (action != null) {
143 				final BeanDesc beanDesc = BeanDescFactory.getBeanDesc(action
144 						.getClass());
145 				if (beanDesc.hasPropertyAttribute(name)) {
146 					final Attribute attribute = beanDesc
147 							.getPropertyAttribute(name);
148 					if (attribute.isReadable()) {
149 						value = attribute.getValue(action);
150 					} else {
151 						value = super.getAttribute(name);
152 					}
153 				} else {
154 					value = super.getAttribute(name);
155 				}
156 			} else {
157 				value = super.getAttribute(name);
158 			}
159 		}
160 		return value;
161 	}
162 
163 	/**
164 	 * この要求で利用できる属性の名前が格納された <code>Enumeration</code> を返します。利用できる属性が要求にない場合は、空の
165 	 * <code>Enumeration</code> を返します。
166 	 * 
167 	 * @return 要求に付随する属性の名前が格納された文字列の <code>Enumeration</code>
168 	 */
169 	@SuppressWarnings("unchecked")
170 	@Override
171 	public Enumeration getAttributeNames() {
172 		final Set attributeNames = new HashSet();
173 
174 		attributeNames.add(ATTR_CONTEXT_PATH);
175 		attributeNames.add(ATTR_ACTION);
176 		attributeNames.add(ATTR_MESSAGES);
177 		attributeNames.add(ATTR_FORM_WRAPPER_FACTORY);
178 
179 		final Object action = super.getAttribute(ATTR_ACTION);
180 		if (action != null) {
181 			final BeanDesc beanDesc = BeanDescFactory.getBeanDesc(action
182 					.getClass());
183 			for (final Attribute attribute : beanDesc.findtPropertyAttributes()) {
184 				if (attribute.isReadable()) {
185 					attributeNames.add(attribute.getName());
186 				}
187 			}
188 		}
189 
190 		final Enumeration defaultAttributeNames = super.getAttributeNames();
191 		while (defaultAttributeNames.hasMoreElements()) {
192 			attributeNames.add(defaultAttributeNames.nextElement());
193 		}
194 		return new IteratorEnumeration(attributeNames.iterator());
195 	}
196 
197 	/**
198 	 * 要求パラメータの値を <code>String</code> として返します。
199 	 * <p>
200 	 * パラメータが存在しない場合は、<code>null</code> を返します。
201 	 * </p>
202 	 * 
203 	 * @param name
204 	 *            パラメータの名前を指定する <code>String</code>
205 	 * @return パラメータの単一の値を表す <code>String</code>
206 	 */
207 	@Override
208 	public String getParameter(final String name) {
209 		final String[] parameters = this.getParameterValues(name);
210 		if (parameters == null) {
211 			return null;
212 		} else {
213 			return parameters[0];
214 		}
215 	}
216 
217 	/**
218 	 * この要求に含まれるパラメータの名前を格納した、<code>String</code> オブジェクトの
219 	 * <code>Enumeration</code> を返します。
220 	 * <p>
221 	 * パラメータが要求にない場合、このメソッドは空の <code>Enumeration</code> を返します。
222 	 * </p>
223 	 */
224 	@SuppressWarnings("unchecked")
225 	@Override
226 	public Enumeration getParameterNames() {
227 		return new IteratorEnumeration(this.getParameterMap().keySet()
228 				.iterator());
229 	}
230 
231 	/**
232 	 * 指定された要求パラメータのすべての値が格納された <code>String</code> オブジェクトの配列を返します。
233 	 * <p>
234 	 * パラメータが存在しない場合は、<code>null</code> を返します。
235 	 * </p>
236 	 * 
237 	 * @param name
238 	 *            取得したいパラメータの名前を表す <code>String</code>
239 	 * @return パラメータの値が格納された <code>String</code> オブジェクトの配列
240 	 */
241 	@SuppressWarnings("unchecked")
242 	@Override
243 	public String[] getParameterValues(final String name) {
244 		final Map<String, String[]> parameterMap = this.getParameterMap();
245 		return parameterMap.get(name);
246 	}
247 
248 	/**
249 	 * この要求から取得できるパラメータを <code>java.util.Map</code> で返します。
250 	 * 
251 	 * @return キーとしてパラメータ名、マップ値としてパラメータ値が格納された不変の <code>java.util.Map</code>。
252 	 *         <p>
253 	 *         パラメータマップ内のキーは <code>String</code> 型。パラメータマップ内の値は
254 	 *         <code>String</code> の配列型
255 	 *         </p>
256 	 */
257 	@SuppressWarnings("unchecked")
258 	@Override
259 	public Map getParameterMap() {
260 		final Map<String, String[]> parameterMap = buildParameterMap(
261 				(HttpServletRequest) getRequest(), uriParameters);
262 		return parameterMap;
263 	}
264 
265 	/**
266 	 * 要求パラメータを構築します。
267 	 * 
268 	 * @param request
269 	 *            要求
270 	 * @param uriParameters
271 	 *            URI パラメータの {@link Map}
272 	 * @return URI パラメータを含むリクエストパラメータの {@link Map}
273 	 */
274 	private Map<String, String[]> buildParameterMap(
275 			final HttpServletRequest request,
276 			final Map<String, String[]> uriParameters) {
277 		final Map<String, List<String>> extendedParameterMap = new HashMap<String, List<String>>();
278 
279 		final Map<?, ?> originalParameterMap = request.getParameterMap();
280 		for (final Entry<?, ?> entry : originalParameterMap.entrySet()) {
281 			final String name = (String) entry.getKey();
282 			final List<String> values = new ArrayList<String>();
283 			for (final String value : (String[]) entry.getValue()) {
284 				values.add(value);
285 			}
286 			extendedParameterMap.put(name, values);
287 		}
288 		for (final Entry<String, String[]> entry : uriParameters.entrySet()) {
289 			final String name = entry.getKey();
290 			if (extendedParameterMap.containsKey(name)) {
291 				final List<String> values = extendedParameterMap.get(name);
292 				for (final String value : entry.getValue()) {
293 					values.add(value);
294 				}
295 			} else {
296 				final List<String> values = new ArrayList<String>();
297 				for (final String value : entry.getValue()) {
298 					values.add(value);
299 				}
300 				extendedParameterMap.put(name, values);
301 			}
302 		}
303 
304 		final Map<String, String[]> parameterMap = new HashMap<String, String[]>();
305 		for (final Entry<String, List<String>> entry : extendedParameterMap
306 				.entrySet()) {
307 			parameterMap.put(entry.getKey(), entry.getValue().toArray(
308 					new String[0]));
309 		}
310 		return parameterMap;
311 	}
312 
313 }