#268 introduction of qualifiers

This commit is contained in:
sjaakd 2014-08-17 12:06:21 +02:00
parent 6d7a4da067
commit 69beb9a6b4
32 changed files with 1130 additions and 47 deletions

View File

@ -18,6 +18,7 @@
*/ */
package org.mapstruct; package org.mapstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -41,4 +42,14 @@ public @interface IterableMapping {
* @return A date format string as processable by {@link SimpleDateFormat}. * @return A date format string as processable by {@link SimpleDateFormat}.
*/ */
String dateFormat(); 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 { };
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.mapstruct; package org.mapstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -50,4 +51,26 @@ public @interface MapMapping {
* @return A date format string as processable by {@link SimpleDateFormat}. * @return A date format string as processable by {@link SimpleDateFormat}.
*/ */
String valueDateFormat() default ""; 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 { };
} }

View File

@ -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.
*
* <p>
* For more info see:
* <ol>
* <li>{@link Mapping#qualifiedBy() }</li>
* <li>{@link IterableMapping#qualifiedBy() }</li>
* <li>{@link MapMapping#keyQualifiedBy() }</li>
* <li>{@link MapMapping#valueQualifiedBy() }</li>
* </p>
* <p>
* <code>
* @Qualifier
* @Target(ElementType.METHOD)
* @Retention(RetentionPolicy.SOURCE)
* public @interface EnglishToGerman {
* }
* </code>
* </p>
*
* @author Sjaak Derksen
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Qualifier {
}

View File

@ -18,6 +18,7 @@
*/ */
package org.mapstruct; package org.mapstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable; import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -31,9 +32,9 @@ import java.util.Date;
* *
* @author Gunnar Morling * @author Gunnar Morling
*/ */
@Retention(RetentionPolicy.SOURCE) @Retention( RetentionPolicy.SOURCE )
@Target(ElementType.METHOD) @Target( ElementType.METHOD )
@Repeatable(Mappings.class) @Repeatable( Mappings.class )
public @interface Mapping { public @interface Mapping {
/** /**
@ -42,8 +43,8 @@ public @interface Mapping {
* <li>The source name of the configured property as defined by the JavaBeans specification.</li> * <li>The source name of the configured property as defined by the JavaBeans specification.</li>
* <li>When used to map an enum constant, the name of the constant member is to be given.</li> * <li>When used to map an enum constant, the name of the constant member is to be given.</li>
* </ol> * </ol>
* Either this attribute or {@link #constant()} or {@link #expression()} may be specified for a given mapping, * Either this attribute or {@link #constant()} or {@link #expression()} may be specified for a given mapping, but
* but not two at the same time. If this attribute is given, the target property must be specified via * not two at the same time. If this attribute is given, the target property must be specified via
* {@link #target()}. * {@link #target()}.
* *
* @return The source name of the configured property or enum constant. * @return The source name of the configured property or enum constant.
@ -67,13 +68,12 @@ public @interface Mapping {
String dateFormat() default ""; String dateFormat() default "";
/** /**
* A constant {@link String} based on which the specified target property is to be set. If the designated * A constant {@link String} based on which the specified target property is to be set. If the designated target
* target property is not of type {@code String}, the value will be converted by applying a matching conversion * property is not of type {@code String}, the value will be converted by applying a matching conversion method or
* method or built-in conversion. * built-in conversion.
* <p> * <p>
* Either this attribute or {@link #source()} or {@link #expression()} may be specified for a given mapping, * Either this attribute or {@link #source()} or {@link #expression()} may be specified for a given mapping, but not
* but not two at the same time. If this attribute is given, the target property must be specified via * two at the same time. If this attribute is given, the target property must be specified via {@link #target()}.
* {@link #target()}.
* *
* @return A constant {@code String} constant specifying the value for the designated target property * @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 * {@code expression = "java(new org.example.TimeAndFormat( s.getTime(), s.getFormat() ))")} will insert the java
* expression in the designated {@link #target()} property. * expression in the designated {@link #target()} property.
* <p> * <p>
* Either this attribute or {@link #source()} or {@link #constant()} may be specified for a given mapping, * Either this attribute or {@link #source()} or {@link #constant()} may be specified for a given mapping, but not
* but not two at the same time. If this attribute is given, the target property must be specified via * two at the same time. If this attribute is given, the target property must be specified via {@link #target()}.
* {@link #target()}.
* *
* @return A constant {@code String} constant specifying the value for the designated target property * @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 * @return {@code true} if the given property should be ignored, {@code false} otherwise
*/ */
boolean ignore() default false; 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 { };
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.mapstruct; package org.mapstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; 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 * @return {@code true} if the given property should be ignored, {@code false} otherwise
*/ */
boolean ignore() default false; 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 { };
} }

View File

@ -18,8 +18,10 @@
*/ */
package org.mapstruct.ap.model.source; package org.mapstruct.ap.model.source;
import java.util.List;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.prism.IterableMappingPrism; import org.mapstruct.ap.prism.IterableMappingPrism;
@ -31,6 +33,7 @@ import org.mapstruct.ap.prism.IterableMappingPrism;
public class IterableMapping { public class IterableMapping {
private final String dateFormat; private final String dateFormat;
private final List<TypeMirror> qualifiers;
private final AnnotationMirror mirror; private final AnnotationMirror mirror;
private final AnnotationValue dateFormatAnnotationValue; private final AnnotationValue dateFormatAnnotationValue;
@ -41,13 +44,19 @@ public class IterableMapping {
return new IterableMapping( return new IterableMapping(
iterableMapping.dateFormat(), iterableMapping.dateFormat(),
iterableMapping.qualifiedBy(),
iterableMapping.mirror, iterableMapping.mirror,
iterableMapping.values.dateFormat() iterableMapping.values.dateFormat()
); );
} }
private IterableMapping(String dateFormat, AnnotationMirror mirror, AnnotationValue dateFormatAnnotationValue) { private IterableMapping(
String dateFormat,
List<TypeMirror> qualifiers,
AnnotationMirror mirror,
AnnotationValue dateFormatAnnotationValue) {
this.dateFormat = dateFormat; this.dateFormat = dateFormat;
this.qualifiers = qualifiers;
this.mirror = mirror; this.mirror = mirror;
this.dateFormatAnnotationValue = dateFormatAnnotationValue; this.dateFormatAnnotationValue = dateFormatAnnotationValue;
} }
@ -56,6 +65,10 @@ public class IterableMapping {
return dateFormat; return dateFormat;
} }
public List<TypeMirror> getQualifiers() {
return qualifiers;
}
public AnnotationMirror getMirror() { public AnnotationMirror getMirror() {
return mirror; return mirror;
} }

View File

@ -18,7 +18,9 @@
*/ */
package org.mapstruct.ap.model.source; package org.mapstruct.ap.model.source;
import java.util.List;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.prism.MapMappingPrism; import org.mapstruct.ap.prism.MapMappingPrism;
@ -30,7 +32,9 @@ import org.mapstruct.ap.prism.MapMappingPrism;
public class MapMapping { public class MapMapping {
private final String keyFormat; private final String keyFormat;
private final List<TypeMirror> keyQualifiers;
private final String valueFormat; private final String valueFormat;
private final List<TypeMirror> valueQualifiers;
private final AnnotationMirror mirror; private final AnnotationMirror mirror;
public static MapMapping fromPrism(MapMappingPrism mapMapping) { public static MapMapping fromPrism(MapMappingPrism mapMapping) {
@ -40,14 +44,23 @@ public class MapMapping {
return new MapMapping( return new MapMapping(
mapMapping.keyDateFormat(), mapMapping.keyDateFormat(),
mapMapping.keyQualifiedBy(),
mapMapping.valueDateFormat(), mapMapping.valueDateFormat(),
mapMapping.valueQualifiedBy(),
mapMapping.mirror mapMapping.mirror
); );
} }
private MapMapping(String keyFormat, String valueFormat, AnnotationMirror mirror) { private MapMapping(
String keyFormat,
List<TypeMirror> keyQualifiers,
String valueFormat,
List<TypeMirror> valueQualifiers,
AnnotationMirror mirror) {
this.keyFormat = keyFormat; this.keyFormat = keyFormat;
this.keyQualifiers = keyQualifiers;
this.valueFormat = valueFormat; this.valueFormat = valueFormat;
this.valueQualifiers = valueQualifiers;
this.mirror = mirror; this.mirror = mirror;
} }
@ -55,10 +68,18 @@ public class MapMapping {
return keyFormat; return keyFormat;
} }
public List<TypeMirror> getKeyQualifiers() {
return keyQualifiers;
}
public String getValueFormat() { public String getValueFormat() {
return valueFormat; return valueFormat;
} }
public List<TypeMirror> getValueQualifiers() {
return valueQualifiers;
}
public AnnotationMirror getMirror() { public AnnotationMirror getMirror() {
return mirror; return mirror;
} }

View File

@ -29,6 +29,7 @@ import javax.annotation.processing.Messager;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import org.mapstruct.ap.prism.MappingPrism; import org.mapstruct.ap.prism.MappingPrism;
@ -52,6 +53,7 @@ public class Mapping {
private final String javaExpression; private final String javaExpression;
private final String targetName; private final String targetName;
private final String dateFormat; private final String dateFormat;
private final List<TypeMirror> qualifiers;
private final boolean isIgnored; private final boolean isIgnored;
private final AnnotationMirror mirror; private final AnnotationMirror mirror;
private final AnnotationValue sourceAnnotationValue; private final AnnotationValue sourceAnnotationValue;
@ -126,6 +128,7 @@ public class Mapping {
mappingPrism.expression(), mappingPrism.expression(),
mappingPrism.target(), mappingPrism.target(),
mappingPrism.dateFormat(), mappingPrism.dateFormat(),
mappingPrism.qualifiedBy(),
mappingPrism.ignore(), mappingPrism.ignore(),
mappingPrism.mirror, mappingPrism.mirror,
mappingPrism.values.source(), mappingPrism.values.source(),
@ -153,9 +156,10 @@ public class Mapping {
} }
//CHECKSTYLE:OFF //CHECKSTYLE:OFF
private Mapping(String sourceName, String sourceParameterName, String sourcePropertyName, String constant, private Mapping( String sourceName, String sourceParameterName, String sourcePropertyName, String constant,
String expression, String targetName, String dateFormat, boolean isIgnored, AnnotationMirror mirror, String expression, String targetName, String dateFormat, List<TypeMirror> qualifiers,
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) { boolean isIgnored, AnnotationMirror mirror, AnnotationValue sourceAnnotationValue,
AnnotationValue targetAnnotationValue) {
this.sourceName = sourceName; this.sourceName = sourceName;
this.sourceParameterName = sourceParameterName; this.sourceParameterName = sourceParameterName;
this.sourcePropertyName = sourcePropertyName; this.sourcePropertyName = sourcePropertyName;
@ -165,6 +169,7 @@ public class Mapping {
this.javaExpression = javaExpressionMatcher.matches() ? javaExpressionMatcher.group( 1 ).trim() : ""; this.javaExpression = javaExpressionMatcher.matches() ? javaExpressionMatcher.group( 1 ).trim() : "";
this.targetName = targetName.equals( "" ) ? sourceName : targetName; this.targetName = targetName.equals( "" ) ? sourceName : targetName;
this.dateFormat = dateFormat; this.dateFormat = dateFormat;
this.qualifiers = qualifiers;
this.isIgnored = isIgnored; this.isIgnored = isIgnored;
this.mirror = mirror; this.mirror = mirror;
this.sourceAnnotationValue = sourceAnnotationValue; this.sourceAnnotationValue = sourceAnnotationValue;
@ -216,6 +221,10 @@ public class Mapping {
return dateFormat; return dateFormat;
} }
public List<TypeMirror> getQualifiers() {
return qualifiers;
}
public boolean isIgnored() { public boolean isIgnored() {
return isIgnored; return isIgnored;
} }
@ -244,6 +253,7 @@ public class Mapping {
expression, expression,
sourceName, sourceName,
dateFormat, dateFormat,
qualifiers,
isIgnored, isIgnored,
mirror, mirror,
sourceAnnotationValue, sourceAnnotationValue,

View File

@ -20,6 +20,7 @@ package org.mapstruct.ap.model.source.selector;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.model.common.Parameter; import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
@ -33,8 +34,14 @@ import org.mapstruct.ap.model.source.Method;
public class InheritanceSelector implements MethodSelector { public class InheritanceSelector implements MethodSelector {
@Override @Override
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods, Type parameterType, public <T extends Method> List<T> getMatchingMethods(
Type returnType, String targetPropertyName) { Method mappingMethod,
List<T> methods,
Type parameterType,
Type returnType,
List<TypeMirror> qualifiers,
String targetPropertyName
) {
List<T> candidatesWithBestMatchingSourceType = new ArrayList<T>(); List<T> candidatesWithBestMatchingSourceType = new ArrayList<T>();
int bestMatchingSourceTypeDistance = Integer.MAX_VALUE; int bestMatchingSourceTypeDistance = Integer.MAX_VALUE;

View File

@ -19,6 +19,7 @@
package org.mapstruct.ap.model.source.selector; package org.mapstruct.ap.model.source.selector;
import java.util.List; import java.util.List;
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.Method;
@ -40,10 +41,12 @@ public interface MethodSelector {
* @param methods list of available methods * @param methods list of available methods
* @param parameterType parameter type that should be matched * @param parameterType parameter type that should be matched
* @param returnType return 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 * @param targetPropertyName some information can be derived from the target property
* *
* @return list of methods that passes the matching process * @return list of methods that passes the matching process
*/ */
<T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods, Type parameterType, <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods, Type parameterType,
Type returnType, String targetPropertyName); Type returnType, List<TypeMirror> qualifiers,
String targetPropertyName);
} }

View File

@ -21,6 +21,8 @@ package org.mapstruct.ap.model.source.selector;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
import org.mapstruct.ap.model.common.Parameter; import org.mapstruct.ap.model.common.Parameter;
@ -37,18 +39,21 @@ public class MethodSelectors implements MethodSelector {
private final List<MethodSelector> selectors; private final List<MethodSelector> selectors;
public MethodSelectors(Types typeUtils, TypeFactory typeFactory) { public MethodSelectors(Types typeUtils, Elements elementUtils, TypeFactory typeFactory) {
selectors = selectors =
Arrays.<MethodSelector>asList( Arrays.<MethodSelector>asList(
new TypeSelector( typeFactory ), new TypeSelector( typeFactory ),
new InheritanceSelector(), new InheritanceSelector(),
new XmlElementDeclSelector( typeUtils ) new XmlElementDeclSelector( typeUtils ),
new QualifierSelector( typeUtils, elementUtils )
); );
} }
@Override @Override
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods, Type parameterType, public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
Type returnType, String targetPropertyName) { Type parameterType, Type returnType,
List<TypeMirror> qualifiers,
String targetPropertyName) {
List<T> candidates = new ArrayList<T>( methods ); List<T> candidates = new ArrayList<T>( methods );
@ -58,6 +63,7 @@ public class MethodSelectors implements MethodSelector {
candidates, candidates,
parameterType, parameterType,
returnType, returnType,
qualifiers,
targetPropertyName targetPropertyName
); );
} }

View File

@ -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 <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
Type parameterType, Type returnType,
List<TypeMirror> qualifiers,
String targetPropertyName) {
List<T> matches = new ArrayList<T>();
if ( qualifiers == null || qualifiers.isEmpty() ) {
return methods;
}
for ( T candidate : methods ) {
if ( !( candidate instanceof SourceMethod ) ) {
continue;
}
// retrieve annotations
Set<TypeMirror> combinedAnnotations = new HashSet<TypeMirror>();
// 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<TypeMirror> 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;
}
}
}
}

View File

@ -20,6 +20,7 @@ package org.mapstruct.ap.model.source.selector;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.lang.model.type.TypeMirror;
import org.mapstruct.ap.model.common.Type; import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
@ -41,8 +42,10 @@ public class TypeSelector implements MethodSelector {
} }
@Override @Override
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods, Type parameterType, public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
Type returnType, String targetPropertyName) { Type parameterType, Type returnType,
List<TypeMirror> qualifiers,
String targetPropertyName) {
List<T> result = new ArrayList<T>(); List<T> result = new ArrayList<T>();
for ( T method : methods ) { for ( T method : methods ) {

View File

@ -51,13 +51,16 @@ public class XmlElementDeclSelector implements MethodSelector {
} }
@Override @Override
public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods, Type parameterType, public <T extends Method> List<T> getMatchingMethods(Method mappingMethod, List<T> methods,
Type returnType, String targetPropertyName) { Type parameterType, Type returnType,
List<TypeMirror> qualifiers,
String targetPropertyName) {
// only true source methods are qualifying // only true source methods are qualifying
if ( !(mappingMethod instanceof SourceMethod) ) { if ( !(mappingMethod instanceof SourceMethod) ) {
return methods; return methods;
} }
SourceMethod sourceMappingMethod = (SourceMethod) mappingMethod; SourceMethod sourceMappingMethod = (SourceMethod) mappingMethod;
List<T> nameMatches = new ArrayList<T>(); List<T> nameMatches = new ArrayList<T>();

View File

@ -297,11 +297,21 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
reverseMappingMethod.getIterableMapping() != null ) { reverseMappingMethod.getIterableMapping() != null ) {
method.setIterableMapping( reverseMappingMethod.getIterableMapping() ); method.setIterableMapping( reverseMappingMethod.getIterableMapping() );
} }
String dateFormat
= method.getIterableMapping() != null ? method.getIterableMapping().getDateFormat() : null;
IterableMappingMethod iterableMappingMethod String dateFormat = null;
= getIterableMappingMethod( mapperReferences, methods, method, dateFormat ); List<TypeMirror> 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; hasFactoryMethod = iterableMappingMethod.getFactoryMethod() != null;
mappingMethods.add( iterableMappingMethod ); mappingMethods.add( iterableMappingMethod );
} }
@ -312,13 +322,24 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
} }
String keyDateFormat = null; String keyDateFormat = null;
String valueDateFormat = null; String valueDateFormat = null;
List<TypeMirror> keyQualifiers = null;
List<TypeMirror> valueQualifiers = null;
if ( method.getMapMapping() != null ) { if ( method.getMapMapping() != null ) {
keyDateFormat = method.getMapMapping().getKeyFormat(); keyDateFormat = method.getMapMapping().getKeyFormat();
valueDateFormat = method.getMapMapping().getValueFormat(); valueDateFormat = method.getMapMapping().getValueFormat();
keyQualifiers = method.getMapMapping().getKeyQualifiers();
valueQualifiers = method.getMapMapping().getValueQualifiers();
} }
MapMappingMethod mapMappingMethod MapMappingMethod mapMappingMethod = getMapMappingMethod(
= getMapMappingMethod( mapperReferences, methods, method, keyDateFormat, valueDateFormat ); mapperReferences,
methods,
method,
keyDateFormat,
valueDateFormat,
keyQualifiers,
valueQualifiers
);
hasFactoryMethod = mapMappingMethod.getFactoryMethod() != null; hasFactoryMethod = mapMappingMethod.getFactoryMethod() != null;
mappingMethods.add( mapMappingMethod ); mappingMethods.add( mapMappingMethod );
} }
@ -438,9 +459,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
// check if there's a mapping defined // check if there's a mapping defined
Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName ); Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
String dateFormat = null; String dateFormat = null;
List<TypeMirror> qualifiers = null;
String sourcePropertyName; String sourcePropertyName;
if ( mapping != null ) { if ( mapping != null ) {
dateFormat = mapping.getDateFormat(); dateFormat = mapping.getDateFormat();
qualifiers = mapping.getQualifiers();
sourcePropertyName = mapping.getSourcePropertyName(); sourcePropertyName = mapping.getSourcePropertyName();
} }
else { else {
@ -466,7 +489,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
sourceAccessor, sourceAccessor,
targetAccessor, targetAccessor,
targetPropertyName, targetPropertyName,
dateFormat dateFormat,
qualifiers
); );
} }
} }
@ -480,7 +504,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
sourceAccessor, sourceAccessor,
targetAccessor, targetAccessor,
targetPropertyName, targetPropertyName,
dateFormat dateFormat,
qualifiers
); );
} }
} }
@ -569,7 +594,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
method, method,
"\"" + mapping.getConstant() + "\"", "\"" + mapping.getConstant() + "\"",
targetAccessor, targetAccessor,
mapping.getDateFormat() mapping.getDateFormat(),
mapping.getQualifiers()
); );
} }
@ -775,7 +801,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
ExecutableElement sourceAccessor, ExecutableElement sourceAccessor,
ExecutableElement targetAccessor, ExecutableElement targetAccessor,
String targetPropertyName, String targetPropertyName,
String dateFormat) { String dateFormat,
List<TypeMirror> qualifiers) {
Type sourceType; Type sourceType;
Type targetType; Type targetType;
@ -815,6 +842,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
targetType, targetType,
targetPropertyName, targetPropertyName,
dateFormat, dateFormat,
qualifiers,
iteratorReference != null ? iteratorReference : sourceReference iteratorReference != null ? iteratorReference : sourceReference
); );
@ -925,7 +953,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
SourceMethod method, SourceMethod method,
String constantExpression, String constantExpression,
ExecutableElement targetAccessor, ExecutableElement targetAccessor,
String dateFormat) { String dateFormat,
List<TypeMirror> qualifiers) {
// source // source
String mappedElement = "constant '" + constantExpression + "'"; String mappedElement = "constant '" + constantExpression + "'";
@ -944,6 +973,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
targetType, targetType,
targetPropertyName, targetPropertyName,
dateFormat, dateFormat,
qualifiers,
constantExpression constantExpression
); );
@ -986,7 +1016,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
private IterableMappingMethod getIterableMappingMethod(List<MapperReference> mapperReferences, private IterableMappingMethod getIterableMappingMethod(List<MapperReference> mapperReferences,
List<SourceMethod> methods, List<SourceMethod> methods,
Method method, Method method,
String dateFormat ) { String dateFormat,
List<TypeMirror> qualifiers) {
Type sourceElementType = method.getSourceParameters().iterator().next().getType().getTypeParameters().get( 0 ); Type sourceElementType = method.getSourceParameters().iterator().next().getType().getTypeParameters().get( 0 );
Type targetElementType = method.getResultType().getTypeParameters().get( 0 ); Type targetElementType = method.getResultType().getTypeParameters().get( 0 );
String conversionStr = Strings.getSaveVariableName( sourceElementType.getName(), method.getParameterNames() ); String conversionStr = Strings.getSaveVariableName( sourceElementType.getName(), method.getParameterNames() );
@ -1000,6 +1031,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
targetElementType, targetElementType,
null, // there is no targetPropertyName null, // there is no targetPropertyName
dateFormat, dateFormat,
qualifiers,
conversionStr conversionStr
); );
@ -1019,8 +1051,13 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
return new IterableMappingMethod( method, assignment, factoryMethod ); return new IterableMappingMethod( method, assignment, factoryMethod );
} }
private MapMappingMethod getMapMappingMethod(List<MapperReference> mapperReferences, List<SourceMethod> methods, private MapMappingMethod getMapMappingMethod(List<MapperReference> mapperReferences,
Method method, String keyDateFormat, String valueDateFormat ) { List<SourceMethod> methods,
Method method,
String keyDateFormat,
String valueDateFormat,
List<TypeMirror> keyQualifiers,
List<TypeMirror> valueQualifiers ) {
List<Type> sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters(); List<Type> sourceTypeParams = method.getSourceParameters().iterator().next().getType().getTypeParameters();
List<Type> resultTypeParams = method.getResultType().getTypeParameters(); List<Type> resultTypeParams = method.getResultType().getTypeParameters();
@ -1037,6 +1074,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
keyTargetType, keyTargetType,
null, // there is no targetPropertyName null, // there is no targetPropertyName
keyDateFormat, keyDateFormat,
keyQualifiers,
"entry.getKey()" "entry.getKey()"
); );
@ -1059,6 +1097,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
valueTargetType, valueTargetType,
null, // there is no targetPropertyName null, // there is no targetPropertyName
valueDateFormat, valueDateFormat,
valueQualifiers,
"entry.getValue()" "entry.getValue()"
); );
@ -1233,7 +1272,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
ForgedMethod methodToGenerate ForgedMethod methodToGenerate
= new ForgedMethod( sourceType, targetType, element ); = new ForgedMethod( sourceType, targetType, element );
IterableMappingMethod iterableMappingMethod IterableMappingMethod iterableMappingMethod
= getIterableMappingMethod( mapperReferences, methods, methodToGenerate, null ); = getIterableMappingMethod( mapperReferences, methods, methodToGenerate, null, null );
if ( !mappingsToGenerate.contains( iterableMappingMethod ) ) { if ( !mappingsToGenerate.contains( iterableMappingMethod ) ) {
mappingsToGenerate.add( iterableMappingMethod ); mappingsToGenerate.add( iterableMappingMethod );
} }
@ -1246,7 +1285,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
ForgedMethod methodToGenerate ForgedMethod methodToGenerate
= new ForgedMethod( sourceType, targetType, element ); = new ForgedMethod( sourceType, targetType, element );
MapMappingMethod mapMappingMethod MapMappingMethod mapMappingMethod
= getMapMappingMethod( mapperReferences, methods, methodToGenerate, null, null ); = getMapMappingMethod( mapperReferences, methods, methodToGenerate, null, null, null, null );
if ( !mappingsToGenerate.contains( mapMappingMethod ) ) { if ( !mappingsToGenerate.contains( mapMappingMethod ) ) {
mappingsToGenerate.add( mapMappingMethod ); mappingsToGenerate.add( mapMappingMethod );
} }

