Permalink
Browse files

#962 Add support for mapping Java 8 Streams

  • Loading branch information...
1 parent 48d7963 commit 6b07dda829788387abef3ff8574066553ad7b646 @filiphr filiphr committed on GitHub Jan 6, 2017
Showing with 4,691 additions and 71 deletions.
  1. +12 −1 core-common/src/main/java/org/mapstruct/IterableMapping.java
  2. +1 −0 documentation/pom.xml
  3. +67 −0 documentation/src/main/asciidoc/mapping-streams.asciidoc
  4. +2 −0 documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc
  5. +56 −65 processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
  6. +412 −0 processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java
  7. +62 −0 processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.java
  8. +23 −1 processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
  9. +8 −1 processor/src/main/java/org/mapstruct/ap/internal/model/common/TypeFactory.java
  10. +13 −1 processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java
  11. +33 −0 processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java
  12. +2 −2 processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java
  13. +36 −0 processor/src/main/java/org/mapstruct/ap/internal/util/JavaStreamConstants.java
  14. +210 −0 processor/src/main/resources/org/mapstruct/ap/internal/model/StreamMappingMethod.ftl
  15. +51 −0 processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.ftl
  16. +1 −0 processor/src/test/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactoryTest.java
  17. +1 −0 processor/src/test/java/org/mapstruct/ap/internal/model/common/DefaultConversionContextTest.java
  18. +23 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/Colour.java
  19. +103 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/Source.java
  20. +73 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/SourceTargetMapper.java
  21. +244 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/StreamMappingTest.java
  22. +66 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/StringHolder.java
  23. +29 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/StringHolderArrayList.java
  24. +105 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/Target.java
  25. +46 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/TestList.java
  26. +25 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/base/MyCustomException.java
  27. +138 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/base/Source.java
  28. +35 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/base/SourceElement.java
  29. +52 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamMapper.java
  30. +131 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/base/StreamsTest.java
  31. +142 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/base/Target.java
  32. +35 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/base/TargetElement.java
  33. +59 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/context/StreamContext.java
  34. +46 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/context/StreamWithContextMapper.java
  35. +64 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/context/StreamWithContextTest.java
  36. +190 −0 ...java/org/mapstruct/ap/test/java8stream/defaultimplementation/DefaultStreamImplementationTest.java
  37. +37 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/NoSetterMapper.java
  38. +37 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/NoSetterSource.java
  39. +61 −0 .../test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/NoSetterStreamMappingTest.java
  40. +34 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/NoSetterTarget.java
  41. +35 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/Source.java
  42. +39 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/SourceFoo.java
  43. +65 −0 ...sor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/SourceTargetMapper.java
  44. +34 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/Target.java
  45. +75 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/defaultimplementation/TargetFoo.java
  46. +43 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/EmptyStreamMappingMapper.java
  47. +38 −0 .../java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousListToStreamNoElementMappingFound.java
  48. +154 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamMappingTest.java
  49. +38 −0 .../java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamToListNoElementMappingFound.java
  50. +31 −0 ...r/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamToNonStreamMapper.java
  51. +27 −0 ...st/java/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamToPrimitivePropertyMapper.java
  52. +37 −0 ...ava/org/mapstruct/ap/test/java8stream/erroneous/ErroneousStreamToStreamNoElementMappingFound.java
  53. +34 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/Source.java
  54. +32 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/erroneous/Target.java
  55. +27 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/Bar.java
  56. +35 −0 ...ssor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ErroneousNonMappableStreamSource.java
  57. +35 −0 ...ssor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ErroneousNonMappableStreamTarget.java
  58. +31 −0 ...rc/test/java/org/mapstruct/ap/test/java8stream/forged/ErroneousStreamNonMappableStreamMapper.java
  59. +27 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/Foo.java
  60. +132 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/ForgedStreamMappingTest.java
  61. +53 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/Source.java
  62. +32 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/StreamMapper.java
  63. +34 −0 ...test/java/org/mapstruct/ap/test/java8stream/forged/StreamMapperNullValueMappingReturnDefault.java
  64. +53 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/forged/Target.java
  65. +34 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/streamtononiterable/Source.java
  66. +32 −0 ...essor/src/test/java/org/mapstruct/ap/test/java8stream/streamtononiterable/SourceTargetMapper.java
  67. +56 −0 ...st/java/org/mapstruct/ap/test/java8stream/streamtononiterable/StreamToNonIterableMappingTest.java
  68. +34 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/streamtononiterable/StringListMapper.java
  69. +32 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/streamtononiterable/Target.java
  70. +35 −0 ...st/java/org/mapstruct/ap/test/java8stream/wildcard/ErroneousIterableExtendsBoundTargetMapper.java
  71. +35 −0 ...test/java/org/mapstruct/ap/test/java8stream/wildcard/ErroneousIterableSuperBoundSourceMapper.java
  72. +35 −0 .../java/org/mapstruct/ap/test/java8stream/wildcard/ErroneousIterableTypeVarBoundMapperOnMapper.java
  73. +35 −0 .../java/org/mapstruct/ap/test/java8stream/wildcard/ErroneousIterableTypeVarBoundMapperOnMethod.java
  74. +49 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/ExtendsBoundSource.java
  75. +36 −0 ...ssor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/ExtendsBoundSourceTargetMapper.java
  76. +27 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Idea.java
  77. +27 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Plan.java
  78. +49 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Source.java
  79. +37 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/SourceSuperBoundTargetMapper.java
  80. +49 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/SuperBoundTarget.java
  81. +49 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/Target.java
  82. +134 −0 processor/src/test/java/org/mapstruct/ap/test/java8stream/wildcard/WildCardTest.java
