Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2.conversion.impl » [javadoc | source]
    1   /*
    2    * Copyright (c) 2002-2007 by OpenSymphony
    3    * All rights reserved.
    4    */
    5   package com.opensymphony.xwork2.conversion.impl;
    6   
    7   import java.lang.reflect.Array;
    8   import java.lang.reflect.Constructor;
    9   import java.lang.reflect.Member;
   10   import java.math.BigDecimal;
   11   import java.math.BigInteger;
   12   import java.text.DateFormat;
   13   import java.text.NumberFormat;
   14   import java.text.ParseException;
   15   import java.text.ParsePosition;
   16   import java.text.SimpleDateFormat;
   17   import java.util.ArrayList;
   18   import java.util.Calendar;
   19   import java.util.Collection;
   20   import java.util.Date;
   21   import java.util.HashSet;
   22   import java.util.Iterator;
   23   import java.util.List;
   24   import java.util.Locale;
   25   import java.util.Map;
   26   import java.util.Set;
   27   import java.util.SortedSet;
   28   import java.util.TreeSet;
   29   
   30   import com.opensymphony.xwork2.ActionContext;
   31   import com.opensymphony.xwork2.ObjectFactory;
   32   import com.opensymphony.xwork2.XWorkException;
   33   import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
   34   import com.opensymphony.xwork2.conversion.TypeConverter;
   35   import com.opensymphony.xwork2.inject.Inject;
   36   import com.opensymphony.xwork2.util.TextUtils;
   37   import com.opensymphony.xwork2.util.XWorkList;
   38   
   39   
   40   /**
   41    * <!-- START SNIPPET: javadoc -->
   42    * <p/>
   43    * XWork will automatically handle the most common type conversion for you. This includes support for converting to
   44    * and from Strings for each of the following:
   45    * <p/>
   46    * <ul>
   47    * <li>String</li>
   48    * <li>boolean / Boolean</li>
   49    * <li>char / Character</li>
   50    * <li>int / Integer, float / Float, long / Long, double / Double</li>
   51    * <li>dates - uses the SHORT format for the Locale associated with the current request</li>
   52    * <li>arrays - assuming the individual strings can be coverted to the individual items</li>
   53    * <li>collections - if not object type can be determined, it is assumed to be a String and a new ArrayList is
   54    * created</li>
   55    * </ul>
   56    * <p/> Note that with arrays the type conversion will defer to the type of the array elements and try to convert each
   57    * item individually. As with any other type conversion, if the conversion can't be performed the standard type
   58    * conversion error reporting is used to indicate a problem occured while processing the type conversion.
   59    * <p/>
   60    * <!-- END SNIPPET: javadoc -->
   61    *
   62    * @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
   63    * @author Mike Mosiewicz
   64    * @author Rainer Hermanns
   65    * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
   66    */
   67   public class XWorkBasicConverter extends DefaultTypeConverter {
   68   
   69       private static String MILLISECOND_FORMAT = ".SSS";
   70       
   71       private ObjectTypeDeterminer objectTypeDeterminer;
   72       private XWorkConverter xworkConverter;
   73       private ObjectFactory objectFactory;
   74       
   75       @Inject
   76       public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {
   77           this.objectTypeDeterminer = det;
   78       }
   79       
   80       @Inject
   81       public void setXWorkConverter(XWorkConverter conv) {
   82           this.xworkConverter = conv;
   83       }
   84       
   85       @Inject
   86       public void setObjectFactory(ObjectFactory fac) {
   87           this.objectFactory = fac;
   88       }
   89   
   90       public Object convertValue(Map context, Object o, Member member, String s, Object value, Class toType) {
   91           Object result = null;
   92   
   93           if (value == null || toType.isAssignableFrom(value.getClass())) {
   94               // no need to convert at all, right?
   95               return value;
   96           }
   97   
   98           if (toType == String.class) {
   99               /* the code below has been disabled as it causes sideffects in Struts2 (XW-512)
  100               // if input (value) is a number then use special conversion method (XW-490)
  101               Class inputType = value.getClass();
  102               if (Number.class.isAssignableFrom(inputType)) {
  103                   result = doConvertFromNumberToString(context, value, inputType);
  104                   if (result != null) {
  105                       return result;
  106                   }
  107               }*/
  108               // okay use default string conversion
  109               result = doConvertToString(context, value);
  110           } else if (toType == boolean.class) {
  111               result = doConvertToBoolean(value);
  112           } else if (toType == Boolean.class) {
  113               result = doConvertToBoolean(value);
  114           } else if (toType.isArray()) {
  115               result = doConvertToArray(context, o, member, s, value, toType);
  116           } else if (Date.class.isAssignableFrom(toType)) {
  117               result = doConvertToDate(context, value, toType);
  118           } else if (Calendar.class.isAssignableFrom(toType)) {
  119               Date dateResult = (Date) doConvertToDate(context, value, Date.class);
  120               if (dateResult != null) {
  121                   Calendar calendar = Calendar.getInstance();
  122                   calendar.setTime(dateResult);
  123                   result = calendar;
  124               } 
  125           } else if (Collection.class.isAssignableFrom(toType)) {
  126               result = doConvertToCollection(context, o, member, s, value, toType);
  127           } else if (toType == Character.class) {
  128               result = doConvertToCharacter(value);
  129           } else if (toType == char.class) {
  130               result = doConvertToCharacter(value);
  131           } else if (Number.class.isAssignableFrom(toType) || toType.isPrimitive()) {
  132               result = doConvertToNumber(context, value, toType);
  133           } else if (toType == Class.class) {
  134               result = doConvertToClass(value);
  135           }
  136   
  137           if (result == null) {
  138               if (value instanceof Object[]) {
  139                   Object[] array = (Object[]) value;
  140   
  141                   if (array.length >= 1) {
  142                       value = array[0];
  143                   }
  144   
  145                   // let's try to convert the first element only
  146                   result = convertValue(context, o, member, s, value, toType);
  147               } else if (!"".equals(value)) { // we've already tried the types we know
  148                   result = super.convertValue(context, value, toType);
  149               }
  150   
  151               if (result == null && value != null && !"".equals(value)) {
  152                   throw new XWorkException("Cannot create type " + toType + " from value " + value);
  153               }
  154           }
  155   
  156           return result;
  157       }
  158   
  159       private Locale getLocale(Map context) {
  160           if (context == null) {
  161               return Locale.getDefault();
  162           }
  163   
  164           Locale locale = (Locale) context.get(ActionContext.LOCALE);
  165   
  166           if (locale == null) {
  167               locale = Locale.getDefault();
  168           }
  169   
  170           return locale;
  171       }
  172   
  173       /**
  174        * Creates a Collection of the specified type.
  175        *
  176        * @param fromObject
  177        * @param propertyName
  178        * @param toType       the type of Collection to create
  179        * @param memberType   the type of object elements in this collection must be
  180        * @param size         the initial size of the collection (ignored if 0 or less)
  181        * @return a Collection of the specified type
  182        */
  183       private Collection createCollection(Object fromObject, String propertyName, Class toType, Class memberType, int size) {
  184   //        try {
  185   //            Object original = Ognl.getValue(OgnlUtil.compile(propertyName),fromObject);
  186   //            if (original instanceof Collection) {
  187   //                Collection coll = (Collection) original;
  188   //                coll.clear();
  189   //                return coll;
  190   //            }
  191   //        } catch (Exception e) {
  192   //            // fail back to creating a new one
  193   //        }
  194   
  195           Collection result;
  196   
  197           if (toType == Set.class) {
  198               if (size > 0) {
  199                   result = new HashSet(size);
  200               } else {
  201                   result = new HashSet();
  202               }
  203           } else if (toType == SortedSet.class) {
  204               result = new TreeSet();
  205           } else {
  206               if (size > 0) {
  207                   result = new XWorkList(objectFactory, xworkConverter, memberType, size);
  208               } else {
  209                   result = new XWorkList(objectFactory, xworkConverter, memberType);
  210               }
  211           }
  212   
  213           return result;
  214       }
  215   
  216       private Object doConvertToArray(Map context, Object o, Member member, String s, Object value, Class toType) {
  217           Object result = null;
  218           Class componentType = toType.getComponentType();
  219   
  220           if (componentType != null) {
  221               TypeConverter converter = getTypeConverter(context);
  222   
  223               if (value.getClass().isArray()) {
  224                   int length = Array.getLength(value);
  225                   result = Array.newInstance(componentType, length);
  226   
  227                   for (int i = 0; i < length; i++) {
  228                       Object valueItem = Array.get(value, i);
  229                       Array.set(result, i, converter.convertValue(context, o, member, s, valueItem, componentType));
  230                   }
  231               } else {
  232                   result = Array.newInstance(componentType, 1);
  233                   Array.set(result, 0, converter.convertValue(context, o, member, s, value, componentType));
  234               }
  235           }
  236   
  237           return result;
  238       }
  239   
  240       private Object doConvertToCharacter(Object value) {
  241           if (value instanceof String) {
  242               String cStr = (String) value;
  243   
  244               return (cStr.length() > 0) ? new Character(cStr.charAt(0)) : null;
  245           }
  246   
  247           return null;
  248       }
  249   
  250       private Object doConvertToBoolean(Object value) {
  251           if (value instanceof String) {
  252               String bStr = (String) value;
  253   
  254               return Boolean.valueOf(bStr);
  255           }
  256   
  257           return null;
  258       }
  259   
  260       private Class doConvertToClass(Object value) {
  261           Class clazz = null;
  262   
  263           if (value instanceof String && value != null && ((String) value).length() > 0) {
  264               try {
  265                   clazz = Class.forName((String) value);
  266               } catch (ClassNotFoundException e) {
  267                   throw new XWorkException(e.getLocalizedMessage(), e);
  268               }
  269           }
  270   
  271           return clazz;
  272       }
  273   
  274       private Collection doConvertToCollection(Map context, Object o, Member member, String prop, Object value, Class toType) {
  275           Collection result;
  276           Class memberType = String.class;
  277   
  278           if (o != null) {
  279               //memberType = (Class) XWorkConverter.getInstance().getConverter(o.getClass(), XWorkConverter.CONVERSION_COLLECTION_PREFIX + prop);
  280               memberType = objectTypeDeterminer.getElementClass(o.getClass(), prop, null);
  281   
  282               if (memberType == null) {
  283                   memberType = String.class;
  284               }
  285           }
  286   
  287           if (toType.isAssignableFrom(value.getClass())) {
  288               // no need to do anything
  289               result = (Collection) value;
  290           } else if (value.getClass().isArray()) {
  291               Object[] objArray = (Object[]) value;
  292               TypeConverter converter = getTypeConverter(context);
  293               result = createCollection(o, prop, toType, memberType, objArray.length);
  294   
  295               for (int i = 0; i < objArray.length; i++) {
  296                   result.add(converter.convertValue(context, o, member, prop, objArray[i], memberType));
  297               }
  298           } else if (Collection.class.isAssignableFrom(value.getClass())) {
  299               Collection col = (Collection) value;
  300               TypeConverter converter = getTypeConverter(context);
  301               result = createCollection(o, prop, toType, memberType, col.size());
  302   
  303               for (Iterator it = col.iterator(); it.hasNext();) {
  304                   result.add(converter.convertValue(context, o, member, prop, it.next(), memberType));
  305               }
  306           } else {
  307               result = createCollection(o, prop, toType, memberType, -1);
  308               result.add(value);
  309           }
  310   
  311           return result;
  312       }
  313   
  314       private Object doConvertToDate(Map context, Object value, Class toType) {
  315           Date result = null;
  316   
  317           if (value instanceof String && value != null && ((String) value).length() > 0) {
  318               String sa = (String) value;
  319               Locale locale = getLocale(context);
  320   
  321               DateFormat df = null;
  322               if (java.sql.Time.class == toType) {
  323                   df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
  324               } else if (java.sql.Timestamp.class == toType) {
  325                   Date check = null;
  326                   SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
  327                           DateFormat.MEDIUM,
  328                           locale);
  329                   SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,
  330                           locale);
  331   
  332                   SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,
  333                           locale);
  334   
  335                   SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};
  336                   for (int i = 0; i < fmts.length; i++) {
  337                       try {
  338                           check = fmts[i].parse(sa);
  339                           df = fmts[i];
  340                           if (check != null) {
  341                               break;
  342                           }
  343                       } catch (ParseException ignore) {
  344                       }
  345                   }
  346               } else if (java.util.Date.class == toType) {
  347                   Date check = null;
  348                   SimpleDateFormat d1 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);
  349                   SimpleDateFormat d2 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
  350                   SimpleDateFormat d3 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
  351                   SimpleDateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
  352                   SimpleDateFormat[] dfs = {d1, d2, d3, rfc3399}; //added RFC 3339 date format (XW-473)
  353                   for (int i = 0; i < dfs.length; i++) {
  354                       try {
  355                           check = dfs[i].parse(sa);
  356                           df = dfs[i];
  357                           if (check != null) {
  358                               break;
  359                           }
  360                       }
  361                       catch (ParseException ignore) {
  362                       }
  363                   }
  364               }
  365               //final fallback for dates without time
  366               if (df == null) {
  367                   df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
  368               }
  369               try {
  370                   df.setLenient(false); // let's use strict parsing (XW-341)
  371                   result = df.parse(sa);
  372                   if (!(Date.class == toType)) {
  373                       try {
  374                           Constructor constructor = toType.getConstructor(new Class[]{long.class});
  375                           return constructor.newInstance(new Object[]{new Long(result.getTime())});
  376                       } catch (Exception e) {
  377                           throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e);
  378                       }
  379                   }
  380               } catch (ParseException e) {
  381                   throw new XWorkException("Could not parse date", e);
  382               }
  383           } else if (Date.class.isAssignableFrom(value.getClass())) {
  384               result = (Date) value;
  385           }
  386           return result;
  387       }
  388   
  389       private Object doConvertToNumber(Map context, Object value, Class toType) {
  390           if (value instanceof String) {
  391               if (toType == BigDecimal.class) {
  392                   return new BigDecimal((String) value);
  393               } else if (toType == BigInteger.class) {
  394                   return new BigInteger((String) value);
  395               } else if (toType.isPrimitive()) {
  396                   return super.convertValue(context, value, toType);
  397               } else {
  398                   String stringValue = (String) value;
  399                   if (!toType.isPrimitive() && (stringValue == null || stringValue.length() == 0)) {
  400                       return null;
  401                   }
  402                   NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
  403                   ParsePosition parsePos = new ParsePosition(0);
  404                   if (isIntegerType(toType)) {
  405                       numFormat.setParseIntegerOnly(true);
  406                   }
  407                   numFormat.setGroupingUsed(true);
  408                   Number number = numFormat.parse(stringValue, parsePos);
  409   
  410                   if (parsePos.getIndex() != stringValue.length()) {
  411                       throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position "
  412                               + parsePos.getIndex());
  413                   } else {
  414                       value = super.convertValue(context, number, toType);
  415                   }
  416               }
  417           } else if (value instanceof Object[]) {
  418               Object[] objArray = (Object[]) value;
  419   
  420               if (objArray.length == 1) {
  421                   return doConvertToNumber(context, objArray[0], toType);
  422               }
  423           }
  424   
  425           // pass it through DefaultTypeConverter
  426           return super.convertValue(context, value, toType);
  427       }
  428   
  429       protected boolean isIntegerType(Class type) {
  430           if (double.class == type || float.class == type || Double.class == type || Float.class == type
  431                   || char.class == type || Character.class == type) {
  432               return false;
  433           }
  434   
  435           return true;
  436       }
  437   
  438       /**
  439        * Converts the input as a number using java's number formatter to a string output.
  440        */
  441       private String doConvertFromNumberToString(Map context, Object value, Class toType) {
  442           // XW-409: If the input is a Number we should format it to a string using the choosen locale and use java's numberformatter
  443           if (Number.class.isAssignableFrom(toType)) {
  444               NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
  445               if (isIntegerType(toType)) {
  446                   numFormat.setParseIntegerOnly(true);
  447               }
  448               numFormat.setGroupingUsed(true);
  449               numFormat.setMaximumFractionDigits(99); // to be sure we include all digits after decimal seperator, otherwise some of the fractions can be chopped
  450   
  451               String number = numFormat.format(value);
  452               if (number != null) {
  453                   return number;
  454               }
  455           }
  456   
  457           return null; // no number
  458       }
  459   
  460   
  461       private String doConvertToString(Map context, Object value) {
  462           String result = null;
  463   
  464           if (value instanceof int[]) {
  465               int[] x = (int[]) value;
  466               List intArray = new ArrayList(x.length);
  467   
  468               for (int i = 0; i < x.length; i++) {
  469                   intArray.add(new Integer(x[i]));
  470               }
  471   
  472               result = TextUtils.join(", ", intArray);
  473           } else if (value instanceof long[]) {
  474               long[] x = (long[]) value;
  475               List intArray = new ArrayList(x.length);
  476   
  477               for (int i = 0; i < x.length; i++) {
  478                   intArray.add(new Long(x[i]));
  479               }
  480   
  481               result = TextUtils.join(", ", intArray);
  482           } else if (value instanceof double[]) {
  483               double[] x = (double[]) value;
  484               List intArray = new ArrayList(x.length);
  485   
  486               for (int i = 0; i < x.length; i++) {
  487                   intArray.add(new Double(x[i]));
  488               }
  489   
  490               result = TextUtils.join(", ", intArray);
  491           } else if (value instanceof boolean[]) {
  492               boolean[] x = (boolean[]) value;
  493               List intArray = new ArrayList(x.length);
  494   
  495               for (int i = 0; i < x.length; i++) {
  496                   intArray.add(new Boolean(x[i]));
  497               }
  498   
  499               result = TextUtils.join(", ", intArray);
  500           } else if (value instanceof Date) {
  501               DateFormat df = null;
  502               if (value instanceof java.sql.Time) {
  503                   df = DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale(context));
  504               } else if (value instanceof java.sql.Timestamp) {
  505                   SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
  506                           DateFormat.MEDIUM,
  507                           getLocale(context));
  508                   df = new SimpleDateFormat(dfmt.toPattern() + MILLISECOND_FORMAT);
  509               } else {
  510                   df = DateFormat.getDateInstance(DateFormat.SHORT, getLocale(context));
  511               }
  512               result = df.format(value);
  513           } else if (value instanceof String[]) {
  514               result = TextUtils.join(", ", (String[]) value);
  515           }
  516   
  517           return result;
  518       }
  519   }

Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2.conversion.impl » [javadoc | source]