Home / Class/ BindConverter Class — spring-boot Architecture

BindConverter Class — spring-boot Architecture

Architecture documentation for the BindConverter class in BindConverter.java from the spring-boot codebase.

Entity Profile

Relationship Graph

Source Code

core/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/BindConverter.java lines 58–255

final class BindConverter {

	private static @Nullable BindConverter sharedInstance;

	private final List<ConversionService> delegates;

	private BindConverter(@Nullable List<ConversionService> conversionServices,
			@Nullable Consumer<PropertyEditorRegistry> propertyEditorInitializer) {
		List<ConversionService> delegates = new ArrayList<>();
		delegates.add(new TypeConverterConversionService(propertyEditorInitializer));
		boolean hasApplication = false;
		if (!CollectionUtils.isEmpty(conversionServices)) {
			for (ConversionService conversionService : conversionServices) {
				delegates.add(conversionService);
				hasApplication = hasApplication || conversionService instanceof ApplicationConversionService;
			}
		}
		if (!hasApplication) {
			delegates.add(ApplicationConversionService.getSharedInstance());
		}
		this.delegates = Collections.unmodifiableList(delegates);
	}

	boolean canConvert(@Nullable Object source, ResolvableType targetType, Annotation... targetAnnotations) {
		return canConvert(TypeDescriptor.forObject(source),
				new ResolvableTypeDescriptor(targetType, targetAnnotations));
	}

	private boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
		for (ConversionService service : this.delegates) {
			if (service.canConvert(sourceType, targetType)) {
				return true;
			}
		}
		return false;
	}

	<T> @Nullable T convert(@Nullable Object source, Bindable<T> target) {
		return convert(source, target.getType(), target.getAnnotations());
	}

	@SuppressWarnings("unchecked")
	<T> @Nullable T convert(@Nullable Object source, ResolvableType targetType, Annotation... targetAnnotations) {
		TypeDescriptor sourceType = (source != null) ? TypeDescriptor.forObject(source) : null;
		return (T) convert(source, sourceType, new ResolvableTypeDescriptor(targetType, targetAnnotations));
	}

	private @Nullable Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType,
			TypeDescriptor targetType) {
		ConversionException failure = null;
		for (ConversionService delegate : this.delegates) {
			try {
				if (delegate.canConvert(sourceType, targetType)) {
					return delegate.convert(source, sourceType, targetType);
				}
			}
			catch (ConversionException ex) {
				if (failure == null && ex instanceof ConversionFailedException) {
					failure = ex;
				}
			}
		}
		if (source == null) {
			return null;
		}
		throw (failure != null) ? failure : new ConverterNotFoundException(sourceType, targetType);
	}

	static BindConverter get(@Nullable List<ConversionService> conversionServices,
			@Nullable Consumer<PropertyEditorRegistry> propertyEditorInitializer) {
		boolean sharedApplicationConversionService = (conversionServices == null) || (conversionServices.size() == 1
				&& conversionServices.get(0) == ApplicationConversionService.getSharedInstance());
		if (propertyEditorInitializer == null && sharedApplicationConversionService) {
			return getSharedInstance();
		}
		return new BindConverter(conversionServices, propertyEditorInitializer);
	}

	private static BindConverter getSharedInstance() {
		if (sharedInstance == null) {
			sharedInstance = new BindConverter(null, null);
		}
		return sharedInstance;
	}

	/**
	 * A {@link TypeDescriptor} backed by a {@link ResolvableType}.
	 */
	private static class ResolvableTypeDescriptor extends TypeDescriptor {

		ResolvableTypeDescriptor(ResolvableType resolvableType, Annotation[] annotations) {
			super(resolvableType, null, annotations);
		}

	}

	/**
	 * A {@link ConversionService} implementation that delegates to a
	 * {@link SimpleTypeConverter}. Allows {@link PropertyEditor} based conversion for
	 * simple types, arrays and collections.
	 */
	private static class TypeConverterConversionService extends GenericConversionService {

		TypeConverterConversionService(@Nullable Consumer<PropertyEditorRegistry> initializer) {
			ApplicationConversionService.addDelimitedStringConverters(this);
			addConverter(new TypeConverterConverter(initializer));
		}

		@Override
		public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
			// Prefer conversion service to handle things like String to char[].
			if (isPrimitiveArray(targetType)) {
				return false;
			}
			return super.canConvert(sourceType, targetType);
		}

		private static boolean isPrimitiveArray(TypeDescriptor targetType) {
			if (!targetType.isArray()) {
				return false;
			}
			TypeDescriptor elementTypeDescriptor = targetType.getElementTypeDescriptor();
			Assert.state(elementTypeDescriptor != null, "'elementTypeDescriptor' must not be null");
			return elementTypeDescriptor.isPrimitive();
		}

	}

	/**
	 * {@link ConditionalGenericConverter} that delegates to {@link SimpleTypeConverter}.
	 */
	private static class TypeConverterConverter implements ConditionalGenericConverter {

		private static final Set<Class<?>> EXCLUDED_EDITORS;
		static {
			Set<Class<?>> excluded = new HashSet<>();
			excluded.add(CustomNumberEditor.class);
			excluded.add(CustomBooleanEditor.class);
			excluded.add(FileEditor.class);
			EXCLUDED_EDITORS = Collections.unmodifiableSet(excluded);
		}

		private final @Nullable Consumer<PropertyEditorRegistry> initializer;

		// SimpleTypeConverter is not thread-safe to use for conversion but we can use it
		// in a thread-safe way to check if conversion is possible.
		private final SimpleTypeConverter matchesOnlyTypeConverter;

		TypeConverterConverter(@Nullable Consumer<PropertyEditorRegistry> initializer) {
			this.initializer = initializer;
			this.matchesOnlyTypeConverter = createTypeConverter();
		}

		@Override
		public Set<ConvertiblePair> getConvertibleTypes() {
			return Set.of(new ConvertiblePair(String.class, Object.class),
					new ConvertiblePair(String.class, Resource[].class),
					new ConvertiblePair(String.class, Collection.class));
		}

		@Override
		public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
			Class<?> type = targetType.getType();
			if (type == null || type == Object.class || Map.class.isAssignableFrom(type)) {
				return false;
			}
			if (Collection.class.isAssignableFrom(type)) {
				TypeDescriptor elementType = targetType.getElementTypeDescriptor();
				if (elementType == null || (!Resource.class.isAssignableFrom(elementType.getType()))) {
					return false;
				}
			}
			PropertyEditor editor = this.matchesOnlyTypeConverter.getDefaultEditor(type);
			if (editor == null) {
				editor = this.matchesOnlyTypeConverter.findCustomEditor(type, null);
			}
			if (editor == null && String.class != type) {
				editor = BeanUtils.findEditorByConvention(type);
			}
			return (editor != null && !EXCLUDED_EDITORS.contains(editor.getClass()));
		}

		@Override
		public @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
			return createTypeConverter().convertIfNecessary(source, targetType.getType(), targetType);
		}

		private SimpleTypeConverter createTypeConverter() {
			SimpleTypeConverter typeConverter = new SimpleTypeConverter();
			if (this.initializer != null) {
				this.initializer.accept(typeConverter);
			}
			return typeConverter;
		}

	}

}

Domain

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free