From 69beb9a6b4fd5eba7338b2c4824898c1022baa15 Mon Sep 17 00:00:00 2001
From: sjaakd
Date: Sun, 17 Aug 2014 12:06:21 +0200
Subject: [PATCH] #268 introduction of qualifiers
---
.../java/org/mapstruct/IterableMapping.java | 11 ++
.../main/java/org/mapstruct/MapMapping.java | 23 ++++
.../main/java/org/mapstruct/Qualifier.java | 52 ++++++++
.../src/main/java/org/mapstruct/Mapping.java | 37 ++++--
core/src/main/java/org/mapstruct/Mapping.java | 11 ++
.../ap/model/source/IterableMapping.java | 15 ++-
.../mapstruct/ap/model/source/MapMapping.java | 23 +++-
.../mapstruct/ap/model/source/Mapping.java | 16 ++-
.../source/selector/InheritanceSelector.java | 11 +-
.../model/source/selector/MethodSelector.java | 5 +-
.../source/selector/MethodSelectors.java | 14 +-
.../source/selector/QualifierSelector.java | 125 ++++++++++++++++++
.../model/source/selector/TypeSelector.java | 7 +-
.../selector/XmlElementDeclSelector.java | 7 +-
.../ap/processor/MapperCreationProcessor.java | 71 +++++++---
.../processor/creation/MappingResolver.java | 9 +-
.../selection/qualifier/ErroneousMapper.java | 44 ++++++
.../test/selection/qualifier/FactMapper.java | 42 ++++++
.../selection/qualifier/KeyWordMapper.java | 54 ++++++++
.../test/selection/qualifier/MovieMapper.java | 46 +++++++
.../selection/qualifier/QualifierTest.java | 119 +++++++++++++++++
.../qualifier/annotation/EnglishToGerman.java | 35 +++++
.../annotation/NonQualifierAnnotated.java | 33 +++++
.../qualifier/annotation/TitleTranslator.java | 35 +++++
.../qualifier/bean/AbstractEntry.java | 61 +++++++++
.../qualifier/bean/GermanRelease.java | 27 ++++
.../qualifier/bean/OriginalRelease.java | 27 ++++
.../qualifier/handwritten/Facts.java | 45 +++++++
.../qualifier/handwritten/PlotWords.java | 57 ++++++++
.../handwritten/SomeOtherMapper.java | 33 +++++
.../qualifier/handwritten/Titles.java | 52 ++++++++
.../handwritten/YetAnotherMapper.java | 30 +++++
32 files changed, 1130 insertions(+), 47 deletions(-)
create mode 100644 core-common/src/main/java/org/mapstruct/Qualifier.java
create mode 100644 processor/src/main/java/org/mapstruct/ap/model/source/selector/QualifierSelector.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/ErroneousMapper.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/FactMapper.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/KeyWordMapper.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/MovieMapper.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/EnglishToGerman.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/NonQualifierAnnotated.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/TitleTranslator.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/AbstractEntry.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/GermanRelease.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/OriginalRelease.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/Facts.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/PlotWords.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/SomeOtherMapper.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/Titles.java
create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/YetAnotherMapper.java
diff --git a/core-common/src/main/java/org/mapstruct/IterableMapping.java b/core-common/src/main/java/org/mapstruct/IterableMapping.java
index 6090087b8..eae77ca4e 100644
--- a/core-common/src/main/java/org/mapstruct/IterableMapping.java
+++ b/core-common/src/main/java/org/mapstruct/IterableMapping.java
@@ -18,6 +18,7 @@
*/
package org.mapstruct;
+import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -41,4 +42,14 @@ public @interface IterableMapping {
* @return A date format string as processable by {@link SimpleDateFormat}.
*/
String dateFormat();
+
+ /**
+ * A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple
+ * mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' error.
+ *
+ * A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method.
+ *
+ * @return the qualifiers
+ */
+ Class extends Annotation>[] qualifiedBy() default { };
}
diff --git a/core-common/src/main/java/org/mapstruct/MapMapping.java b/core-common/src/main/java/org/mapstruct/MapMapping.java
index 6fe978a6e..941f1c674 100644
--- a/core-common/src/main/java/org/mapstruct/MapMapping.java
+++ b/core-common/src/main/java/org/mapstruct/MapMapping.java
@@ -18,6 +18,7 @@
*/
package org.mapstruct;
+import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -50,4 +51,26 @@ public @interface MapMapping {
* @return A date format string as processable by {@link SimpleDateFormat}.
*/
String valueDateFormat() default "";
+
+ /**
+ * A key value qualifier can be specified to aid the selection process of a suitable mapper. This is useful in
+ * case multiple mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found'
+ * error.
+ *
+ * A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method.
+ *
+ * @return the qualifiers
+ */
+ Class extends Annotation>[] keyQualifiedBy() default { };
+
+
+ /**
+ * A value qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case
+ * multiple mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' error.
+ *
+ * A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method.
+ *
+ * @return the qualifiers
+ */
+ Class extends Annotation>[] valueQualifiedBy() default { };
}
diff --git a/core-common/src/main/java/org/mapstruct/Qualifier.java b/core-common/src/main/java/org/mapstruct/Qualifier.java
new file mode 100644
index 000000000..65d7d2b2b
--- /dev/null
+++ b/core-common/src/main/java/org/mapstruct/Qualifier.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2012-2014 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;
+
+/**
+ * Declares a target annotation to be a qualifier.
+ *
+ *
+ * For more info see:
+ *
+ * - {@link Mapping#qualifiedBy() }
+ * - {@link IterableMapping#qualifiedBy() }
+ * - {@link MapMapping#keyQualifiedBy() }
+ * - {@link MapMapping#valueQualifiedBy() }
+ *
+ *
+ *
+ * @Qualifier
+ * @Target(ElementType.METHOD)
+ * @Retention(RetentionPolicy.SOURCE)
+ * public @interface EnglishToGerman {
+ * }
+ *
+ *
+ *
+ * @author Sjaak Derksen
+ */
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface Qualifier {
+}
diff --git a/core-jdk8/src/main/java/org/mapstruct/Mapping.java b/core-jdk8/src/main/java/org/mapstruct/Mapping.java
index 225d8ea44..d1c06e8b1 100644
--- a/core-jdk8/src/main/java/org/mapstruct/Mapping.java
+++ b/core-jdk8/src/main/java/org/mapstruct/Mapping.java
@@ -18,6 +18,7 @@
*/
package org.mapstruct;
+import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
@@ -31,9 +32,9 @@ import java.util.Date;
*
* @author Gunnar Morling
*/
-@Retention(RetentionPolicy.SOURCE)
-@Target(ElementType.METHOD)
-@Repeatable(Mappings.class)
+@Retention( RetentionPolicy.SOURCE )
+@Target( ElementType.METHOD )
+@Repeatable( Mappings.class )
public @interface Mapping {
/**
@@ -42,8 +43,8 @@ public @interface Mapping {
* The source name of the configured property as defined by the JavaBeans specification.
* When used to map an enum constant, the name of the constant member is to be given.
*
- * Either this attribute or {@link #constant()} or {@link #expression()} may be specified for a given mapping,
- * but not two at the same time. If this attribute is given, the target property must be specified via
+ * Either this attribute or {@link #constant()} or {@link #expression()} may be specified for a given mapping, but
+ * not two at the same time. If this attribute is given, the target property must be specified via
* {@link #target()}.
*
* @return The source name of the configured property or enum constant.
@@ -67,13 +68,12 @@ public @interface Mapping {
String dateFormat() default "";
/**
- * A constant {@link String} based on which the specified target property is to be set. If the designated
- * target property is not of type {@code String}, the value will be converted by applying a matching conversion
- * method or built-in conversion.
+ * A constant {@link String} based on which the specified target property is to be set. If the designated target
+ * property is not of type {@code String}, the value will be converted by applying a matching conversion method or
+ * built-in conversion.
*
- * Either this attribute or {@link #source()} or {@link #expression()} may be specified for a given mapping,
- * but not two at the same time. If this attribute is given, the target property must be specified via
- * {@link #target()}.
+ * Either this attribute or {@link #source()} or {@link #expression()} may be specified for a given mapping, but not
+ * two at the same time. If this attribute is given, the target property must be specified via {@link #target()}.
*
* @return A constant {@code String} constant specifying the value for the designated target property
*/
@@ -86,9 +86,8 @@ public @interface Mapping {
* {@code expression = "java(new org.example.TimeAndFormat( s.getTime(), s.getFormat() ))")} will insert the java
* expression in the designated {@link #target()} property.
*
- * Either this attribute or {@link #source()} or {@link #constant()} may be specified for a given mapping,
- * but not two at the same time. If this attribute is given, the target property must be specified via
- * {@link #target()}.
+ * Either this attribute or {@link #source()} or {@link #constant()} may be specified for a given mapping, but not
+ * two at the same time. If this attribute is given, the target property must be specified via {@link #target()}.
*
* @return A constant {@code String} constant specifying the value for the designated target property
*/
@@ -103,4 +102,14 @@ public @interface Mapping {
* @return {@code true} if the given property should be ignored, {@code false} otherwise
*/
boolean ignore() default false;
+
+ /**
+ * A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple
+ * mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' error.
+ *
+ * A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method.
+ *
+ * @return the qualifiers
+ */
+ Class extends Annotation>[] qualifiedBy() default { };
}
diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java
index 48d4583f0..56e0e9623 100644
--- a/core/src/main/java/org/mapstruct/Mapping.java
+++ b/core/src/main/java/org/mapstruct/Mapping.java
@@ -18,6 +18,7 @@
*/
package org.mapstruct;
+import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -101,4 +102,14 @@ public @interface Mapping {
* @return {@code true} if the given property should be ignored, {@code false} otherwise
*/
boolean ignore() default false;
+
+ /**
+ * A qualifier can be specified to aid the selection process of a suitable mapper. This is useful in case multiple
+ * mappers (hand written of internal) qualify and result in an 'Ambiguous mapping methods found' error.
+ *
+ * A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method.
+ *
+ * @return the qualifiers
+ */
+ Class extends Annotation>[] qualifiedBy() default { };
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/IterableMapping.java b/processor/src/main/java/org/mapstruct/ap/model/source/IterableMapping.java
index 1e2ee91e6..6381926ba 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/IterableMapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/IterableMapping.java
@@ -18,8 +18,10 @@
*/
package org.mapstruct.ap.model.source;
+import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.prism.IterableMappingPrism;
@@ -31,6 +33,7 @@ import org.mapstruct.ap.prism.IterableMappingPrism;
public class IterableMapping {
private final String dateFormat;
+ private final List qualifiers;
private final AnnotationMirror mirror;
private final AnnotationValue dateFormatAnnotationValue;
@@ -41,13 +44,19 @@ public class IterableMapping {
return new IterableMapping(
iterableMapping.dateFormat(),
+ iterableMapping.qualifiedBy(),
iterableMapping.mirror,
iterableMapping.values.dateFormat()
);
}
- private IterableMapping(String dateFormat, AnnotationMirror mirror, AnnotationValue dateFormatAnnotationValue) {
+ private IterableMapping(
+ String dateFormat,
+ List qualifiers,
+ AnnotationMirror mirror,
+ AnnotationValue dateFormatAnnotationValue) {
this.dateFormat = dateFormat;
+ this.qualifiers = qualifiers;
this.mirror = mirror;
this.dateFormatAnnotationValue = dateFormatAnnotationValue;
}
@@ -56,6 +65,10 @@ public class IterableMapping {
return dateFormat;
}
+ public List getQualifiers() {
+ return qualifiers;
+ }
+
public AnnotationMirror getMirror() {
return mirror;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/MapMapping.java b/processor/src/main/java/org/mapstruct/ap/model/source/MapMapping.java
index 48bfdd873..8552a93ab 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/MapMapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/MapMapping.java
@@ -18,7 +18,9 @@
*/
package org.mapstruct.ap.model.source;
+import java.util.List;
import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.prism.MapMappingPrism;
@@ -30,7 +32,9 @@ import org.mapstruct.ap.prism.MapMappingPrism;
public class MapMapping {
private final String keyFormat;
+ private final List keyQualifiers;
private final String valueFormat;
+ private final List valueQualifiers;
private final AnnotationMirror mirror;
public static MapMapping fromPrism(MapMappingPrism mapMapping) {
@@ -40,14 +44,23 @@ public class MapMapping {
return new MapMapping(
mapMapping.keyDateFormat(),
+ mapMapping.keyQualifiedBy(),
mapMapping.valueDateFormat(),
+ mapMapping.valueQualifiedBy(),
mapMapping.mirror
);
}
- private MapMapping(String keyFormat, String valueFormat, AnnotationMirror mirror) {
+ private MapMapping(
+ String keyFormat,
+ List keyQualifiers,
+ String valueFormat,
+ List valueQualifiers,
+ AnnotationMirror mirror) {
this.keyFormat = keyFormat;
+ this.keyQualifiers = keyQualifiers;
this.valueFormat = valueFormat;
+ this.valueQualifiers = valueQualifiers;
this.mirror = mirror;
}
@@ -55,10 +68,18 @@ public class MapMapping {
return keyFormat;
}
+ public List getKeyQualifiers() {
+ return keyQualifiers;
+ }
+
public String getValueFormat() {
return valueFormat;
}
+ public List getValueQualifiers() {
+ return valueQualifiers;
+ }
+
public AnnotationMirror getMirror() {
return mirror;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java
index 64a12fbc5..45deaf4ae 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java
@@ -29,6 +29,7 @@ import javax.annotation.processing.Messager;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import org.mapstruct.ap.prism.MappingPrism;
@@ -52,6 +53,7 @@ public class Mapping {
private final String javaExpression;
private final String targetName;
private final String dateFormat;
+ private final List qualifiers;
private final boolean isIgnored;
private final AnnotationMirror mirror;
private final AnnotationValue sourceAnnotationValue;
@@ -126,6 +128,7 @@ public class Mapping {
mappingPrism.expression(),
mappingPrism.target(),
mappingPrism.dateFormat(),
+ mappingPrism.qualifiedBy(),
mappingPrism.ignore(),
mappingPrism.mirror,
mappingPrism.values.source(),
@@ -153,9 +156,10 @@ public class Mapping {
}
//CHECKSTYLE:OFF
- private Mapping(String sourceName, String sourceParameterName, String sourcePropertyName, String constant,
- String expression, String targetName, String dateFormat, boolean isIgnored, AnnotationMirror mirror,
- AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) {
+ private Mapping( String sourceName, String sourceParameterName, String sourcePropertyName, String constant,
+ String expression, String targetName, String dateFormat, List qualifiers,
+ boolean isIgnored, AnnotationMirror mirror, AnnotationValue sourceAnnotationValue,
+ AnnotationValue targetAnnotationValue) {
this.sourceName = sourceName;
this.sourceParameterName = sourceParameterName;
this.sourcePropertyName = sourcePropertyName;
@@ -165,6 +169,7 @@ public class Mapping {
this.javaExpression = javaExpressionMatcher.matches() ? javaExpressionMatcher.group( 1 ).trim() : "";
this.targetName = targetName.equals( "" ) ? sourceName : targetName;
this.dateFormat = dateFormat;
+ this.qualifiers = qualifiers;
this.isIgnored = isIgnored;
this.mirror = mirror;
this.sourceAnnotationValue = sourceAnnotationValue;
@@ -216,6 +221,10 @@ public class Mapping {
return dateFormat;
}
+ public List getQualifiers() {
+ return qualifiers;
+ }
+
public boolean isIgnored() {
return isIgnored;
}
@@ -244,6 +253,7 @@ public class Mapping {
expression,
sourceName,
dateFormat,
+ qualifiers,
isIgnored,
mirror,
sourceAnnotationValue,
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java
index 4f31e86ba..7e9cebcd8 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/InheritanceSelector.java
@@ -20,6 +20,7 @@ package org.mapstruct.ap.model.source.selector;
import java.util.ArrayList;
import java.util.List;
+import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
@@ -33,8 +34,14 @@ import org.mapstruct.ap.model.source.Method;
public class InheritanceSelector implements MethodSelector {
@Override
- public List getMatchingMethods(Method mappingMethod, List methods, Type parameterType,
- Type returnType, String targetPropertyName) {
+ public List getMatchingMethods(
+ Method mappingMethod,
+ List methods,
+ Type parameterType,
+ Type returnType,
+ List qualifiers,
+ String targetPropertyName
+ ) {
List candidatesWithBestMatchingSourceType = new ArrayList();
int bestMatchingSourceTypeDistance = Integer.MAX_VALUE;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelector.java
index 2fb494da5..f939d1747 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelector.java
@@ -19,6 +19,7 @@
package org.mapstruct.ap.model.source.selector;
import java.util.List;
+import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method;
@@ -40,10 +41,12 @@ public interface MethodSelector {
* @param methods list of available methods
* @param parameterType parameter type that should be matched
* @param returnType return type that should be matched
+ * @param qualifiers list of custom annotations, used in the qualifying process
* @param targetPropertyName some information can be derived from the target property
*
* @return list of methods that passes the matching process
*/
List getMatchingMethods(Method mappingMethod, List methods, Type parameterType,
- Type returnType, String targetPropertyName);
+ Type returnType, List qualifiers,
+ String targetPropertyName);
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java
index ecd352c21..f95bfe396 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java
@@ -21,6 +21,8 @@ package org.mapstruct.ap.model.source.selector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.model.common.Parameter;
@@ -37,18 +39,21 @@ public class MethodSelectors implements MethodSelector {
private final List selectors;
- public MethodSelectors(Types typeUtils, TypeFactory typeFactory) {
+ public MethodSelectors(Types typeUtils, Elements elementUtils, TypeFactory typeFactory) {
selectors =
Arrays.asList(
new TypeSelector( typeFactory ),
new InheritanceSelector(),
- new XmlElementDeclSelector( typeUtils )
+ new XmlElementDeclSelector( typeUtils ),
+ new QualifierSelector( typeUtils, elementUtils )
);
}
@Override
- public List getMatchingMethods(Method mappingMethod, List methods, Type parameterType,
- Type returnType, String targetPropertyName) {
+ public List getMatchingMethods(Method mappingMethod, List methods,
+ Type parameterType, Type returnType,
+ List qualifiers,
+ String targetPropertyName) {
List candidates = new ArrayList( methods );
@@ -58,6 +63,7 @@ public class MethodSelectors implements MethodSelector {
candidates,
parameterType,
returnType,
+ qualifiers,
targetPropertyName
);
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/QualifierSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/QualifierSelector.java
new file mode 100644
index 000000000..3c4beb05a
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/QualifierSelector.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright 2012-2014 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.model.source.selector;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import org.mapstruct.Qualifier;
+import org.mapstruct.ap.model.common.Type;
+import org.mapstruct.ap.model.source.Method;
+import org.mapstruct.ap.model.source.SourceMethod;
+
+/**
+ * This selector selects a best match based on qualifiers name.
+ *
+ * @author Sjaak Derksen
+ */
+public class QualifierSelector implements MethodSelector {
+
+ private final Types typeUtils;
+ private final TypeMirror qualifierType;
+
+
+ public QualifierSelector( Types typeUtils, Elements elementUtils ) {
+ this.typeUtils = typeUtils;
+ qualifierType = elementUtils.getTypeElement( Qualifier.class.getCanonicalName() ).asType();
+ }
+
+ @Override
+ public List getMatchingMethods(Method mappingMethod, List methods,
+ Type parameterType, Type returnType,
+ List qualifiers,
+ String targetPropertyName) {
+ List matches = new ArrayList();
+
+ if ( qualifiers == null || qualifiers.isEmpty() ) {
+ return methods;
+ }
+
+ for ( T candidate : methods ) {
+
+ if ( !( candidate instanceof SourceMethod ) ) {
+ continue;
+ }
+
+ // retrieve annotations
+ Set combinedAnnotations = new HashSet();
+
+ // first from the method itself
+ SourceMethod candidateSM = (SourceMethod) candidate;
+ List extends AnnotationMirror> methodAnnotations = candidateSM.getExecutable().getAnnotationMirrors();
+ for ( AnnotationMirror methodAnnotation : methodAnnotations ) {
+ addOnlyWhenQualifier( combinedAnnotations, methodAnnotation );
+ }
+
+ // then from the mapper (if declared)
+ Type mapper = candidate.getDeclaringMapper();
+ if ( mapper != null ) {
+ List extends AnnotationMirror> mapperAnnotations = mapper.getTypeElement().getAnnotationMirrors();
+ for ( AnnotationMirror mapperAnnotation : mapperAnnotations ) {
+ addOnlyWhenQualifier( combinedAnnotations, mapperAnnotation );
+ }
+ }
+
+ // now count if all qualifiers are machted
+ int matchingQualifierCounter = 0;
+ for ( TypeMirror qualifier : qualifiers) {
+ for ( TypeMirror annotationType : combinedAnnotations ) {
+ if ( typeUtils.isSameType( qualifier, annotationType ) ) {
+ matchingQualifierCounter++;
+ break;
+ }
+ }
+ }
+
+ if ( matchingQualifierCounter == qualifiers.size() ) {
+ // all qualifiers are matched with a qualifying annotation, add candidate
+ matches.add( candidate );
+ }
+ }
+ if ( !matches.isEmpty() ) {
+ return matches;
+ }
+ else {
+ return methods;
+ }
+ }
+
+ private void addOnlyWhenQualifier( Set annotationSet, AnnotationMirror candidate ) {
+
+ // get all the annotations of the candidate annotation
+ List extends AnnotationMirror> annotationsOfCandidate
+ = candidate.getAnnotationType().asElement().getAnnotationMirrors();
+
+ // only add the candidate annotation when the candidate itself has the annotation 'Qualifier'
+ for ( AnnotationMirror annotationOfCandidate : annotationsOfCandidate ) {
+ if ( typeUtils.isSameType( annotationOfCandidate.getAnnotationType(), qualifierType ) ) {
+ annotationSet.add( candidate.getAnnotationType() );
+ break;
+ }
+ }
+ }
+}
+
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/TypeSelector.java
index 6d7504f4d..1ae06ac81 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/selector/TypeSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/TypeSelector.java
@@ -20,6 +20,7 @@ package org.mapstruct.ap.model.source.selector;
import java.util.ArrayList;
import java.util.List;
+import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory;
@@ -41,8 +42,10 @@ public class TypeSelector implements MethodSelector {
}
@Override
- public List getMatchingMethods(Method mappingMethod, List methods, Type parameterType,
- Type returnType, String targetPropertyName) {
+ public List getMatchingMethods(Method mappingMethod, List methods,
+ Type parameterType, Type returnType,
+ List qualifiers,
+ String targetPropertyName) {
List result = new ArrayList();
for ( T method : methods ) {
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/XmlElementDeclSelector.java
index 479b94abb..6ba689ed9 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/selector/XmlElementDeclSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/XmlElementDeclSelector.java
@@ -51,13 +51,16 @@ public class XmlElementDeclSelector implements MethodSelector {
}
@Override
- public List getMatchingMethods(Method mappingMethod, List methods, Type parameterType,
- Type returnType, String targetPropertyName) {
+ public List getMatchingMethods(Method mappingMethod, List methods,
+ Type parameterType, Type returnType,
+ List qualifiers,
+ String targetPropertyName) {
// only true source methods are qualifying
if ( !(mappingMethod instanceof SourceMethod) ) {
return methods;
}
+
SourceMethod sourceMappingMethod = (SourceMethod) mappingMethod;
List nameMatches = new ArrayList();
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
index e4e4bd146..b4cf61f2b 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
@@ -297,11 +297,21 @@ public class MapperCreationProcessor implements ModelElementProcessor qualifiers = null;
+ if ( method.getIterableMapping() != null ) {
+ dateFormat = method.getIterableMapping().getDateFormat();
+ qualifiers = method.getIterableMapping().getQualifiers();
+ }
+
+ IterableMappingMethod iterableMappingMethod = getIterableMappingMethod(
+ mapperReferences,
+ methods,
+ method,
+ dateFormat,
+ qualifiers
+ );
hasFactoryMethod = iterableMappingMethod.getFactoryMethod() != null;
mappingMethods.add( iterableMappingMethod );
}
@@ -312,13 +322,24 @@ public class MapperCreationProcessor implements ModelElementProcessor keyQualifiers = null;
+ List valueQualifiers = null;
if ( method.getMapMapping() != null ) {
keyDateFormat = method.getMapMapping().getKeyFormat();
valueDateFormat = method.getMapMapping().getValueFormat();
+ keyQualifiers = method.getMapMapping().getKeyQualifiers();
+ valueQualifiers = method.getMapMapping().getValueQualifiers();
}
- MapMappingMethod mapMappingMethod
- = getMapMappingMethod( mapperReferences, methods, method, keyDateFormat, valueDateFormat );
+ MapMappingMethod mapMappingMethod = getMapMappingMethod(
+ mapperReferences,
+ methods,
+ method,
+ keyDateFormat,
+ valueDateFormat,
+ keyQualifiers,
+ valueQualifiers
+ );
hasFactoryMethod = mapMappingMethod.getFactoryMethod() != null;
mappingMethods.add( mapMappingMethod );
}
@@ -438,9 +459,11 @@ public class MapperCreationProcessor implements ModelElementProcessor qualifiers = null;
String sourcePropertyName;
if ( mapping != null ) {
dateFormat = mapping.getDateFormat();
+ qualifiers = mapping.getQualifiers();
sourcePropertyName = mapping.getSourcePropertyName();
}
else {
@@ -466,7 +489,8 @@ public class MapperCreationProcessor implements ModelElementProcessor qualifiers) {
Type sourceType;
Type targetType;
@@ -815,6 +842,7 @@ public class MapperCreationProcessor implements ModelElementProcessor qualifiers) {
// source
String mappedElement = "constant '" + constantExpression + "'";
@@ -944,6 +973,7 @@ public class MapperCreationProcessor implements ModelElementProcessor mapperReferences,
List methods,
Method method,
- String dateFormat ) {
+ String dateFormat,
+ List qualifiers) {
Type sourceElementType = method.getSourceParameters().iterator().next().getType().getTypeParameters().get( 0 );
Type targetElementType = method.getResultType().getTypeParameters().get( 0 );
String conversionStr = Strings.getSaveVariableName( sourceElementType.getName(), method.getParameterNames() );
@@ -1000,6 +1031,7 @@ public class MapperCreationProcessor implements ModelElementProcessor mapperReferences, List methods,
- Method method, String keyDateFormat, String valueDateFormat ) {
+ private MapMappingMethod getMapMappingMethod(List mapperReferences,
+ List methods,
+ Method method,
+ String keyDateFormat,
+ String valueDateFormat,
+ List keyQualifiers,
+ List valueQualifiers ) {
List sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters();
List resultTypeParams = method.getResultType().getTypeParameters();
@@ -1037,6 +1074,7 @@ public class MapperCreationProcessor implements ModelElementProcessor();
- this.methodSelectors = new MethodSelectors( typeUtils, typeFactory );
+ this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, typeFactory );
this.typeUtils = typeUtils;
}
@@ -105,6 +105,7 @@ public class MappingResolver {
* @param targetType return type to match
* @param targetPropertyName name of the target property
* @param dateFormat used for formatting dates in build in methods that need context information
+ * @param qualifiers used for further select the appropriate mapping method based on class and name
* @param sourceReference call to source type as string
*
* @return an assignment to a method parameter, which can either be:
@@ -123,6 +124,7 @@ public class MappingResolver {
Type targetType,
String targetPropertyName,
String dateFormat,
+ List qualifiers,
String sourceReference ) {
ResolvingAttempt attempt = new ResolvingAttempt( mappingMethod,
@@ -131,6 +133,7 @@ public class MappingResolver {
methods,
targetPropertyName,
dateFormat,
+ qualifiers,
sourceReference,
this
);
@@ -151,6 +154,7 @@ public class MappingResolver {
private final List methods;
private final String targetPropertyName;
private final String dateFormat;
+ private final List qualifiers;
private final String sourceReference;
private final MappingResolver context;
@@ -165,6 +169,7 @@ public class MappingResolver {
List methods,
String targetPropertyName,
String dateFormat,
+ List qualifiers,
String sourceReference,
MappingResolver context ) {
this.mappingMethod = mappingMethod;
@@ -173,6 +178,7 @@ public class MappingResolver {
this.methods = methods;
this.targetPropertyName = targetPropertyName;
this.dateFormat = dateFormat;
+ this.qualifiers = qualifiers;
this.sourceReference = sourceReference;
this.context = context;
this.virtualMethodCandidates = new HashSet();
@@ -403,6 +409,7 @@ public class MappingResolver {
methods,
sourceType,
returnType,
+ qualifiers,
targetPropertyName
);
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/ErroneousMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/ErroneousMapper.java
new file mode 100644
index 000000000..7cef559a0
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/ErroneousMapper.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier;
+
+import org.mapstruct.ap.test.selection.qualifier.bean.GermanRelease;
+import org.mapstruct.ap.test.selection.qualifier.bean.OriginalRelease;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.ap.test.selection.qualifier.annotation.NonQualifierAnnotated;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.SomeOtherMapper;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.YetAnotherMapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@Mapper( uses = { SomeOtherMapper.class, YetAnotherMapper.class } )
+public interface ErroneousMapper {
+
+ ErroneousMapper INSTANCE = Mappers.getMapper( ErroneousMapper.class );
+
+ @Mappings( {
+ @Mapping( source = "title", qualifiedBy = { NonQualifierAnnotated.class } ), } )
+ GermanRelease toGerman( OriginalRelease movies );
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/FactMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/FactMapper.java
new file mode 100644
index 000000000..776820873
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/FactMapper.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier;
+
+import java.util.List;
+import java.util.Map;
+import org.mapstruct.MapMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.Facts;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.PlotWords;
+import org.mapstruct.factory.Mappers;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@Mapper( uses = { Facts.class, PlotWords.class } )
+public interface FactMapper {
+
+ FactMapper INSTANCE = Mappers.getMapper( FactMapper.class );
+
+ @MapMapping( keyQualifiedBy = { EnglishToGerman.class }, valueQualifiedBy = { EnglishToGerman.class } )
+ Map> mapFacts( Map> keyWords );
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/KeyWordMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/KeyWordMapper.java
new file mode 100644
index 000000000..dbf3a7d02
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/KeyWordMapper.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+import java.util.Map;
+import org.mapstruct.IterableMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.SomeOtherMapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@Mapper( uses = { SomeOtherMapper.class } )
+public abstract class KeyWordMapper {
+
+ private static final Map EN_GER = ImmutableMap.builder()
+ .put( "magnificent", "Großartig" )
+ .put( "evergreen", "Evergreen" )
+ .put( "classic", "Klassiker" )
+ .put( "box office flop", "Kasse Flop" )
+ .build();
+
+
+ public static final KeyWordMapper INSTANCE = Mappers.getMapper( KeyWordMapper.class );
+
+ @IterableMapping( dateFormat = "", qualifiedBy = { EnglishToGerman.class } )
+ abstract List mapKeyWords( List keyWords );
+
+ @EnglishToGerman
+ public String mapKeyWord( String keyword ) {
+ return EN_GER.get( keyword );
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/MovieMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/MovieMapper.java
new file mode 100644
index 000000000..bf1bc9b61
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/MovieMapper.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier;
+
+import org.mapstruct.ap.test.selection.qualifier.bean.GermanRelease;
+import org.mapstruct.ap.test.selection.qualifier.bean.OriginalRelease;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.Titles;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman;
+import org.mapstruct.ap.test.selection.qualifier.annotation.TitleTranslator;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.SomeOtherMapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@Mapper( uses = { Titles.class, SomeOtherMapper.class, KeyWordMapper.class, FactMapper.class } )
+public interface MovieMapper {
+
+ MovieMapper INSTANCE = Mappers.getMapper( MovieMapper.class );
+
+ @Mappings( {
+ @Mapping( source = "title", qualifiedBy = { TitleTranslator.class, EnglishToGerman.class } ),
+ } )
+ GermanRelease toGerman( OriginalRelease movies );
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java
new file mode 100644
index 000000000..a54d871e0
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/QualifierTest.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier;
+
+import org.mapstruct.ap.test.selection.qualifier.bean.GermanRelease;
+import org.mapstruct.ap.test.selection.qualifier.bean.OriginalRelease;
+import org.mapstruct.ap.test.selection.qualifier.bean.AbstractEntry;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.Titles;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.tools.Diagnostic.Kind;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.MapAssert.entry;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman;
+import org.mapstruct.ap.test.selection.qualifier.annotation.NonQualifierAnnotated;
+import org.mapstruct.ap.test.selection.qualifier.annotation.TitleTranslator;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.Facts;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.PlotWords;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.SomeOtherMapper;
+import org.mapstruct.ap.test.selection.qualifier.handwritten.YetAnotherMapper;
+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;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@IssueKey( "268" )
+@WithClasses( {
+ OriginalRelease.class,
+ GermanRelease.class,
+ AbstractEntry.class,
+ SomeOtherMapper.class,
+ NonQualifierAnnotated.class
+} )
+@RunWith( AnnotationProcessorTestRunner.class )
+public class QualifierTest {
+
+ @Test
+ @WithClasses( {
+ Titles.class,
+ Facts.class,
+ PlotWords.class,
+ OriginalRelease.class,
+ EnglishToGerman.class,
+ TitleTranslator.class,
+ MovieMapper.class,
+ KeyWordMapper.class,
+ FactMapper.class } )
+ public void shouldMatchClassAndMethod() {
+
+ OriginalRelease foreignMovies = new OriginalRelease();
+ foreignMovies.setTitle( "Sixth Sense, The" );
+ foreignMovies.setKeyWords( Arrays.asList( "evergreen", "magnificent" ) );
+ Map> facts = new HashMap>();
+ facts.put( "director", Arrays.asList( "M. Night Shyamalan" ) );
+ facts.put( "cast", Arrays.asList( "Bruce Willis", "Haley Joel Osment", "Toni Collette" ) );
+ facts.put( "plot keywords", Arrays.asList( "boy", "child psychologist", "I see dead people" ) );
+ foreignMovies.setFacts( facts );
+
+ GermanRelease germanMovies = MovieMapper.INSTANCE.toGerman( foreignMovies );
+ assertThat( germanMovies ).isNotNull();
+ assertThat( germanMovies.getTitle() ).isEqualTo( "Der sechste Sinn" );
+ assertThat( germanMovies.getKeyWords() ).isNotNull();
+ assertThat( germanMovies.getKeyWords().size() ).isEqualTo( 2 );
+ assertThat( germanMovies.getKeyWords() ).containsSequence( "Evergreen", "Großartig" );
+
+ assertThat( germanMovies.getFacts() ).isNotNull();
+ assertThat( germanMovies.getFacts() ).hasSize( 3 );
+ assertThat( germanMovies.getFacts() ).includes(
+ entry( "Regisseur", Arrays.asList( "M. Night Shyamalan" ) ),
+ entry( "Besetzung", Arrays.asList( "Bruce Willis", "Haley Joel Osment", "Toni Collette" ) ),
+ entry( "Handlungstichwörter", Arrays.asList( "Jungen", "Kinderpsychologe", "Ich sehe tote Menschen" ) )
+ );
+ }
+
+ @Test
+ @WithClasses( {
+ YetAnotherMapper.class,
+ ErroneousMapper.class
+ } )
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic( type = ErroneousMapper.class,
+ kind = Kind.ERROR,
+ line = 42,
+ messageRegExp = "Ambiguous mapping methods found for mapping property 'title' "
+ + "from java.lang.String to java.lang.String.*" )
+ }
+ )
+ public void shouldNotProduceMatchingMethod() {
+ }
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/EnglishToGerman.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/EnglishToGerman.java
new file mode 100644
index 000000000..853a3bf9d
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/EnglishToGerman.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.mapstruct.Qualifier;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@Qualifier
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.SOURCE)
+public @interface EnglishToGerman {
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/NonQualifierAnnotated.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/NonQualifierAnnotated.java
new file mode 100644
index 000000000..e5673d9fb
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/NonQualifierAnnotated.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.SOURCE)
+public @interface NonQualifierAnnotated {
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/TitleTranslator.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/TitleTranslator.java
new file mode 100644
index 000000000..b4f59175a
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/annotation/TitleTranslator.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.mapstruct.Qualifier;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@Qualifier
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.SOURCE)
+public @interface TitleTranslator {
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/AbstractEntry.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/AbstractEntry.java
new file mode 100644
index 000000000..661f63411
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/AbstractEntry.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.bean;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+public abstract class AbstractEntry {
+
+ private String title;
+
+ private List keyWords;
+
+ private Map> facts;
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle( String title ) {
+ this.title = title;
+ }
+
+ public List getKeyWords() {
+ return keyWords;
+ }
+
+ public void setKeyWords( List keyWords ) {
+ this.keyWords = keyWords;
+ }
+
+ public Map > getFacts() {
+ return facts;
+ }
+
+ public void setFacts( Map > facts ) {
+ this.facts = facts;
+ }
+
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/GermanRelease.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/GermanRelease.java
new file mode 100644
index 000000000..7088fcbe4
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/GermanRelease.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.bean;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+public class GermanRelease extends AbstractEntry {
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/OriginalRelease.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/OriginalRelease.java
new file mode 100644
index 000000000..180a86f5f
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/bean/OriginalRelease.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.bean;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+public class OriginalRelease extends AbstractEntry {
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/Facts.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/Facts.java
new file mode 100644
index 000000000..9e2f6b27e
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/Facts.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.handwritten;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+public class Facts {
+
+ private static final Map EN_GER = ImmutableMap.builder()
+ .put( "director", "Regisseur" )
+ .put( "cast", "Besetzung" )
+ .put( "cameo", "Kurzauftritt" )
+ .put( "soundtrack", "Filmmusik" )
+ .put( "plot keywords", "Handlungstichwörter" )
+ .build();
+
+ @EnglishToGerman
+ public String translateFactName( String fact ) {
+ String result = EN_GER.get( fact );
+ return result != null ? result : fact;
+ }
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/PlotWords.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/PlotWords.java
new file mode 100644
index 000000000..e42834495
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/PlotWords.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.handwritten;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+public class PlotWords {
+
+ private static final Map EN_GER = ImmutableMap.builder()
+ .put( "boy", "Jungen" )
+ .put( "child psychologist", "Kinderpsychologe" )
+ .put( "I see dead people", "Ich sehe tote Menschen" )
+ .build();
+
+ @EnglishToGerman
+ public List translate( List keywords ) {
+ List result = new ArrayList();
+ for ( String keyword : keywords ) {
+ if ( EN_GER.containsKey( keyword ) ) {
+ result.add( EN_GER.get( keyword ) );
+ }
+ else {
+ result.add( keyword );
+ }
+ }
+ return result;
+ }
+
+ public List methodNotToSelect( List title ) {
+ throw new AssertionError( "method should not be called" );
+ }
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/SomeOtherMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/SomeOtherMapper.java
new file mode 100644
index 000000000..4adf6ca9d
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/SomeOtherMapper.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.handwritten;
+
+import org.mapstruct.ap.test.selection.qualifier.annotation.NonQualifierAnnotated;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+public class SomeOtherMapper {
+
+ @NonQualifierAnnotated
+ public String methodNotToSelect( String title ) {
+ throw new AssertionError( "method should not be called" );
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/Titles.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/Titles.java
new file mode 100644
index 000000000..f3e0d71ba
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/Titles.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.handwritten;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.mapstruct.ap.test.selection.qualifier.annotation.EnglishToGerman;
+import org.mapstruct.ap.test.selection.qualifier.annotation.TitleTranslator;
+//import org.mapstruct.ap.test.selection.qualifier.annotation.TitleTranslator;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+@TitleTranslator
+public class Titles {
+
+ private static final Map EN_GER = ImmutableMap.builder()
+ .put( "Star Wars", "Krieg der Sterne" )
+ .put( "Sixth Sense, The", "Der sechste Sinn" )
+ .put( "Shawshank Redemption, The", "Die Verurteilten" )
+ .put( "Trainspotting", "Neue Helden" )
+ .put( "Never Say Never", "Sag niemals nie" )
+ .build();
+
+ @EnglishToGerman
+ public String translateTitle(String title) {
+ return EN_GER.get( title );
+ }
+
+
+ public String methodNotToSelect( String title ) {
+ throw new AssertionError( "method should not be called" );
+ }
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/YetAnotherMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/YetAnotherMapper.java
new file mode 100644
index 000000000..0aea924e2
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/qualifier/handwritten/YetAnotherMapper.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2012-2014 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.selection.qualifier.handwritten;
+
+/**
+ *
+ * @author Sjaak Derksen
+ */
+public class YetAnotherMapper {
+
+ public String methodNotToSelect( String title ) {
+ throw new AssertionError( "method should not be called" );
+ }
+}