Skip to content

DATACMNS-1034 - Introduced API to easily register bidirectional, type-based converters. #209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from

Conversation

odrotbohm
Copy link
Member

@odrotbohm odrotbohm commented Apr 20, 2017

Introduced ConverterBuilder which exposes API to register Spring GenericConverters for the use with a store module's CustomConversions. This allows simple registration of such converters using Java 8 lambdas.

ConverterAware converters = ConverterBuilder
  .reading(String.class, Long.class, it -> Long.valueOf(it))
  .andWriting(it -> Object::toString);

The setup can also be done from the reading side which would just need the method invocations inverted. The resulting ConverterAware will expose the registered converters so that they can be easily handed to a CustomConversions instance. Partial creation of either reading or writing converters is possible, too with the returned instance then only exposing one of the two converters.

…va 8 lambdas.

Introduced ConverterBuilder which exposes API to register Spring GenericConverters for the use with a store module's CustomConversions. This allows simple registration of such converters using Java 8 lambdas.

ConverterAware converters = ConverterBuilder.reading(String.class, Long.class,
  it -> Long.valueOf(it)).andWriting(it -> Object::toString);

The setup can also be done from the reading side which would just need the method invocations inverted. The resulting ConverterAware will expose the registered converters so that they can be easily handed to a CustomConversions instance. Partial creation of either reading or writing converters is possible, too with the returned instance then only exposing one of the two converters.
readme.md Outdated
@@ -31,7 +31,7 @@ This README as well as the [reference documentation](http://docs.spring.io/sprin

The main project [website](http://projects.spring.io/spring-data/) contains links to basic project information such as source code, JavaDocs, Issue tracking, etc.

For more detailed questions, please refer to [spring-data on stackoverflow](http://stackoverflow.com/questions/tagged/spring-data). If you are new to Spring as well as to Spring Data, look for information about [Spring projects](https://spring.io/projects).
For more detailed questions, please refer to [spring-data on stackoverflow](http://stackoverflow.com/questions/tagged/spring-data). If you are new to Spring as well as to Spring Data, look for information about [Spring projects](https://spring.io/projects).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the place to suggest moving to asciidoctor?

* @return
*/
static <S, T> ReadingConverterBuilder<T, S> reading(Class<T> source, Class<S> target,
Function<? super T, ? extends S> function) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class<T> source looks backward (S is source, right?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I flipped those.

* @since 2.0
*/
interface ReadingConverterBuilder<T, S> extends ConverterBuilder, ReadingConverterAware {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be <S, T>?


/**
* A {@link ConverterBuilder} ware of both a reading and writing converter.
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aware

@Override
public Set<GenericConverter> getConverters() {
return Optionals.toStream(getOptionalReadingConverter(), getOptionalWritingConverter()).collect(Collectors.toSet());
}
Copy link
Contributor

@gregturn gregturn Apr 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's just me, but I almost prefer reading this Java 8 stream as..

return Optionals
    .toStream(...)
    .collect(...)

Even if this results in an extra blank line to comply. ¯\_(ツ)_/¯

private static class Writing<S, T> extends ConfigurableGenericConverter<S, T> {

public Writing(ConvertiblePair convertiblePair, Function<? super S, ? extends T> function) {
super(convertiblePair, function);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is public visibility modifier required?

private static class Reading<S, T> extends ConfigurableGenericConverter<S, T> {

public Reading(ConvertiblePair convertiblePair, Function<? super S, ? extends T> function) {
super(convertiblePair, function);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Public required?


/**
* Creates a {@link PagedResources} with an empt collection {@link EmbeddedWrapper} for the given domain type.
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty

}

/**
* Creates a {@link PagedResources} with an empt collection {@link EmbeddedWrapper} for the given domain type.
*
* @param page must not be {@literal null}, content must be empty.
* @param type must not be {@literal null}.
* @param link can be {@literal null}.
* @param link must not be {@literal null}.
* @return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually the place for specify this, given the assertion isn't here?

private static void assertOnlyConverter(ConverterBuilder builder, Supplier<GenericConverter> supplier) {
assertThat(builder.getConverters()).containsExactly(supplier.get());
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case is simply beautiful.

@odrotbohm
Copy link
Member Author

Somehow some bogus commits slipped into this one. Removing.

Made nested API interfaces public so that they can actually be used. CustomConversions now considers ConverterAware and treats them appropriately for ConvertiblePair registration as well as during the registration in the ConversionService. Tweaked parameter types in CustomConversions to rather accept a Collection<?> for the converters (previously List<?>). Also, registerConverterIn(…) now takes a ConverterRegistry over a GenericConversionService. Polished Javadoc and non-null assertions.
@odrotbohm odrotbohm force-pushed the issue/DATACMNS-1034 branch from 3f73bc7 to 3436c63 Compare April 21, 2017 15:54
@odrotbohm
Copy link
Member Author

Tweaked code according to @gregturn's suggestions.

Copy link
Member

@mp911de mp911de left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments about minor issues and thoughts. LGTM, besides some typos.

* @author Oliver Gierke
* @since 2.0
*/
public interface ConverterAware extends ConverterBuilder, ReadingConverterAware, WritingConverterAware {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConverterBuilder implies mutability whereas ConverterAware is the final result of the builder. It reads a bit strange.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming suggestions? ;)

* @author Oliver Gierke
* @since 2.0
*/
public interface WritingConverterAware {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public not required. All nested types of in an interface inherit public visibility.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I though at some point, Eclipse presented them as package protected so I thought the default of public only applied to methods. Rolling this back then.

conversionService.addConverter(Converter.class.cast(it));
added = true;
}
boolean added = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found it easier to read (understand) multiple return's instead of setting the added state:

if (candidate instanceof Converter) {
	conversionService.addConverter(Converter.class.cast(candidate));
	return;
}

so throw new IllegalArgumentException(…) would be the last and unconditional statement in the method body. Does this make sense?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a quick glance, yes. Not at a second. The approach used allows the instance to implement multiple interfaces and thus obtains all ConvertibleType instances that implementing multiple ones would expose. An early return would stop that process after the first step.

Not sure this is still needed as I am unsure how likely this is in the first place. I just kept it around to preserve behavioural consistency to the old CustomConversions. We could think about removing that functionality, though.


/**
* API to easily set up {@link GenericConverter} instances using Java 8 lambdas, mostly in bidirectional fashion for
* easy registration as custom type converters of the SPring Data mapping subsystem. The registration starts either with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/SPring/Spring

/**
* API to easily set up {@link GenericConverter} instances using Java 8 lambdas, mostly in bidirectional fashion for
* easy registration as custom type converters of the SPring Data mapping subsystem. The registration starts either with
* the definition of a reading or writing converter that can then be completed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add dot at the end.


/**
* Creates a new {@link WritingConverterBuilder} to produce a converter to write values of the given source (the
* domain type type) into the given target (the store type).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's 2x type in domain type type

Typos in ConverterBuilder. Removed obsolete visibility modifiers for nested types in ConverterBuilder.
odrotbohm added a commit that referenced this pull request May 5, 2017
…va 8 lambdas.

Introduced ConverterBuilder which exposes API to register Spring GenericConverters for the use with a store module's CustomConversions. This allows simple registration of such converters using Java 8 lambdas.

ConverterAware converters = ConverterBuilder.reading(String.class, Long.class,
  it -> Long.valueOf(it)).andWriting(it -> Object::toString);

The setup can also be done from the reading side which would just need the method invocations inverted. The resulting ConverterAware will expose the registered converters so that they can be easily handed to a CustomConversions instance. Partial creation of either reading or writing converters is possible, too with the returned instance then only exposing one of the two converters.

CustomConversions now considers ConverterAware and treats them appropriately for ConvertiblePair registration as well as during the registration in the ConversionService. Tweaked parameter types in CustomConversions to rather accept a Collection<?> for the converters (previously List<?>). Also, registerConverterIn(…) now takes a ConverterRegistry over a GenericConversionService. Polished Javadoc and non-null assertions.

Original pull request: #209.
@odrotbohm odrotbohm closed this May 5, 2017
@odrotbohm odrotbohm deleted the issue/DATACMNS-1034 branch May 5, 2017 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants