/projects/struts-2.2.1/src/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverter.java
Java | 583 lines | 416 code | 82 blank | 85 comment | 200 complexity | 519c3e872857b1087dfaa9d2cf738c9a MD5 | raw file
- /*
- * Copyright 2002-2007,2009 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.opensymphony.xwork2.conversion.impl;
- import com.opensymphony.xwork2.ActionContext;
- import com.opensymphony.xwork2.ObjectFactory;
- import com.opensymphony.xwork2.XWorkException;
- import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
- import com.opensymphony.xwork2.conversion.TypeConverter;
- import com.opensymphony.xwork2.inject.Inject;
- import com.opensymphony.xwork2.util.XWorkList;
- import java.lang.reflect.Array;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Member;
- import java.math.BigDecimal;
- import java.math.BigInteger;
- import java.text.*;
- import java.util.*;
- import org.apache.commons.lang.StringUtils;
- /**
- * <!-- START SNIPPET: javadoc -->
- * <p/>
- * XWork will automatically handle the most common type conversion for you. This includes support for converting to
- * and from Strings for each of the following:
- * <p/>
- * <ul>
- * <li>String</li>
- * <li>boolean / Boolean</li>
- * <li>char / Character</li>
- * <li>int / Integer, float / Float, long / Long, double / Double</li>
- * <li>dates - uses the SHORT format for the Locale associated with the current request</li>
- * <li>arrays - assuming the individual strings can be coverted to the individual items</li>
- * <li>collections - if not object type can be determined, it is assumed to be a String and a new ArrayList is
- * created</li>
- * </ul>
- * <p/> Note that with arrays the type conversion will defer to the type of the array elements and try to convert each
- * item individually. As with any other type conversion, if the conversion can't be performed the standard type
- * conversion error reporting is used to indicate a problem occured while processing the type conversion.
- * <p/>
- * <!-- END SNIPPET: javadoc -->
- *
- * @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
- * @author Mike Mosiewicz
- * @author Rainer Hermanns
- * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
- */
- public class XWorkBasicConverter extends DefaultTypeConverter {
- private static String MILLISECOND_FORMAT = ".SSS";
-
- private ObjectTypeDeterminer objectTypeDeterminer;
- private XWorkConverter xworkConverter;
- private ObjectFactory objectFactory;
- @Inject
- public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {
- this.objectTypeDeterminer = det;
- }
-
- @Inject
- public void setXWorkConverter(XWorkConverter conv) {
- this.xworkConverter = conv;
- }
-
- @Inject
- public void setObjectFactory(ObjectFactory fac) {
- this.objectFactory = fac;
- }
- @Override
- public Object convertValue(Map<String, Object> context, Object o, Member member, String s, Object value, Class toType) {
- Object result = null;
- if (value == null || toType.isAssignableFrom(value.getClass())) {
- // no need to convert at all, right?
- return value;
- }
- if (toType == String.class) {
- /* the code below has been disabled as it causes sideffects in Struts2 (XW-512)
- // if input (value) is a number then use special conversion method (XW-490)
- Class inputType = value.getClass();
- if (Number.class.isAssignableFrom(inputType)) {
- result = doConvertFromNumberToString(context, value, inputType);
- if (result != null) {
- return result;
- }
- }*/
- // okay use default string conversion
- result = doConvertToString(context, value);
- } else if (toType == boolean.class) {
- result = doConvertToBoolean(value);
- } else if (toType == Boolean.class) {
- result = doConvertToBoolean(value);
- } else if (toType.isArray()) {
- result = doConvertToArray(context, o, member, s, value, toType);
- } else if (Date.class.isAssignableFrom(toType)) {
- result = doConvertToDate(context, value, toType);
- } else if (Calendar.class.isAssignableFrom(toType)) {
- Date dateResult = (Date) doConvertToDate(context, value, Date.class);
- if (dateResult != null) {
- Calendar calendar = Calendar.getInstance();
- calendar.setTime(dateResult);
- result = calendar;
- }
- } else if (Collection.class.isAssignableFrom(toType)) {
- result = doConvertToCollection(context, o, member, s, value, toType);
- } else if (toType == Character.class) {
- result = doConvertToCharacter(value);
- } else if (toType == char.class) {
- result = doConvertToCharacter(value);
- } else if (Number.class.isAssignableFrom(toType) || toType.isPrimitive()) {
- result = doConvertToNumber(context, value, toType);
- } else if (toType == Class.class) {
- result = doConvertToClass(value);
- }
- if (result == null) {
- if (value instanceof Object[]) {
- Object[] array = (Object[]) value;
- if (array.length >= 1) {
- value = array[0];
- } else {
- value = null;
- }
- // let's try to convert the first element only
- result = convertValue(context, o, member, s, value, toType);
- } else if (!"".equals(value)) { // we've already tried the types we know
- result = super.convertValue(context, value, toType);
- }
- if (result == null && value != null && !"".equals(value)) {
- throw new XWorkException("Cannot create type " + toType + " from value " + value);
- }
- }
- return result;
- }
- private Locale getLocale(Map<String, Object> context) {
- if (context == null) {
- return Locale.getDefault();
- }
- Locale locale = (Locale) context.get(ActionContext.LOCALE);
- if (locale == null) {
- locale = Locale.getDefault();
- }
- return locale;
- }
- /**
- * Creates a Collection of the specified type.
- *
- * @param fromObject
- * @param propertyName
- * @param toType the type of Collection to create
- * @param memberType the type of object elements in this collection must be
- * @param size the initial size of the collection (ignored if 0 or less)
- * @return a Collection of the specified type
- */
- private Collection createCollection(Object fromObject, String propertyName, Class toType, Class memberType, int size) {
- // try {
- // Object original = Ognl.getValue(OgnlUtil.compile(propertyName),fromObject);
- // if (original instanceof Collection) {
- // Collection coll = (Collection) original;
- // coll.clear();
- // return coll;
- // }
- // } catch (Exception e) {
- // // fail back to creating a new one
- // }
- Collection result;
- if (toType == Set.class) {
- if (size > 0) {
- result = new HashSet(size);
- } else {
- result = new HashSet();
- }
- } else if (toType == SortedSet.class) {
- result = new TreeSet();
- } else {
- if (size > 0) {
- result = new XWorkList(objectFactory, xworkConverter, memberType, size);
- } else {
- result = new XWorkList(objectFactory, xworkConverter, memberType);
- }
- }
- return result;
- }
- private Object doConvertToArray(Map<String, Object> context, Object o, Member member, String s, Object value, Class toType) {
- Object result = null;
- Class componentType = toType.getComponentType();
- if (componentType != null) {
- TypeConverter converter = getTypeConverter(context);
- if (value.getClass().isArray()) {
- int length = Array.getLength(value);
- result = Array.newInstance(componentType, length);
- for (int i = 0; i < length; i++) {
- Object valueItem = Array.get(value, i);
- Array.set(result, i, converter.convertValue(context, o, member, s, valueItem, componentType));
- }
- } else {
- result = Array.newInstance(componentType, 1);
- Array.set(result, 0, converter.convertValue(context, o, member, s, value, componentType));
- }
- }
- return result;
- }
- private Object doConvertToCharacter(Object value) {
- if (value instanceof String) {
- String cStr = (String) value;
- return (cStr.length() > 0) ? new Character(cStr.charAt(0)) : null;
- }
- return null;
- }
- private Object doConvertToBoolean(Object value) {
- if (value instanceof String) {
- String bStr = (String) value;
- return Boolean.valueOf(bStr);
- }
- return null;
- }
- private Class doConvertToClass(Object value) {
- Class clazz = null;
- if (value instanceof String && value != null && ((String) value).length() > 0) {
- try {
- clazz = Class.forName((String) value);
- } catch (ClassNotFoundException e) {
- throw new XWorkException(e.getLocalizedMessage(), e);
- }
- }
- return clazz;
- }
- private Collection doConvertToCollection(Map<String, Object> context, Object o, Member member, String prop, Object value, Class toType) {
- Collection result;
- Class memberType = String.class;
- if (o != null) {
- //memberType = (Class) XWorkConverter.getInstance().getConverter(o.getClass(), XWorkConverter.CONVERSION_COLLECTION_PREFIX + prop);
- memberType = objectTypeDeterminer.getElementClass(o.getClass(), prop, null);
- if (memberType == null) {
- memberType = String.class;
- }
- }
- if (toType.isAssignableFrom(value.getClass())) {
- // no need to do anything
- result = (Collection) value;
- } else if (value.getClass().isArray()) {
- Object[] objArray = (Object[]) value;
- TypeConverter converter = getTypeConverter(context);
- result = createCollection(o, prop, toType, memberType, objArray.length);
- for (Object anObjArray : objArray) {
- result.add(converter.convertValue(context, o, member, prop, anObjArray, memberType));
- }
- } else if (Collection.class.isAssignableFrom(value.getClass())) {
- Collection col = (Collection) value;
- TypeConverter converter = getTypeConverter(context);
- result = createCollection(o, prop, toType, memberType, col.size());
- for (Object aCol : col) {
- result.add(converter.convertValue(context, o, member, prop, aCol, memberType));
- }
- } else {
- result = createCollection(o, prop, toType, memberType, -1);
- result.add(value);
- }
- return result;
- }
- private Object doConvertToDate(Map<String, Object> context, Object value, Class toType) {
- Date result = null;
- if (value instanceof String && value != null && ((String) value).length() > 0) {
- String sa = (String) value;
- Locale locale = getLocale(context);
- DateFormat df = null;
- if (java.sql.Time.class == toType) {
- df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
- } else if (java.sql.Timestamp.class == toType) {
- Date check = null;
- SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
- DateFormat.MEDIUM,
- locale);
- SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,
- locale);
- SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,
- locale);
- SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};
- for (SimpleDateFormat fmt : fmts) {
- try {
- check = fmt.parse(sa);
- df = fmt;
- if (check != null) {
- break;
- }
- } catch (ParseException ignore) {
- }
- }
- } else if (java.util.Date.class == toType) {
- Date check = null;
- DateFormat[] dfs = getDateFormats(locale);
- for (DateFormat df1 : dfs) {
- try {
- check = df1.parse(sa);
- df = df1;
- if (check != null) {
- break;
- }
- }
- catch (ParseException ignore) {
- }
- }
- }
- //final fallback for dates without time
- if (df == null) {
- df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
- }
- try {
- df.setLenient(false); // let's use strict parsing (XW-341)
- result = df.parse(sa);
- if (!(Date.class == toType)) {
- try {
- Constructor constructor = toType.getConstructor(new Class[]{long.class});
- return constructor.newInstance(new Object[]{Long.valueOf(result.getTime())});
- } catch (Exception e) {
- throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e);
- }
- }
- } catch (ParseException e) {
- throw new XWorkException("Could not parse date", e);
- }
- } else if (Date.class.isAssignableFrom(value.getClass())) {
- result = (Date) value;
- }
- return result;
- }
- private DateFormat[] getDateFormats(Locale locale) {
- DateFormat dt1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);
- DateFormat dt2 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
- DateFormat dt3 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
- DateFormat d1 = DateFormat.getDateInstance(DateFormat.SHORT, locale);
- DateFormat d2 = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
- DateFormat d3 = DateFormat.getDateInstance(DateFormat.LONG, locale);
- DateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- DateFormat[] dfs = {dt1, dt2, dt3, rfc3399, d1, d2, d3}; //added RFC 3339 date format (XW-473)
- return dfs;
- }
- private Object doConvertToNumber(Map<String, Object> context, Object value, Class toType) {
- if (value instanceof String) {
- if (toType == BigDecimal.class) {
- return new BigDecimal((String) value);
- } else if (toType == BigInteger.class) {
- return new BigInteger((String) value);
- } else if (toType.isPrimitive()) {
- Object convertedValue = super.convertValue(context, value, toType);
- String stringValue = (String) value;
- if (!isInRange((Number)convertedValue, stringValue, toType))
- throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + convertedValue.getClass().getName());
- return convertedValue;
- } else {
- String stringValue = (String) value;
- if (!toType.isPrimitive() && (stringValue == null || stringValue.length() == 0)) {
- return null;
- }
- NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
- ParsePosition parsePos = new ParsePosition(0);
- if (isIntegerType(toType)) {
- numFormat.setParseIntegerOnly(true);
- }
- numFormat.setGroupingUsed(true);
- Number number = numFormat.parse(stringValue, parsePos);
- if (parsePos.getIndex() != stringValue.length()) {
- throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position "
- + parsePos.getIndex());
- } else {
- if (!isInRange(number, stringValue, toType))
- throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + number.getClass().getName());
-
- value = super.convertValue(context, number, toType);
- }
- }
- } else if (value instanceof Object[]) {
- Object[] objArray = (Object[]) value;
- if (objArray.length == 1) {
- return doConvertToNumber(context, objArray[0], toType);
- }
- }
- // pass it through DefaultTypeConverter
- return super.convertValue(context, value, toType);
- }
- protected boolean isInRange(Number value, String stringValue, Class toType) {
- Number bigValue = null;
- Number lowerBound = null;
- Number upperBound = null;
- try {
- if (double.class == toType || Double.class == toType) {
- bigValue = new BigDecimal(stringValue);
- // Double.MIN_VALUE is the smallest positive non-zero number
- lowerBound = BigDecimal.valueOf(Double.MAX_VALUE).negate();
- upperBound = BigDecimal.valueOf(Double.MAX_VALUE);
- } else if (float.class == toType || Float.class == toType) {
- bigValue = new BigDecimal(stringValue);
- // Float.MIN_VALUE is the smallest positive non-zero number
- lowerBound = BigDecimal.valueOf(Float.MAX_VALUE).negate();
- upperBound = BigDecimal.valueOf(Float.MAX_VALUE);
- } else if (byte.class == toType || Byte.class == toType) {
- bigValue = new BigInteger(stringValue);
- lowerBound = BigInteger.valueOf(Byte.MIN_VALUE);
- upperBound = BigInteger.valueOf(Byte.MAX_VALUE);
- } else if (char.class == toType || Character.class == toType) {
- bigValue = new BigInteger(stringValue);
- lowerBound = BigInteger.valueOf(Character.MIN_VALUE);
- upperBound = BigInteger.valueOf(Character.MAX_VALUE);
- } else if (short.class == toType || Short.class == toType) {
- bigValue = new BigInteger(stringValue);
- lowerBound = BigInteger.valueOf(Short.MIN_VALUE);
- upperBound = BigInteger.valueOf(Short.MAX_VALUE);
- } else if (int.class == toType || Integer.class == toType) {
- bigValue = new BigInteger(stringValue);
- lowerBound = BigInteger.valueOf(Integer.MIN_VALUE);
- upperBound = BigInteger.valueOf(Integer.MAX_VALUE);
- } else if (long.class == toType || Long.class == toType) {
- bigValue = new BigInteger(stringValue);
- lowerBound = BigInteger.valueOf(Long.MIN_VALUE);
- upperBound = BigInteger.valueOf(Long.MAX_VALUE);
- }
- } catch (NumberFormatException e) {
- //shoult it fail here? BigInteger doesnt seem to be so nice parsing numbers as NumberFormat
- return true;
- }
- return ((Comparable)bigValue).compareTo(lowerBound) >= 0 && ((Comparable)bigValue).compareTo(upperBound) <= 0;
- }
- protected boolean isIntegerType(Class type) {
- if (double.class == type || float.class == type || Double.class == type || Float.class == type
- || char.class == type || Character.class == type) {
- return false;
- }
- return true;
- }
- /**
- * Converts the input as a number using java's number formatter to a string output.
- */
- private String doConvertFromNumberToString(Map<String, Object> context, Object value, Class toType) {
- // XW-409: If the input is a Number we should format it to a string using the choosen locale and use java's numberformatter
- if (Number.class.isAssignableFrom(toType)) {
- NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
- if (isIntegerType(toType)) {
- numFormat.setParseIntegerOnly(true);
- }
- numFormat.setGroupingUsed(true);
- numFormat.setMaximumFractionDigits(99); // to be sure we include all digits after decimal seperator, otherwise some of the fractions can be chopped
- String number = numFormat.format(value);
- if (number != null) {
- return number;
- }
- }
- return null; // no number
- }
- private String doConvertToString(Map<String, Object> context, Object value) {
- String result = null;
- if (value instanceof int[]) {
- int[] x = (int[]) value;
- List<Integer> intArray = new ArrayList<Integer>(x.length);
- for (int aX : x) {
- intArray.add(Integer.valueOf(aX));
- }
- result = StringUtils.join(intArray, ", ");
- } else if (value instanceof long[]) {
- long[] x = (long[]) value;
- List<Long> longArray = new ArrayList<Long>(x.length);
- for (long aX : x) {
- longArray.add(Long.valueOf(aX));
- }
- result = StringUtils.join(longArray, ", ");
- } else if (value instanceof double[]) {
- double[] x = (double[]) value;
- List<Double> doubleArray = new ArrayList<Double>(x.length);
- for (double aX : x) {
- doubleArray.add(new Double(aX));
- }
- result = StringUtils.join(doubleArray, ", ");
- } else if (value instanceof boolean[]) {
- boolean[] x = (boolean[]) value;
- List<Boolean> booleanArray = new ArrayList<Boolean>(x.length);
- for (boolean aX : x) {
- booleanArray.add(new Boolean(aX));
- }
- result = StringUtils.join(booleanArray, ", ");
- } else if (value instanceof Date) {
- DateFormat df = null;
- if (value instanceof java.sql.Time) {
- df = DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale(context));
- } else if (value instanceof java.sql.Timestamp) {
- SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
- DateFormat.MEDIUM,
- getLocale(context));
- df = new SimpleDateFormat(dfmt.toPattern() + MILLISECOND_FORMAT);
- } else {
- df = DateFormat.getDateInstance(DateFormat.SHORT, getLocale(context));
- }
- result = df.format(value);
- } else if (value instanceof String[]) {
- result = StringUtils.join((String[]) value, ", ");
- }
- return result;
- }
- }