PageRenderTime 4265ms CodeModel.GetById 28ms RepoModel.GetById 6ms app.codeStats 2ms

/projects/struts-2.2.1/src/xwork-core/src/main/java/com/opensymphony/xwork2/conversion/impl/XWorkBasicConverter.java

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