diff --git a/core-common/src/main/java/org/mapstruct/MappingConstants.java b/core-common/src/main/java/org/mapstruct/MappingConstants.java new file mode 100644 index 000000000..dd9ff487e --- /dev/null +++ b/core-common/src/main/java/org/mapstruct/MappingConstants.java @@ -0,0 +1,46 @@ +/** + * Copyright 2012-2016 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; + +/** + * Contains all constants defined in the mapping process. + * + * @author Sjaak Derksen + */ +public final class MappingConstants { + + private MappingConstants() { + } + + /** + * Represents a {@code null} source or target. + */ + public static final String NULL = ""; + + /** + * Represents any source that is not already mapped by either a defined mapping or by means of name based mapping. + */ + public static final String ANY_REMAINING = ""; + + /** + * Represents any source that is not already mapped by a defined mapping. + */ + public static final String ANY_UNMAPPED = ""; + +} diff --git a/core-jdk8/src/main/java/org/mapstruct/Mapping.java b/core-jdk8/src/main/java/org/mapstruct/Mapping.java index 43dbbb7d9..c7e4b0d83 100644 --- a/core-jdk8/src/main/java/org/mapstruct/Mapping.java +++ b/core-jdk8/src/main/java/org/mapstruct/Mapping.java @@ -37,6 +37,10 @@ import java.util.Date; * In addition, the attributes {@link #dateFormat()} and {@link #qualifiedBy()} may be used to further define the * mapping. * + *

+ * IMPORTANT NOTE: the enum mapping capability is deprecated and replaced by {@link ValueMapping} it + * will be removed in subsequent versions. + * * @author Gunnar Morling */ @Repeatable(Mappings.class) diff --git a/core-jdk8/src/main/java/org/mapstruct/ValueMapping.java b/core-jdk8/src/main/java/org/mapstruct/ValueMapping.java new file mode 100644 index 000000000..77c700471 --- /dev/null +++ b/core-jdk8/src/main/java/org/mapstruct/ValueMapping.java @@ -0,0 +1,123 @@ +/** + * Copyright 2012-2016 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.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Configures the mapping of source constant value to target constant value. + *

+ * Supported mappings are + *

    + *
  1. Enumeration to Enumeration
  2. + *
+ *

+ * Example 1: + *

+ * 
+ * public enum OrderType { RETAIL, B2B, EXTRA, STANDARD, NORMAL }
+ *
+ * public enum ExternalOrderType { RETAIL, B2B, SPECIAL, DEFAULT }
+ *
+ * @ValueMapping(source = "EXTRA", target = "SPECIAL"),
+ * @ValueMapping(source = "STANDARD", target = "DEFAULT"),
+ * @ValueMapping(source = "NORMAL", target = "DEFAULT")
+ * ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
+ * 
+ * Mapping result:
+ * ╔═════════════════════╦════════════════════════════╗
+ * ║ OrderType           ║ ExternalOrderType          ║
+ * ╠═════════════════════╬════════════════════════════╣
+ * ║ null                ║ null                       ║
+ * ║ OrderType.EXTRA     ║ ExternalOrderType.SPECIAL  ║
+ * ║ OrderType.STANDARD  ║ ExternalOrderType.DEFAULT  ║
+ * ║ OrderType.NORMAL    ║ ExternalOrderType.DEFAULT  ║
+ * ║ OrderType.RETAIL    ║ ExternalOrderType.RETAIL   ║
+ * ║ OrderType.B2B       ║ ExternalOrderType.B2B      ║
+ * ╚═════════════════════╩════════════════════════════╝
+ * 
+ * MapStruct will WARN on incomplete mappings. However, if for some reason no match is found an + * {@link java.lang.IllegalStateException} will be thrown. + *

+ * Example 2: + *

+ * 
+ * @ValueMapping( source = "<NULL>", target = "DEFAULT" ),
+ * @ValueMapping( source = "STANDARD", target = "<NULL>" ),
+ * @ValueMapping( source = "<ANY_REMAINING>", target = "SPECIAL" )
+ * ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
+ * 
+ * Mapping result:
+ * ╔═════════════════════╦════════════════════════════╗
+ * ║ OrderType           ║ ExternalOrderType          ║
+ * ╠═════════════════════╬════════════════════════════╣
+ * ║ null                ║ ExternalOrderType.DEFAULT  ║
+ * ║ OrderType.STANDARD  ║ null                       ║
+ * ║ OrderType.RETAIL    ║ ExternalOrderType.RETAIL   ║
+ * ║ OrderType.B2B       ║ ExternalOrderType.B2B      ║
+ * ║ OrderType.NORMAL    ║ ExternalOrderType.SPECIAL  ║
+ * ║ OrderType.EXTRA     ║ ExternalOrderType.SPECIAL  ║
+ * ╚═════════════════════╩════════════════════════════╝
+ * 
+ * + * @author Sjaak Derksen + */ +@Repeatable(ValueMappings.class) +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +public @interface ValueMapping { + /** + * The source value constant to use for this mapping. + * + *

+ * Valid values: + *

    + *
  1. enum constant name
  2. + *
  3. {@link MappingConstants#NULL}
  4. + *
  5. {@link MappingConstants#ANY_REMAINING}
  6. + *
  7. {@link MappingConstants#ANY_UNMAPPED}
  8. + *
+ *

+ * NOTE:When using <ANY_REMAINING>, MapStruct will perform the normal name based mapping, in which + * source is mapped to target based on enum identifier equality. Using <ANY_UNMAPPED> will not apply name + * based mapping. + * + * @return The source value. + */ + String source(); + + /** + * The target value constant to use for this mapping. + * + *

+ * Valid values: + *

    + *
  1. enum constant name
  2. + *
  3. {@link MappingConstants#NULL}
  4. + *
+ * + * @return The target value. + */ + String target(); + +} diff --git a/core-jdk8/src/main/java/org/mapstruct/ValueMappings.java b/core-jdk8/src/main/java/org/mapstruct/ValueMappings.java new file mode 100644 index 000000000..24fcbf55d --- /dev/null +++ b/core-jdk8/src/main/java/org/mapstruct/ValueMappings.java @@ -0,0 +1,37 @@ +/** + * Copyright 2012-2016 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.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Constructs a set of value (constant) mappings. + * + * @author Sjaak Derksen + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ValueMappings { + + ValueMapping[] value(); + +} diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 022cc143f..7cdb05d12 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -36,6 +36,10 @@ import java.util.Date; * In addition, the attributes {@link #dateFormat()} and {@link #qualifiedBy()} may be used to further define the * mapping. * + *

+ * IMPORTANT NOTE: the enum mapping capability is deprecated and replaced by {@link ValueMapping} it + * will be removed in subsequent versions. + * * @author Gunnar Morling */ @Retention(RetentionPolicy.CLASS) diff --git a/core/src/main/java/org/mapstruct/ValueMapping.java b/core/src/main/java/org/mapstruct/ValueMapping.java new file mode 100644 index 000000000..690c66073 --- /dev/null +++ b/core/src/main/java/org/mapstruct/ValueMapping.java @@ -0,0 +1,126 @@ +/** + * Copyright 2012-2016 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.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Configures the mapping of source constant value to target constant value. + *

+ * Supported mappings are + *

    + *
  1. Enumeration to Enumeration
  2. + *
+ *

+ * Example 1: + *

+ * 
+ * public enum OrderType { RETAIL, B2B, EXTRA, STANDARD, NORMAL }
+ *
+ * public enum ExternalOrderType { RETAIL, B2B, SPECIAL, DEFAULT }
+ *
+ * @ValueMappings({
+ *    @ValueMapping(source = "EXTRA", target = "SPECIAL"),
+ *    @ValueMapping(source = "STANDARD", target = "DEFAULT"),
+ *    @ValueMapping(source = "NORMAL", target = "DEFAULT")
+ * })
+ * ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
+ * 
+ * Mapping result:
+ * ╔═════════════════════╦════════════════════════════╗
+ * ║ OrderType           ║ ExternalOrderType          ║
+ * ╠═════════════════════╬════════════════════════════╣
+ * ║ null                ║ null                       ║
+ * ║ OrderType.EXTRA     ║ ExternalOrderType.SPECIAL  ║
+ * ║ OrderType.STANDARD  ║ ExternalOrderType.DEFAULT  ║
+ * ║ OrderType.NORMAL    ║ ExternalOrderType.DEFAULT  ║
+ * ║ OrderType.RETAIL    ║ ExternalOrderType.RETAIL   ║
+ * ║ OrderType.B2B       ║ ExternalOrderType.B2B      ║
+ * ╚═════════════════════╩════════════════════════════╝
+ * 
+ * MapStruct will WARN on incomplete mappings. However, if for some reason no match is found an + * {@link java.lang.IllegalStateException} will be thrown. + *

+ * Example 2: + *

+ * 
+ * @ValueMappings({
+ *    @ValueMapping( source = "<NULL>", target = "DEFAULT" ),
+ *    @ValueMapping( source = "STANDARD", target = "<NULL>" ),
+ *    @ValueMapping( source = "<ANY_REMAINING>", target = "SPECIAL" )
+ * })
+ * ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
+ * 
+ * Mapping result:
+ * ╔═════════════════════╦════════════════════════════╗
+ * ║ OrderType           ║ ExternalOrderType          ║
+ * ╠═════════════════════╬════════════════════════════╣
+ * ║ null                ║ ExternalOrderType.DEFAULT  ║
+ * ║ OrderType.STANDARD  ║ null                       ║
+ * ║ OrderType.RETAIL    ║ ExternalOrderType.RETAIL   ║
+ * ║ OrderType.B2B       ║ ExternalOrderType.B2B      ║
+ * ║ OrderType.NORMAL    ║ ExternalOrderType.SPECIAL  ║
+ * ║ OrderType.EXTRA     ║ ExternalOrderType.SPECIAL  ║
+ * ╚═════════════════════╩════════════════════════════╝
+ * 
+ * + * @author Sjaak Derksen + */ +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +public @interface ValueMapping { + + /** + * The source value constant to use for this mapping. + * + *

+ * Valid values: + *

    + *
  1. enum constant name
  2. + *
  3. {@link MappingConstants#NULL}
  4. + *
  5. {@link MappingConstants#ANY_REMAINING}
  6. + *
  7. {@link MappingConstants#ANY_UNMAPPED}
  8. + *
+ *

+ * NOTE:When using <ANY_REMAINING>, MapStruct will perform the normal name based mapping, in which + * source is mapped to target based on enum identifier equality. Using <ANY_UNMAPPED> will not apply name + * based mapping. + * + * @return The source value. + */ + String source(); + + /** + * The target value constant to use for this mapping. + * + *

+ * Valid values: + *

    + *
  1. enum constant name
  2. + *
  3. {@link MappingConstants#NULL}
  4. + *
+ * + * @return The target value. + */ + String target(); + +} diff --git a/core/src/main/java/org/mapstruct/ValueMappings.java b/core/src/main/java/org/mapstruct/ValueMappings.java new file mode 100644 index 000000000..24fcbf55d --- /dev/null +++ b/core/src/main/java/org/mapstruct/ValueMappings.java @@ -0,0 +1,37 @@ +/** + * Copyright 2012-2016 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.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Constructs a set of value (constant) mappings. + * + * @author Sjaak Derksen + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ValueMappings { + + ValueMapping[] value(); + +} diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc index 8cb7214cb..beede4d28 100644 --- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc +++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc @@ -1095,11 +1095,13 @@ When an iterable or map mapping method declares an interface type as return type |=== [[mapping-enum-types]] -== Mapping enum types +== Mapping Values + +=== Mapping enum types MapStruct supports the generation of methods which map one Java enum type into another. -By default, each constant from the source enum is mapped to a constant with the same name in the target enum type. If required, a constant from the source enum may be mapped to a constant with another name with help of the `@Mapping` annotation. Several constants from the source enum can be mapped to the same constant in the target type. +By default, each constant from the source enum is mapped to a constant with the same name in the target enum type. If required, a constant from the source enum may be mapped to a constant with another name with help of the `@ValueMapping` annotation. Several constants from the source enum can be mapped to the same constant in the target type. The following shows an example: @@ -1113,17 +1115,129 @@ public interface OrderMapper { OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); - @Mappings({ - @Mapping(source = "EXTRA", target = "SPECIAL"), - @Mapping(source = "STANDARD", target = "DEFAULT"), - @Mapping(source = "NORMAL", target = "DEFAULT") + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = "DEFAULT") }) ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); } ---- ==== -Note that an error will be raised by MapStruct in case a constant of the source enum type does not have a corresponding constant with the same name in the target type and also is not mapped to another constant via `@Mapping`. This ensures that all constants are mapped in a safe and predictable manner. +.Enum mapping method result +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class OrderMapperImpl implements OrderMapper { + + @Override + public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) { + if ( orderType == null ) { + return null; + } + + ExternalOrderType externalOrderType_; + + switch ( orderType ) { + case EXTRA: externalOrderType_ = ExternalOrderType.SPECIAL; + break; + case STANDARD: externalOrderType_ = ExternalOrderType.DEFAULT; + break; + case NORMAL: externalOrderType_ = ExternalOrderType.DEFAULT; + break; + case RETAIL: externalOrderType_ = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType_ = ExternalOrderType.B2B; + break; + default: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType ); + } + + return externalOrderType_; + } +} +---- +==== +By default an error will be raised by MapStruct in case a constant of the source enum type does not have a corresponding constant with the same name in the target type and also is not mapped to another constant via `@ValueMapping`. This ensures that all constants are mapped in a safe and predictable manner. The generated +mapping method will throw an IllegalStateException if for some reason an unrecognized source value occurs. + +MapStruct also has a mechanism for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings. It comes in two flavors: `` and ``. + +In case of source `` MapStruct will continue to map a source enum constant to a target enum constant with the same name. The remainder of the source enum constants will be mapped to the target specified in the `@ValueMapping` with `` source. + +MapStruct will *not* attempt such name based mapping for `` and directly apply the target specified in the `@ValueMapping` with `` source to the remainder. + +MapStruct is able to handle `null` sources and `null` targets by means of the `` keyword. + +[TIP] +==== +Constants for ``, `` and `` are available in the `MappingConstants` class. +==== + +Finally `@InheritInverseConfiguration` and `@InheritConfiguration` can be used in combination with `@ValueMappings`. + +.Enum mapping method, and +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper +public interface SpecialOrderMapper { + + SpecialOrderMapper INSTANCE = Mappers.getMapper( SpecialOrderMapper.class ); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), + @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" ) + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); +} +---- +==== + +.Enum mapping method result, and +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +// GENERATED CODE +public class SpecialOrderMapperImpl implements SpecialOrderMapper { + + @Override + public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) { + if ( orderType == null ) { + return ExternalOrderType.DEFAULT; + } + + ExternalOrderType externalOrderType_; + + switch ( orderType ) { + case STANDARD: externalOrderType_ = null; + break; + case RETAIL: externalOrderType_ = ExternalOrderType.RETAIL; + break; + case B2B: externalOrderType_ = ExternalOrderType.B2B; + break; + default: externalOrderType_ = ExternalOrderType.SPECIAL; + } + + return externalOrderType_; + } +} +---- +==== + +*Note:* MapStruct would have refrained from mapping the `RETAIL` and `B2B` when `` was used instead of ``. + + +[WARNING] +==== +The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It will be removed from future versions of MapStruct. Please adapt existing enum mapping methods to make use of `@ValueMapping` instead. +==== + [[object-factories]] == Object factories diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/EnumMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/EnumMappingMethod.java index d1d222657..245a37ceb 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/EnumMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/EnumMappingMethod.java @@ -90,7 +90,7 @@ public class EnumMappingMethod extends MappingMethod { targetConstants.add( mapping.getTargetName() ); } ctx.getMessager().printMessage( method.getExecutable(), - Message.ENUMMAPPING_MULTIPLE_TARGETS, + Message.ENUMMAPPING_MULTIPLE_SOURCES, enumConstant, Strings.join( targetConstants, ", " ) ); @@ -183,7 +183,7 @@ public class EnumMappingMethod extends MappingMethod { if ( !unmappedSourceEnumConstants.isEmpty() ) { ctx.getMessager().printMessage( method.getExecutable(), - Message.ENUMMAPPING_UNMAPPED_TARGETS, + Message.ENUMMAPPING_UNMAPPED_SOURCES, Strings.join( unmappedSourceEnumConstants, ", " ) ); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java new file mode 100644 index 000000000..cd121e3d9 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ValueMappingMethod.java @@ -0,0 +1,289 @@ +/** + * Copyright 2012-2016 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.ArrayList; +import java.util.List; + +import javax.lang.model.type.TypeMirror; + +import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.model.source.SelectionParameters; +import org.mapstruct.ap.internal.model.source.SourceMethod; +import org.mapstruct.ap.internal.model.source.ValueMapping; +import org.mapstruct.ap.internal.prism.BeanMappingPrism; +import org.mapstruct.ap.internal.prism.MappingConstantsPrism; +import static org.mapstruct.ap.internal.util.Collections.first; +import org.mapstruct.ap.internal.util.Message; +import org.mapstruct.ap.internal.util.Strings; + +/** + * A {@link ValueMappingMethod} which maps one value type to another, optionally configured by one or more + * {@link ValueMapping}s. For now, only enum-to-enum mapping is supported. + * + * @author Sjaak Derksen + */ +public class ValueMappingMethod extends MappingMethod { + + private final List valueMappings; + private final String defaultTarget; + private final String nullTarget; + private final boolean throwIllegalArgumentException; + + public static class Builder { + + private SourceMethod method; + private MappingBuilderContext ctx; + private final List trueValueMappings = new ArrayList(); + private ValueMapping defaultTargetValue = null; + private ValueMapping nullTargetValue = null; + private boolean applyNamebasedMappings = true; + + + public Builder mappingContext(MappingBuilderContext mappingContext) { + this.ctx = mappingContext; + return this; + } + + public Builder souceMethod(SourceMethod sourceMethod) { + this.method = sourceMethod; + return this; + } + + public Builder valueMappings(List valueMappings) { + for ( ValueMapping valueMapping : valueMappings ) { + if ( MappingConstantsPrism.ANY_REMAINING.equals( valueMapping.getSource() ) ) { + defaultTargetValue = valueMapping; + } + else if ( MappingConstantsPrism.ANY_UNMAPPED.equals( valueMapping.getSource() ) ) { + defaultTargetValue = valueMapping; + applyNamebasedMappings = false; + } + else if ( MappingConstantsPrism.NULL.equals( valueMapping.getSource() ) ) { + nullTargetValue = valueMapping; + } + else { + trueValueMappings.add( valueMapping ); + } + } + return this; + } + + public ValueMappingMethod build( ) { + + // initialize all relevant parameters + List mappingEntries = new ArrayList(); + String nullTarget = null; + String defaultTarget = null; + boolean throwIllegalArgumentException = false; + + // for now, we're only dealing with enum mappings, populate relevant parameters based on enum-2-enum + if ( first( method.getSourceParameters() ).getType().isEnumType() && method.getResultType().isEnumType() ) { + mappingEntries.addAll( enumToEnumMapping( method ) ); + + if ( (nullTargetValue != null) && !MappingConstantsPrism.NULL.equals( nullTargetValue.getTarget() ) ) { + // absense nulltargetvalue reverts to null. Or it could be a deliberate choice to return null + nullTarget = nullTargetValue.getTarget(); + } + if ( defaultTargetValue != null ) { + defaultTarget = defaultTargetValue.getTarget(); + } + else { + throwIllegalArgumentException = true; + } + + } + + // do before / after lifecycle mappings + SelectionParameters selectionParameters = getSelectionParameters( method ); + List beforeMappingMethods + = LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx ); + List afterMappingMethods + = LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx ); + + + // finallyn return a mapping + return new ValueMappingMethod( method, mappingEntries, nullTarget, defaultTarget, + throwIllegalArgumentException, beforeMappingMethods, afterMappingMethods ); + } + + private List enumToEnumMapping(SourceMethod method) { + + List mappings = new ArrayList(); + List unmappedSourceConstants + = new ArrayList( first( method.getSourceParameters() ).getType().getEnumConstants() ); + + + if ( !reportErrorIfMappedEnumConstantsDontExist( method ) ) { + return mappings; + } + + // Start to fill the mappings with the defined valuemappings + for ( ValueMapping valueMapping : trueValueMappings ) { + String target = + MappingConstantsPrism.NULL.equals( valueMapping.getTarget() ) ? null : valueMapping.getTarget(); + mappings.add( new MappingEntry( valueMapping.getSource(), target ) ); + unmappedSourceConstants.remove( valueMapping.getSource() ); + } + + + // add mappings based on name + if ( applyNamebasedMappings ) { + + // get all target constants + List targetConstants = method.getReturnType().getEnumConstants(); + for ( String sourceConstant : new ArrayList( unmappedSourceConstants ) ) { + if ( targetConstants.contains( sourceConstant ) ) { + mappings.add( new MappingEntry( sourceConstant, sourceConstant ) ); + unmappedSourceConstants.remove( sourceConstant ); + } + } + + if ( defaultTargetValue == null && !unmappedSourceConstants.isEmpty() ) { + // all sources should now be matched, there's no default to fall back to, so if sources remain, + // we have an issue. + ctx.getMessager().printMessage( method.getExecutable(), + Message.VALUE_MAPPING_UNMAPPED_SOURCES, + Strings.join( unmappedSourceConstants, ", " ) + ); + + } + } + return mappings; + } + + private SelectionParameters getSelectionParameters(SourceMethod method) { + BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() ); + if ( beanMappingPrism != null ) { + List qualifiers = beanMappingPrism.qualifiedBy(); + List qualifyingNames = beanMappingPrism.qualifiedByName(); + TypeMirror resultType = beanMappingPrism.resultType(); + return new SelectionParameters( qualifiers, qualifyingNames, resultType ); + } + return null; + } + + private boolean reportErrorIfMappedEnumConstantsDontExist(SourceMethod method) { + List sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants(); + List targetEnumConstants = method.getReturnType().getEnumConstants(); + + boolean foundIncorrectMapping = false; + + for ( ValueMapping mappedConstant : trueValueMappings ) { + + if ( !sourceEnumConstants.contains( mappedConstant.getSource() ) ) { + ctx.getMessager().printMessage( method.getExecutable(), + mappedConstant.getMirror(), + mappedConstant.getSourceAnnotationValue(), + Message.VALUEMAPPING_NON_EXISTING_CONSTANT, + mappedConstant.getSource(), + first( method.getSourceParameters() ).getType() + ); + foundIncorrectMapping = true; + } + if ( !MappingConstantsPrism.NULL.equals( mappedConstant.getTarget() ) + && !targetEnumConstants.contains( mappedConstant.getTarget() ) ) { + ctx.getMessager().printMessage( method.getExecutable(), + mappedConstant.getMirror(), + mappedConstant.getTargetAnnotationValue(), + Message.VALUEMAPPING_NON_EXISTING_CONSTANT, + mappedConstant.getTarget(), + method.getReturnType() + ); + foundIncorrectMapping = true; + } + } + + if ( defaultTargetValue != null && !MappingConstantsPrism.NULL.equals( defaultTargetValue.getTarget() ) + && !targetEnumConstants.contains( defaultTargetValue.getTarget() ) ) { + ctx.getMessager().printMessage( method.getExecutable(), + defaultTargetValue.getMirror(), + defaultTargetValue.getTargetAnnotationValue(), + Message.VALUEMAPPING_NON_EXISTING_CONSTANT, + defaultTargetValue.getTarget(), + method.getReturnType() + ); + foundIncorrectMapping = true; + } + + if ( nullTargetValue != null && MappingConstantsPrism.NULL.equals( nullTargetValue.getTarget() ) + && !targetEnumConstants.contains( nullTargetValue.getTarget() ) ) { + ctx.getMessager().printMessage( method.getExecutable(), + nullTargetValue.getMirror(), + nullTargetValue.getTargetAnnotationValue(), + Message.VALUEMAPPING_NON_EXISTING_CONSTANT, + nullTargetValue.getTarget(), + method.getReturnType() + ); + foundIncorrectMapping = true; + } + + return !foundIncorrectMapping; + } + } + + private ValueMappingMethod(Method method, List enumMappings, String nullTarget, String defaultTarget, + boolean throwIllegalArgumentException, List beforeMappingMethods, + List afterMappingMethods) { + super( method, beforeMappingMethods, afterMappingMethods ); + this.valueMappings = enumMappings; + this.nullTarget = nullTarget; + this.defaultTarget = defaultTarget; + this.throwIllegalArgumentException = throwIllegalArgumentException; + } + + public List getValueMappings() { + return valueMappings; + } + + public String getDefaultTarget() { + return defaultTarget; + } + + public String getNullTarget() { + return nullTarget; + } + + public boolean isThrowIllegalArgumentException() { + return throwIllegalArgumentException; + } + + public Parameter getSourceParameter() { + return first( getParameters() ); + } + + public static class MappingEntry { + private final String source; + private final String target; + + MappingEntry( String source, String target ) { + this.source = source; + this.target = target; + } + + public String getSource() { + return source; + } + + public String getTarget() { + return target; + } + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java index 95b38b4ae..a81a6527a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MappingOptions.java @@ -19,6 +19,7 @@ package org.mapstruct.ap.internal.model.source; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,14 +37,16 @@ public class MappingOptions { private IterableMapping iterableMapping; private MapMapping mapMapping; private BeanMapping beanMapping; + private List valueMappings; private boolean fullyInitialized; public MappingOptions(Map> mappings, IterableMapping iterableMapping, MapMapping mapMapping, - BeanMapping beanMapping) { + BeanMapping beanMapping, List valueMappings ) { this.mappings = mappings; this.iterableMapping = iterableMapping; this.mapMapping = mapMapping; this.beanMapping = beanMapping; + this.valueMappings = valueMappings; } /** @@ -66,6 +69,10 @@ public class MappingOptions { return beanMapping; } + public List getValueMappings() { + return valueMappings; + } + public void setMappings(Map> mappings) { this.mappings = mappings; } @@ -82,6 +89,10 @@ public class MappingOptions { this.beanMapping = beanMapping; } + public void setValueMappings(List valueMappings) { + this.valueMappings = valueMappings; + } + /** * @return the {@code true}, iff the options have been fully initialized by applying all available inheritance * options @@ -124,6 +135,29 @@ public class MappingOptions { } } + if ( getValueMappings() == null ) { + if ( inherited.getValueMappings() != null ) { + // there were no mappings, so the inherited mappings are the new ones + setValueMappings( inherited.getValueMappings() ); + } + else { + setValueMappings( Collections.emptyList() ); + } + } + else { + if ( inherited.getValueMappings() != null ) { + // iff there are also inherited mappings, we reverse and add them. + for ( ValueMapping inheritedValueMapping : inherited.getValueMappings() ) { + ValueMapping valueMapping = isInverse ? inheritedValueMapping.reverse() : inheritedValueMapping; + if ( valueMapping != null + && !getValueMappings().contains( valueMapping ) ) { + getValueMappings().add( valueMapping ); + } + } + } + + } + Map> newMappings = new HashMap>(); for ( List lmappings : inherited.getMappings().values() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 8ca6bae09..e59425e6f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -91,6 +91,7 @@ public class SourceMethod implements Method { private FormattingMessager messager = null; private MapperConfiguration mapperConfig = null; private List prototypeMethods = Collections.emptyList(); + private List valueMappings; public Builder() { } @@ -140,6 +141,11 @@ public class SourceMethod implements Method { return this; } + public Builder setValueMappings(List valueMappings) { + this.valueMappings = valueMappings; + return this; + } + public Builder setTypeUtils(Types typeUtils) { this.typeUtils = typeUtils; return this; @@ -173,7 +179,7 @@ public class SourceMethod implements Method { public SourceMethod build() { MappingOptions mappingOptions - = new MappingOptions( mappings, iterableMapping, mapMapping, beanMapping ); + = new MappingOptions( mappings, iterableMapping, mapMapping, beanMapping, valueMappings ); SourceMethod sourceMethod = new SourceMethod( @@ -199,6 +205,8 @@ public class SourceMethod implements Method { } return sourceMethod; } + + } @SuppressWarnings("checkstyle:parameternumber") @@ -374,6 +382,16 @@ public class SourceMethod implements Method { && getResultType().isEnumType(); } + /** + * The default enum mapping (no mappings specified) will from now on be handled as a value mapping. If there + * are any @Mapping / @Mappings defined on the method, then the deprecated enum behavior should be executed. + * + * @return whether (true) or not (false) to execute value mappings + */ + public boolean isValueMapping() { + return isEnumMapping() && mappingOptions.getMappings().isEmpty(); + } + private boolean equals(Object o1, Object o2) { return (o1 == null && o2 == null) || (o1 != null) && o1.equals( o2 ); } @@ -554,4 +572,5 @@ public class SourceMethod implements Method { public boolean isUpdateMethod() { return getMappingTargetParameter() != null; } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java new file mode 100644 index 000000000..7579deceb --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/ValueMapping.java @@ -0,0 +1,164 @@ +/** + * Copyright 2012-2016 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.source; + +import java.util.List; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; +import org.mapstruct.ap.internal.prism.MappingConstantsPrism; +import org.mapstruct.ap.internal.prism.ValueMappingPrism; +import org.mapstruct.ap.internal.prism.ValueMappingsPrism; +import org.mapstruct.ap.internal.util.FormattingMessager; +import org.mapstruct.ap.internal.util.Message; + +/** + * Represents the mapping between one value constant and another. + * + * @author Sjaak Derksen + */ +public class ValueMapping { + + private final String source; + private final String target; + private final AnnotationMirror mirror; + private final AnnotationValue sourceAnnotationValue; + private final AnnotationValue targetAnnotationValue; + + public static void fromMappingsPrism(ValueMappingsPrism mappingsAnnotation, ExecutableElement method, + FormattingMessager messager, List mappings) { + + boolean anyFound = false; + for ( ValueMappingPrism mappingPrism : mappingsAnnotation.value() ) { + ValueMapping mapping = fromMappingPrism( mappingPrism, method, messager ); + if ( mapping != null ) { + + if ( !mappings.contains( mapping ) ) { + mappings.add( mapping ); + } + else { + messager.printMessage( + method, + mappingPrism.mirror, + mappingPrism.values.target(), + Message.VALUEMAPPING_DUPLICATE_SOURCE, + mappingPrism.source() + ); + } + if ( MappingConstantsPrism.ANY_REMAINING.equals( mapping.source ) + || MappingConstantsPrism.ANY_UNMAPPED.equals( mapping.source ) ) { + if ( anyFound ) { + messager.printMessage( + method, + mappingPrism.mirror, + mappingPrism.values.target(), + Message.VALUEMAPPING_ANY_AREADY_DEFINED, + mappingPrism.source() + ); + } + anyFound = true; + } + } + } + } + + public static ValueMapping fromMappingPrism(ValueMappingPrism mappingPrism, ExecutableElement element, + FormattingMessager messager) { + + return new ValueMapping( mappingPrism.source(), mappingPrism.target(), mappingPrism.mirror, + mappingPrism.values.source(), mappingPrism.values.target() ); + } + + + private ValueMapping(String source, String target, AnnotationMirror mirror, AnnotationValue sourceAnnotationValue, + AnnotationValue targetAnnotationValue ) { + this.source = source; + this.target = target; + this.mirror = mirror; + this.sourceAnnotationValue = sourceAnnotationValue; + this.targetAnnotationValue = targetAnnotationValue; + } + + /** + * @return the name of the constant in the source. + */ + public String getSource() { + return source; + } + + /** + * @return the name of the constant in the target. + */ + public String getTarget() { + return target; + } + + public AnnotationMirror getMirror() { + return mirror; + } + + public AnnotationValue getSourceAnnotationValue() { + return sourceAnnotationValue; + } + + public AnnotationValue getTargetAnnotationValue() { + return targetAnnotationValue; + } + + public ValueMapping reverse() { + ValueMapping result; + if ( !MappingConstantsPrism.ANY_REMAINING.equals( source ) + || !MappingConstantsPrism.ANY_UNMAPPED.equals( source ) ) { + result = new ValueMapping( + target, + source, + mirror, + sourceAnnotationValue, + targetAnnotationValue ); + } + else { + result = null; + } + return result; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + (this.source != null ? this.source.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + final ValueMapping other = (ValueMapping) obj; + if ( (this.source == null) ? (other.source != null) : !this.source.equals( other.source ) ) { + return false; + } + return true; + } + + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java new file mode 100644 index 000000000..b34567025 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/prism/MappingConstantsPrism.java @@ -0,0 +1,37 @@ +/** + * Copyright 2012-2016 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.prism; + +/** + * Prism for the enum {@link org.mapstruct.MappingConstants} + * + * @author Sjaak Derksen + */ +public final class MappingConstantsPrism { + + private MappingConstantsPrism() { + } + + public static final String NULL = ""; + + public static final String ANY_REMAINING = ""; + + public static final String ANY_UNMAPPED = ""; + +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java index 57d77445e..35dfd2d0f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/prism/PrismGenerator.java @@ -35,6 +35,8 @@ import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; import org.mapstruct.Mappings; import org.mapstruct.Qualifier; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; import org.mapstruct.TargetType; import net.java.dev.hickory.prism.GeneratePrism; @@ -63,6 +65,8 @@ import org.mapstruct.Named; @GeneratePrism(value = Named.class, publicAccess = true), @GeneratePrism(value = AfterMapping.class, publicAccess = true), @GeneratePrism(value = BeforeMapping.class, publicAccess = true), + @GeneratePrism(value = ValueMapping.class, publicAccess = true), + @GeneratePrism(value = ValueMappings.class, publicAccess = true), // external types @GeneratePrism(value = XmlElementDecl.class, publicAccess = true), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index fa0d34536..483ff3f9f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -47,6 +47,7 @@ import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.MapperReference; import org.mapstruct.ap.internal.model.MappingBuilderContext; import org.mapstruct.ap.internal.model.MappingMethod; +import org.mapstruct.ap.internal.model.ValueMappingMethod; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.MappingOptions; @@ -320,8 +321,21 @@ public class MapperCreationProcessor implements ModelElementProcessor getValueMappings(ExecutableElement method) { + List valueMappings = new ArrayList(); + + ValueMappingPrism mappingAnnotation = ValueMappingPrism.getInstanceOn( method ); + ValueMappingsPrism mappingsAnnotation = ValueMappingsPrism.getInstanceOn( method ); + + if ( mappingAnnotation != null ) { + ValueMapping valueMapping = ValueMapping.fromMappingPrism( mappingAnnotation, method, messager ); + if ( valueMapping != null ) { + valueMappings.add( valueMapping ); + } + } + + if ( mappingsAnnotation != null ) { + ValueMapping.fromMappingsPrism( mappingsAnnotation, method, messager, valueMappings ); + } + + return valueMappings; + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 052004f9d..71accec8f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -63,11 +63,12 @@ public enum Message { ITERABLEMAPPING_MAPPING_NOT_FOUND( "No implementation can be generated for this method. Found no method nor implicit conversion for mapping source element type into target element type." ), ITERABLEMAPPING_NO_ELEMENTS( "'nullValueMappingStrategy','dateformat', 'qualifiedBy' and 'elementTargetType' are undefined in @IterableMapping, define at least one of them." ), - ENUMMAPPING_MULTIPLE_TARGETS( "One enum constant must not be mapped to more than one target constant, but constant %s is mapped to %s." ), + ENUMMAPPING_MULTIPLE_SOURCES( "One enum constant must not be mapped to more than one target constant, but constant %s is mapped to %s." ), ENUMMAPPING_UNDEFINED_SOURCE( "A source constant must be specified for mappings of an enum mapping method." ), ENUMMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ), ENUMMAPPING_UNDEFINED_TARGET( "A target constant must be specified for mappings of an enum mapping method." ), - ENUMMAPPING_UNMAPPED_TARGETS( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via @Mapping: %s." ), + ENUMMAPPING_UNMAPPED_SOURCES( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via adding additional mappings: %s." ), + ENUMMAPPING_DEPRECATED( "Mapping of Enums via @Mapping is going to be removed in future versions of MapStruct. Please use @ValueMapping instead!", Diagnostic.Kind.WARNING ), DECORATOR_NO_SUBTYPE( "Specified decorator type is no subtype of the annotated mapper type." ), DECORATOR_CONSTRUCTOR( "Specified decorator type has no default constructor nor a constructor with a single parameter accepting the decorated mapper type." ), @@ -95,8 +96,6 @@ public enum Message { RETRIEVAL_WILDCARD_SUPER_BOUND_SOURCE( "Can't generate mapping method for a wildcard super bound source." ), RETRIEVAL_WILDCARD_EXTENDS_BOUND_RESULT( "Can't generate mapping method for a wildcard extends bound result." ), - - INHERITCONFIGURATION_BOTH( "Method cannot be annotated with both a @InheritConfiguration and @InheritInverseConfiguration." ), INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ), INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ), @@ -107,7 +106,13 @@ public enum Message { INHERITCONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s." ), INHERITCONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ), INHERITCONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH( "More than one configuration prototype method is applicable. Use @InheritConfiguration to select one of them explicitly: %s." ), - INHERITCONFIGURATION_CYCLE( "Cycle detected while evaluating inherited configurations. Inheritance path: %s" ); + INHERITCONFIGURATION_CYCLE( "Cycle detected while evaluating inherited configurations. Inheritance path: %s" ), + + VALUEMAPPING_DUPLICATE_SOURCE( "Source value mapping: \"%s\" cannot be mapped more than once." ), + VALUEMAPPING_ANY_AREADY_DEFINED( "Source = \"\" or \"\" can only be used once." ), + VALUE_MAPPING_UNMAPPED_SOURCES( "The following constants from the source enum have no corresponding constant in the target enum and must be be mapped via adding additional mappings: %s." ), + VALUEMAPPING_NON_EXISTING_CONSTANT( "Constant %s doesn't exist in enum type %s." ); + // CHECKSTYLE:ON private final String description; diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl new file mode 100644 index 000000000..354055d8d --- /dev/null +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/ValueMappingMethod.ftl @@ -0,0 +1,56 @@ +<#-- + + Copyright 2012-2016 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. + +--> +@Override +public <@includeModel object=returnType/> ${name}(<@includeModel object=sourceParameter/>) { + <#list beforeMappingReferencesWithoutMappingTarget as callback> + <@includeModel object=callback targetBeanName=resultName targetType=resultType/> + <#if !callback_has_next> + + + + if ( ${sourceParameter.name} == null ) { + return <#if nullTarget??><@includeModel object=returnType/>.${nullTarget}<#else>null; + } + + <@includeModel object=resultType/> ${resultName}; + + switch ( ${sourceParameter.name} ) { + <#list valueMappings as valueMapping> + case ${valueMapping.source}: ${resultName} = <#if valueMapping.target??><@includeModel object=returnType/>.${valueMapping.target}<#else>null; + break; + + default: <#if throwIllegalArgumentException>throw new IllegalArgumentException( "Unexpected enum constant: " + ${sourceParameter.name} )<#else>${resultName} = <#if defaultTarget??><@includeModel object=returnType/>.${defaultTarget}<#else>null; + } + <#list beforeMappingReferencesWithMappingTarget as callback> + <#if callback_index = 0> + + + <@includeModel object=callback targetBeanName=resultName targetType=resultType/> + + <#list afterMappingReferences as callback> + <#if callback_index = 0> + + + <@includeModel object=callback targetBeanName=resultName targetType=resultType/> + + + return ${resultName}; +} \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/enums/EnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/enums/EnumMappingTest.java index ff3f9cec9..2044bc264 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/enums/EnumMappingTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/enums/EnumMappingTest.java @@ -37,11 +37,22 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; * @author Gunnar Morling */ @IssueKey("128") -@WithClasses({ OrderMapper.class, OrderEntity.class, OrderType.class, OrderDto.class, ExternalOrderType.class }) +@WithClasses({ OrderEntity.class, OrderType.class, OrderDto.class, ExternalOrderType.class }) @RunWith(AnnotationProcessorTestRunner.class) public class EnumMappingTest { @Test + @WithClasses( OrderMapper.class ) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = OrderMapper.class, + kind = Kind.WARNING, + line = 41, + messageRegExp = "Mapping of Enums via @Mapping is going to be removed in future versions of " + + "MapStruct\\. Please use @ValueMapping instead!") + } + ) public void shouldGenerateEnumMappingMethod() { ExternalOrderType target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.B2B ); assertThat( target ).isEqualTo( ExternalOrderType.B2B ); @@ -51,6 +62,17 @@ public class EnumMappingTest { } @Test + @WithClasses(OrderMapper.class) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = OrderMapper.class, + kind = Kind.WARNING, + line = 41, + messageRegExp = "Mapping of Enums via @Mapping is going to be removed in future versions of " + + "MapStruct\\. Please use @ValueMapping instead!") + } + ) public void shouldConsiderConstantMappings() { ExternalOrderType target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.EXTRA ); assertThat( target ).isEqualTo( ExternalOrderType.SPECIAL ); @@ -63,6 +85,17 @@ public class EnumMappingTest { } @Test + @WithClasses( OrderMapper.class ) + @ExpectedCompilationOutcome( + value = CompilationResult.SUCCEEDED, + diagnostics = { + @Diagnostic(type = OrderMapper.class, + kind = Kind.WARNING, + line = 41, + messageRegExp = "Mapping of Enums via @Mapping is going to be removed in future versions of " + + "MapStruct\\. Please use @ValueMapping instead!") + } + ) public void shouldInvokeEnumMappingMethodForPropertyMapping() { OrderEntity order = new OrderEntity(); order.setOrderType( OrderType.EXTRA ); @@ -73,15 +106,21 @@ public class EnumMappingTest { } @Test - @WithClasses(ErroneousOrderMapperMappingSameConstantTwice.class) + @WithClasses( ErroneousOrderMapperMappingSameConstantTwice.class ) @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { + @Diagnostic(type = ErroneousOrderMapperMappingSameConstantTwice.class, kind = Kind.ERROR, line = 42, messageRegExp = "One enum constant must not be mapped to more than one target constant, but " + - "constant EXTRA is mapped to SPECIAL, DEFAULT\\.") + "constant EXTRA is mapped to SPECIAL, DEFAULT\\."), + @Diagnostic(type = ErroneousOrderMapperMappingSameConstantTwice.class, + kind = Kind.WARNING, + line = 42, + messageRegExp = "Mapping of Enums via @Mapping is going to be removed in future versions of " + + "MapStruct\\. Please use @ValueMapping instead!") } ) public void shouldRaiseErrorIfSameSourceEnumConstantIsMappedTwice() { @@ -92,6 +131,11 @@ public class EnumMappingTest { @ExpectedCompilationOutcome( value = CompilationResult.FAILED, diagnostics = { + @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, + kind = Kind.WARNING, + line = 40, + messageRegExp = "Mapping of Enums via @Mapping is going to be removed in future versions of " + + "MapStruct\\. Please use @ValueMapping instead!"), @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, kind = Kind.ERROR, line = 37, @@ -115,7 +159,7 @@ public class EnumMappingTest { kind = Kind.ERROR, line = 34, messageRegExp = "The following constants from the source enum have no corresponding constant in the " + - "target enum and must be be mapped via @Mapping: EXTRA, STANDARD, NORMAL") + "target enum and must be be mapped via adding additional mappings: EXTRA, STANDARD, NORMAL") } ) public void shouldRaiseErrorIfSourceConstantWithoutMatchingConstantInTargetTypeIsNotMapped() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/prism/ConstantTest.java b/processor/src/test/java/org/mapstruct/ap/test/prism/ConstantTest.java new file mode 100644 index 000000000..404c41e5e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/prism/ConstantTest.java @@ -0,0 +1,42 @@ +/** + * Copyright 2012-2016 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.prism; + +import static org.fest.assertions.Assertions.assertThat; + + +import org.junit.Test; +import org.mapstruct.MappingConstants; +import org.mapstruct.ap.internal.prism.MappingConstantsPrism; + +/** + * Test constants values + * + * @author Sjaak Derksen + */ +public class ConstantTest { + + @Test + public void constantsShouldBeEqual() { + assertThat( MappingConstants.ANY_REMAINING ).isEqualTo( MappingConstantsPrism.ANY_REMAINING ); + assertThat( MappingConstants.ANY_UNMAPPED ).isEqualTo( MappingConstantsPrism.ANY_UNMAPPED ); + assertThat( MappingConstants.NULL ).isEqualTo( MappingConstantsPrism.NULL ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java new file mode 100644 index 000000000..460b42720 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/DefaultOrderMapper.java @@ -0,0 +1,38 @@ +/** + * Copyright 2012-2016 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.value; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface DefaultOrderMapper { + + DefaultOrderMapper INSTANCE = Mappers.getMapper( DefaultOrderMapper.class ); + + OrderDto orderEntityToDto(OrderEntity order); + + @ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "DEFAULT" ) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java new file mode 100644 index 000000000..502eab2bd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/EnumToEnumMappingTest.java @@ -0,0 +1,256 @@ +/** + * Copyright 2012-2016 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.value; + +import static org.fest.assertions.Assertions.assertThat; + +import javax.tools.Diagnostic.Kind; + +import org.junit.Test; +import org.junit.runner.RunWith; +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; + +/** + * Test for the generation and invocation of enum mapping methods. + * + * @author Gunnar Morling, Sjaak Derksen + */ +@IssueKey("128") +@WithClasses({ OrderMapper.class, SpecialOrderMapper.class, DefaultOrderMapper.class, OrderEntity.class, + OrderType.class, OrderDto.class, ExternalOrderType.class }) +@RunWith(AnnotationProcessorTestRunner.class) +public class EnumToEnumMappingTest { + + @Test + public void shouldGenerateEnumMappingMethod() { + ExternalOrderType target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.B2B ); + assertThat( target ).isEqualTo( ExternalOrderType.B2B ); + + target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.RETAIL ); + assertThat( target ).isEqualTo( ExternalOrderType.RETAIL ); + } + + @Test + public void shouldConsiderConstantMappings() { + ExternalOrderType target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.EXTRA ); + assertThat( target ).isEqualTo( ExternalOrderType.SPECIAL ); + + target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.STANDARD ); + assertThat( target ).isEqualTo( ExternalOrderType.DEFAULT ); + + target = OrderMapper.INSTANCE.orderTypeToExternalOrderType( OrderType.NORMAL ); + assertThat( target ).isEqualTo( ExternalOrderType.DEFAULT ); + } + + @Test + public void shouldInvokeEnumMappingMethodForPropertyMapping() { + OrderEntity order = new OrderEntity(); + order.setOrderType( OrderType.EXTRA ); + + OrderDto orderDto = OrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.SPECIAL ); + } + + @Test + public void shouldApplyReverseMappings() { + + OrderType result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); + assertThat( result ).isEqualTo( OrderType.EXTRA ); + + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); + assertThat( result ).isEqualTo( OrderType.STANDARD ); + + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); + assertThat( result ).isEqualTo( OrderType.RETAIL ); + + result = OrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); + assertThat( result ).isEqualTo( OrderType.B2B ); + + } + + @Test + public void shouldApplyNullMapping() { + OrderEntity order = new OrderEntity(); + order.setOrderType( null ); + + OrderDto orderDto = SpecialOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.DEFAULT ); + } + + @Test + public void shouldApplyTargetIsNullMapping() { + OrderEntity order = new OrderEntity(); + order.setOrderType( OrderType.STANDARD ); + + OrderDto orderDto = SpecialOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isNull(); + } + + @Test + public void shouldApplyDefaultMappings() { + OrderEntity order = new OrderEntity(); + + // try all other + order.setOrderType( OrderType.B2B ); + + OrderDto orderDto = SpecialOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.B2B ); + + order.setOrderType( OrderType.EXTRA ); + + orderDto = SpecialOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.SPECIAL ); + + order.setOrderType( OrderType.NORMAL ); + + orderDto = SpecialOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.SPECIAL ); + + order.setOrderType( OrderType.RETAIL ); + + orderDto = SpecialOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.RETAIL ); + } + + @Test + public void shouldApplyDefaultReverseMappings() { + + OrderType result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.SPECIAL ); + assertThat( result ).isEqualTo( OrderType.EXTRA ); + + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.DEFAULT ); + assertThat( result ).isNull(); + + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.RETAIL ); + assertThat( result ).isEqualTo( OrderType.RETAIL ); + + result = SpecialOrderMapper.INSTANCE.externalOrderTypeToOrderType( ExternalOrderType.B2B ); + assertThat( result ).isEqualTo( OrderType.B2B ); + + } + + @Test + public void shouldMappAllUnmappedToDefault() { + + OrderEntity order = new OrderEntity(); + order.setOrderType( OrderType.RETAIL ); + OrderDto orderDto = DefaultOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.DEFAULT ); + + order.setOrderType( OrderType.B2B ); + orderDto = DefaultOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.DEFAULT ); + + order.setOrderType( OrderType.EXTRA ); + orderDto = DefaultOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.DEFAULT ); + + order.setOrderType( OrderType.STANDARD ); + orderDto = DefaultOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.DEFAULT ); + + order.setOrderType( OrderType.NORMAL ); + orderDto = DefaultOrderMapper.INSTANCE.orderEntityToDto( order ); + assertThat( orderDto ).isNotNull(); + assertThat( orderDto.getOrderType() ).isEqualTo( ExternalOrderType.DEFAULT ); + + } + + + @Test + @WithClasses(ErroneousOrderMapperMappingSameConstantTwice.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperMappingSameConstantTwice.class, + kind = Kind.ERROR, + line = 38, + messageRegExp = "Source value mapping: \"EXTRA\" cannot be mapped more than once\\.") + } + ) + public void shouldRaiseErrorIfSameSourceEnumConstantIsMappedTwice() { + } + + @Test + @WithClasses(ErroneousOrderMapperUsingUnknownEnumConstants.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, + kind = Kind.ERROR, + line = 37, + messageRegExp = "Constant FOO doesn't exist in enum type org.mapstruct.ap.test.value.OrderType\\."), + @Diagnostic(type = ErroneousOrderMapperUsingUnknownEnumConstants.class, + kind = Kind.ERROR, + line = 38, + messageRegExp = "Constant BAR doesn't exist in enum type org.mapstruct.ap.test.value." + + "ExternalOrderType\\.") + } + ) + public void shouldRaiseErrorIfUnknownEnumConstantsAreSpecifiedInMapping() { + } + + @Test + @WithClasses(ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.class, + kind = Kind.ERROR, + line = 34, + messageRegExp = "The following constants from the source enum have no corresponding constant in the " + + "target enum and must be be mapped via adding additional mappings: EXTRA, STANDARD, NORMAL") + } + ) + public void shouldRaiseErrorIfSourceConstantWithoutMatchingConstantInTargetTypeIsNotMapped() { + } + + @Test + @WithClasses(ErroneousOrderMapperDuplicateANY.class) + @ExpectedCompilationOutcome( + value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousOrderMapperDuplicateANY.class, + kind = Kind.ERROR, + line = 39, + messageRegExp = "Source = \"\" or \"\" can only be used once\\." ) + } + ) + public void shouldRaiseErrorIfMappingsContainDuplicateANY() { + } + + + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java b/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java new file mode 100644 index 000000000..2ca2f5550 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperDuplicateANY.java @@ -0,0 +1,42 @@ +/** + * Copyright 2012-2016 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.value; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ErroneousOrderMapperDuplicateANY { + + ErroneousOrderMapperDuplicateANY INSTANCE = Mappers.getMapper( ErroneousOrderMapperDuplicateANY.class ); + + @ValueMappings( { + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = "DEFAULT"), + @ValueMapping( source = "", target = "DEFAULT" ), + @ValueMapping( source = "", target = "DEFAULT" ) + } ) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java b/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java new file mode 100644 index 000000000..17dce60f0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperMappingSameConstantTwice.java @@ -0,0 +1,43 @@ +/** + * Copyright 2012-2016 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.value; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.factory.Mappers; + +/** + * @author Gunnar Morling + */ +@Mapper +public interface ErroneousOrderMapperMappingSameConstantTwice { + + ErroneousOrderMapperMappingSameConstantTwice INSTANCE = Mappers.getMapper( + ErroneousOrderMapperMappingSameConstantTwice.class + ); + + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "EXTRA", target = "DEFAULT"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = "DEFAULT") + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java b/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java new file mode 100644 index 000000000..6f2f1e893 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.java @@ -0,0 +1,35 @@ +/** + * Copyright 2012-2016 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.value; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Gunnar Morling + */ +@Mapper +public interface ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType { + + ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType INSTANCE = Mappers.getMapper( + ErroneousOrderMapperNotMappingConstantWithoutMatchInTargetType.class + ); + + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java b/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java new file mode 100644 index 000000000..da240fe3e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/ErroneousOrderMapperUsingUnknownEnumConstants.java @@ -0,0 +1,41 @@ +/** + * Copyright 2012-2016 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.value; + +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.factory.Mappers; + +/** + * @author Gunnar Morling + */ +@Mapper +public interface ErroneousOrderMapperUsingUnknownEnumConstants { + + ErroneousOrderMapperUsingUnknownEnumConstants INSTANCE = Mappers.getMapper( + ErroneousOrderMapperUsingUnknownEnumConstants.class + ); + + @ValueMappings({ + @ValueMapping(source = "FOO", target = "SPECIAL"), + @ValueMapping(source = "EXTRA", target = "BAR") + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/ExternalOrderType.java b/processor/src/test/java/org/mapstruct/ap/test/value/ExternalOrderType.java new file mode 100644 index 000000000..54335a040 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/ExternalOrderType.java @@ -0,0 +1,28 @@ +/** + * Copyright 2012-2016 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.value; + + +/** + * @author Gunnar Morling + */ +public enum ExternalOrderType { + + RETAIL, B2B, SPECIAL, DEFAULT +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java b/processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java new file mode 100644 index 000000000..a26e8c600 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/OrderDto.java @@ -0,0 +1,36 @@ +/** + * Copyright 2012-2016 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.value; + + +/** + * @author Gunnar Morling + */ +public class OrderDto { + + private ExternalOrderType orderType; + + public ExternalOrderType getOrderType() { + return orderType; + } + + public void setOrderType(ExternalOrderType orderType) { + this.orderType = orderType; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java b/processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java new file mode 100644 index 000000000..f115cf41a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/OrderEntity.java @@ -0,0 +1,36 @@ +/** + * Copyright 2012-2016 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.value; + + +/** + * @author Gunnar Morling + */ +public class OrderEntity { + + private OrderType orderType; + + public OrderType getOrderType() { + return orderType; + } + + public void setOrderType(OrderType orderType) { + this.orderType = orderType; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java new file mode 100644 index 000000000..f9eb5381f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/OrderMapper.java @@ -0,0 +1,47 @@ +/** + * Copyright 2012-2016 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.value; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.factory.Mappers; + +/** + * @author Gunnar Morling + */ +@Mapper +public interface OrderMapper { + + OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + OrderDto orderEntityToDto(OrderEntity order); + + @ValueMappings({ + @ValueMapping(source = "EXTRA", target = "SPECIAL"), + @ValueMapping(source = "STANDARD", target = "DEFAULT"), + @ValueMapping(source = "NORMAL", target = "DEFAULT") + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); + + @InheritInverseConfiguration + OrderType externalOrderTypeToOrderType(ExternalOrderType orderType); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/OrderType.java b/processor/src/test/java/org/mapstruct/ap/test/value/OrderType.java new file mode 100644 index 000000000..814962ea7 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/OrderType.java @@ -0,0 +1,28 @@ +/** + * Copyright 2012-2016 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.value; + + +/** + * @author Gunnar Morling + */ +public enum OrderType { + + RETAIL, B2B, EXTRA, STANDARD, NORMAL +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java b/processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java new file mode 100644 index 000000000..eaefb2cb6 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/value/SpecialOrderMapper.java @@ -0,0 +1,48 @@ +/** + * Copyright 2012-2016 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.value; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.mapstruct.ValueMappings; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface SpecialOrderMapper { + + SpecialOrderMapper INSTANCE = Mappers.getMapper( SpecialOrderMapper.class ); + + OrderDto orderEntityToDto(OrderEntity order); + + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ), + @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" ) + }) + ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); + + @InheritInverseConfiguration + @ValueMapping( target = "EXTRA", source = "SPECIAL" ) + OrderType externalOrderTypeToOrderType(ExternalOrderType orderType); +}