@@ -28,11 +28,22 @@
import java.util.Date;
/**
- * Configures the mapping between two iterable types, e.g. {@code List<String>} and {@code List<Date>}.
+ * Configures the mapping between two iterable like types, e.g. {@code List<String>} and {@code List<Date>}.
+ *
*
* <p>Note: either @IterableMapping#dateFormat, @IterableMapping#resultType or @IterableMapping#qualifiedBy
* must be specified</p>
*
+ * Supported mappings are:
+ * <ul>
+ * <li>{@code Iterable<A>} to/from {@code Iterable<B>}/{@code Iterable<A>}</li>
+ * <li>{@code Iterable<A>} to/from {@code B[]}/{@code A[]}</li>
+ * <li>{@code Iterable<A>} to/from {@code Stream<B>}/{@code Stream<A>}</li>
+ * <li>{@code A[]} to/from {@code Stream<B>}/{@code Stream<A>}</li>
+ * <li>{@code A[]} to/from {@code B[]}</li>
+ * <li>{@code Stream<A>} to/from {@code Stream<B>}</li>
+ * </ul>
+ *
* @author Gunnar Morling
*/
@Target(ElementType.METHOD)
@@ -66,6 +66,7 @@
</dependencies>
<configuration>
<sourceHighlighter>coderay</sourceHighlighter>
+ <sourceDocumentName>mapstruct-reference-guide.asciidoc</sourceDocumentName>
<attributes>
<mapstructVersion>${project.version}</mapstructVersion>
<icons>font</icons>
@@ -0,0 +1,67 @@
+[[mapping-streams]]
+== Mapping Streams
+
+The mapping of `java.util.Stream` is done in a similar way as the mapping of collection types, i.e. by defining mapping
+methods with the required source and target types in a mapper interface.
+
+The generated code will contain the creation of a `Stream` from the provided `Iterable`/array or will collect the
+provided `Stream` into an `Iterable`/array. If a mapping method or an implicit conversion for the source and target
+element types exists, then this conversion will be done in `Stream#map()`. The following shows an example:
+
+.Mapper with stream mapping methods
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+@Mapper
+public interface CarMapper {
+
+ Set<String> integerStreamToStringSet(Stream<Integer> integers);
+
+ List<CarDto> carsToCarDtos(Stream<Car> cars);
+
+ CarDto carToCarDto(Car car);
+}
+----
+====
+
+The generated implementation of the `integerStreamToStringSet()` performs the conversion from `Integer` to `String` for
+each element, while the generated `carsToCarDtos()` method invokes the `carToCarDto()` method for each contained
+element as shown in the following:
+
+.Generated stream mapping methods
+====
+[source, java, linenums]
+[subs="verbatim,attributes"]
+----
+//GENERATED CODE
+@Override
+public Set<String> integerStreamToStringSet(Stream<Integer> integers) {
+ if ( integers == null ) {
+ return null;
+ }
+
+ return integers.stream().map( integer -> String.valueOf( integer ) )
+ .collect( Collectors.toCollection( HashSet<String>::new ) );
+}
+
+@Override
+public List<CarDto> carsToCarDtos(Stream<Car> cars) {
+ if ( cars == null ) {
+ return null;
+ }
+
+ return integers.stream().map( car -> carToCarDto( car ) )
+ .collect( Collectors.toCollection( ArrayList<CarDto>::new ) );
+}
+----
+====
+
+[WARNING]
+====
+If a mapping from a `Stream` to an `Iterable` or an array is performed, then the passed `Stream` will be consumed
+and it will no longer be possible to consume it.
+====
+
+The same implementation types as in <<implementation-types-for-collection-mappings>> are used for the creation of the
+collection when doing `Stream` to `Iterable` mapping.
@@ -1338,6 +1338,8 @@ When an iterable or map mapping method declares an interface type as return type
|`ConcurrentNavigableMap`|`ConcurrentSkipListMap`
|===
+include::mapping-streams.asciidoc[]
+
[[mapping-enum-types]]
== Mapping Values
@@ -18,17 +18,11 @@
*/
package org.mapstruct.ap.internal.model;
-import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.DIRECT;
-import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS;
-import static org.mapstruct.ap.internal.util.Collections.first;
-import static org.mapstruct.ap.internal.util.Collections.last;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.DeclaredType;
@@ -59,6 +53,11 @@
import org.mapstruct.ap.internal.util.ValueProvider;
import org.mapstruct.ap.internal.util.accessor.Accessor;
+import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.DIRECT;
+import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS;
+import static org.mapstruct.ap.internal.util.Collections.first;
+import static org.mapstruct.ap.internal.util.Collections.last;
+
/**
* Represents the mapping between a source and target property, e.g. from {@code String Source#foo} to
* {@code int Target#bar}. Name and type of source and target property can differ. If they have different types, the
@@ -262,6 +261,11 @@ public PropertyMapping build() {
else if ( sourceType.isMapType() && targetType.isMapType() ) {
assignment = forgeMapMapping( sourceType, targetType, rightHandSide, method.getExecutable() );
}
+ else if ( ( sourceType.isIterableType() && targetType.isStreamType() ) ||
+ ( sourceType.isStreamType() && targetType.isStreamType() ) ||
+ ( sourceType.isStreamType() && targetType.isIterableType() ) ) {
+ assignment = forgeStreamMapping( sourceType, targetType, rightHandSide, method.getExecutable() );
+ }
else {
assignment = forgeMapping( rightHandSide );
}
@@ -533,29 +537,26 @@ private String getSourcePresenceCheckerRef( SourceReference sourceReference ) {
return sourcePresenceChecker;
}
+ private Assignment forgeStreamMapping(Type sourceType, Type targetType, SourceRHS source,
+ ExecutableElement element) {
+
+ ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, element );
+
+ StreamMappingMethod.Builder builder = new StreamMappingMethod.Builder();
+ StreamMappingMethod streamMappingMethod = builder
+ .mappingContext( ctx )
+ .method( methodRef )
+ .selectionParameters( selectionParameters )
+ .callingContextTargetPropertyName( targetPropertyName )
+ .build();
+
+ return getForgedAssignment( source, methodRef, streamMappingMethod );
+ }
+
private Assignment forgeIterableMapping(Type sourceType, Type targetType, SourceRHS source,
ExecutableElement element) {
- Assignment assignment = null;
- String name = getName( sourceType, targetType );
- name = Strings.getSaveVariableName( name, ctx.getNamesOfMappingsToGenerate() );
-
- // copy mapper configuration from the source method, its the same mapper
- MapperConfiguration config = method.getMapperConfiguration();
- ForgedMethod methodRef = new ForgedMethod(
- name,
- sourceType,
- targetType,
- config,
- element,
- method.getContextParameters(),
- new ForgedMethodHistory( getForgedMethodHistory( source ),
- source.getSourceErrorMessagePart(),
- targetPropertyName,
- source.getSourceType(),
- targetType
- )
- );
+ ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, element );
IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder();
IterableMappingMethod iterableMappingMethod = builder
@@ -565,78 +566,68 @@ private Assignment forgeIterableMapping(Type sourceType, Type targetType, Source
.callingContextTargetPropertyName( targetPropertyName )
.build();
- if ( iterableMappingMethod != null ) {
- if ( !ctx.getMappingsToGenerate().contains( iterableMappingMethod ) ) {
- ctx.getMappingsToGenerate().add( iterableMappingMethod );
+ return getForgedAssignment( source, methodRef, iterableMappingMethod );
+ }
+
+ private Assignment getForgedAssignment(SourceRHS source, ForgedMethod methodRef,
+ MappingMethod mappingMethod) {
+ Assignment assignment = null;
+ if ( mappingMethod != null ) {
+ if ( !ctx.getMappingsToGenerate().contains( mappingMethod ) ) {
+ ctx.getMappingsToGenerate().add( mappingMethod );
}
else {
- String existingName = ctx.getExistingMappingMethod( iterableMappingMethod ).getName();
+ String existingName = ctx.getExistingMappingMethod( mappingMethod ).getName();
methodRef = new ForgedMethod( existingName, methodRef );
}
assignment = new MethodReference(
methodRef,
null,
- ParameterBinding.fromParameters( methodRef.getParameters() ) );
-
+ ParameterBinding.fromParameters( methodRef.getParameters() )
+ );
assignment.setAssignment( source );
-
- forgedMethods.addAll( iterableMappingMethod.getForgedMethods() );
+ forgedMethods.addAll( mappingMethod.getForgedMethods() );
}
return assignment;
}
- private Assignment forgeMapMapping(Type sourceType, Type targetType, SourceRHS source,
- ExecutableElement element) {
-
- Assignment assignment = null;
-
+ private ForgedMethod prepareForgedMethod(Type sourceType, Type targetType, SourceRHS source,
+ ExecutableElement element) {
String name = getName( sourceType, targetType );
name = Strings.getSaveVariableName( name, ctx.getNamesOfMappingsToGenerate() );
// copy mapper configuration from the source method, its the same mapper
MapperConfiguration config = method.getMapperConfiguration();
- ForgedMethod methodRef =
- new ForgedMethod(
- name,
- sourceType,
- targetType,
- config,
- element,
- method.getContextParameters(),
+ return new ForgedMethod(
+ name,
+ sourceType,
+ targetType,
+ config,
+ element,
+ method.getContextParameters(),
new ForgedMethodHistory( getForgedMethodHistory( source ),
source.getSourceErrorMessagePart(),
targetPropertyName,
source.getSourceType(),
targetType
)
);
+ }
+
+ private Assignment forgeMapMapping(Type sourceType, Type targetType, SourceRHS source,
+ ExecutableElement element) {
+
+ ForgedMethod methodRef = prepareForgedMethod( sourceType, targetType, source, element );
MapMappingMethod.Builder builder = new MapMappingMethod.Builder();
MapMappingMethod mapMappingMethod = builder
.mappingContext( ctx )
.method( methodRef )
.build();
- if ( mapMappingMethod != null ) {
- if ( !ctx.getMappingsToGenerate().contains( mapMappingMethod ) ) {
- ctx.getMappingsToGenerate().add( mapMappingMethod );
- }
- else {
- String existingName = ctx.getExistingMappingMethod( mapMappingMethod ).getName();
- methodRef = new ForgedMethod( existingName, methodRef );
- }
- assignment = new MethodReference(
- methodRef,
- null,
- ParameterBinding.fromParameters( methodRef.getParameters() ) );
- assignment.setAssignment( source );
-
- forgedMethods.addAll( mapMappingMethod.getForgedMethods() );
- }
-
- return assignment;
+ return getForgedAssignment( source, methodRef, mapMappingMethod );
}
private Assignment forgeMapping(SourceRHS sourceRHS) {
Oops, something went wrong.

0 comments on commit 6b07dda

Please sign in to comment.