1 //--------------------------------------------------------------------------
2 // Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // Neither the name of the Drew Davidson nor the names of its contributors
15 // may be used to endorse or promote products derived from this software
16 // without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 // DAMAGE.
30 //--------------------------------------------------------------------------
31 package com.opensymphony.xwork2.conversion.impl;
32
33 import java.lang.reflect.Array;
34 import java.lang.reflect.Member;
35 import java.math.BigDecimal;
36 import java.math.BigInteger;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.Map;
40
41 import com.opensymphony.xwork2.conversion.TypeConverter;
42 import com.opensymphony.xwork2.ognl.XWorkTypeConverterWrapper;
43
44 /**
45 * Default type conversion. Converts among numeric types and also strings. Contains the basic
46 * type mapping code from OGNL.
47 *
48 * @author Luke Blanshard (blanshlu@netscape.net)
49 * @author Drew Davidson (drew@ognl.org)
50 */
51 public class DefaultTypeConverter implements TypeConverter {
52 private static final String NULL_STRING = "null";
53
54 private final Map<Class, Object> primitiveDefaults;
55
56 public DefaultTypeConverter() {
57 Map<Class, Object> map = new HashMap<Class, Object>();
58 map.put(Boolean.TYPE, Boolean.FALSE);
59 map.put(Byte.TYPE, new Byte((byte) 0));
60 map.put(Short.TYPE, new Short((short) 0));
61 map.put(Character.TYPE, new Character((char) 0));
62 map.put(Integer.TYPE, new Integer(0));
63 map.put(Long.TYPE, new Long(0L));
64 map.put(Float.TYPE, new Float(0.0f));
65 map.put(Double.TYPE, new Double(0.0));
66 map.put(BigInteger.class, new BigInteger("0"));
67 map.put(BigDecimal.class, new BigDecimal(0.0));
68 primitiveDefaults = Collections.unmodifiableMap(map);
69 }
70
71 public Object convertValue(Map context, Object value, Class toType) {
72 return convertValue(value, toType);
73 }
74
75 public Object convertValue(Map context, Object target, Member member,
76 String propertyName, Object value, Class toType) {
77 return convertValue(context, value, toType);
78 }
79
80 public TypeConverter getTypeConverter( Map context )
81 {
82 Object obj = context.get(TypeConverter.TYPE_CONVERTER_CONTEXT_KEY);
83 if (obj instanceof TypeConverter) {
84 return (TypeConverter) obj;
85
86 // for backwards-compatibility
87 } else if (obj instanceof ognl.TypeConverter) {
88 return new XWorkTypeConverterWrapper((ognl.TypeConverter) obj);
89 }
90 return null;
91 }
92
93 /**
94 * Returns the value converted numerically to the given class type
95 *
96 * This method also detects when arrays are being converted and converts the
97 * components of one array to the type of the other.
98 *
99 * @param value
100 * an object to be converted to the given type
101 * @param toType
102 * class type to be converted to
103 * @return converted value of the type given, or value if the value cannot
104 * be converted to the given type.
105 */
106 public Object convertValue(Object value, Class toType) {
107 Object result = null;
108
109 if (value != null) {
110 /* If array -> array then convert components of array individually */
111 if (value.getClass().isArray() && toType.isArray()) {
112 Class componentType = toType.getComponentType();
113
114 result = Array.newInstance(componentType, Array
115 .getLength(value));
116 for (int i = 0, icount = Array.getLength(value); i < icount; i++) {
117 Array.set(result, i, convertValue(Array.get(value, i),
118 componentType));
119 }
120 } else {
121 if ((toType == Integer.class) || (toType == Integer.TYPE))
122 result = new Integer((int) longValue(value));
123 if ((toType == Double.class) || (toType == Double.TYPE))
124 result = new Double(doubleValue(value));
125 if ((toType == Boolean.class) || (toType == Boolean.TYPE))
126 result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;
127 if ((toType == Byte.class) || (toType == Byte.TYPE))
128 result = new Byte((byte) longValue(value));
129 if ((toType == Character.class) || (toType == Character.TYPE))
130 result = new Character((char) longValue(value));
131 if ((toType == Short.class) || (toType == Short.TYPE))
132 result = new Short((short) longValue(value));
133 if ((toType == Long.class) || (toType == Long.TYPE))
134 result = new Long(longValue(value));
135 if ((toType == Float.class) || (toType == Float.TYPE))
136 result = new Float(doubleValue(value));
137 if (toType == BigInteger.class)
138 result = bigIntValue(value);
139 if (toType == BigDecimal.class)
140 result = bigDecValue(value);
141 if (toType == String.class)
142 result = stringValue(value);
143 if (Enum.class.isAssignableFrom(toType))
144 result = enumValue((Class<Enum>)toType, value);
145 }
146 } else {
147 if (toType.isPrimitive()) {
148 result = primitiveDefaults.get(toType);
149 }
150 }
151 return result;
152 }
153
154 /**
155 * Evaluates the given object as a boolean: if it is a Boolean object, it's
156 * easy; if it's a Number or a Character, returns true for non-zero objects;
157 * and otherwise returns true for non-null objects.
158 *
159 * @param value
160 * an object to interpret as a boolean
161 * @return the boolean value implied by the given object
162 */
163 public static boolean booleanValue(Object value) {
164 if (value == null)
165 return false;
166 Class c = value.getClass();
167 if (c == Boolean.class)
168 return ((Boolean) value).booleanValue();
169 // if ( c == String.class )
170 // return ((String)value).length() > 0;
171 if (c == Character.class)
172 return ((Character) value).charValue() != 0;
173 if (value instanceof Number)
174 return ((Number) value).doubleValue() != 0;
175 return true; // non-null
176 }
177
178 public Enum<?> enumValue(Class toClass, Object o) {
179 Enum<?> result = null;
180 if (o == null) {
181 result = null;
182 } else if (o instanceof String[]) {
183 result = Enum.valueOf(toClass, ((String[]) o)[0]);
184 } else if (o instanceof String) {
185 result = Enum.valueOf(toClass, (String) o);
186 }
187 return result;
188 }
189
190 /**
191 * Evaluates the given object as a long integer.
192 *
193 * @param value
194 * an object to interpret as a long integer
195 * @return the long integer value implied by the given object
196 * @throws NumberFormatException
197 * if the given object can't be understood as a long integer
198 */
199 public static long longValue(Object value) throws NumberFormatException {
200 if (value == null)
201 return 0L;
202 Class c = value.getClass();
203 if (c.getSuperclass() == Number.class)
204 return ((Number) value).longValue();
205 if (c == Boolean.class)
206 return ((Boolean) value).booleanValue() ? 1 : 0;
207 if (c == Character.class)
208 return ((Character) value).charValue();
209 return Long.parseLong(stringValue(value, true));
210 }
211
212 /**
213 * Evaluates the given object as a double-precision floating-point number.
214 *
215 * @param value
216 * an object to interpret as a double
217 * @return the double value implied by the given object
218 * @throws NumberFormatException
219 * if the given object can't be understood as a double
220 */
221 public static double doubleValue(Object value) throws NumberFormatException {
222 if (value == null)
223 return 0.0;
224 Class c = value.getClass();
225 if (c.getSuperclass() == Number.class)
226 return ((Number) value).doubleValue();
227 if (c == Boolean.class)
228 return ((Boolean) value).booleanValue() ? 1 : 0;
229 if (c == Character.class)
230 return ((Character) value).charValue();
231 String s = stringValue(value, true);
232
233 return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
234 /*
235 * For 1.1 parseDouble() is not available
236 */
237 // return Double.valueOf( value.toString() ).doubleValue();
238 }
239
240 /**
241 * Evaluates the given object as a BigInteger.
242 *
243 * @param value
244 * an object to interpret as a BigInteger
245 * @return the BigInteger value implied by the given object
246 * @throws NumberFormatException
247 * if the given object can't be understood as a BigInteger
248 */
249 public static BigInteger bigIntValue(Object value)
250 throws NumberFormatException {
251 if (value == null)
252 return BigInteger.valueOf(0L);
253 Class c = value.getClass();
254 if (c == BigInteger.class)
255 return (BigInteger) value;
256 if (c == BigDecimal.class)
257 return ((BigDecimal) value).toBigInteger();
258 if (c.getSuperclass() == Number.class)
259 return BigInteger.valueOf(((Number) value).longValue());
260 if (c == Boolean.class)
261 return BigInteger.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
262 if (c == Character.class)
263 return BigInteger.valueOf(((Character) value).charValue());
264 return new BigInteger(stringValue(value, true));
265 }
266
267 /**
268 * Evaluates the given object as a BigDecimal.
269 *
270 * @param value
271 * an object to interpret as a BigDecimal
272 * @return the BigDecimal value implied by the given object
273 * @throws NumberFormatException
274 * if the given object can't be understood as a BigDecimal
275 */
276 public static BigDecimal bigDecValue(Object value)
277 throws NumberFormatException {
278 if (value == null)
279 return BigDecimal.valueOf(0L);
280 Class c = value.getClass();
281 if (c == BigDecimal.class)
282 return (BigDecimal) value;
283 if (c == BigInteger.class)
284 return new BigDecimal((BigInteger) value);
285 if (c.getSuperclass() == Number.class)
286 return new BigDecimal(((Number) value).doubleValue());
287 if (c == Boolean.class)
288 return BigDecimal.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
289 if (c == Character.class)
290 return BigDecimal.valueOf(((Character) value).charValue());
291 return new BigDecimal(stringValue(value, true));
292 }
293
294 /**
295 * Evaluates the given object as a String and trims it if the trim flag is
296 * true.
297 *
298 * @param value
299 * an object to interpret as a String
300 * @return the String value implied by the given object as returned by the
301 * toString() method, or "null" if the object is null.
302 */
303 public static String stringValue(Object value, boolean trim) {
304 String result;
305
306 if (value == null) {
307 result = NULL_STRING;
308 } else {
309 result = value.toString();
310 if (trim) {
311 result = result.trim();
312 }
313 }
314 return result;
315 }
316
317 /**
318 * Evaluates the given object as a String.
319 *
320 * @param value
321 * an object to interpret as a String
322 * @return the String value implied by the given object as returned by the
323 * toString() method, or "null" if the object is null.
324 */
325 public static String stringValue(Object value) {
326 return stringValue(value, false);
327 }
328 }