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.controller.impl;
17  
18  import java.lang.reflect.Array;
19  import java.lang.reflect.Field;
20  import java.lang.reflect.Method;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.LinkedHashSet;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  import java.util.Map.Entry;
28  
29  import org.seasar.cubby.action.Action;
30  import org.seasar.cubby.action.Form;
31  import org.seasar.cubby.action.RequestParameter;
32  import org.seasar.cubby.action.RequestParameterBindingType;
33  import org.seasar.cubby.controller.RequestParameterBinder;
34  import org.seasar.cubby.converter.ConversionHelper;
35  import org.seasar.cubby.converter.Converter;
36  import org.seasar.cubby.converter.ConverterFactory;
37  import org.seasar.cubby.util.CubbyUtils;
38  import org.seasar.framework.beans.BeanDesc;
39  import org.seasar.framework.beans.ParameterizedClassDesc;
40  import org.seasar.framework.beans.PropertyDesc;
41  import org.seasar.framework.beans.factory.BeanDescFactory;
42  
43  /**
44   * リクエストパラメータをオブジェクトへバインドするクラスの実装です。
45   * 
46   * @author baba
47   * @since 1.1.0
48   */
49  public class RequestParameterBinderImpl implements RequestParameterBinder {
50  
51  	/** コンバータのファクトリクラス。 */
52  	private ConverterFactory converterFactory;
53  
54  	/** 変換のヘルパクラス。 */
55  	private ConversionHelper conversionHelper;
56  
57  	/**
58  	 * コンバータのファクトリクラスを設定します。
59  	 * 
60  	 * @param converterFactory
61  	 *            コンバータのファクトリクラス
62  	 */
63  	public void setConverterFactory(final ConverterFactory converterFactory) {
64  		this.converterFactory = converterFactory;
65  	}
66  
67  	/**
68  	 * 変換のヘルパクラスを設定します。
69  	 * 
70  	 * @param conversionHelper
71  	 *            変換のヘルパクラス
72  	 */
73  	public void setConversionHelper(final ConversionHelper conversionHelper) {
74  		this.conversionHelper = conversionHelper;
75  	}
76  
77  	/**
78  	 * {@inheritDoc}
79  	 */
80  	public void bind(final Map<String, Object[]> parameterMap,
81  			final Object dest, final Class<? extends Action> actionClass,
82  			final Method method) {
83  		if (parameterMap == null) {
84  			return;
85  		}
86  		final BeanDesc destBeanDesc = BeanDescFactory.getBeanDesc(dest
87  				.getClass());
88  		for (final Entry<String, Object[]> entry : parameterMap.entrySet()) {
89  			final String sourceName = entry.getKey();
90  			if (destBeanDesc.hasPropertyDesc(sourceName)) {
91  				final PropertyDesc destPropertyDesc = destBeanDesc
92  						.getPropertyDesc(sourceName);
93  				if (destPropertyDesc.isReadable()
94  						&& destPropertyDesc.isWritable()) {
95  					if (!isBindToAllProperties(actionClass, method)) {
96  						final RequestParameter requestParameter = getRequestParameterAnnotation(destPropertyDesc);
97  						if (requestParameter == null) {
98  							continue;
99  						}
100 					}
101 
102 					try {
103 						final Object value = convert(entry.getValue(),
104 								destPropertyDesc);
105 						destPropertyDesc.setValue(dest, value);
106 					} catch (final Exception e) {
107 						destPropertyDesc.setValue(dest, null);
108 					}
109 				}
110 			}
111 		}
112 	}
113 
114 	/**
115 	 * 指定されたリクエストパラメータの値を出力先のプロパティの型に変換します。
116 	 * 
117 	 * @param values
118 	 *            リクエストパラメータの値
119 	 * @param destPropertyDesc
120 	 *            出力先のプロパティの定義
121 	 * @return 変換された値
122 	 */
123 	private Object convert(final Object[] values,
124 			final PropertyDesc destPropertyDesc) {
125 		final Class<?> destClass = destPropertyDesc.getPropertyType();
126 
127 		final Converter converter = converterFactory.getConverter(values[0]
128 				.getClass(), destClass);
129 		if (converter != null) {
130 			return converter.convertToObject(values[0], destClass,
131 					conversionHelper);
132 		}
133 
134 		if (destClass.isArray()) {
135 			return convertToArray(values, destClass.getComponentType());
136 		}
137 		if (List.class.isAssignableFrom(destClass)) {
138 			final List<Object> list = new ArrayList<Object>();
139 			convertToCollection(values, list, destPropertyDesc);
140 			return list;
141 		}
142 		if (Set.class.isAssignableFrom(destClass)) {
143 			final Set<Object> set = new LinkedHashSet<Object>();
144 			convertToCollection(values, set, destPropertyDesc);
145 			return set;
146 		}
147 		return convertToScalar(values[0], destClass);
148 	}
149 
150 	/**
151 	 * 指定された値を指定された要素の型の配列に変換します。
152 	 * 
153 	 * @param values
154 	 *            変換する値
155 	 * @param componentType
156 	 *            要素の型
157 	 * @return 変換後の値
158 	 */
159 	private Object convertToArray(final Object[] values,
160 			final Class<?> componentType) {
161 		final Object dest = Array.newInstance(componentType, values.length);
162 		for (int i = 0; i < values.length; i++) {
163 			final Object convertedValue = convertToScalar(values[i],
164 					componentType);
165 			Array.set(dest, i, convertedValue);
166 		}
167 		return dest;
168 	}
169 
170 	/**
171 	 * 指定された値を変換してコレクションに追加します。
172 	 * 
173 	 * @param values
174 	 *            変換する値
175 	 * @param collection
176 	 *            コレクション
177 	 * @param propertyDesc
178 	 *            プロパティの定義
179 	 */
180 	private void convertToCollection(final Object[] values,
181 			final Collection<Object> collection, final PropertyDesc propertyDesc) {
182 		if (propertyDesc.isParameterized()) {
183 			final ParameterizedClassDesc parameterizedClassDesc = propertyDesc
184 					.getParameterizedClassDesc();
185 			final Class<?> destElementType = parameterizedClassDesc
186 					.getArguments()[0].getRawClass();
187 			for (final Object value : values) {
188 				final Object convertedValue = convertToScalar(value,
189 						destElementType);
190 				collection.add(convertedValue);
191 			}
192 		} else {
193 			for (final Object value : values) {
194 				collection.add(value);
195 			}
196 		}
197 	}
198 
199 	/**
200 	 * 指定された値を指定された型に変換します。
201 	 * 
202 	 * @param value
203 	 *            変換する値
204 	 * @param destClass
205 	 *            変換する型
206 	 * @return 変換後の値
207 	 */
208 	private Object convertToScalar(final Object value, final Class<?> destClass) {
209 		if (value == null) {
210 			return null;
211 		}
212 		if (destClass.isAssignableFrom(value.getClass())) {
213 			return value;
214 		}
215 		final Converter converter = converterFactory.getConverter(value
216 				.getClass(), destClass);
217 		if (converter == null) {
218 			return null;
219 		}
220 		return converter.convertToObject(value, destClass, conversionHelper);
221 	}
222 
223 	/**
224 	 * 指定されたアクションメソッドを実行する際に、オブジェクトのすべてのプロパティにリクエストパラメータをバインドするかを示します。
225 	 * 
226 	 * @param method
227 	 *            アクションメソッド
228 	 * @return オブジェクトのすべてのプロパティにリクエストパラメータをバインドする場合は <code>true</code>、そうでない場合は
229 	 *         <code>false</code>
230 	 */
231 	private static boolean isBindToAllProperties(
232 			final Class<? extends Action> actionClass, final Method method) {
233 		final Form form = CubbyUtils.getForm(actionClass, method);
234 		if (form == null) {
235 			return false;
236 		}
237 
238 		final RequestParameterBindingType type = form.bindingType();
239 		switch (type) {
240 		case ALL_PROPERTIES:
241 			return true;
242 		case ONLY_SPECIFIED_PROPERTIES:
243 			return false;
244 		default:
245 			throw new IllegalStateException(type.toString());
246 		}
247 	}
248 
249 	/**
250 	 * 指定されたプロパティから {@link RequestParameter} アノテーションを取得します。
251 	 * 
252 	 * @param propertyDesc
253 	 *            プロパティの定義
254 	 * @return {@link RequestParameter} アノテーション、指定されたプロパティが装飾されていない場合は
255 	 *         <code>null</code>
256 	 */
257 	private static RequestParameter getRequestParameterAnnotation(
258 			final PropertyDesc propertyDesc) {
259 		final RequestParameter request;
260 		if (propertyDesc.hasWriteMethod()) {
261 			final Method method = propertyDesc.getWriteMethod();
262 			if (method.isAnnotationPresent(RequestParameter.class)) {
263 				request = method.getAnnotation(RequestParameter.class);
264 			} else {
265 				final Field field = propertyDesc.getField();
266 				request = field.getAnnotation(RequestParameter.class);
267 			}
268 		} else {
269 			final Field field = propertyDesc.getField();
270 			request = field.getAnnotation(RequestParameter.class);
271 		}
272 		return request;
273 	}
274 
275 }