@Override @SuppressWarnings("unchecked") public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Annotation ann = sourceType.getAnnotation(this.annotationType); if (ann == null) { throw new IllegalStateException( "Expected [" + this.annotationType.getName() + "] to be present on " + sourceType); } AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, sourceType.getObjectType()); GenericConverter converter = cachedPrinters.get(converterKey); if (converter == null) { Printer<?> printer = this.annotationFormatterFactory.getPrinter( converterKey.getAnnotation(), converterKey.getFieldType()); converter = new PrinterConverter(this.fieldType, printer, FormattingConversionService.this); cachedPrinters.put(converterKey, converter); } return converter.convert(source, sourceType, targetType); }
@Override @SuppressWarnings("unchecked") public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Annotation ann = targetType.getAnnotation(this.annotationType); if (ann == null) { throw new IllegalStateException( "Expected [" + this.annotationType.getName() + "] to be present on " + targetType); } AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, targetType.getObjectType()); GenericConverter converter = cachedParsers.get(converterKey); if (converter == null) { Parser<?> parser = this.annotationFormatterFactory.getParser( converterKey.getAnnotation(), converterKey.getFieldType()); converter = new ParserConverter(this.fieldType, parser, FormattingConversionService.this); cachedParsers.put(converterKey, converter); } return converter.convert(source, sourceType, targetType); }
/** * Register the given Converter objects with the given target ConverterRegistry. * @param converters the converter objects: implementing {@link Converter}, * {@link ConverterFactory}, or {@link GenericConverter} * @param registry the target registry */ public static void registerConverters(Set<?> converters, ConverterRegistry registry) { if (converters != null) { for (Object converter : converters) { if (converter instanceof GenericConverter) { registry.addConverter((GenericConverter) converter); } else if (converter instanceof Converter<?, ?>) { registry.addConverter((Converter<?, ?>) converter); } else if (converter instanceof ConverterFactory<?, ?>) { registry.addConverterFactory((ConverterFactory<?, ?>) converter); } else { throw new IllegalArgumentException("Each converter object must implement one of the " + "Converter, ConverterFactory, or GenericConverter interfaces"); } } } }
@Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Assert.notNull(targetType, "targetType to convert to cannot be null"); if (sourceType == null) { Assert.isTrue(source == null, "source must be [null] if sourceType == [null]"); return handleResult(null, targetType, convertNullSource(null, targetType)); } if (source != null && !sourceType.getObjectType().isInstance(source)) { throw new IllegalArgumentException("source to convert from must be an instance of " + sourceType + "; instead it was a " + source.getClass().getName()); } GenericConverter converter = getConverter(sourceType, targetType); if (converter != null) { Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType); return handleResult(sourceType, targetType, result); } return handleConverterNotFound(source, sourceType, targetType); }
/** * Hook method to lookup the converter for a given sourceType/targetType pair. * First queries this ConversionService's converter cache. * On a cache miss, then performs an exhaustive search for a matching converter. * If no converter matches, returns the default converter. * @param sourceType the source type to convert from * @param targetType the target type to convert to * @return the generic converter that will perform the conversion, * or {@code null} if no suitable converter was found * @see #getDefaultConverter(TypeDescriptor, TypeDescriptor) */ protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType); GenericConverter converter = this.converterCache.get(key); if (converter != null) { return (converter != NO_MATCH ? converter : null); } converter = this.converters.find(sourceType, targetType); if (converter == null) { converter = getDefaultConverter(sourceType, targetType); } if (converter != null) { this.converterCache.put(key, converter); return converter; } this.converterCache.put(key, NO_MATCH); return null; }
/** * Find a {@link GenericConverter} given a source and target type. * <p>This method will attempt to match all possible converters by working * through the class and interface hierarchy of the types. * @param sourceType the source type * @param targetType the target type * @return a matching {@link GenericConverter}, or {@code null} if none found */ public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) { // Search the full type hierarchy List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType()); List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType()); for (Class<?> sourceCandidate : sourceCandidates) { for (Class<?> targetCandidate : targetCandidates) { ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate); GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair); if (converter != null) { return converter; } } } return null; }
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType, TypeDescriptor targetType, ConvertiblePair convertiblePair) { // Check specifically registered converters ConvertersForPair convertersForPair = this.converters.get(convertiblePair); if (convertersForPair != null) { GenericConverter converter = convertersForPair.getConverter(sourceType, targetType); if (converter != null) { return converter; } } // Check ConditionalConverters for a dynamic match for (GenericConverter globalConverter : this.globalConverters) { if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) { return globalConverter; } } return null; }
@Bean public GenericConverter genericConverter() { return new GenericConverter() { @Override public Set<ConvertiblePair> getConvertibleTypes() { return Collections .singleton(new ConvertiblePair(String.class, Bar.class)); } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { return new Bar((String) source); } }; }
/** * Register custom converters within given {@link org.springframework.core.convert.support.GenericConversionService} * * @param conversionService must not be null */ public void registerConvertersIn(GenericConversionService conversionService) { Assert.notNull(conversionService); for (Object converter : converters) { if (converter instanceof Converter) { conversionService.addConverter((Converter<?, ?>) converter); } else if (converter instanceof ConverterFactory) { conversionService.addConverterFactory((ConverterFactory<?, ?>) converter); } else if (converter instanceof GenericConverter) { conversionService.addConverter((GenericConverter) converter); } else { throw new IllegalArgumentException("Given object '" + converter + "' expected to be a Converter, ConverterFactory or GenericeConverter!"); } } }
private void registerConversion(Object converter) { Class<?> type = converter.getClass(); boolean isWriting = type.isAnnotationPresent(WritingConverter.class); boolean isReading = type.isAnnotationPresent(ReadingConverter.class); if (!isReading && !isWriting) { isReading = true; isWriting = true; } if (converter instanceof GenericConverter) { GenericConverter genericConverter = (GenericConverter) converter; for (ConvertiblePair pair : genericConverter.getConvertibleTypes()) { register(new ConvertibleContext(pair, isReading, isWriting)); } } else if (converter instanceof Converter) { Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); register(new ConvertibleContext(arguments[0], arguments[1], isReading, isWriting)); } else { throw new IllegalArgumentException("Unsupported Converter type! Expected either GenericConverter if Converter."); } }
/** * Populates the given {@link GenericConversionService} with the converters registered. * * @param conversionService the service to register. */ public void registerConvertersIn(final GenericConversionService conversionService) { for (Object converter : converters) { boolean added = false; if (converter instanceof Converter) { conversionService.addConverter((Converter<?, ?>) converter); added = true; } if (converter instanceof ConverterFactory) { conversionService.addConverterFactory((ConverterFactory<?, ?>) converter); added = true; } if (converter instanceof GenericConverter) { conversionService.addConverter((GenericConverter) converter); added = true; } if (!added) { throw new IllegalArgumentException("Given set contains element that is neither Converter nor ConverterFactory!"); } } }
/** * Registers a conversion for the given converter. Inspects either generics or the convertible pairs returned * by a {@link GenericConverter}. * * @param converter the converter to register. */ private void registerConversion(final Object converter) { Class<?> type = converter.getClass(); boolean isWriting = type.isAnnotationPresent(WritingConverter.class); boolean isReading = type.isAnnotationPresent(ReadingConverter.class); if (converter instanceof GenericConverter) { GenericConverter genericConverter = (GenericConverter) converter; for (GenericConverter.ConvertiblePair pair : genericConverter.getConvertibleTypes()) { register(new ConverterRegistration(pair, isReading, isWriting)); } } else if (converter instanceof Converter) { Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); register(new ConverterRegistration(arguments[0], arguments[1], isReading, isWriting)); } else { throw new IllegalArgumentException("Unsupported Converter type!"); } }
/** * Registers the given {@link ConverterRegistration} as reading or writing pair depending on the type sides being basic * Crate types. * * @param registration the registration. */ private void register(final ConverterRegistration registration) { GenericConverter.ConvertiblePair pair = registration.getConvertiblePair(); if (registration.isReading()) { readingPairs.add(pair); if (LOG.isWarnEnabled() && !registration.isSimpleSourceType()) { LOG.warn(String.format(READ_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType())); } } if (registration.isWriting()) { writingPairs.add(pair); customSimpleTypes.add(pair.getSourceType()); if (LOG.isWarnEnabled() && !registration.isSimpleTargetType()) { LOG.warn(String.format(WRITE_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType())); } } }
/** * Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the * returned {@link Class} could be an assignable type to the given {@code requestedTargetType}. * * @param sourceType must not be {@literal null}. * @param requestedTargetType can be {@literal null}. * @return */ private Class<?> getCustomReadTarget(Class<?> sourceType, Class<?> requestedTargetType) { notNull(sourceType); if (requestedTargetType == null) { return null; } GenericConverter.ConvertiblePair lookupKey = new GenericConverter.ConvertiblePair(sourceType, requestedTargetType); CacheValue readTargetTypeValue = customReadTargetTypes.get(lookupKey); if (readTargetTypeValue != null) { return readTargetTypeValue.getType(); } readTargetTypeValue = CacheValue.of(getCustomTarget(sourceType, requestedTargetType, readingPairs)); CacheValue cacheValue = customReadTargetTypes.putIfAbsent(lookupKey, readTargetTypeValue); return cacheValue != null ? cacheValue.getType() : readTargetTypeValue.getType(); }
/** * Inspects the given {@link org.springframework.core.convert.converter.GenericConverter.ConvertiblePair} for ones * that have a source compatible type as source. Additionally checks assignability of the target type if one is * given. * * @param sourceType must not be {@literal null}. * @param requestedTargetType can be {@literal null}. * @param pairs must not be {@literal null}. * @return */ private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType, Iterable<GenericConverter.ConvertiblePair> pairs) { notNull(sourceType); notNull(pairs); for (GenericConverter.ConvertiblePair typePair : pairs) { if (typePair.getSourceType().isAssignableFrom(sourceType)) { Class<?> targetType = typePair.getTargetType(); if (requestedTargetType == null || targetType.isAssignableFrom(requestedTargetType)) { return targetType; } } } return null; }
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Assert.notNull(targetType,"The targetType to convert to cannot be null"); if (sourceType == null) { Assert.isTrue(source == null, "The source must be [null] if sourceType == [null]"); return handleResult(sourceType, targetType, convertNullSource(sourceType, targetType)); } if (source != null && !sourceType.getObjectType().isInstance(source)) { throw new IllegalArgumentException("The source to convert from must be an instance of " + sourceType + "; instead it was a " + source.getClass().getName()); } GenericConverter converter = getConverter(sourceType, targetType); if (converter != null) { Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType); return handleResult(sourceType, targetType, result); } return handleConverterNotFound(source, sourceType, targetType); }
/** * Hook method to lookup the converter for a given sourceType/targetType pair. * First queries this ConversionService's converter cache. * On a cache miss, then performs an exhaustive search for a matching converter. * If no converter matches, returns the default converter. * Subclasses may override. * @param sourceType the source type to convert from * @param targetType the target type to convert to * @return the generic converter that will perform the conversion, or {@code null} if * no suitable converter was found * @see #getDefaultConverter(TypeDescriptor, TypeDescriptor) */ protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType); GenericConverter converter = this.converterCache.get(key); if (converter != null) { return (converter != NO_MATCH ? converter : null); } converter = this.converters.find(sourceType, targetType); if (converter == null) { converter = getDefaultConverter(sourceType, targetType); } if (converter != null) { this.converterCache.put(key, converter); return converter; } this.converterCache.put(key, NO_MATCH); return null; }
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType, TypeDescriptor targetType, ConvertiblePair convertiblePair) { // Check specifically registered converters ConvertersForPair convertersForPair = this.converters.get(convertiblePair); if (convertersForPair != null) { GenericConverter converter = convertersForPair.getConverter(sourceType, targetType); if (converter != null) { return converter; } } // Check ConditionalGenericConverter that match all types for (GenericConverter globalConverter : this.globalConverters) { if (((ConditionalConverter)globalConverter).matches(sourceType, targetType)) { return globalConverter; } } return null; }
@Test public void shouldNotSupportNullConvertibleTypesFromNonConditionalGenericConverter() { GenericConversionService conversionService = new GenericConversionService(); GenericConverter converter = new GenericConverter() { @Override public Set<ConvertiblePair> getConvertibleTypes() { return null; } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { return null; } }; try { conversionService.addConverter(converter); fail("Did not throw"); } catch (IllegalStateException ex) { assertEquals("Only conditional converters may return null convertible types", ex.getMessage()); } }
/** * Register custom converters within given {@link GenericConversionService} * * @param conversionService must not be null */ public void registerConvertersIn(GenericConversionService conversionService) { Assert.notNull(conversionService); for (Object converter : converters) { if (converter instanceof Converter) { conversionService.addConverter((Converter<?, ?>) converter); } else if (converter instanceof ConverterFactory) { conversionService.addConverterFactory((ConverterFactory<?, ?>) converter); } else if (converter instanceof GenericConverter) { conversionService.addConverter((GenericConverter) converter); } else { throw new IllegalArgumentException("Given object '" + converter + "' expected to be a Converter, ConverterFactory or GenericeConverter!"); } } }
/** * Initialize default ConversionService * * @return */ private ConfigurableConversionService getDefaultConversionService() { if (this.defaultConversionService == null) { DefaultConversionService conversionService = new DefaultConversionService(); this.applicationContext.getAutowireCapableBeanFactory().autowireBean(this); for (Converter<?, ?> converter : this.converters) { conversionService.addConverter(converter); } for (GenericConverter genericConverter : this.genericConverters) { conversionService.addConverter(genericConverter); } this.defaultConversionService = conversionService; } return this.defaultConversionService; }
/** * Populates the given {@link GenericConversionService} with the convertes registered. * * @param conversionService */ public void registerConvertersIn(GenericConversionService conversionService) { for (Object converter : converters) { boolean added = false; if (converter instanceof Converter) { conversionService.addConverter((Converter<?, ?>) converter); added = true; } if (converter instanceof ConverterFactory) { conversionService.addConverterFactory((ConverterFactory<?, ?>) converter); added = true; } if (converter instanceof GenericConverter) { conversionService.addConverter((GenericConverter) converter); added = true; } if (!added) { throw new IllegalArgumentException( "Given set contains element that is neither Converter nor ConverterFactory!"); } } }
@Override @SuppressWarnings("unchecked") public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { AnnotationConverterKey converterKey = new AnnotationConverterKey(sourceType.getAnnotation(annotationType), sourceType.getObjectType()); GenericConverter converter = cachedPrinters.get(converterKey); if (converter == null) { Printer<?> printer = annotationFormatterFactory.getPrinter( converterKey.getAnnotation(), converterKey.getFieldType()); converter = new PrinterConverter(fieldType, printer, FormattingConversionService.this); cachedPrinters.put(converterKey, converter); } return converter.convert(source, sourceType, targetType); }
@Override @SuppressWarnings("unchecked") public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { AnnotationConverterKey converterKey = new AnnotationConverterKey(targetType.getAnnotation(annotationType), targetType.getObjectType()); GenericConverter converter = cachedParsers.get(converterKey); if (converter == null) { Parser<?> parser = annotationFormatterFactory.getParser( converterKey.getAnnotation(), converterKey.getFieldType()); converter = new ParserConverter(fieldType, parser, FormattingConversionService.this); cachedParsers.put(converterKey, converter); } return converter.convert(source, sourceType, targetType); }
@Override public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { Assert.notNull(targetType, "targetType to convert to cannot be null"); if (sourceType == null) { return true; } GenericConverter converter = getConverter(sourceType, targetType); return (converter != null); }
public void add(GenericConverter converter) { Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes(); if (convertibleTypes == null) { Assert.state(converter instanceof ConditionalConverter, "Only conditional converters may return null convertible types"); this.globalConverters.add(converter); } else { for (ConvertiblePair convertiblePair : convertibleTypes) { ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair); convertersForPair.add(converter); } } }
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { for (GenericConverter converter : this.converters) { if (!(converter instanceof ConditionalGenericConverter) || ((ConditionalGenericConverter) converter).matches(sourceType, targetType)) { return converter; } } return null; }
@Test public void testConvertiblePairEqualsAndHash() { GenericConverter.ConvertiblePair pair = new GenericConverter.ConvertiblePair(Number.class, String.class); GenericConverter.ConvertiblePair pairEqual = new GenericConverter.ConvertiblePair(Number.class, String.class); assertEquals(pair, pairEqual); assertEquals(pair.hashCode(), pairEqual.hashCode()); }
@Test public void testConvertiblePairDifferentEqualsAndHash() { GenericConverter.ConvertiblePair pair = new GenericConverter.ConvertiblePair(Number.class, String.class); GenericConverter.ConvertiblePair pairOpposite = new GenericConverter.ConvertiblePair(String.class, Number.class); assertFalse(pair.equals(pairOpposite)); assertFalse(pair.hashCode() == pairOpposite.hashCode()); }