mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
* Add new @Builder annotation for defining a build method * When there are multiple build methods look for a method named `build` and if found use it * If @Builder is defined than look for a build method with the defined method * When a type has multiple builder creation methods throw an exception and don't use the builder Defaulting to a method named `build` will make sure that a correct method is selected for: * FreeBuilder - it has two methods: `build` and `buildPartial` * Protobuf - it has three methods: `getDefaultInstanceForType`, `build` and `buildPartial`
This commit is contained in:
parent
62ffa3fa43
commit
ef270caecb
@ -97,4 +97,23 @@ public @interface BeanMapping {
|
||||
* @since 1.3
|
||||
*/
|
||||
String[] ignoreUnmappedSourceProperties() default {};
|
||||
|
||||
/**
|
||||
* The information that should be used for the builder mappings. This can be used to define custom build methods
|
||||
* for the builder strategy that one uses.
|
||||
*
|
||||
* If no builder is defined the builder given via {@link MapperConfig#builder()} or {@link Mapper#builder()}
|
||||
* will be applied.
|
||||
* <p>
|
||||
* NOTE: In case no builder is defined here, in {@link Mapper} or {@link MapperConfig} and there is a single
|
||||
* build method, then that method would be used.
|
||||
* <p>
|
||||
* If the builder is defined and there is a single method that does not match the name of the finisher than
|
||||
* a compile error will occurs
|
||||
*
|
||||
* @return the builder information for the method level
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
Builder builder() default @Builder;
|
||||
}
|
||||
|
45
core-common/src/main/java/org/mapstruct/Builder.java
Normal file
45
core-common/src/main/java/org/mapstruct/Builder.java
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.mapstruct.util.Experimental;
|
||||
|
||||
/**
|
||||
* Configuration of builders, e.g. the name of the final build method.
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({})
|
||||
@Experimental
|
||||
public @interface Builder {
|
||||
|
||||
/**
|
||||
* The name of the build method that needs to be invoked on the builder to create the type being build
|
||||
*
|
||||
* @return the method that needs to tbe invoked on the builder
|
||||
*/
|
||||
String buildMethod() default "build";
|
||||
}
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.mapstruct;
|
||||
|
||||
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@ -27,6 +25,8 @@ import java.lang.annotation.Target;
|
||||
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
|
||||
|
||||
/**
|
||||
* Marks an interface or abstract class as a mapper and activates the generation of a implementation of that type via
|
||||
* MapStruct.
|
||||
@ -200,4 +200,22 @@ public @interface Mapper {
|
||||
*/
|
||||
boolean disableSubMappingMethodsGeneration() default false;
|
||||
|
||||
/**
|
||||
* The information that should be used for the builder mappings. This can be used to define custom build methods
|
||||
* for the builder strategy that one uses.
|
||||
*
|
||||
* If no builder is defined the builder given via {@link MapperConfig#builder()} will be applied.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: In case no builder is defined here, in {@link BeanMapping} or {@link MapperConfig} and there is a single
|
||||
* build method, then that method would be used.
|
||||
* <p>
|
||||
* If the builder is defined and there is a single method that does not match the name of the finisher than
|
||||
* a compile error will occurs
|
||||
*
|
||||
* @return the builder information
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
Builder builder() default @Builder;
|
||||
}
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.mapstruct;
|
||||
|
||||
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@ -27,6 +25,8 @@ import java.lang.annotation.Target;
|
||||
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
|
||||
|
||||
/**
|
||||
* Marks a class or interface as configuration source for generated mappers. This allows to share common configurations
|
||||
* between several mapper classes.
|
||||
@ -186,4 +186,24 @@ public @interface MapperConfig {
|
||||
* @since 1.2
|
||||
*/
|
||||
boolean disableSubMappingMethodsGeneration() default false;
|
||||
|
||||
/**
|
||||
* The information that should be used for the builder mappings. This can be used to define custom build methods
|
||||
* for the builder strategy that one uses.
|
||||
*
|
||||
* <p>
|
||||
* Can be overridden by {@link MapperConfig#builder()}.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: In case no builder is defined here, in {@link BeanMapping} or {@link Mapper} and there is a single
|
||||
* build method, then that method would be used.
|
||||
* <p>
|
||||
* If the builder is defined and there is a single method that does not match the name of the finisher than
|
||||
* a compile error will occurs
|
||||
*
|
||||
* @return the builder information
|
||||
*
|
||||
* @since 1.3
|
||||
*/
|
||||
Builder builder() default @Builder;
|
||||
}
|
||||
|
@ -639,6 +639,12 @@ The default implementation of the `BuilderProvider` assumes the following:
|
||||
So for example `Person` has a public static method that returns `PersonBuilder`.
|
||||
* The builder type has a parameterless public method (build method) that returns the type being build
|
||||
In our example `PersonBuilder` has a method returning `Person`.
|
||||
* In case there are multiple build methods, MapStruct will look for a method called `build` if such methods exists
|
||||
than this would be used, otherwise a compilation error would be created.
|
||||
* A specific build method can be defined by using `@Builder` within: `@BeanMapping`, `@Mapper` or `@MapperConfig`
|
||||
* In case there are multiple builder creation methods that satisfy the above conditions then a `MoreThanOneBuilderCreationMethodException`
|
||||
will be thrown from the `DefaultBuilderProvider` SPI.
|
||||
In case of a `MoreThanOneBuilderCreationMethodException` MapStruct will write a warning in the compilation and not use any builder.
|
||||
|
||||
If such type is found then MapStruct will use that type to perform the mapping to (i.e. it will look for setters into that type).
|
||||
To finish the mapping MapStruct generates code that will invoke the build method of the builder.
|
||||
|
@ -287,7 +287,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MethodReference.forMethodCall( builderType.getBuildMethod() );
|
||||
return BuilderFinisherMethodResolver.getBuilderFinisherMethod( method, builderType, ctx );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||
import org.mapstruct.ap.internal.model.source.BeanMapping;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
||||
import org.mapstruct.ap.internal.util.MapperConfiguration;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class BuilderFinisherMethodResolver {
|
||||
|
||||
private static final String DEFAULT_BUILD_METHOD_NAME = "build";
|
||||
|
||||
private BuilderFinisherMethodResolver() {
|
||||
}
|
||||
|
||||
public static MethodReference getBuilderFinisherMethod(Method method, BuilderType builderType,
|
||||
MappingBuilderContext ctx) {
|
||||
Collection<ExecutableElement> buildMethods = builderType.getBuildMethods();
|
||||
if ( buildMethods.isEmpty() ) {
|
||||
//If we reach this method this should never happen
|
||||
return null;
|
||||
}
|
||||
|
||||
BuilderPrism builderMapping = builderMappingPrism( method, ctx );
|
||||
if ( builderMapping == null && buildMethods.size() == 1 ) {
|
||||
return MethodReference.forMethodCall( first( buildMethods ).getSimpleName().toString() );
|
||||
}
|
||||
else {
|
||||
String buildMethodPattern = DEFAULT_BUILD_METHOD_NAME;
|
||||
if ( builderMapping != null ) {
|
||||
buildMethodPattern = builderMapping.buildMethod();
|
||||
}
|
||||
for ( ExecutableElement buildMethod : buildMethods ) {
|
||||
String methodName = buildMethod.getSimpleName().toString();
|
||||
if ( methodName.matches( buildMethodPattern ) ) {
|
||||
return MethodReference.forMethodCall( methodName );
|
||||
}
|
||||
}
|
||||
|
||||
if ( builderMapping == null ) {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
Message.BUILDER_NO_BUILD_METHOD_FOUND_DEFAULT,
|
||||
buildMethodPattern,
|
||||
builderType.getBuilder(),
|
||||
builderType.getBuildingType(),
|
||||
Strings.join( buildMethods, ", " )
|
||||
);
|
||||
}
|
||||
else {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
builderMapping.mirror,
|
||||
Message.BUILDER_NO_BUILD_METHOD_FOUND,
|
||||
buildMethodPattern,
|
||||
builderType.getBuilder(),
|
||||
builderType.getBuildingType(),
|
||||
Strings.join( buildMethods, ", " )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BuilderPrism builderMappingPrism(Method method, MappingBuilderContext ctx) {
|
||||
BeanMapping beanMapping = method.getMappingOptions().getBeanMapping();
|
||||
if ( beanMapping != null && beanMapping.getBuilder() != null ) {
|
||||
return beanMapping.getBuilder();
|
||||
}
|
||||
return MapperConfiguration.getInstanceOn( ctx.getMapperTypeElement() ).getBuilderPrism();
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model.common;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Types;
|
||||
@ -33,20 +34,20 @@ public class BuilderType {
|
||||
private final Type owningType;
|
||||
private final Type buildingType;
|
||||
private final ExecutableElement builderCreationMethod;
|
||||
private final ExecutableElement buildMethod;
|
||||
private final Collection<ExecutableElement> buildMethods;
|
||||
|
||||
private BuilderType(
|
||||
Type builder,
|
||||
Type owningType,
|
||||
Type buildingType,
|
||||
ExecutableElement builderCreationMethod,
|
||||
ExecutableElement buildMethod
|
||||
Collection<ExecutableElement> buildMethods
|
||||
) {
|
||||
this.builder = builder;
|
||||
this.owningType = owningType;
|
||||
this.buildingType = buildingType;
|
||||
this.builderCreationMethod = builderCreationMethod;
|
||||
this.buildMethod = buildMethod;
|
||||
this.buildMethods = buildMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,18 +88,17 @@ public class BuilderType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the method that needs to be invoked on the builder to create the type being built.
|
||||
*
|
||||
* @return the name of the method that needs to be invoked on the type that is being built
|
||||
* The build methods that can be invoked to create the type being built.
|
||||
* @return the build methods that can be invoked to create the type being built
|
||||
*/
|
||||
public String getBuildMethod() {
|
||||
return buildMethod.getSimpleName().toString();
|
||||
public Collection<ExecutableElement> getBuildMethods() {
|
||||
return buildMethods;
|
||||
}
|
||||
|
||||
public BuilderInfo asBuilderInfo() {
|
||||
return new BuilderInfo.Builder()
|
||||
.builderCreationMethod( this.builderCreationMethod )
|
||||
.buildMethod( this.buildMethod )
|
||||
.buildMethod( this.buildMethods )
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -107,11 +107,6 @@ public class BuilderType {
|
||||
if ( builderInfo == null ) {
|
||||
return null;
|
||||
}
|
||||
ExecutableElement buildMethod = builderInfo.getBuildMethod();
|
||||
if ( !typeUtils.isAssignable( buildMethod.getReturnType(), typeToBuild.getTypeMirror() ) ) {
|
||||
//TODO throw error
|
||||
throw new IllegalArgumentException( "Build return type is not assignable" );
|
||||
}
|
||||
|
||||
Type builder = typeFactory.getType( builderInfo.getBuilderCreationMethod().getReturnType() );
|
||||
ExecutableElement builderCreationMethod = builderInfo.getBuilderCreationMethod();
|
||||
@ -133,7 +128,7 @@ public class BuilderType {
|
||||
owner,
|
||||
typeToBuild,
|
||||
builderCreationMethod,
|
||||
buildMethod
|
||||
builderInfo.getBuildMethods()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -55,11 +55,16 @@ import javax.lang.model.util.Types;
|
||||
|
||||
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
|
||||
import org.mapstruct.ap.internal.util.Collections;
|
||||
import org.mapstruct.ap.internal.util.Extractor;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.JavaStreamConstants;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.RoundContext;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
|
||||
import org.mapstruct.ap.spi.BuilderInfo;
|
||||
import org.mapstruct.ap.spi.MoreThanOneBuilderCreationMethodException;
|
||||
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
|
||||
|
||||
import static org.mapstruct.ap.internal.model.common.ImplementationType.withDefaultConstructor;
|
||||
@ -73,8 +78,17 @@ import static org.mapstruct.ap.internal.model.common.ImplementationType.withLoad
|
||||
*/
|
||||
public class TypeFactory {
|
||||
|
||||
private static final Extractor<BuilderInfo, String> BUILDER_INFO_CREATION_METHOD_EXTRACTOR =
|
||||
new Extractor<BuilderInfo, String>() {
|
||||
@Override
|
||||
public String apply(BuilderInfo builderInfo) {
|
||||
return builderInfo.getBuilderCreationMethod().toString();
|
||||
}
|
||||
};
|
||||
|
||||
private final Elements elementUtils;
|
||||
private final Types typeUtils;
|
||||
private final FormattingMessager messager;
|
||||
private final RoundContext roundContext;
|
||||
|
||||
private final TypeMirror iterableType;
|
||||
@ -85,9 +99,10 @@ public class TypeFactory {
|
||||
private final Map<String, ImplementationType> implementationTypes = new HashMap<String, ImplementationType>();
|
||||
private final Map<String, String> importedQualifiedTypesBySimpleName = new HashMap<String, String>();
|
||||
|
||||
public TypeFactory(Elements elementUtils, Types typeUtils, RoundContext roundContext) {
|
||||
public TypeFactory(Elements elementUtils, Types typeUtils, FormattingMessager messager, RoundContext roundContext) {
|
||||
this.elementUtils = elementUtils;
|
||||
this.typeUtils = typeUtils;
|
||||
this.messager = messager;
|
||||
this.roundContext = roundContext;
|
||||
|
||||
iterableType = typeUtils.erasure( elementUtils.getTypeElement( Iterable.class.getCanonicalName() ).asType() );
|
||||
@ -502,9 +517,21 @@ public class TypeFactory {
|
||||
}
|
||||
|
||||
private BuilderInfo findBuilder(TypeMirror type) {
|
||||
return roundContext.getAnnotationProcessorContext()
|
||||
.getBuilderProvider()
|
||||
.findBuilderInfo( type, elementUtils, typeUtils );
|
||||
try {
|
||||
return roundContext.getAnnotationProcessorContext()
|
||||
.getBuilderProvider()
|
||||
.findBuilderInfo( type, elementUtils, typeUtils );
|
||||
}
|
||||
catch ( MoreThanOneBuilderCreationMethodException ex ) {
|
||||
messager.printMessage(
|
||||
typeUtils.asElement( type ),
|
||||
Message.BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD,
|
||||
type,
|
||||
Strings.join( ex.getBuilderInfo(), ", ", BUILDER_INFO_CREATION_METHOD_EXTRACTOR )
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeMirror getComponentType(TypeMirror mirror) {
|
||||
|
@ -25,6 +25,7 @@ import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
import org.mapstruct.ap.internal.prism.BeanMappingPrism;
|
||||
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
||||
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism;
|
||||
import org.mapstruct.ap.internal.prism.ReportingPolicyPrism;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
@ -42,6 +43,7 @@ public class BeanMapping {
|
||||
private final ReportingPolicyPrism reportingPolicy;
|
||||
private final boolean ignoreByDefault;
|
||||
private final List<String> ignoreUnmappedSourceProperties;
|
||||
private final BuilderPrism builder;
|
||||
|
||||
/**
|
||||
* creates a mapping for inheritance. Will set ignoreByDefault to false.
|
||||
@ -55,7 +57,8 @@ public class BeanMapping {
|
||||
map.nullValueMappingStrategy,
|
||||
map.reportingPolicy,
|
||||
false,
|
||||
map.ignoreUnmappedSourceProperties
|
||||
map.ignoreUnmappedSourceProperties,
|
||||
map.builder
|
||||
);
|
||||
}
|
||||
|
||||
@ -74,9 +77,15 @@ public class BeanMapping {
|
||||
: NullValueMappingStrategyPrism.valueOf( beanMapping.nullValueMappingStrategy() );
|
||||
|
||||
boolean ignoreByDefault = beanMapping.ignoreByDefault();
|
||||
BuilderPrism builderMapping = null;
|
||||
if ( beanMapping.values.builder() != null ) {
|
||||
builderMapping = beanMapping.builder();
|
||||
}
|
||||
|
||||
if ( !resultTypeIsDefined && beanMapping.qualifiedBy().isEmpty() && beanMapping.qualifiedByName().isEmpty()
|
||||
&& beanMapping.ignoreUnmappedSourceProperties().isEmpty()
|
||||
&& ( nullValueMappingStrategy == null ) && !ignoreByDefault ) {
|
||||
&& ( nullValueMappingStrategy == null ) && !ignoreByDefault
|
||||
&& builderMapping == null ) {
|
||||
|
||||
messager.printMessage( method, Message.BEANMAPPING_NO_ELEMENTS );
|
||||
}
|
||||
@ -94,7 +103,8 @@ public class BeanMapping {
|
||||
nullValueMappingStrategy,
|
||||
null,
|
||||
ignoreByDefault,
|
||||
beanMapping.ignoreUnmappedSourceProperties()
|
||||
beanMapping.ignoreUnmappedSourceProperties(),
|
||||
builderMapping
|
||||
);
|
||||
}
|
||||
|
||||
@ -105,17 +115,18 @@ public class BeanMapping {
|
||||
* @return bean mapping that needs to be used for Mappings
|
||||
*/
|
||||
public static BeanMapping forForgedMethods() {
|
||||
return new BeanMapping( null, null, ReportingPolicyPrism.IGNORE, false, Collections.<String>emptyList() );
|
||||
return new BeanMapping( null, null, ReportingPolicyPrism.IGNORE, false, Collections.<String>emptyList(), null );
|
||||
}
|
||||
|
||||
private BeanMapping(SelectionParameters selectionParameters, NullValueMappingStrategyPrism nvms,
|
||||
ReportingPolicyPrism reportingPolicy, boolean ignoreByDefault,
|
||||
List<String> ignoreUnmappedSourceProperties) {
|
||||
List<String> ignoreUnmappedSourceProperties, BuilderPrism builder) {
|
||||
this.selectionParameters = selectionParameters;
|
||||
this.nullValueMappingStrategy = nvms;
|
||||
this.reportingPolicy = reportingPolicy;
|
||||
this.ignoreByDefault = ignoreByDefault;
|
||||
this.ignoreUnmappedSourceProperties = ignoreUnmappedSourceProperties;
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public SelectionParameters getSelectionParameters() {
|
||||
@ -137,4 +148,8 @@ public class BeanMapping {
|
||||
public List<String> getIgnoreUnmappedSourceProperties() {
|
||||
return ignoreUnmappedSourceProperties;
|
||||
}
|
||||
|
||||
public BuilderPrism getBuilder() {
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlElementRef;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.BeforeMapping;
|
||||
import org.mapstruct.Builder;
|
||||
import org.mapstruct.Context;
|
||||
import org.mapstruct.DecoratedWith;
|
||||
import org.mapstruct.InheritConfiguration;
|
||||
@ -71,6 +72,7 @@ import net.java.dev.hickory.prism.GeneratePrisms;
|
||||
@GeneratePrism(value = ValueMapping.class, publicAccess = true),
|
||||
@GeneratePrism(value = ValueMappings.class, publicAccess = true),
|
||||
@GeneratePrism(value = Context.class, publicAccess = true),
|
||||
@GeneratePrism(value = Builder.class, publicAccess = true),
|
||||
|
||||
// external types
|
||||
@GeneratePrism(value = XmlElementDecl.class, publicAccess = true),
|
||||
|
@ -64,6 +64,7 @@ public class DefaultModelElementProcessorContext implements ProcessorContext {
|
||||
this.typeFactory = new TypeFactory(
|
||||
processingEnvironment.getElementUtils(),
|
||||
delegatingTypes,
|
||||
messager,
|
||||
roundContext
|
||||
);
|
||||
this.options = options;
|
||||
|
@ -28,6 +28,7 @@ import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import org.mapstruct.ap.internal.option.Options;
|
||||
import org.mapstruct.ap.internal.prism.BuilderPrism;
|
||||
import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
|
||||
import org.mapstruct.ap.internal.prism.InjectionStrategyPrism;
|
||||
import org.mapstruct.ap.internal.prism.MapperConfigPrism;
|
||||
@ -238,6 +239,18 @@ public class MapperConfiguration {
|
||||
return mapperPrism.disableSubMappingMethodsGeneration(); // fall back to default defined in the annotation
|
||||
}
|
||||
|
||||
public BuilderPrism getBuilderPrism() {
|
||||
if ( mapperPrism.values.builder() != null ) {
|
||||
return mapperPrism.builder();
|
||||
}
|
||||
else if ( mapperConfigPrism != null && mapperConfigPrism.values.builder() != null ) {
|
||||
return mapperConfigPrism.builder();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public DeclaredType config() {
|
||||
return config;
|
||||
}
|
||||
|
@ -100,6 +100,10 @@ public enum Message {
|
||||
GENERAL_NOT_ALL_FORGED_CREATED( "Internal Error in creation of Forged Methods, it was expected all Forged Methods to finished with creation, but %s did not" ),
|
||||
GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible parameterless constructor." ),
|
||||
|
||||
BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ),
|
||||
BUILDER_NO_BUILD_METHOD_FOUND("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\".", Diagnostic.Kind.ERROR ),
|
||||
BUILDER_NO_BUILD_METHOD_FOUND_DEFAULT("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\". Consider to add @Builder in order to select the correct build method.", Diagnostic.Kind.ERROR ),
|
||||
|
||||
RETRIEVAL_NO_INPUT_ARGS( "Can't generate mapping method with no input arguments." ),
|
||||
RETRIEVAL_DUPLICATE_MAPPING_TARGETS( "Can't generate mapping method with more than one @MappingTarget parameter." ),
|
||||
RETRIEVAL_VOID_MAPPING_METHOD( "Can't generate mapping method with return type void." ),
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.mapstruct.ap.spi;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
|
||||
/**
|
||||
@ -28,11 +29,11 @@ import javax.lang.model.element.ExecutableElement;
|
||||
public class BuilderInfo {
|
||||
|
||||
private final ExecutableElement builderCreationMethod;
|
||||
private final ExecutableElement buildMethod;
|
||||
private final Collection<ExecutableElement> buildMethods;
|
||||
|
||||
private BuilderInfo(ExecutableElement builderCreationMethod, ExecutableElement buildMethod) {
|
||||
private BuilderInfo(ExecutableElement builderCreationMethod, Collection<ExecutableElement> buildMethods) {
|
||||
this.builderCreationMethod = builderCreationMethod;
|
||||
this.buildMethod = buildMethod;
|
||||
this.buildMethods = buildMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,18 +51,18 @@ public class BuilderInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* The method that can be used to build the type being built.
|
||||
* This should be a {@code public} method within the builder itself
|
||||
* The methods that can be used to build the type being built.
|
||||
* This should be {@code public} methods within the builder itself
|
||||
*
|
||||
* @return the build method for the type
|
||||
*/
|
||||
public ExecutableElement getBuildMethod() {
|
||||
return buildMethod;
|
||||
public Collection<ExecutableElement> getBuildMethods() {
|
||||
return buildMethods;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private ExecutableElement builderCreationMethod;
|
||||
private ExecutableElement buildMethod;
|
||||
private Collection<ExecutableElement> buildMethods;
|
||||
|
||||
/**
|
||||
* @see BuilderInfo#getBuilderCreationMethod()
|
||||
@ -72,10 +73,10 @@ public class BuilderInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BuilderInfo#getBuildMethod()
|
||||
* @see BuilderInfo#getBuildMethods()
|
||||
*/
|
||||
public Builder buildMethod(ExecutableElement method) {
|
||||
this.buildMethod = method;
|
||||
public Builder buildMethod(Collection<ExecutableElement> methods) {
|
||||
this.buildMethods = methods;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -87,10 +88,13 @@ public class BuilderInfo {
|
||||
if ( builderCreationMethod == null ) {
|
||||
throw new IllegalArgumentException( "Builder creation method is mandatory" );
|
||||
}
|
||||
else if ( buildMethod == null ) {
|
||||
throw new IllegalArgumentException( "Build method is mandatory" );
|
||||
else if ( buildMethods == null ) {
|
||||
throw new IllegalArgumentException( "Build methods are mandatory" );
|
||||
}
|
||||
return new BuilderInfo( builderCreationMethod, buildMethod );
|
||||
else if ( buildMethods.isEmpty() ) {
|
||||
throw new IllegalArgumentException( "Build methods must not be empty" );
|
||||
}
|
||||
return new BuilderInfo( builderCreationMethod, buildMethods );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ public interface BuilderProvider {
|
||||
*
|
||||
* @throws TypeHierarchyErroneousException if the type that needs to be visited is not ready yet, this signals the
|
||||
* MapStruct processor to postpone the generation of the mappers to the next round
|
||||
* @throws MoreThanOneBuilderCreationMethodException if {@code type} has more than one method that can create the
|
||||
* builder
|
||||
*/
|
||||
BuilderInfo findBuilderInfo(TypeMirror type, Elements elements, Types types);
|
||||
}
|
||||
|
@ -18,6 +18,9 @@
|
||||
*/
|
||||
package org.mapstruct.ap.spi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
@ -141,17 +144,22 @@ public class DefaultBuilderProvider implements BuilderProvider {
|
||||
* <p>
|
||||
* The default implementation iterates over all the methods in {@code typeElement} and uses
|
||||
* {@link DefaultBuilderProvider#isPossibleBuilderCreationMethod(ExecutableElement, TypeElement, Types)} and
|
||||
* {@link DefaultBuilderProvider#findBuildMethod(TypeElement, TypeElement, Types)} to create the
|
||||
* {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement, Types)} to create the
|
||||
* {@link BuilderInfo}.
|
||||
* <p>
|
||||
* The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the
|
||||
* {@code typeElement} should be ignored.
|
||||
* <p>
|
||||
* In case there are multiple {@link BuilderInfo} then a {@link MoreThanOneBuilderCreationMethodException} is
|
||||
* thrown.
|
||||
*
|
||||
* @param typeElement the type element for which a builder searched
|
||||
* @param elements the util elements that can be used for operating on the type element
|
||||
* @param types the util types that can be used for operation on {@link TypeMirror}(s)
|
||||
*
|
||||
* @return the {@link BuilderInfo} or {@code null} if no builder was found for the type
|
||||
* {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement, Types)}
|
||||
* @throws MoreThanOneBuilderCreationMethodException if there are multiple builder creation methods
|
||||
*/
|
||||
protected BuilderInfo findBuilderInfo(TypeElement typeElement, Elements elements, Types types) {
|
||||
if ( shouldIgnore( typeElement ) ) {
|
||||
@ -159,18 +167,28 @@ public class DefaultBuilderProvider implements BuilderProvider {
|
||||
}
|
||||
|
||||
List<ExecutableElement> methods = ElementFilter.methodsIn( typeElement.getEnclosedElements() );
|
||||
List<BuilderInfo> builderInfo = new ArrayList<BuilderInfo>();
|
||||
for ( ExecutableElement method : methods ) {
|
||||
if ( isPossibleBuilderCreationMethod( method, typeElement, types ) ) {
|
||||
TypeElement builderElement = getTypeElement( method.getReturnType() );
|
||||
ExecutableElement buildMethod = findBuildMethod( builderElement, typeElement, types );
|
||||
if ( buildMethod != null ) {
|
||||
return new BuilderInfo.Builder()
|
||||
Collection<ExecutableElement> buildMethods = findBuildMethods( builderElement, typeElement, types );
|
||||
if ( !buildMethods.isEmpty() ) {
|
||||
builderInfo.add( new BuilderInfo.Builder()
|
||||
.builderCreationMethod( method )
|
||||
.buildMethod( buildMethod )
|
||||
.build();
|
||||
.buildMethod( buildMethods )
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( builderInfo.size() == 1 ) {
|
||||
return builderInfo.get( 0 );
|
||||
}
|
||||
else if ( builderInfo.size() > 1 ) {
|
||||
throw new MoreThanOneBuilderCreationMethodException( typeElement.asType(), builderInfo );
|
||||
}
|
||||
|
||||
return findBuilderInfo( typeElement.getSuperclass(), elements, types );
|
||||
}
|
||||
|
||||
@ -207,30 +225,40 @@ public class DefaultBuilderProvider implements BuilderProvider {
|
||||
* <p>
|
||||
* The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the
|
||||
* {@code builderElement} should be ignored, i.e. not checked for build elements.
|
||||
*
|
||||
* <p>
|
||||
* If there are multiple methods that satisfy
|
||||
* {@link DefaultBuilderProvider#isBuildMethod(ExecutableElement, TypeElement, Types)} and one of those methods
|
||||
* is names {@code build} that that method would be considered as a build method.
|
||||
* @param builderElement the element for the builder
|
||||
* @param typeElement the element for the type that is being built
|
||||
* @param types the util types tat can be used for operations on {@link TypeMirror}(s)
|
||||
*
|
||||
* @return the build method for the {@code typeElement} if it exists, or {@code null} if it does not
|
||||
* {@code build}
|
||||
*/
|
||||
protected ExecutableElement findBuildMethod(TypeElement builderElement, TypeElement typeElement, Types types) {
|
||||
protected Collection<ExecutableElement> findBuildMethods(TypeElement builderElement, TypeElement typeElement,
|
||||
Types types) {
|
||||
if ( shouldIgnore( builderElement ) ) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<ExecutableElement> builderMethods = ElementFilter.methodsIn( builderElement.getEnclosedElements() );
|
||||
List<ExecutableElement> buildMethods = new ArrayList<ExecutableElement>();
|
||||
for ( ExecutableElement buildMethod : builderMethods ) {
|
||||
if ( isBuildMethod( buildMethod, typeElement, types ) ) {
|
||||
return buildMethod;
|
||||
buildMethods.add( buildMethod );
|
||||
}
|
||||
}
|
||||
|
||||
return findBuildMethod(
|
||||
getTypeElement( builderElement.getSuperclass() ),
|
||||
typeElement,
|
||||
types
|
||||
);
|
||||
if ( buildMethods.isEmpty() ) {
|
||||
return findBuildMethods(
|
||||
getTypeElement( builderElement.getSuperclass() ),
|
||||
typeElement,
|
||||
types
|
||||
);
|
||||
}
|
||||
|
||||
return buildMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.spi;
|
||||
|
||||
import java.util.List;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
/**
|
||||
* Indicates that a type has too many builder creation methods.
|
||||
* This exception can be used to signal the MapStruct processor that more than one builder creation method was found.
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class MoreThanOneBuilderCreationMethodException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final TypeMirror type;
|
||||
private final List<BuilderInfo> builderCreationMethods;
|
||||
|
||||
public MoreThanOneBuilderCreationMethodException(TypeMirror type, List<BuilderInfo> builderCreationMethods) {
|
||||
this.type = type;
|
||||
this.builderCreationMethods = builderCreationMethods;
|
||||
}
|
||||
|
||||
public TypeMirror getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public List<BuilderInfo> getBuilderInfo() {
|
||||
return builderCreationMethods;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Builder;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ap.test.builder.multiple.build.Process;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper(config = BuilderMapperConfig.class)
|
||||
public interface BuilderConfigDefinedMapper {
|
||||
|
||||
BuilderConfigDefinedMapper INSTANCE = Mappers.getMapper( BuilderConfigDefinedMapper.class );
|
||||
|
||||
Process map(Source source);
|
||||
|
||||
@BeanMapping(builder = @Builder(buildMethod = "wrongCreate"))
|
||||
Process wrongMap(Source source);
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Builder;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ap.test.builder.multiple.build.Process;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper(builder = @Builder(buildMethod = "create"))
|
||||
public interface BuilderDefinedMapper {
|
||||
|
||||
BuilderDefinedMapper INSTANCE = Mappers.getMapper( BuilderDefinedMapper.class );
|
||||
|
||||
Process map(Source source);
|
||||
|
||||
@BeanMapping(builder = @Builder(buildMethod = "wrongCreate"))
|
||||
Process wrongMap(Source source);
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
import org.mapstruct.Builder;
|
||||
import org.mapstruct.MapperConfig;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@MapperConfig(builder = @Builder(buildMethod = "create"))
|
||||
public interface BuilderMapperConfig {
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface DefaultBuildMethodMapper {
|
||||
|
||||
DefaultBuildMethodMapper INSTANCE = Mappers.getMapper( DefaultBuildMethodMapper.class );
|
||||
|
||||
Task map(Source source);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Builder;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ap.test.builder.multiple.build.Process;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface ErroneousMoreThanOneBuildMethodMapper {
|
||||
|
||||
Process map(Source source);
|
||||
|
||||
@BeanMapping(builder = @Builder(buildMethod = "missingBuild"))
|
||||
Process map2(Source source);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
import org.mapstruct.Builder;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ap.test.builder.multiple.build.Process;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper(builder = @Builder(buildMethod = "mapperBuild"))
|
||||
public interface ErroneousMoreThanOneBuildMethodWithMapperDefinedMappingMapper {
|
||||
|
||||
Process map(Source source);
|
||||
}
|
@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.builder.multiple.build.Process;
|
||||
import org.mapstruct.ap.test.builder.multiple.builder.Case;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
@IssueKey("1479")
|
||||
@WithClasses({
|
||||
Process.class,
|
||||
Case.class,
|
||||
Task.class,
|
||||
Source.class
|
||||
})
|
||||
public class MultipleBuilderMapperTest {
|
||||
|
||||
@WithClasses({
|
||||
ErroneousMoreThanOneBuildMethodMapper.class
|
||||
})
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(
|
||||
type = ErroneousMoreThanOneBuildMethodMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 32,
|
||||
messageRegExp = "No build method \"build\" found in \".*\\.multiple\\.build\\.Process\\.Builder\" " +
|
||||
"for \".*\\.multiple\\.build\\.Process\"\\. " +
|
||||
"Found methods: " +
|
||||
"\".*wrongCreate\\(\\) ?, " +
|
||||
".*create\\(\\) ?\"\\. " +
|
||||
"Consider to add @Builder in order to select the correct build method."
|
||||
),
|
||||
@Diagnostic(
|
||||
type = ErroneousMoreThanOneBuildMethodMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 34,
|
||||
messageRegExp = "No build method \"missingBuild\" found " +
|
||||
"in \".*\\.multiple\\.build\\.Process\\.Builder\" " +
|
||||
"for \".*\\.multiple\\.build\\.Process\"\\. " +
|
||||
"Found methods: " +
|
||||
"\".*wrongCreate\\(\\) ?, " +
|
||||
".*create\\(\\) ?\"\\."
|
||||
)
|
||||
})
|
||||
@Test
|
||||
public void moreThanOneBuildMethod() {
|
||||
}
|
||||
|
||||
@WithClasses({
|
||||
ErroneousMoreThanOneBuildMethodWithMapperDefinedMappingMapper.class
|
||||
})
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(
|
||||
type = ErroneousMoreThanOneBuildMethodWithMapperDefinedMappingMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 31,
|
||||
messageRegExp =
|
||||
"No build method \"mapperBuild\" found in \".*\\.multiple\\.build\\.Process\\.Builder\" " +
|
||||
"for \".*\\.multiple\\.build\\.Process\"\\. " +
|
||||
"Found methods: " +
|
||||
"\".*wrongCreate\\(\\) ?, " +
|
||||
".*create\\(\\) ?\"\\."
|
||||
)
|
||||
})
|
||||
@Test
|
||||
public void moreThanOneBuildMethodDefinedOnMapper() {
|
||||
}
|
||||
|
||||
@WithClasses({
|
||||
BuilderDefinedMapper.class
|
||||
})
|
||||
@Test
|
||||
public void builderMappingDefined() {
|
||||
Process map = BuilderDefinedMapper.INSTANCE.map( new Source( "map" ) );
|
||||
Process wrongMap = BuilderDefinedMapper.INSTANCE.wrongMap( new Source( "wrongMap" ) );
|
||||
|
||||
assertThat( map.getBuildMethod() ).isEqualTo( "create" );
|
||||
assertThat( wrongMap.getBuildMethod() ).isEqualTo( "wrongCreate" );
|
||||
}
|
||||
|
||||
@WithClasses({
|
||||
BuilderMapperConfig.class,
|
||||
BuilderConfigDefinedMapper.class
|
||||
})
|
||||
@Test
|
||||
public void builderMappingMapperConfigDefined() {
|
||||
Process map = BuilderConfigDefinedMapper.INSTANCE.map( new Source( "map" ) );
|
||||
Process wrongMap = BuilderConfigDefinedMapper.INSTANCE.wrongMap( new Source( "wrongMap" ) );
|
||||
|
||||
assertThat( map.getBuildMethod() ).isEqualTo( "create" );
|
||||
assertThat( wrongMap.getBuildMethod() ).isEqualTo( "wrongCreate" );
|
||||
}
|
||||
|
||||
@WithClasses({
|
||||
TooManyBuilderCreationMethodsMapper.class
|
||||
})
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED,
|
||||
// We have 2 diagnostics, as we don't do caching of the types, so a type is processed multiple types
|
||||
diagnostics = {
|
||||
@Diagnostic(
|
||||
type = Case.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 24,
|
||||
messageRegExp = "More than one builder creation method for \".*\\.multiple\\.builder.Case\"\\. " +
|
||||
"Found methods: " +
|
||||
"\".*wrongBuilder\\(\\) ?, " +
|
||||
".*builder\\(\\) ?\"\\. " +
|
||||
"Builder will not be used\\. Consider implementing a custom BuilderProvider SPI\\."
|
||||
),
|
||||
@Diagnostic(
|
||||
type = Case.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 24,
|
||||
messageRegExp = "More than one builder creation method for \".*\\.multiple\\.builder.Case\"\\. " +
|
||||
"Found methods: " +
|
||||
"\".*wrongBuilder\\(\\) ?, " +
|
||||
".*builder\\(\\) ?\"\\. " +
|
||||
"Builder will not be used\\. Consider implementing a custom BuilderProvider SPI\\."
|
||||
)
|
||||
})
|
||||
@Test
|
||||
public void tooManyBuilderCreationMethods() {
|
||||
Case caseTarget = TooManyBuilderCreationMethodsMapper.INSTANCE.map( new Source( "test" ) );
|
||||
|
||||
assertThat( caseTarget ).isNotNull();
|
||||
assertThat( caseTarget.getName() ).isEqualTo( "test" );
|
||||
assertThat( caseTarget.getBuilderCreationMethod() ).isNull();
|
||||
assertThat( caseTarget.getBuildMethod() ).isEqualTo( "constructor" );
|
||||
}
|
||||
|
||||
@WithClasses( {
|
||||
DefaultBuildMethodMapper.class
|
||||
} )
|
||||
@Test
|
||||
public void defaultBuildMethod() {
|
||||
Task task = DefaultBuildMethodMapper.INSTANCE.map( new Source( "test" ) );
|
||||
|
||||
assertThat( task ).isNotNull();
|
||||
assertThat( task.getName() ).isEqualTo( "test" );
|
||||
assertThat( task.getBuilderCreationMethod() ).isEqualTo( "builder" );
|
||||
assertThat( task.getBuildMethod() ).isEqualTo( "build" );
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Source {
|
||||
|
||||
private final String name;
|
||||
|
||||
public Source(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Task {
|
||||
|
||||
private final String builderCreationMethod;
|
||||
private final String buildMethod;
|
||||
private String name;
|
||||
|
||||
public Task() {
|
||||
this.builderCreationMethod = null;
|
||||
this.buildMethod = "constructor";
|
||||
}
|
||||
|
||||
public Task(Builder builder, String buildMethod) {
|
||||
this.builderCreationMethod = builder.builderCreationMethod;
|
||||
this.buildMethod = buildMethod;
|
||||
this.name = builder.name;
|
||||
}
|
||||
|
||||
public String getBuilderCreationMethod() {
|
||||
return builderCreationMethod;
|
||||
}
|
||||
|
||||
public String getBuildMethod() {
|
||||
return buildMethod;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder( "builder" );
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String name;
|
||||
private String builderCreationMethod;
|
||||
|
||||
protected Builder(String builderCreationMethod) {
|
||||
this.builderCreationMethod = builderCreationMethod;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Task wrongCreate() {
|
||||
return new Task( this, "wrongCreate" );
|
||||
}
|
||||
|
||||
public Task build() {
|
||||
return new Task( this, "build" );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ap.test.builder.multiple.builder.Case;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface TooManyBuilderCreationMethodsMapper {
|
||||
|
||||
TooManyBuilderCreationMethodsMapper INSTANCE = Mappers.getMapper( TooManyBuilderCreationMethodsMapper.class );
|
||||
|
||||
Case map(Source source);
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple.build;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Process {
|
||||
|
||||
private final String builderCreationMethod;
|
||||
private final String buildMethod;
|
||||
private String name;
|
||||
|
||||
public Process() {
|
||||
this.builderCreationMethod = null;
|
||||
this.buildMethod = "constructor";
|
||||
}
|
||||
|
||||
public Process(Builder builder, String buildMethod) {
|
||||
this.builderCreationMethod = builder.builderCreationMethod;
|
||||
this.buildMethod = buildMethod;
|
||||
this.name = builder.name;
|
||||
}
|
||||
|
||||
public String getBuilderCreationMethod() {
|
||||
return builderCreationMethod;
|
||||
}
|
||||
|
||||
public String getBuildMethod() {
|
||||
return buildMethod;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder( "builder" );
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String name;
|
||||
private String builderCreationMethod;
|
||||
|
||||
protected Builder(String builderCreationMethod) {
|
||||
this.builderCreationMethod = builderCreationMethod;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Process wrongCreate() {
|
||||
return new Process( this, "wrongCreate" );
|
||||
}
|
||||
|
||||
public Process create() {
|
||||
return new Process( this, "create" );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.test.builder.multiple.builder;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Case {
|
||||
|
||||
private final String builderCreationMethod;
|
||||
private final String buildMethod;
|
||||
private String name;
|
||||
|
||||
public Case() {
|
||||
this.builderCreationMethod = null;
|
||||
this.buildMethod = "constructor";
|
||||
}
|
||||
|
||||
public Case(Builder builder, String buildMethod) {
|
||||
this.builderCreationMethod = builder.builderCreationMethod;
|
||||
this.buildMethod = buildMethod;
|
||||
this.name = builder.name;
|
||||
}
|
||||
|
||||
public String getBuilderCreationMethod() {
|
||||
return builderCreationMethod;
|
||||
}
|
||||
|
||||
public String getBuildMethod() {
|
||||
return buildMethod;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static Builder wrongBuilder() {
|
||||
return new Builder( "wrongBuilder" );
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder( "builder" );
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String name;
|
||||
private String builderCreationMethod;
|
||||
|
||||
protected Builder(String builderCreationMethod) {
|
||||
this.builderCreationMethod = builderCreationMethod;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Case create() {
|
||||
return new Case( this, "create" );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user