View File

@ -89,7 +89,7 @@ public class MappingResolver {
this.conversions = new Conversions( elementUtils, typeFactory ); this.conversions = new Conversions( elementUtils, typeFactory );
this.builtInMethods = new BuiltInMappingMethods( typeFactory ); this.builtInMethods = new BuiltInMappingMethods( typeFactory );
this.virtualMethods = new HashSet<VirtualMappingMethod>(); this.virtualMethods = new HashSet<VirtualMappingMethod>();
this.methodSelectors = new MethodSelectors( typeUtils, typeFactory ); this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, typeFactory );
this.typeUtils = typeUtils; this.typeUtils = typeUtils;
} }
@ -105,6 +105,7 @@ public class MappingResolver {
* @param targetType return type to match * @param targetType return type to match
* @param targetPropertyName name of the target property * @param targetPropertyName name of the target property
* @param dateFormat used for formatting dates in build in methods that need context information * @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 * @param sourceReference call to source type as string
* *
* @return an assignment to a method parameter, which can either be: * @return an assignment to a method parameter, which can either be:
@ -123,6 +124,7 @@ public class MappingResolver {
Type targetType, Type targetType,
String targetPropertyName, String targetPropertyName,
String dateFormat, String dateFormat,
List<TypeMirror> qualifiers,
String sourceReference ) { String sourceReference ) {
ResolvingAttempt attempt = new ResolvingAttempt( mappingMethod, ResolvingAttempt attempt = new ResolvingAttempt( mappingMethod,
@ -131,6 +133,7 @@ public class MappingResolver {
methods, methods,
targetPropertyName, targetPropertyName,
dateFormat, dateFormat,
qualifiers,
sourceReference, sourceReference,
this this
); );
@ -151,6 +154,7 @@ public class MappingResolver {
private final List<SourceMethod> methods; private final List<SourceMethod> methods;
private final String targetPropertyName; private final String targetPropertyName;
private final String dateFormat; private final String dateFormat;
private final List<TypeMirror> qualifiers;
private final String sourceReference; private final String sourceReference;
private final MappingResolver context; private final MappingResolver context;
@ -165,6 +169,7 @@ public class MappingResolver {
List<SourceMethod> methods, List<SourceMethod> methods,
String targetPropertyName, String targetPropertyName,
String dateFormat, String dateFormat,
List<TypeMirror> qualifiers,
String sourceReference, String sourceReference,
MappingResolver context ) { MappingResolver context ) {
this.mappingMethod = mappingMethod; this.mappingMethod = mappingMethod;
@ -173,6 +178,7 @@ public class MappingResolver {
this.methods = methods; this.methods = methods;
this.targetPropertyName = targetPropertyName; this.targetPropertyName = targetPropertyName;
this.dateFormat = dateFormat; this.dateFormat = dateFormat;
this.qualifiers = qualifiers;
this.sourceReference = sourceReference; this.sourceReference = sourceReference;
this.context = context; this.context = context;
this.virtualMethodCandidates = new HashSet<VirtualMappingMethod>(); this.virtualMethodCandidates = new HashSet<VirtualMappingMethod>();
@ -403,6 +409,7 @@ public class MappingResolver {
methods, methods,
sourceType, sourceType,
returnType, returnType,
qualifiers,
targetPropertyName targetPropertyName
); );

View File

@ -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 );
}

View File

@ -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<String, List<String>> mapFacts( Map<String, List<String>> keyWords );
}

View File

@ -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<String, String> EN_GER = ImmutableMap.<String, String>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<String> mapKeyWords( List<String> keyWords );
@EnglishToGerman
public String mapKeyWord( String keyword ) {
return EN_GER.get( keyword );
}
}

View File

@ -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 );
}

