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 (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 					.getParameterizedClassDesc());
141 			return list;
142 		}
143 		if (Set.class.isAssignableFrom(destClass)) {
144 			final Set<Object> set = new LinkedHashSet<Object>();
145 			convertToCollection(values, set, destPropertyDesc
146 					.getParameterizedClassDesc());
147 			return set;
148 		}
149 		return convertToScalar(values[0], destClass);
150 	}
151 
152 	/**
153 	 * 指定された値を指定された要素の型の配列に変換します。
154 	 * 
155 	 * @param values
156 	 *            変換する値
157 	 * @param componentType
158 	 *            要素の型
159 	 * @return 変換後の値
160 	 */
161 	private Object convertToArray(final Object[] values,
162 			final Class<?> componentType) {
163 		final Object dest = Array.newInstance(componentType, values.length);
164 		for (int i = 0; i < values.length; i++) {
165 			final Object convertedValue = convertToScalar(values[i],
166 					componentType);
167 			Array.set(dest, i, convertedValue);
168 		}
169 		return dest;
170 	}
171 
172 	/**
173 	 * 指定された値を変換してコレクションに追加します。
174 	 * 
175 	 * @param values
176 	 *            変換する値
177 	 * @param collection
178 	 *            コレクション
179 	 * @param parameterizedClassDesc
180 	 *            パラメタ化された要素の定義
181 	 */
182 	private void convertToCollection(final Object[] values,
183 			final Collection<Object> collection,
184 			final ParameterizedClassDesc parameterizedClassDesc) {
185 		if (parameterizedClassDesc == null) {
186 			for (final Object value : values) {
187 				collection.add(value);
188 			}
189 		} else {
190 			final Class<?> destElementType = parameterizedClassDesc
191 					.getArguments()[0].getRawClass();
192 			for (final Object value : values) {
193 				final Object convertedValue = convertToScalar(value,
194 						destElementType);
195 				collection.add(convertedValue);
196 			}
197 		}
198 	}
199 
200 	/**
201 	 * 指定された値を指定された型に変換します。
202 	 * 
203 	 * @param value
204 	 *            変換する値
205 	 * @param destClass
206 	 *            変換する型
207 	 * @return 変換後の値
208 	 */
209 	private Object convertToScalar(final Object value, final Class<?> destClass) {
210 		if (value == null) {
211 			return null;
212 		}
213 		if (destClass.isAssignableFrom(value.getClass())) {
214 			return value;
215 		}
216 		final Converter converter = converterFactory.getConverter(value
217 				.getClass(), destClass);
218 		if (converter == null) {
219 			return null;
220 		}
221 		return converter.convertToObject(value, destClass, conversionHelper);
222 	}
223 
224 	/**
225 	 * 指定されたアクションメソッドを実行する際に、オブジェクトのすべてのプロパティにリクエストパラメータをバインドするかを示します。
226 	 * 
227 	 * @param method
228 	 *            アクションメソッド
229 	 * @return オブジェクトのすべてのプロパティにリクエストパラメータをバインドする場合は <code>true</code>、そうでない場合は
230 	 *         <code>false</code>
231 	 */
232 	private static boolean isBindToAllProperties(
233 			final Class<? extends Action> actionClass, final Method method) {
234 		final Form form = CubbyUtils.getForm(actionClass, method);
235 		if (form == null) {
236 			return false;
237 		}
238 
239 		final RequestParameterBindingType type = form.bindingType();
240 		switch (type) {
241 		case ALL_PROPERTIES:
242 			return true;
243 		case ONLY_SPECIFIED_PROPERTIES:
244 			return false;
245 		default:
246 			throw new IllegalStateException(type.toString());
247 		}
248 	}
249 
250 	/**
251 	 * 指定されたプロパティから {@link RequestParameter} アノテーションを取得します。
252 	 * 
253 	 * @param propertyDesc
254 	 *            プロパティの定義
255 	 * @return {@link RequestParameter} アノテーション、指定されたプロパティが装飾されていない場合は
256 	 *         <code>null</code>
257 	 */
258 	private static RequestParameter getRequestParameterAnnotation(
259 			final PropertyDesc propertyDesc) {
260 		final RequestParameter request;
261 		if (propertyDesc.hasWriteMethod()) {
262 			final Method method = propertyDesc.getWriteMethod();
263 			if (method.isAnnotationPresent(RequestParameter.class)) {
264 				request = method.getAnnotation(RequestParameter.class);
265 			} else {
266 				final Field field = propertyDesc.getField();
267 				request = field.getAnnotation(RequestParameter.class);
268 			}
269 		} else {
270 			final Field field = propertyDesc.getField();
271 			request = field.getAnnotation(RequestParameter.class);
272 		}
273 		return request;
274 	}
275 
276 }