|
17 | 17 | package org.springframework.boot.convert;
|
18 | 18 |
|
19 | 19 | import java.lang.annotation.Annotation;
|
| 20 | +import java.util.Collections; |
20 | 21 | import java.util.LinkedHashSet;
|
| 22 | +import java.util.Map; |
| 23 | +import java.util.Optional; |
21 | 24 | import java.util.Set;
|
22 | 25 |
|
23 | 26 | import org.springframework.beans.factory.ListableBeanFactory;
|
| 27 | +import org.springframework.beans.factory.config.BeanDefinition; |
| 28 | +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; |
| 29 | +import org.springframework.context.ConfigurableApplicationContext; |
| 30 | +import org.springframework.context.i18n.LocaleContextHolder; |
| 31 | +import org.springframework.core.GenericTypeResolver; |
| 32 | +import org.springframework.core.ResolvableType; |
24 | 33 | import org.springframework.core.convert.ConversionService;
|
25 | 34 | import org.springframework.core.convert.TypeDescriptor;
|
| 35 | +import org.springframework.core.convert.converter.ConditionalConverter; |
| 36 | +import org.springframework.core.convert.converter.ConditionalGenericConverter; |
26 | 37 | import org.springframework.core.convert.converter.Converter;
|
27 | 38 | import org.springframework.core.convert.converter.ConverterFactory;
|
28 | 39 | import org.springframework.core.convert.converter.ConverterRegistry;
|
|
37 | 48 | import org.springframework.format.Printer;
|
38 | 49 | import org.springframework.format.support.DefaultFormattingConversionService;
|
39 | 50 | import org.springframework.format.support.FormattingConversionService;
|
| 51 | +import org.springframework.util.StringUtils; |
40 | 52 | import org.springframework.util.StringValueResolver;
|
41 | 53 |
|
42 | 54 | /**
|
|
49 | 61 | * against registry instance.
|
50 | 62 | *
|
51 | 63 | * @author Phillip Webb
|
| 64 | + * @author Shixiong Guo(viviel) |
52 | 65 | * @since 2.0.0
|
53 | 66 | */
|
54 | 67 | public class ApplicationConversionService extends FormattingConversionService {
|
@@ -272,28 +285,313 @@ public static void addApplicationFormatters(FormatterRegistry registry) {
|
272 | 285 | * @since 2.2.0
|
273 | 286 | */
|
274 | 287 | public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) {
|
275 |
| - Set<Object> beans = new LinkedHashSet<>(); |
276 |
| - beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values()); |
277 |
| - beans.addAll(beanFactory.getBeansOfType(Converter.class).values()); |
278 |
| - beans.addAll(beanFactory.getBeansOfType(Printer.class).values()); |
279 |
| - beans.addAll(beanFactory.getBeansOfType(Parser.class).values()); |
280 |
| - for (Object bean : beans) { |
281 |
| - if (bean instanceof GenericConverter) { |
282 |
| - registry.addConverter((GenericConverter) bean); |
| 288 | + Set<Map.Entry<String, ?>> entries = new LinkedHashSet<>(); |
| 289 | + entries.addAll(beanFactory.getBeansOfType(GenericConverter.class).entrySet()); |
| 290 | + entries.addAll(beanFactory.getBeansOfType(Converter.class).entrySet()); |
| 291 | + entries.addAll(beanFactory.getBeansOfType(Printer.class).entrySet()); |
| 292 | + entries.addAll(beanFactory.getBeansOfType(Parser.class).entrySet()); |
| 293 | + for (Map.Entry<String, ?> e : entries) { |
| 294 | + String beanName = e.getKey(); |
| 295 | + Object bean = e.getValue(); |
| 296 | + try { |
| 297 | + doAddBean(registry, bean); |
283 | 298 | }
|
284 |
| - else if (bean instanceof Converter) { |
285 |
| - registry.addConverter((Converter<?, ?>) bean); |
| 299 | + catch (IllegalArgumentException ex) { |
| 300 | + if (!tryAddFactoryMethodBean(registry, beanFactory, beanName, bean)) { |
| 301 | + throw ex; |
| 302 | + } |
| 303 | + } |
| 304 | + } |
| 305 | + } |
| 306 | + |
| 307 | + private static void doAddBean(FormatterRegistry registry, Object bean) { |
| 308 | + if (bean instanceof GenericConverter) { |
| 309 | + registry.addConverter((GenericConverter) bean); |
| 310 | + } |
| 311 | + else if (bean instanceof Converter) { |
| 312 | + registry.addConverter((Converter<?, ?>) bean); |
| 313 | + } |
| 314 | + else if (bean instanceof Formatter) { |
| 315 | + registry.addFormatter((Formatter<?>) bean); |
| 316 | + } |
| 317 | + else if (bean instanceof Printer) { |
| 318 | + registry.addPrinter((Printer<?>) bean); |
| 319 | + } |
| 320 | + else if (bean instanceof Parser) { |
| 321 | + registry.addParser((Parser<?>) bean); |
| 322 | + } |
| 323 | + } |
| 324 | + |
| 325 | + private static boolean tryAddFactoryMethodBean(FormatterRegistry registry, ListableBeanFactory beanFactory, |
| 326 | + String beanName, Object bean) { |
| 327 | + ConfigurableListableBeanFactory clbf = getConfigurableListableBeanFactory(beanFactory); |
| 328 | + if (clbf == null) { |
| 329 | + return false; |
| 330 | + } |
| 331 | + if (!isFactoryMethod(clbf, beanName)) { |
| 332 | + return false; |
| 333 | + } |
| 334 | + if (bean instanceof Converter) { |
| 335 | + return addConverter(registry, clbf, beanName, (Converter<?, ?>) bean); |
| 336 | + } |
| 337 | + else if (bean instanceof Printer) { |
| 338 | + return addPrinter(registry, clbf, beanName, (Printer<?>) bean); |
| 339 | + } |
| 340 | + else if (bean instanceof Parser) { |
| 341 | + return addParser(registry, clbf, beanName, (Parser<?>) bean); |
| 342 | + } |
| 343 | + return false; |
| 344 | + } |
| 345 | + |
| 346 | + private static ConfigurableListableBeanFactory getConfigurableListableBeanFactory(ListableBeanFactory beanFactory) { |
| 347 | + ListableBeanFactory bf = beanFactory; |
| 348 | + if (bf instanceof ConfigurableApplicationContext) { |
| 349 | + bf = ((ConfigurableApplicationContext) bf).getBeanFactory(); |
| 350 | + } |
| 351 | + if (bf instanceof ConfigurableListableBeanFactory) { |
| 352 | + return (ConfigurableListableBeanFactory) bf; |
| 353 | + } |
| 354 | + return null; |
| 355 | + } |
| 356 | + |
| 357 | + private static boolean isFactoryMethod(ConfigurableListableBeanFactory clbf, String beanName) { |
| 358 | + BeanDefinition bd = clbf.getMergedBeanDefinition(beanName); |
| 359 | + return bd.getFactoryMethodName() != null; |
| 360 | + } |
| 361 | + |
| 362 | + private static boolean addConverter(FormatterRegistry registry, ConfigurableListableBeanFactory beanFactory, |
| 363 | + String beanName, Converter<?, ?> converter) { |
| 364 | + ConverterAdapter adapter = getConverterAdapter(beanFactory, beanName, converter); |
| 365 | + if (adapter == null) { |
| 366 | + return false; |
| 367 | + } |
| 368 | + registry.addConverter(adapter); |
| 369 | + return true; |
| 370 | + } |
| 371 | + |
| 372 | + private static ConverterAdapter getConverterAdapter(ConfigurableListableBeanFactory beanFactory, String beanName, |
| 373 | + Converter<?, ?> converter) { |
| 374 | + ResolvableType[] types = getResolvableType(beanFactory, beanName); |
| 375 | + if (types.length < 2) { |
| 376 | + return null; |
| 377 | + } |
| 378 | + return new ConverterAdapter(converter, types[0], types[1]); |
| 379 | + } |
| 380 | + |
| 381 | + private static ResolvableType[] getResolvableType(ConfigurableListableBeanFactory beanFactory, String beanName) { |
| 382 | + BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName); |
| 383 | + ResolvableType resolvableType = beanDefinition.getResolvableType(); |
| 384 | + return resolvableType.getGenerics(); |
| 385 | + } |
| 386 | + |
| 387 | + private static boolean addPrinter(FormatterRegistry registry, ConfigurableListableBeanFactory beanFactory, |
| 388 | + String beanName, Printer<?> printer) { |
| 389 | + PrinterAdapter adapter = getPrinterAdapter(beanFactory, beanName, printer); |
| 390 | + if (adapter == null) { |
| 391 | + return false; |
| 392 | + } |
| 393 | + registry.addConverter(adapter); |
| 394 | + return true; |
| 395 | + } |
| 396 | + |
| 397 | + private static PrinterAdapter getPrinterAdapter(ConfigurableListableBeanFactory beanFactory, String beanName, |
| 398 | + Printer<?> printer) { |
| 399 | + ResolvableType[] types = getResolvableType(beanFactory, beanName); |
| 400 | + if (types.length < 1) { |
| 401 | + return null; |
| 402 | + } |
| 403 | + ConversionService conversionService = beanFactory.getBean(ConversionService.class); |
| 404 | + return new PrinterAdapter(types[0].resolve(), printer, conversionService); |
| 405 | + } |
| 406 | + |
| 407 | + private static boolean addParser(FormatterRegistry registry, ConfigurableListableBeanFactory beanFactory, |
| 408 | + String beanName, Parser<?> parser) { |
| 409 | + ParserAdapter adapter = getParserAdapter(beanFactory, beanName, parser); |
| 410 | + if (adapter == null) { |
| 411 | + return false; |
| 412 | + } |
| 413 | + registry.addConverter(adapter); |
| 414 | + return true; |
| 415 | + } |
| 416 | + |
| 417 | + private static ParserAdapter getParserAdapter(ConfigurableListableBeanFactory beanFactory, String beanName, |
| 418 | + Parser<?> parser) { |
| 419 | + ResolvableType[] types = getResolvableType(beanFactory, beanName); |
| 420 | + if (types.length < 1) { |
| 421 | + return null; |
| 422 | + } |
| 423 | + ConversionService conversionService = beanFactory.getBean(ConversionService.class); |
| 424 | + return new ParserAdapter(types[0].resolve(), parser, conversionService); |
| 425 | + } |
| 426 | + |
| 427 | + /** |
| 428 | + * Adapts a {@link Converter} to a {@link GenericConverter}. |
| 429 | + * <p> |
| 430 | + * Reference from |
| 431 | + * {@link org.springframework.core.convert.support.GenericConversionService.ConverterAdapter} |
| 432 | + */ |
| 433 | + @SuppressWarnings("unchecked") |
| 434 | + private static final class ConverterAdapter implements ConditionalGenericConverter { |
| 435 | + |
| 436 | + private final Converter<Object, Object> converter; |
| 437 | + |
| 438 | + private final ConvertiblePair typeInfo; |
| 439 | + |
| 440 | + private final ResolvableType targetType; |
| 441 | + |
| 442 | + ConverterAdapter(Converter<?, ?> converter, ResolvableType sourceType, ResolvableType targetType) { |
| 443 | + this.converter = (Converter<Object, Object>) converter; |
| 444 | + this.typeInfo = new ConvertiblePair(sourceType.toClass(), targetType.toClass()); |
| 445 | + this.targetType = targetType; |
| 446 | + } |
| 447 | + |
| 448 | + @Override |
| 449 | + public Set<ConvertiblePair> getConvertibleTypes() { |
| 450 | + return Collections.singleton(this.typeInfo); |
| 451 | + } |
| 452 | + |
| 453 | + @Override |
| 454 | + public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { |
| 455 | + // Check raw type first... |
| 456 | + if (this.typeInfo.getTargetType() != targetType.getObjectType()) { |
| 457 | + return false; |
286 | 458 | }
|
287 |
| - else if (bean instanceof Formatter) { |
288 |
| - registry.addFormatter((Formatter<?>) bean); |
| 459 | + // Full check for complex generic type match required? |
| 460 | + ResolvableType rt = targetType.getResolvableType(); |
| 461 | + if (!(rt.getType() instanceof Class) && !rt.isAssignableFrom(this.targetType) |
| 462 | + && !this.targetType.hasUnresolvableGenerics()) { |
| 463 | + return false; |
289 | 464 | }
|
290 |
| - else if (bean instanceof Printer) { |
291 |
| - registry.addPrinter((Printer<?>) bean); |
| 465 | + return !(this.converter instanceof ConditionalConverter) |
| 466 | + || ((ConditionalConverter) this.converter).matches(sourceType, targetType); |
| 467 | + } |
| 468 | + |
| 469 | + @Override |
| 470 | + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { |
| 471 | + if (source == null) { |
| 472 | + return convertNullSource(sourceType, targetType); |
292 | 473 | }
|
293 |
| - else if (bean instanceof Parser) { |
294 |
| - registry.addParser((Parser<?>) bean); |
| 474 | + return this.converter.convert(source); |
| 475 | + } |
| 476 | + |
| 477 | + @Override |
| 478 | + public String toString() { |
| 479 | + return (this.typeInfo + " : " + this.converter); |
| 480 | + } |
| 481 | + |
| 482 | + /** |
| 483 | + * Template method to convert a {@code null} source. |
| 484 | + * <p> |
| 485 | + * The default implementation returns {@code null} or the Java 8 |
| 486 | + * {@link java.util.Optional#empty()} instance if the target type is |
| 487 | + * {@code java.util.Optional}. Subclasses may override this to return custom |
| 488 | + * {@code null} objects for specific target types. |
| 489 | + * @param sourceType the source type to convert from |
| 490 | + * @param targetType the target type to convert to |
| 491 | + * @return the converted null object |
| 492 | + */ |
| 493 | + private Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor targetType) { |
| 494 | + if (targetType.getObjectType() == Optional.class) { |
| 495 | + return Optional.empty(); |
295 | 496 | }
|
| 497 | + return null; |
| 498 | + } |
| 499 | + |
| 500 | + } |
| 501 | + |
| 502 | + private static class PrinterAdapter implements GenericConverter { |
| 503 | + |
| 504 | + private final Class<?> fieldType; |
| 505 | + |
| 506 | + private final TypeDescriptor printerObjectType; |
| 507 | + |
| 508 | + @SuppressWarnings("rawtypes") |
| 509 | + private final Printer printer; |
| 510 | + |
| 511 | + private final ConversionService conversionService; |
| 512 | + |
| 513 | + PrinterAdapter(Class<?> fieldType, Printer<?> printer, ConversionService conversionService) { |
| 514 | + this.fieldType = fieldType; |
| 515 | + this.printerObjectType = TypeDescriptor.valueOf(resolvePrinterObjectType(printer)); |
| 516 | + this.printer = printer; |
| 517 | + this.conversionService = conversionService; |
| 518 | + } |
| 519 | + |
| 520 | + @Override |
| 521 | + public Set<ConvertiblePair> getConvertibleTypes() { |
| 522 | + return Collections.singleton(new ConvertiblePair(this.fieldType, String.class)); |
296 | 523 | }
|
| 524 | + |
| 525 | + @Override |
| 526 | + @SuppressWarnings("unchecked") |
| 527 | + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { |
| 528 | + if (!sourceType.isAssignableTo(this.printerObjectType)) { |
| 529 | + source = this.conversionService.convert(source, sourceType, this.printerObjectType); |
| 530 | + } |
| 531 | + if (source == null) { |
| 532 | + return ""; |
| 533 | + } |
| 534 | + return this.printer.print(source, LocaleContextHolder.getLocale()); |
| 535 | + } |
| 536 | + |
| 537 | + private Class<?> resolvePrinterObjectType(Printer<?> printer) { |
| 538 | + return GenericTypeResolver.resolveTypeArgument(printer.getClass(), Printer.class); |
| 539 | + } |
| 540 | + |
| 541 | + @Override |
| 542 | + public String toString() { |
| 543 | + return (this.fieldType.getName() + " -> " + String.class.getName() + " : " + this.printer); |
| 544 | + } |
| 545 | + |
| 546 | + } |
| 547 | + |
| 548 | + private static class ParserAdapter implements GenericConverter { |
| 549 | + |
| 550 | + private final Class<?> fieldType; |
| 551 | + |
| 552 | + private final Parser<?> parser; |
| 553 | + |
| 554 | + private final ConversionService conversionService; |
| 555 | + |
| 556 | + ParserAdapter(Class<?> fieldType, Parser<?> parser, ConversionService conversionService) { |
| 557 | + this.fieldType = fieldType; |
| 558 | + this.parser = parser; |
| 559 | + this.conversionService = conversionService; |
| 560 | + } |
| 561 | + |
| 562 | + @Override |
| 563 | + public Set<ConvertiblePair> getConvertibleTypes() { |
| 564 | + return Collections.singleton(new ConvertiblePair(String.class, this.fieldType)); |
| 565 | + } |
| 566 | + |
| 567 | + @Override |
| 568 | + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { |
| 569 | + String text = (String) source; |
| 570 | + if (!StringUtils.hasText(text)) { |
| 571 | + return null; |
| 572 | + } |
| 573 | + Object result; |
| 574 | + try { |
| 575 | + result = this.parser.parse(text, LocaleContextHolder.getLocale()); |
| 576 | + } |
| 577 | + catch (IllegalArgumentException ex) { |
| 578 | + throw ex; |
| 579 | + } |
| 580 | + catch (Throwable ex) { |
| 581 | + throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex); |
| 582 | + } |
| 583 | + TypeDescriptor resultType = TypeDescriptor.valueOf(result.getClass()); |
| 584 | + if (!resultType.isAssignableTo(targetType)) { |
| 585 | + result = this.conversionService.convert(result, resultType, targetType); |
| 586 | + } |
| 587 | + return result; |
| 588 | + } |
| 589 | + |
| 590 | + @Override |
| 591 | + public String toString() { |
| 592 | + return (String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser); |
| 593 | + } |
| 594 | + |
297 | 595 | }
|
298 | 596 |
|
299 | 597 | }
|
0 commit comments