View File

@ -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<String, List<String>> facts = new HashMap<String, List<String>>();
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() {
}
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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<String> keyWords;
private Map<String, List<String>> facts;
public String getTitle() {
return title;
}
public void setTitle( String title ) {
this.title = title;
}
public List<String> getKeyWords() {
return keyWords;
}
public void setKeyWords( List<String> keyWords ) {
this.keyWords = keyWords;
}
public Map<String, List<String> > getFacts() {
return facts;
}
public void setFacts( Map<String, List<String> > facts ) {
this.facts = facts;
}
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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<String, String> EN_GER = ImmutableMap.<String, String>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;
}
}

View File

@ -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<String, String> EN_GER = ImmutableMap.<String, String>builder()
.put( "boy", "Jungen" )
.put( "child psychologist", "Kinderpsychologe" )
.put( "I see dead people", "Ich sehe tote Menschen" )
.build();
@EnglishToGerman
public List<String> translate( List<String> keywords ) {
List<String> result = new ArrayList<String>();
for ( String keyword : keywords ) {
if ( EN_GER.containsKey( keyword ) ) {
result.add( EN_GER.get( keyword ) );
}
else {
result.add( keyword );
}
}
return result;
}
public List<String> methodNotToSelect( List<String> title ) {
throw new AssertionError( "method should not be called" );
}
}

View File

@ -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" );
}
}

View File

@ -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<String, String> EN_GER = ImmutableMap.<String, String>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" );
}
}

View File

@ -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" );
}
}