ConvertUtilBean.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /**
  2. * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
  3. */
  4. package com.crunii.micro.common.converter;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.apache.commons.beanutils.ConversionException;
  7. import org.apache.commons.beanutils.Converter;
  8. import org.apache.commons.beanutils.converters.DateConverter;
  9. import org.apache.commons.beanutils.converters.*;
  10. import java.io.File;
  11. import java.lang.reflect.Array;
  12. import java.math.BigDecimal;
  13. import java.math.BigInteger;
  14. import java.net.URL;
  15. import java.sql.Timestamp;
  16. import java.util.Calendar;
  17. import java.util.Collection;
  18. import java.util.concurrent.ConcurrentHashMap;
  19. /**
  20. * @author 田平 create 2020年3月31日下午3:44:04
  21. */
  22. @Slf4j
  23. public class ConvertUtilBean {
  24. private static final Integer ZERO = new Integer(0);
  25. private static final Character SPACE = new Character(' ');
  26. private final ConcurrentHashMap<Class<?>, Converter> converters = new ConcurrentHashMap<Class<?>, Converter>(32);
  27. public ConvertUtilBean() {
  28. register();
  29. }
  30. /**
  31. * Convert the specified value into a String. If the specified value is an array, the first element (converted to a String) will be returned. The registered {@link Converter} for the <code>java.lang.String</code> class will be used, which allows applications to customize Object->String conversions (the default implementation simply uses toString()).
  32. *
  33. * @param value Value to be converted (may be null)
  34. * @return The converted String value or null if value is null
  35. */
  36. public String convert(Object value) {
  37. if (value == null) {
  38. return null;
  39. } else if (value.getClass().isArray()) {
  40. if (Array.getLength(value) < 1) {
  41. return (null);
  42. }
  43. value = Array.get(value, 0);
  44. if (value == null) {
  45. return null;
  46. } else {
  47. final Converter converter = lookup(String.class);
  48. return (converter.convert(String.class, value));
  49. }
  50. } else {
  51. final Converter converter = lookup(String.class);
  52. return (converter.convert(String.class, value));
  53. }
  54. }
  55. /**
  56. * Convert the specified value to an object of the specified class (if possible). Otherwise, return a String representation of the value.
  57. *
  58. * @param value Value to be converted (may be null)
  59. * @param clazz Java class to be converted to (must not be null)
  60. * @return The converted value
  61. * @throws ConversionException if thrown by an underlying Converter
  62. */
  63. public Object convert(final String value, final Class<?> clazz) {
  64. if (log.isDebugEnabled()) {
  65. log.debug("Convert string '" + value + "' to class '" +
  66. clazz.getName() + "'");
  67. }
  68. Converter converter = lookup(clazz);
  69. if (converter == null) {
  70. converter = lookup(String.class);
  71. }
  72. if (log.isTraceEnabled()) {
  73. log.trace(" Using converter " + converter);
  74. }
  75. return (converter.convert(clazz, value));
  76. }
  77. /**
  78. * Convert an array of specified values to an array of objects of the specified class (if possible). If the specified Java class is itself an array class, this class will be the type of the returned value. Otherwise, an array will be constructed whose component type is the specified class.
  79. *
  80. * @param values Array of values to be converted
  81. * @param clazz Java array or element class to be converted to (must not be null)
  82. * @return The converted value
  83. * @throws ConversionException if thrown by an underlying Converter
  84. */
  85. public Object convert(final String[] values, final Class<?> clazz) {
  86. Class<?> type = clazz;
  87. if (clazz.isArray()) {
  88. type = clazz.getComponentType();
  89. }
  90. if (log.isDebugEnabled()) {
  91. log.debug("Convert String[" + values.length + "] to class '" +
  92. type.getName() + "[]'");
  93. }
  94. Converter converter = lookup(type);
  95. if (converter == null) {
  96. converter = lookup(String.class);
  97. }
  98. if (log.isTraceEnabled()) {
  99. log.trace(" Using converter " + converter);
  100. }
  101. final Object array = Array.newInstance(type, values.length);
  102. for (int i = 0; i < values.length; i++) {
  103. Array.set(array, i, converter.convert(type, values[i]));
  104. }
  105. return (array);
  106. }
  107. /**
  108. * Convert the value to an object of the specified class (if possible). If no converter for the desired target type is registered, the passed in object is returned unchanged.
  109. *
  110. * @param value Value to be converted (may be null)
  111. * @param targetType Class of the value to be converted to (must not be null)
  112. * @return The converted value
  113. * @throws ConversionException if thrown by an underlying Converter
  114. */
  115. public Object convert(final Object value, final Class<?> targetType) {
  116. final Class<?> sourceType = value == null ? null : value.getClass();
  117. if (log.isDebugEnabled()) {
  118. if (value == null) {
  119. log.debug("Convert null value to type '" +
  120. targetType.getName() + "'");
  121. } else {
  122. log.debug("Convert type '" + sourceType.getName() + "' value '" + value +
  123. "' to type '" + targetType.getName() + "'");
  124. }
  125. }
  126. Object converted = value;
  127. Converter converter = lookup(sourceType, targetType);
  128. if (converter != null) {
  129. if (log.isTraceEnabled()) {
  130. log.trace(" Using converter " + converter);
  131. }
  132. converted = converter.convert(targetType, value);
  133. }
  134. if (String.class.equals(targetType) && converted != null &&
  135. !(converted instanceof String)) {
  136. // NOTE: For backwards compatibility, if the Converter
  137. // doesn't handle conversion-->String then
  138. // use the registered String Converter
  139. converter = lookup(String.class);
  140. if (converter != null) {
  141. if (log.isTraceEnabled()) {
  142. log.trace(" Using converter " + converter);
  143. }
  144. converted = converter.convert(String.class, converted);
  145. }
  146. // If the object still isn't a String, use toString() method
  147. if (converted != null && !(converted instanceof String)) {
  148. converted = converted.toString();
  149. }
  150. }
  151. return converted;
  152. }
  153. /**
  154. * Remove all registered {@link Converter}s, and re-establish the standard Converters.
  155. */
  156. private void register() {
  157. registerPrimitives(true);
  158. registerStandard(true, false);
  159. registerOther(true);
  160. registerArrays(true, 0);
  161. register(BigDecimal.class, new BigDecimalConverter(false));
  162. register(BigInteger.class, new BigIntegerConverter(false));
  163. }
  164. /**
  165. * Register the converters for primitive types.
  166. * </p>
  167. * This method registers the following converters:
  168. * <ul>
  169. * <li><code>Boolean.TYPE</code> - {@link BooleanConverter}</li>
  170. * <li><code>Byte.TYPE</code> - {@link ByteConverter}</li>
  171. * <li><code>Character.TYPE</code> - {@link CharacterConverter}</li>
  172. * <li><code>Double.TYPE</code> - {@link DoubleConverter}</li>
  173. * <li><code>Float.TYPE</code> - {@link FloatConverter}</li>
  174. * <li><code>Integer.TYPE</code> - {@link IntegerConverter}</li>
  175. * <li><code>Long.TYPE</code> - {@link LongConverter}</li>
  176. * <li><code>Short.TYPE</code> - {@link ShortConverter}</li>
  177. * </ul>
  178. *
  179. * @param throwException <code>true</code> if the converters should throw an exception when a conversion error occurs, otherwise <code>
  180. * <code>false</code> if a default value should be used.
  181. */
  182. private void registerPrimitives(final boolean throwException) {
  183. register(Boolean.TYPE, throwException ? new BooleanConverter() : new BooleanConverter(Boolean.FALSE));
  184. register(Byte.TYPE, throwException ? new ByteConverter() : new ByteConverter(ZERO));
  185. register(Character.TYPE, throwException ? new CharacterConverter() : new CharacterConverter(SPACE));
  186. register(Double.TYPE, throwException ? new DoubleConverter() : new DoubleConverter(ZERO));
  187. register(Float.TYPE, throwException ? new FloatConverter() : new FloatConverter(ZERO));
  188. register(Integer.TYPE, throwException ? new IntegerConverter() : new IntegerConverter(ZERO));
  189. register(Long.TYPE, throwException ? new LongConverter() : new LongConverter(ZERO));
  190. register(Short.TYPE, throwException ? new ShortConverter() : new ShortConverter(ZERO));
  191. }
  192. /**
  193. * Register the converters for standard types.
  194. * </p>
  195. * This method registers the following converters:
  196. * <ul>
  197. * <li><code>BigDecimal.class</code> - {@link BigDecimalConverter}</li>
  198. * <li><code>BigInteger.class</code> - {@link BigIntegerConverter}</li>
  199. * <li><code>Boolean.class</code> - {@link BooleanConverter}</li>
  200. * <li><code>Byte.class</code> - {@link ByteConverter}</li>
  201. * <li><code>Character.class</code> - {@link CharacterConverter}</li>
  202. * <li><code>Double.class</code> - {@link DoubleConverter}</li>
  203. * <li><code>Float.class</code> - {@link FloatConverter}</li>
  204. * <li><code>Integer.class</code> - {@link IntegerConverter}</li>
  205. * <li><code>Long.class</code> - {@link LongConverter}</li>
  206. * <li><code>Short.class</code> - {@link ShortConverter}</li>
  207. * <li><code>String.class</code> - {@link StringConverter}</li>
  208. * </ul>
  209. *
  210. * @param throwException <code>true</code> if the converters should throw an exception when a conversion error occurs, otherwise <code>
  211. * <code>false</code> if a default value should be used.
  212. * @param defaultNull <code>true</code>if the <i>standard</i> converters
  213. */
  214. private void registerStandard(final boolean throwException, final boolean defaultNull) {
  215. final Number defaultNumber = defaultNull ? null : ZERO;
  216. final BigDecimal bigDecDeflt = defaultNull ? null : new BigDecimal("0.0");
  217. final BigInteger bigIntDeflt = defaultNull ? null : new BigInteger("0");
  218. final Boolean booleanDefault = defaultNull ? null : Boolean.FALSE;
  219. final Character charDefault = defaultNull ? null : SPACE;
  220. final String stringDefault = defaultNull ? null : "";
  221. register(BigDecimal.class, throwException ? new BigDecimalConverter() : new BigDecimalConverter(bigDecDeflt));
  222. register(BigInteger.class, throwException ? new BigIntegerConverter() : new BigIntegerConverter(bigIntDeflt));
  223. register(Boolean.class, throwException ? new BooleanConverter() : new BooleanConverter(booleanDefault));
  224. register(Byte.class, throwException ? new ByteConverter() : new ByteConverter(defaultNumber));
  225. register(Character.class, throwException ? new CharacterConverter() : new CharacterConverter(charDefault));
  226. register(Double.class, throwException ? new DoubleConverter() : new DoubleConverter(defaultNumber));
  227. register(Float.class, throwException ? new FloatConverter() : new FloatConverter(defaultNumber));
  228. register(Integer.class, throwException ? new IntegerConverter() : new IntegerConverter(defaultNumber));
  229. register(Long.class, throwException ? new LongConverter() : new LongConverter(defaultNumber));
  230. register(Short.class, throwException ? new ShortConverter() : new ShortConverter(defaultNumber));
  231. register(String.class, throwException ? new StringConverter() : new StringConverter(stringDefault));
  232. }
  233. /**
  234. * Register the converters for other types.
  235. * </p>
  236. * This method registers the following converters:
  237. * <ul>
  238. * <li><code>Class.class</code> - {@link ClassConverter}</li>
  239. * <li><code>java.util.Date.class</code> - {@link DateConverter}</li>
  240. * <li><code>java.util.Calendar.class</code> - {@link CalendarConverter}</li>
  241. * <li><code>File.class</code> - {@link FileConverter}</li>
  242. * <li><code>java.sql.Date.class</code> - {@link SqlDateConverter}</li>
  243. * <li><code>java.sql.Time.class</code> - {@link SqlTimeConverter}</li>
  244. * <li><code>java.sql.Timestamp.class</code> - {@link SqlTimestampConverter}</li>
  245. * <li><code>URL.class</code> - {@link URLConverter}</li>
  246. * </ul>
  247. *
  248. * @param throwException <code>true</code> if the converters should throw an exception when a conversion error occurs, otherwise <code>
  249. * <code>false</code> if a default value should be used.
  250. */
  251. private void registerOther(final boolean throwException) {
  252. register(Class.class, throwException ? new ClassConverter() : new ClassConverter(null));
  253. //register(java.util.Date.class, throwException ? new DateConverter() : new DateConverter(null));
  254. register(java.util.Date.class, com.crunii.micro.common.converter.DateConverter.getDateConverter());
  255. register(Calendar.class, throwException ? new CalendarConverter() : new CalendarConverter(null));
  256. register(File.class, throwException ? new FileConverter() : new FileConverter(null));
  257. register(java.sql.Date.class, throwException ? new SqlDateConverter() : new SqlDateConverter(null));
  258. register(java.sql.Time.class, throwException ? new SqlTimeConverter() : new SqlTimeConverter(null));
  259. register(Timestamp.class, throwException ? new SqlTimestampConverter() : new SqlTimestampConverter(null));
  260. register(URL.class, throwException ? new URLConverter() : new URLConverter(null));
  261. }
  262. /**
  263. * Register array converters.
  264. *
  265. * @param throwException <code>true</code> if the converters should throw an exception when a conversion error occurs, otherwise <code>
  266. * <code>false</code> if a default value should be used.
  267. * @param defaultArraySize The size of the default array value for array converters (N.B. This values is ignored if <code>throwException</code> is <code>true</code>). Specifying a value less than zero causes a <code>null<code> value to be used for the default.
  268. */
  269. private void registerArrays(final boolean throwException, final int defaultArraySize) {
  270. // Primitives
  271. registerArrayConverter(Boolean.TYPE, new BooleanConverter(), throwException, defaultArraySize);
  272. registerArrayConverter(Byte.TYPE, new ByteConverter(), throwException, defaultArraySize);
  273. registerArrayConverter(Character.TYPE, new CharacterConverter(), throwException, defaultArraySize);
  274. registerArrayConverter(Double.TYPE, new DoubleConverter(), throwException, defaultArraySize);
  275. registerArrayConverter(Float.TYPE, new FloatConverter(), throwException, defaultArraySize);
  276. registerArrayConverter(Integer.TYPE, new IntegerConverter(), throwException, defaultArraySize);
  277. registerArrayConverter(Long.TYPE, new LongConverter(), throwException, defaultArraySize);
  278. registerArrayConverter(Short.TYPE, new ShortConverter(), throwException, defaultArraySize);
  279. // Standard
  280. registerArrayConverter(BigDecimal.class, new BigDecimalConverter(), throwException, defaultArraySize);
  281. registerArrayConverter(BigInteger.class, new BigIntegerConverter(), throwException, defaultArraySize);
  282. registerArrayConverter(Boolean.class, new BooleanConverter(), throwException, defaultArraySize);
  283. registerArrayConverter(Byte.class, new ByteConverter(), throwException, defaultArraySize);
  284. registerArrayConverter(Character.class, new CharacterConverter(), throwException, defaultArraySize);
  285. registerArrayConverter(Double.class, new DoubleConverter(), throwException, defaultArraySize);
  286. registerArrayConverter(Float.class, new FloatConverter(), throwException, defaultArraySize);
  287. registerArrayConverter(Integer.class, new IntegerConverter(), throwException, defaultArraySize);
  288. registerArrayConverter(Long.class, new LongConverter(), throwException, defaultArraySize);
  289. registerArrayConverter(Short.class, new ShortConverter(), throwException, defaultArraySize);
  290. registerArrayConverter(String.class, new StringConverter(), throwException, defaultArraySize);
  291. // Other
  292. registerArrayConverter(Class.class, new ClassConverter(), throwException, defaultArraySize);
  293. //registerArrayConverter(java.util.Date.class, new DateConverter(), throwException, defaultArraySize);
  294. registerArrayConverter(java.util.Date.class, com.crunii.micro.common.converter.DateConverter.getDateConverter(), throwException,
  295. defaultArraySize);
  296. registerArrayConverter(Calendar.class, new DateConverter(), throwException, defaultArraySize);
  297. registerArrayConverter(File.class, new FileConverter(), throwException, defaultArraySize);
  298. registerArrayConverter(java.sql.Date.class, new SqlDateConverter(), throwException, defaultArraySize);
  299. registerArrayConverter(java.sql.Time.class, new SqlTimeConverter(), throwException, defaultArraySize);
  300. registerArrayConverter(Timestamp.class, new SqlTimestampConverter(), throwException, defaultArraySize);
  301. registerArrayConverter(URL.class, new URLConverter(), throwException, defaultArraySize);
  302. }
  303. /**
  304. * Register a new ArrayConverter with the specified element delegate converter that returns a default array of the specified size in the event of conversion errors.
  305. *
  306. * @param componentType The component type of the array
  307. * @param componentConverter The converter to delegate to for the array elements
  308. * @param throwException Whether a conversion exception should be thrown or a default value used in the event of a conversion error
  309. * @param defaultArraySize The size of the default array
  310. */
  311. private void registerArrayConverter(final Class<?> componentType, final Converter componentConverter, final boolean throwException, final int defaultArraySize) {
  312. final Class<?> arrayType = Array.newInstance(componentType, 0).getClass();
  313. Converter arrayConverter = null;
  314. if (throwException) {
  315. arrayConverter = new ArrayConverter(arrayType, componentConverter);
  316. } else {
  317. arrayConverter = new ArrayConverter(arrayType, componentConverter, defaultArraySize);
  318. }
  319. register(arrayType, arrayConverter);
  320. }
  321. /**
  322. * strictly for convenience since it has same parameter order as Map.put
  323. */
  324. private void register(final Class<?> clazz, final Converter converter) {
  325. register(new ConverterFacade(converter), clazz);
  326. }
  327. /**
  328. * Look up and return any registered {@link Converter} for the specified destination class; if there is no registered Converter, return <code>null</code>.
  329. *
  330. * @param clazz Class for which to return a registered Converter
  331. * @return The registered {@link Converter} or <code>null</code> if not found
  332. */
  333. private Converter lookup(final Class<?> clazz) {
  334. return (converters.get(clazz));
  335. }
  336. /**
  337. * Look up and return any registered {@link Converter} for the specified source and destination class; if there is no registered Converter, return <code>null</code>.
  338. *
  339. * @param sourceType Class of the value being converted
  340. * @param targetType Class of the value to be converted to
  341. * @return The registered {@link Converter} or <code>null</code> if not found
  342. */
  343. private Converter lookup(final Class<?> sourceType, final Class<?> targetType) {
  344. if (targetType == null) {
  345. throw new IllegalArgumentException("Target type is missing");
  346. }
  347. if (sourceType == null) {
  348. return lookup(targetType);
  349. }
  350. Converter converter = null;
  351. // Convert --> String
  352. if (targetType == String.class) {
  353. converter = lookup(sourceType);
  354. if (converter == null && (sourceType.isArray() ||
  355. Collection.class.isAssignableFrom(sourceType))) {
  356. converter = lookup(String[].class);
  357. }
  358. if (converter == null) {
  359. converter = lookup(String.class);
  360. }
  361. return converter;
  362. }
  363. // Convert --> String array
  364. if (targetType == String[].class) {
  365. if (sourceType.isArray() || Collection.class.isAssignableFrom(sourceType)) {
  366. converter = lookup(sourceType);
  367. }
  368. if (converter == null) {
  369. converter = lookup(String[].class);
  370. }
  371. return converter;
  372. }
  373. return lookup(targetType);
  374. }
  375. /**
  376. * Register a custom {@link Converter} for the specified destination <code>Class</code>, replacing any previously registered Converter.
  377. *
  378. * @param converter Converter to be registered
  379. * @param clazz Destination class for conversions performed by this Converter
  380. */
  381. private void register(final Converter converter, final Class<?> clazz) {
  382. converters.put(clazz, converter);
  383. }
  384. }