#120 Method and BuiltInMethod implement a common interface that can be used for matching.

This commit is contained in:
sjaakd 2014-02-18 21:00:45 +01:00 committed by Gunnar Morling
parent 08b6008ed1
commit 2471edcf69
20 changed files with 517 additions and 516 deletions

View File

@ -18,103 +18,27 @@
*/ */
package org.mapstruct.ap.builtin; package org.mapstruct.ap.builtin;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.Messager;
import javax.tools.Diagnostic;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.util.Strings;
/** /**
* Registry for all build in methods.
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class BuiltInMappingMethods { public class BuiltInMappingMethods extends HashSet<BuiltInMethod> {
private final Set<BuiltInMappingMethod> builtInMethods = new HashSet<BuiltInMappingMethod>(); public BuiltInMappingMethods( TypeFactory typeFactory ) {
private final Messager messager;
public BuiltInMappingMethods( Messager messager, TypeFactory typeFactory ) { add( new JaxbElemToValue( typeFactory ) );
this.messager = messager; add( new ListOfJaxbElemToListOfValue( typeFactory ) );
add( new DateToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new JaxbElemToValue( typeFactory ) ); add( new XmlGregorianCalendarToDate( typeFactory ) );
builtInMethods.add( new ListOfJaxbElemToListOfValue( typeFactory ) ); add( new StringToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new DateToXmlGregorianCalendar( typeFactory ) ); add( new XmlGregorianCalendarToString( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToDate( typeFactory ) ); add( new CalendarToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new StringToXmlGregorianCalendar( typeFactory ) ); add( new XmlGregorianCalendarToCalendar( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToString( typeFactory ) );
builtInMethods.add( new CalendarToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToCalendar( typeFactory ) );
}
/**
* The method looks for a match on equal source type and best matching target type (minimum distance) TODO:
* investigate whether also the best matching source type should be investigating iso equal.
*
* @param sourceType
* @param targetType
* @return
*/
public BuiltInMappingMethod getConversion( Type sourceType, Type targetType ) {
List<BuiltInMappingMethod> candidates = new ArrayList<BuiltInMappingMethod>();
int bestMatchingTargetTypeDistance = Integer.MAX_VALUE;
for ( BuiltInMappingMethod entry : builtInMethods ) {
if ( targetType.erasure().isAssignableTo( entry.target() )
&& sourceType.erasure().isAssignableTo( entry.source() ) ) {
if ( entry.doGenericsMatch( sourceType, targetType ) ) {
int sourceTypeDistance = targetType.distanceTo( entry.target() );
bestMatchingTargetTypeDistance
= addToCandidateListIfMinimal(
candidates,
bestMatchingTargetTypeDistance,
entry,
sourceTypeDistance );
}
}
}
if ( candidates.isEmpty() ) {
return null;
}
if ( candidates.size() > 1 ) {
// print a warning if we find more than one method with minimum source type distance
List<String> builtInMethodNames = new ArrayList<String>();
for ( BuiltInMappingMethod candidate : candidates ) {
builtInMethodNames.add( candidate.getName() );
}
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(
"MapStruct error. Conflicting build-in methods %s for sourceType: %s, targetTypes %s.",
Strings.join( builtInMethodNames, ", " ),
sourceType,
targetType )
);
}
return candidates.get( 0 );
}
private int addToCandidateListIfMinimal( List<BuiltInMappingMethod> candidatesWithBestMathingType,
int bestMatchingTypeDistance, BuiltInMappingMethod builtInMethod, int currentTypeDistance ) {
if ( currentTypeDistance == bestMatchingTypeDistance ) {
candidatesWithBestMathingType.add( builtInMethod );
}
else if ( currentTypeDistance < bestMatchingTypeDistance ) {
bestMatchingTypeDistance = currentTypeDistance;
candidatesWithBestMathingType.clear();
candidatesWithBestMathingType.add( builtInMethod );
}
return bestMatchingTypeDistance;
} }
} }

View File

@ -18,15 +18,13 @@
*/ */
package org.mapstruct.ap.builtin; package org.mapstruct.ap.builtin;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import static java.util.Arrays.asList;
import java.util.Calendar; import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Set; import java.util.Set;
import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.datatype.XMLGregorianCalendar;
import org.mapstruct.ap.model.MethodReference;
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;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
@ -36,25 +34,17 @@ import static org.mapstruct.ap.util.Collections.asSet;
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class CalendarToXmlGregorianCalendar extends BuiltInMappingMethod { public class CalendarToXmlGregorianCalendar extends BuiltInMethod {
private static final Class SOURCE = Calendar.class; private final Parameter parameter;
private static final Class TARGET = XMLGregorianCalendar.class; private final Type returnType;
private final TypeFactory typeFactory; private final TypeFactory typeFactory;
public CalendarToXmlGregorianCalendar( TypeFactory typeFactory ) { public CalendarToXmlGregorianCalendar( TypeFactory typeFactory ) {
this.typeFactory = typeFactory; this.typeFactory = typeFactory;
} this.parameter = typeFactory.createParameter( "cal ", Calendar.class );
this.returnType = typeFactory.getType( XMLGregorianCalendar.class );
@Override
public MethodReference createMethodReference() {
return new MethodReference(
getName(),
asList( new Parameter[] { typeFactory.createParameter( "cal", SOURCE ) } ),
typeFactory.getType( TARGET ),
null
);
} }
@Override @Override
@ -65,12 +55,12 @@ public class CalendarToXmlGregorianCalendar extends BuiltInMappingMethod {
} }
@Override @Override
public Type source() { public Parameter getParameter() {
return typeFactory.getType( SOURCE ).erasure(); return parameter;
} }
@Override @Override
public Type target() { public Type getReturnType() {
return typeFactory.getType( TARGET ).erasure(); return returnType;
} }
} }

View File

@ -18,15 +18,13 @@
*/ */
package org.mapstruct.ap.builtin; package org.mapstruct.ap.builtin;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import static java.util.Arrays.asList;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Set; import java.util.Set;
import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.datatype.XMLGregorianCalendar;
import org.mapstruct.ap.model.MethodReference;
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;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
@ -36,25 +34,17 @@ import static org.mapstruct.ap.util.Collections.asSet;
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class DateToXmlGregorianCalendar extends BuiltInMappingMethod { public class DateToXmlGregorianCalendar extends BuiltInMethod {
private static final Class SOURCE = Date.class; private final Parameter parameter;
private static final Class TARGET = XMLGregorianCalendar.class; private final Type returnType;
private final TypeFactory typeFactory; private final TypeFactory typeFactory;
public DateToXmlGregorianCalendar( TypeFactory typeFactory ) { public DateToXmlGregorianCalendar( TypeFactory typeFactory ) {
this.typeFactory = typeFactory; this.typeFactory = typeFactory;
} this.parameter = typeFactory.createParameter( "date", Date.class );
this.returnType = typeFactory.getType( XMLGregorianCalendar.class );
@Override
public MethodReference createMethodReference() {
return new MethodReference(
getName(),
asList( new Parameter[] { typeFactory.createParameter( "date", SOURCE ) } ),
typeFactory.getType( TARGET ),
null
);
} }
@Override @Override
@ -65,12 +55,12 @@ public class DateToXmlGregorianCalendar extends BuiltInMappingMethod {
} }
@Override @Override
public Type source() { public Parameter getParameter() {
return typeFactory.getType( SOURCE ).erasure(); return parameter;
} }
@Override @Override
public Type target() { public Type getReturnType() {
return typeFactory.getType( TARGET ).erasure(); return returnType;
} }
} }

View File

@ -18,10 +18,8 @@
*/ */
package org.mapstruct.ap.builtin; package org.mapstruct.ap.builtin;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import static java.util.Arrays.asList;
import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBElement;
import org.mapstruct.ap.model.MethodReference;
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;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
@ -30,29 +28,18 @@ import org.mapstruct.ap.model.common.TypeFactory;
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class JaxbElemToValue extends BuiltInMappingMethod { public class JaxbElemToValue extends BuiltInMethod {
private static final Class SOURCE = JAXBElement.class; private final Parameter parameter;
private static final Class TARGET = Object.class; private final Type returnType;
private final TypeFactory typeFactory;
public JaxbElemToValue( TypeFactory typeFactory ) { public JaxbElemToValue( TypeFactory typeFactory ) {
this.typeFactory = typeFactory; this.parameter = typeFactory.createParameter( "element", JAXBElement.class );
this.returnType = typeFactory.getType( Object.class );
} }
@Override @Override
public MethodReference createMethodReference() { public boolean doTypeVarsMatch(Type sourceType, Type targetType) {
return new MethodReference(
getName(),
asList( new Parameter[] { typeFactory.createParameter( "element", SOURCE ) } ),
typeFactory.getType( TARGET ),
null
);
}
@Override
public boolean doGenericsMatch(Type sourceType, Type targetType) {
boolean match = false; boolean match = false;
if (sourceType.getTypeParameters().size() == 1) { if (sourceType.getTypeParameters().size() == 1) {
match = sourceType.getTypeParameters().get( 0 ).equals( targetType ); match = sourceType.getTypeParameters().get( 0 ).equals( targetType );
@ -61,12 +48,12 @@ public class JaxbElemToValue extends BuiltInMappingMethod {
} }
@Override @Override
public Type source() { public Parameter getParameter() {
return typeFactory.getType( SOURCE ).erasure(); return parameter;
} }
@Override @Override
public Type target() { public Type getReturnType() {
return typeFactory.getType( TARGET ).erasure(); return returnType;
} }
} }

View File

@ -18,11 +18,9 @@
*/ */
package org.mapstruct.ap.builtin; package org.mapstruct.ap.builtin;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import static java.util.Arrays.asList;
import java.util.List; import java.util.List;
import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBElement;
import org.mapstruct.ap.model.MethodReference;
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;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
@ -31,36 +29,24 @@ import org.mapstruct.ap.model.common.TypeFactory;
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class ListOfJaxbElemToListOfValue extends BuiltInMappingMethod { public class ListOfJaxbElemToListOfValue extends BuiltInMethod {
private static final Class SOURCE = List.class; private final Parameter parameter;
private static final Class TARGET = List.class; private final Type returnType;
private static final Class TARGET_PARAM = JAXBElement.class; private final Type genericParam;
private final TypeFactory typeFactory;
public ListOfJaxbElemToListOfValue( TypeFactory typeFactory ) { public ListOfJaxbElemToListOfValue( TypeFactory typeFactory ) {
this.typeFactory = typeFactory; this.parameter = typeFactory.createParameter( "elementList", List.class );
this.returnType = typeFactory.getType( List.class );
this.genericParam = typeFactory.getType( JAXBElement.class ).erasure();
} }
@Override @Override
public MethodReference createMethodReference() { public boolean doTypeVarsMatch(Type sourceType, Type targetType) {
return new MethodReference(
getName(),
asList( new Parameter[] { typeFactory.createParameter( "elementList", SOURCE ) } ),
typeFactory.getType( TARGET ),
null
);
}
@Override
public boolean doGenericsMatch(Type sourceType, Type targetType) {
boolean match = false; boolean match = false;
if ( ( sourceType.getTypeParameters().size() == 1 ) if ( ( sourceType.getTypeParameters().size() == 1 ) && ( targetType.getTypeParameters().size() == 1 ) ) {
&& ( targetType.getTypeParameters().size() == 1 ) ) {
Type typeParam = sourceType.getTypeParameters().get( 0 ); Type typeParam = sourceType.getTypeParameters().get( 0 );
if ( typeParam.erasure().equals( typeFactory.getType( TARGET_PARAM ).erasure() ) && if ( typeParam.erasure().equals( genericParam ) && ( typeParam.getTypeParameters().size() == 1 ) ) {
( typeParam.getTypeParameters().size() == 1 ) ) {
match = typeParam.getTypeParameters().get( 0 ).equals( targetType.getTypeParameters().get( 0 ) ); match = typeParam.getTypeParameters().get( 0 ).equals( targetType.getTypeParameters().get( 0 ) );
} }
} }
@ -68,12 +54,12 @@ public class ListOfJaxbElemToListOfValue extends BuiltInMappingMethod {
} }
@Override @Override
public Type source() { public Parameter getParameter() {
return typeFactory.getType( SOURCE ).erasure(); return parameter;
} }
@Override @Override
public Type target() { public Type getReturnType() {
return typeFactory.getType( TARGET ).erasure(); return returnType;
} }
} }

View File

@ -21,14 +21,12 @@ package org.mapstruct.ap.builtin;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import static java.util.Arrays.asList;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Set; import java.util.Set;
import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.datatype.XMLGregorianCalendar;
import org.mapstruct.ap.model.MethodReference;
import org.mapstruct.ap.model.common.ConversionContext; import org.mapstruct.ap.model.common.ConversionContext;
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;
@ -39,26 +37,18 @@ import static org.mapstruct.ap.util.Collections.asSet;
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class StringToXmlGregorianCalendar extends BuiltInMappingMethod { public class StringToXmlGregorianCalendar extends BuiltInMethod {
private static final Class SOURCE = String.class; private final Parameter parameter;
private static final Class TARGET = XMLGregorianCalendar.class; private final Type returnType;
private final TypeFactory typeFactory; private final TypeFactory typeFactory;
private ConversionContext conversionContext;
public StringToXmlGregorianCalendar( TypeFactory typeFactory ) { public StringToXmlGregorianCalendar( TypeFactory typeFactory ) {
this.typeFactory = typeFactory; this.typeFactory = typeFactory;
} this.parameter = typeFactory.createParameter( "date" , String.class );
this.returnType = typeFactory.getType( XMLGregorianCalendar.class );
@Override
public MethodReference createMethodReference() {
return new MethodReference(
getName(),
asList( new Parameter[] { typeFactory.createParameter( "date" , SOURCE ) } ),
typeFactory.getType( TARGET ),
getContextParm()
);
} }
@Override @Override
@ -72,21 +62,17 @@ public class StringToXmlGregorianCalendar extends BuiltInMappingMethod {
} }
@Override @Override
public Type source() { public Parameter getParameter() {
return typeFactory.getType( SOURCE ).erasure(); return parameter;
} }
@Override @Override
public Type target() { public Type getReturnType() {
return typeFactory.getType( TARGET ).erasure(); return returnType;
} }
@Override @Override
public void setConversionContext(ConversionContext conversionContext) { public String getContextParameter(ConversionContext conversionContext) {
this.conversionContext = conversionContext;
}
private String getContextParm() {
return conversionContext.getDateFormat() != null ? "\"" + conversionContext.getDateFormat() + "\"" : "null"; return conversionContext.getDateFormat() != null ? "\"" + conversionContext.getDateFormat() + "\"" : "null";
} }
} }

View File

@ -18,11 +18,9 @@
*/ */
package org.mapstruct.ap.builtin; package org.mapstruct.ap.builtin;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import static java.util.Arrays.asList;
import java.util.Calendar; import java.util.Calendar;
import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.datatype.XMLGregorianCalendar;
import org.mapstruct.ap.model.MethodReference;
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;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
@ -31,34 +29,23 @@ import org.mapstruct.ap.model.common.TypeFactory;
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class XmlGregorianCalendarToCalendar extends BuiltInMappingMethod { public class XmlGregorianCalendarToCalendar extends BuiltInMethod {
private static final Class SOURCE = XMLGregorianCalendar.class; private final Parameter parameter;
private static final Class TARGET = Calendar.class; private final Type returnType;
private final TypeFactory typeFactory;
public XmlGregorianCalendarToCalendar( TypeFactory typeFactory ) { public XmlGregorianCalendarToCalendar( TypeFactory typeFactory ) {
this.typeFactory = typeFactory; this.parameter = typeFactory.createParameter( "xcal", XMLGregorianCalendar.class );
this.returnType = typeFactory.getType( Calendar.class );
} }
@Override @Override
public MethodReference createMethodReference() { public Parameter getParameter() {
return new MethodReference( return parameter;
getName(),
asList( new Parameter[] { typeFactory.createParameter( "xcal", SOURCE ) } ),
typeFactory.getType( TARGET ),
null
);
} }
@Override @Override
public Type source() { public Type getReturnType() {
return typeFactory.getType( SOURCE ).erasure(); return returnType;
}
@Override
public Type target() {
return typeFactory.getType( TARGET ).erasure();
} }
} }

View File

@ -18,11 +18,9 @@
*/ */
package org.mapstruct.ap.builtin; package org.mapstruct.ap.builtin;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import static java.util.Arrays.asList;
import java.util.Date; import java.util.Date;
import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.datatype.XMLGregorianCalendar;
import org.mapstruct.ap.model.MethodReference;
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;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
@ -31,34 +29,23 @@ import org.mapstruct.ap.model.common.TypeFactory;
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class XmlGregorianCalendarToDate extends BuiltInMappingMethod { public class XmlGregorianCalendarToDate extends BuiltInMethod {
private static final Class SOURCE = XMLGregorianCalendar.class; private final Parameter parameter;
private static final Class TARGET = Date.class; private final Type returnType;
private final TypeFactory typeFactory;
public XmlGregorianCalendarToDate( TypeFactory typeFactory ) { public XmlGregorianCalendarToDate( TypeFactory typeFactory ) {
this.typeFactory = typeFactory; this.parameter = typeFactory.createParameter( "xcal", XMLGregorianCalendar.class );
this.returnType = typeFactory.getType( Date.class );
} }
@Override @Override
public MethodReference createMethodReference() { public Parameter getParameter() {
return new MethodReference( return parameter;
getName(),
asList( new Parameter[] { typeFactory.createParameter( "xcal", SOURCE ) } ),
typeFactory.getType( TARGET ),
null
);
} }
@Override @Override
public Type source() { public Type getReturnType() {
return typeFactory.getType( SOURCE ).erasure(); return returnType;
}
@Override
public Type target() {
return typeFactory.getType( TARGET ).erasure();
} }
} }

View File

@ -18,10 +18,8 @@
*/ */
package org.mapstruct.ap.builtin; package org.mapstruct.ap.builtin;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import static java.util.Arrays.asList;
import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.datatype.XMLGregorianCalendar;
import org.mapstruct.ap.model.MethodReference;
import org.mapstruct.ap.model.common.ConversionContext; import org.mapstruct.ap.model.common.ConversionContext;
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;
@ -31,44 +29,28 @@ import org.mapstruct.ap.model.common.TypeFactory;
* *
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class XmlGregorianCalendarToString extends BuiltInMappingMethod { public class XmlGregorianCalendarToString extends BuiltInMethod {
private static final Class SOURCE = XMLGregorianCalendar.class; private final Parameter parameter;
private static final Class TARGET = String.class; private final Type returnType;
private final TypeFactory typeFactory;
private ConversionContext conversionContext;
public XmlGregorianCalendarToString( TypeFactory typeFactory ) { public XmlGregorianCalendarToString( TypeFactory typeFactory ) {
this.typeFactory = typeFactory; this.parameter = typeFactory.createParameter( "xcal" , XMLGregorianCalendar.class );
this.returnType = typeFactory.getType( String.class );
} }
@Override @Override
public MethodReference createMethodReference() { public Parameter getParameter() {
return new MethodReference( return parameter;
getName(),
asList( new Parameter[] { typeFactory.createParameter( "xcal" , SOURCE ) } ),
typeFactory.getType( TARGET ),
getContextParm()
);
} }
@Override @Override
public Type source() { public Type getReturnType() {
return typeFactory.getType( SOURCE ).erasure(); return returnType;
} }
@Override @Override
public Type target() { public String getContextParameter(ConversionContext conversionContext) {
return typeFactory.getType( TARGET ).erasure();
}
@Override
public void setConversionContext(ConversionContext conversionContext) {
this.conversionContext = conversionContext;
}
private String getContextParm() {
return conversionContext.getDateFormat() != null ? "\"" + conversionContext.getDateFormat() + "\"" : "null"; return conversionContext.getDateFormat() != null ? "\"" + conversionContext.getDateFormat() + "\"" : "null";
} }
} }

View File

@ -40,8 +40,7 @@ public interface ConversionProvider {
* *
* @param sourceReference A reference to the source object, e.g. * @param sourceReference A reference to the source object, e.g.
* {@code beanName.getFoo()}. * {@code beanName.getFoo()}.
* @param conversionContext ConversionContext providing optional information required for creating * @param conversionContext ConversionContext providing optional information required for creating the conversion.
the conversion.
* *
* @return A conversion from source to target. * @return A conversion from source to target.
*/ */
@ -52,8 +51,7 @@ public interface ConversionProvider {
* *
* @param targetReference A reference to the targetReference object, e.g. * @param targetReference A reference to the targetReference object, e.g.
* {@code beanName.getFoo()}. * {@code beanName.getFoo()}.
* @param conversionContext ConversionContext providing optional information required for creating * @param conversionContext ConversionContext providing optional information required for creating the conversion.
the conversion.
* *
* @return A conversion from target to source. * @return A conversion from target to source.
*/ */

View File

@ -1,112 +0,0 @@
/**
* 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;
import java.util.Collections;
import java.util.Set;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.util.Strings;
/**
* Implementations create:
* 1) an implementation of this build in method.
* 2) a reference to a build in method, to use in property mappings
* 3) a name for logging purposes.
*
* @author Sjaak Derksen
*/
public abstract class BuiltInMappingMethod extends ModelElement {
/**
* Creates a reference to the conversion method
*
* @return reference to method implementation
*/
public abstract MethodReference createMethodReference();
/**
* Sets the conversion context which is used to add context information such as date / time
* conversion pattern, etc.
*
* @param conversionContext ConversionContext providing optional information required for creating
the conversion.
*/
public void setConversionContext(ConversionContext conversionContext) { }
/**
* hashCode
*
* @return hashCode
*/
@Override
public int hashCode() {
return this.getClass().hashCode();
}
/**
* equals based on class
*
* @param obj other class
* @return true when classes are the same
*/
@Override
public boolean equals( Object obj ) {
if ( obj == null ) {
return false;
}
return ( getClass() == obj.getClass() );
}
/**
* tests whether generics do match. Default true.
*
* @param sourceType
* @param targetType
* @return
*/
public boolean doGenericsMatch( Type sourceType, Type targetType ) {
return true;
}
/**
* method name
* @return default method name is equal to class name of conversionmethod
*/
public String getName() {
return Strings.decapitalize( this.getClass().getSimpleName() );
}
/**
* imported types default. Only used types should be added. Source and Target types are coming via
* the MethodReference
*
* @return set of used types.
*/
@Override
public Set<Type> getImportTypes() {
return Collections.<Type>emptySet();
}
public abstract Type source();
public abstract Type target();
}

View File

@ -0,0 +1,166 @@
/**
* 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;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.mapstruct.ap.model.common.Accessibility;
import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.source.BasicMethod;
import org.mapstruct.ap.util.Strings;
/**
* Implementations create:
* 1) an implementation of this build in method.
* 2) a reference to a build in method, to use in property mappings
* 3) a name for logging purposes.
*
* @author Sjaak Derksen
*/
public abstract class BuiltInMethod extends ModelElement implements BasicMethod {
/**
* method name
* @return default method name is equal to class name of build in mehtod
*/
@Override
public String getName() {
return Strings.decapitalize( this.getClass().getSimpleName() );
}
/**
* imported types default. Only used types should be added. Source and Target types are coming via
* the MethodReference
*
* @return set of used types.
*/
@Override
public Set<Type> getImportTypes() {
return Collections.<Type>emptySet();
}
@Override
public boolean matches( Type sourceType, Type targetType ) {
if ( targetType.erasure().isAssignableTo( getReturnType().erasure() )
&& sourceType.erasure().isAssignableTo( getParameter().getType().erasure() ) ) {
return doTypeVarsMatch( sourceType, targetType );
}
return false;
}
/**
*
* @return all parameters are source parameters for build-in methods.
*/
@Override
public List<Parameter> getSourceParameters() {
return getParameters();
}
/**
* declaring mapper is always null, being the MapperImpl
* @return null
*/
@Override
public Type getDeclaringMapper() {
return null;
}
@Override
public List<Parameter> getParameters() {
return Arrays.asList( new Parameter[] { getParameter() } );
}
/**
* target parameter mechanism not supported for build-in-method
* @return null
*/
@Override
public Parameter getTargetParameter() {
return null;
}
/**
* the conversion context is used to format an auxiliary parameter in the method call
* with context specific information such as a date format.
*
* @param conversionContext
* @return null if no context parameter should be included
* "null" if there should be an explicit null call
* "'dateFormat'" for instance, to indicate how the build-in method should format the date
*/
public String getContextParameter(ConversionContext conversionContext) {
return null;
}
/**
* hashCode
*
* @return hashCode
*/
@Override
public int hashCode() {
return this.getClass().hashCode();
}
/**
* equals based on class
*
* @param obj other class
* @return true when classes are the same
*/
@Override
public boolean equals( Object obj ) {
if ( obj == null ) {
return false;
}
return ( getClass() == obj.getClass() );
}
/**
* Analyzes the Java Generics type variables in the parameter do match the type variables in the build in method
* same goes for the returnType.
*
* @param parameter source
* @param returnType target
* @return
*/
public boolean doTypeVarsMatch( Type parameter, Type returnType ) {
return true;
}
/**
* There's currently only one parameter foreseen instead of a list of parameter
*
* @return the parameter
*/
public abstract Parameter getParameter();
@Override
public Accessibility getAccessibility() {
return Accessibility.PRIVATE;
}
}

View File

@ -53,14 +53,14 @@ public class Mapper extends ModelElement {
private final List<Annotation> annotations; private final List<Annotation> annotations;
private final List<MappingMethod> mappingMethods; private final List<MappingMethod> mappingMethods;
private final List<MapperReference> referencedMappers; private final List<MapperReference> referencedMappers;
private final Set<BuiltInMappingMethod> builtInMethods; private final Set<BuiltInMethod> builtInMethods;
private final boolean suppressGeneratorTimestamp; private final boolean suppressGeneratorTimestamp;
private final Accessibility accessibility; private final Accessibility accessibility;
private Mapper(TypeFactory typeFactory, String packageName, boolean superTypeIsInterface, String interfaceName, private Mapper(TypeFactory typeFactory, String packageName, boolean superTypeIsInterface, String interfaceName,
String implementationName, List<MappingMethod> mappingMethods, String implementationName, List<MappingMethod> mappingMethods,
List<MapperReference> referencedMappers, boolean suppressGeneratorTimestamp, List<MapperReference> referencedMappers, boolean suppressGeneratorTimestamp,
Set<BuiltInMappingMethod> builtInMethods, Accessibility accessibility) { Set<BuiltInMethod> builtInMethods, Accessibility accessibility) {
this.packageName = packageName; this.packageName = packageName;
this.superTypeIsInterface = superTypeIsInterface; this.superTypeIsInterface = superTypeIsInterface;
this.interfaceName = interfaceName; this.interfaceName = interfaceName;
@ -80,7 +80,7 @@ public class Mapper extends ModelElement {
private TypeElement element; private TypeElement element;
private List<MappingMethod> mappingMethods; private List<MappingMethod> mappingMethods;
private List<MapperReference> mapperReferences; private List<MapperReference> mapperReferences;
private Set<BuiltInMappingMethod> builtInMethods; private Set<BuiltInMethod> builtInMethods;
private Elements elementUtils; private Elements elementUtils;
private boolean suppressGeneratorTimestamp; private boolean suppressGeneratorTimestamp;
@ -100,7 +100,7 @@ public class Mapper extends ModelElement {
return this; return this;
} }
public Builder builtInMethods(Set<BuiltInMappingMethod> builtInMethods) { public Builder builtInMethods(Set<BuiltInMethod> builtInMethods) {
this.builtInMethods = builtInMethods; this.builtInMethods = builtInMethods;
return this; return this;
} }
@ -157,7 +157,7 @@ public class Mapper extends ModelElement {
addWithDependents( importedTypes, annotation.getType() ); addWithDependents( importedTypes, annotation.getType() );
} }
for ( BuiltInMappingMethod builtInMethod : builtInMethods ) { for ( BuiltInMethod builtInMethod : builtInMethods ) {
for ( Type type : builtInMethod.getImportTypes() ) { for ( Type type : builtInMethod.getImportTypes() ) {
addWithDependents( importedTypes, type ); addWithDependents( importedTypes, type );
} }
@ -250,7 +250,7 @@ public class Mapper extends ModelElement {
return accessibility; return accessibility;
} }
public Set<BuiltInMappingMethod> getBuiltInMethods() { public Set<BuiltInMethod> getBuiltInMethods() {
return builtInMethods; return builtInMethods;
} }
} }

View File

@ -27,7 +27,7 @@ import org.mapstruct.ap.model.common.Accessibility;
import org.mapstruct.ap.model.common.ModelElement; import org.mapstruct.ap.model.common.ModelElement;
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;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.BasicMethod;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
/** /**
@ -43,7 +43,7 @@ public abstract class MappingMethod extends ModelElement {
private final Parameter targetParameter; private final Parameter targetParameter;
private final Accessibility accessibility; private final Accessibility accessibility;
protected MappingMethod(Method method) { protected MappingMethod(BasicMethod method) {
this.name = method.getName(); this.name = method.getName();
this.parameters = method.getParameters(); this.parameters = method.getParameters();
this.returnType = method.getReturnType(); this.returnType = method.getReturnType();

View File

@ -19,9 +19,8 @@
package org.mapstruct.ap.model; package org.mapstruct.ap.model;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.mapstruct.ap.model.common.Parameter; import org.mapstruct.ap.model.common.ConversionContext;
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;
@ -36,16 +35,17 @@ public class MethodReference extends MappingMethod {
private final MapperReference declaringMapper; private final MapperReference declaringMapper;
private final String contextParam; private final String contextParam;
public MethodReference(Method method, MapperReference declaringMapper) {
public MethodReference(Method method, MapperReference declaringMapper ) {
super( method ); super( method );
this.declaringMapper = declaringMapper; this.declaringMapper = declaringMapper;
this.contextParam = null; this.contextParam = null;
} }
public MethodReference(String name, List<Parameter> parameters, Type returnType, String contextParam ) { public MethodReference(BuiltInMethod method, ConversionContext contextParam ) {
super( name, parameters, returnType, null ); super( method );
this.contextParam = contextParam;
this.declaringMapper = null; this.declaringMapper = null;
this.contextParam = method.getContextParameter( contextParam );
} }
public MapperReference getDeclaringMapper() { public MapperReference getDeclaringMapper() {
@ -63,4 +63,11 @@ public class MethodReference extends MappingMethod {
public String getContextParam() { public String getContextParam() {
return contextParam; return contextParam;
} }
private String quoteParamWhenNotNull(String param) {
if (param != null ) {
return param != null ? "\"" + param + "\"" : "null";
}
return null;
}
} }

View File

@ -0,0 +1,55 @@
/**
* 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;
import java.util.List;
import org.mapstruct.ap.model.common.Accessibility;
import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
/**
*
* @author Sjaak Derksen
*/
public interface BasicMethod {
/**
*
* @param sourceType
* @param targetType
* @return
*/
boolean matches( Type sourceType, Type targetType );
List<Parameter> getSourceParameters();
Type getDeclaringMapper();
String getName();
List<Parameter> getParameters();
Type getReturnType();
Parameter getTargetParameter();
Accessibility getAccessibility();
}

View File

@ -22,8 +22,10 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.util.Types;
import org.mapstruct.ap.model.common.Accessibility; import org.mapstruct.ap.model.common.Accessibility;
import org.mapstruct.ap.model.common.Parameter; import org.mapstruct.ap.model.common.Parameter;
@ -40,7 +42,7 @@ import org.mapstruct.ap.util.Strings;
* *
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class Method { public class Method implements BasicMethod {
private final Type declaringMapper; private final Type declaringMapper;
private final ExecutableElement executable; private final ExecutableElement executable;
@ -48,6 +50,7 @@ public class Method {
private final Parameter targetParameter; private final Parameter targetParameter;
private final Type returnType; private final Type returnType;
private final Accessibility accessibility; private final Accessibility accessibility;
private final Types typeUtils;
private Map<String, List<Mapping>> mappings; private Map<String, List<Mapping>> mappings;
private IterableMapping iterableMapping; private IterableMapping iterableMapping;
@ -57,13 +60,22 @@ public class Method {
public static Method forMethodRequiringImplementation(ExecutableElement executable, List<Parameter> parameters, public static Method forMethodRequiringImplementation(ExecutableElement executable, List<Parameter> parameters,
Type returnType, Map<String, List<Mapping>> mappings, Type returnType, Map<String, List<Mapping>> mappings,
IterableMapping iterableMapping, MapMapping mapMapping) { IterableMapping iterableMapping, MapMapping mapMapping,
Types typeUtils ) {
return new Method( null, executable, parameters, returnType, mappings, iterableMapping, mapMapping ); return new Method(
null,
executable,
parameters,
returnType,
mappings,
iterableMapping,
mapMapping,
typeUtils );
} }
public static Method forReferencedMethod(Type declaringMapper, ExecutableElement executable, public static Method forReferencedMethod(Type declaringMapper, ExecutableElement executable,
List<Parameter> parameters, Type returnType) { List<Parameter> parameters, Type returnType, Types typeUtils ) {
return new Method( return new Method(
declaringMapper, declaringMapper,
@ -72,12 +84,13 @@ public class Method {
returnType, returnType,
Collections.<String, List<Mapping>>emptyMap(), Collections.<String, List<Mapping>>emptyMap(),
null, null,
null null,
typeUtils
); );
} }
public static Method forFactoryMethod(Type declaringMapper, ExecutableElement executable, public static Method forFactoryMethod(Type declaringMapper, ExecutableElement executable,
Type returnType) { Type returnType, Types typeUtils) {
return new Method( return new Method(
declaringMapper, declaringMapper,
@ -86,12 +99,14 @@ public class Method {
returnType, returnType,
Collections.<String, List<Mapping>>emptyMap(), Collections.<String, List<Mapping>>emptyMap(),
null, null,
null null,
typeUtils
); );
} }
private Method(Type declaringMapper, ExecutableElement executable, List<Parameter> parameters, Type returnType, private Method(Type declaringMapper, ExecutableElement executable, List<Parameter> parameters, Type returnType,
Map<String, List<Mapping>> mappings, IterableMapping iterableMapping, MapMapping mapMapping) { Map<String, List<Mapping>> mappings, IterableMapping iterableMapping, MapMapping mapMapping,
Types typeUtils ) {
this.declaringMapper = declaringMapper; this.declaringMapper = declaringMapper;
this.executable = executable; this.executable = executable;
this.parameters = parameters; this.parameters = parameters;
@ -102,6 +117,8 @@ public class Method {
this.accessibility = Accessibility.fromModifiers( executable.getModifiers() ); this.accessibility = Accessibility.fromModifiers( executable.getModifiers() );
this.targetParameter = determineTargetParameter( parameters ); this.targetParameter = determineTargetParameter( parameters );
this.typeUtils = typeUtils;
} }
private Parameter determineTargetParameter(Iterable<Parameter> parameters) { private Parameter determineTargetParameter(Iterable<Parameter> parameters) {
@ -120,6 +137,7 @@ public class Method {
* *
* @return The declaring mapper type * @return The declaring mapper type
*/ */
@Override
public Type getDeclaringMapper() { public Type getDeclaringMapper() {
return declaringMapper; return declaringMapper;
} }
@ -128,14 +146,17 @@ public class Method {
return executable; return executable;
} }
@Override
public String getName() { public String getName() {
return executable.getSimpleName().toString(); return executable.getSimpleName().toString();
} }
@Override
public List<Parameter> getParameters() { public List<Parameter> getParameters() {
return parameters; return parameters;
} }
@Override
public List<Parameter> getSourceParameters() { public List<Parameter> getSourceParameters() {
List<Parameter> sourceParameters = new ArrayList<Parameter>(); List<Parameter> sourceParameters = new ArrayList<Parameter>();
@ -162,6 +183,7 @@ public class Method {
return targetParameter != null ? targetParameter.getType() : returnType; return targetParameter != null ? targetParameter.getType() : returnType;
} }
@Override
public Type getReturnType() { public Type getReturnType() {
return returnType; return returnType;
} }
@ -203,6 +225,7 @@ public class Method {
&& equals( getResultType(), method.getSourceParameters().iterator().next().getType() ); && equals( getResultType(), method.getSourceParameters().iterator().next().getType() );
} }
@Override
public Parameter getTargetParameter() { public Parameter getTargetParameter() {
return targetParameter; return targetParameter;
} }
@ -267,8 +290,23 @@ public class Method {
/** /**
* Whether an implementation of this method must be generated or not. * Whether an implementation of this method must be generated or not.
*
* @return true when an implementation is required
*/ */
public boolean requiresImplementation() { public boolean requiresImplementation() {
return declaringMapper == null && executable.getModifiers().contains( Modifier.ABSTRACT ); return declaringMapper == null && executable.getModifiers().contains( Modifier.ABSTRACT );
} }
/**
*
* @param sourceType
* @param targetType
* @return
*/
@Override
public boolean matches( Type sourceType, Type targetType ) {
MethodMatcher matcher = new MethodMatcher(typeUtils, this );
return matcher.matches( sourceType, targetType );
}
} }

View File

@ -61,20 +61,31 @@ import org.mapstruct.ap.model.common.Type;
*/ */
public class MethodMatcher { public class MethodMatcher {
private final Type parameter;
private final Type returnType;
private final Method candidateMethod; private final Method candidateMethod;
private final Types typeUtils; private final Types typeUtils;
private final Map<TypeVariable, TypeMirror> genericTypesMap = new HashMap<TypeVariable, TypeMirror>(); private final Map<TypeVariable, TypeMirror> genericTypesMap = new HashMap<TypeVariable, TypeMirror>();
public MethodMatcher(Types typeUtils, Method candidateMethod, Type returnType, Type parameter) { /**
* package scope constructor
*
* @param typeUtils
* @param candidateMethod
*/
MethodMatcher(Types typeUtils, Method candidateMethod) {
this.typeUtils = typeUtils; this.typeUtils = typeUtils;
this.candidateMethod = candidateMethod; this.candidateMethod = candidateMethod;
this.parameter = parameter;
this.returnType = returnType;
} }
public boolean matches() { /**
* package scoped method
*
* @param sourceType
* @param targetType
*
* @return true when both, sourceType and targetType matches the signature.
*/
boolean matches( Type sourceType, Type targetType ) {
// check & collect generic types. // check & collect generic types.
List<? extends VariableElement> candidateParameters = candidateMethod.getExecutable().getParameters(); List<? extends VariableElement> candidateParameters = candidateMethod.getExecutable().getParameters();
@ -83,14 +94,14 @@ public class MethodMatcher {
} }
TypeMatcher parameterMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_FROM ); TypeMatcher parameterMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_FROM );
if ( !parameterMatcher.visit( candidateParameters.iterator().next().asType(), parameter.getTypeMirror() ) ) { if ( !parameterMatcher.visit( candidateParameters.iterator().next().asType(), sourceType.getTypeMirror() ) ) {
return false; return false;
} }
// check return type // check return type
TypeMirror candidateReturnType = candidateMethod.getExecutable().getReturnType(); TypeMirror candidateReturnType = candidateMethod.getExecutable().getReturnType();
TypeMatcher returnTypeMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_TO ); TypeMatcher returnTypeMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_TO );
if ( !returnTypeMatcher.visit( candidateReturnType, returnType.getTypeMirror() ) ) { if ( !returnTypeMatcher.visit( candidateReturnType, targetType.getTypeMirror() ) ) {
return false; return false;
} }

View File

@ -41,7 +41,7 @@ import org.mapstruct.ap.model.common.ConversionContext;
import org.mapstruct.ap.conversion.ConversionProvider; import org.mapstruct.ap.conversion.ConversionProvider;
import org.mapstruct.ap.conversion.Conversions; import org.mapstruct.ap.conversion.Conversions;
import org.mapstruct.ap.model.common.DefaultConversionContext; import org.mapstruct.ap.model.common.DefaultConversionContext;
import org.mapstruct.ap.model.BuiltInMappingMethod; import org.mapstruct.ap.model.BuiltInMethod;
import org.mapstruct.ap.builtin.BuiltInMappingMethods; import org.mapstruct.ap.builtin.BuiltInMappingMethods;
import org.mapstruct.ap.model.BeanMappingMethod; import org.mapstruct.ap.model.BeanMappingMethod;
import org.mapstruct.ap.model.DefaultMapperReference; import org.mapstruct.ap.model.DefaultMapperReference;
@ -56,9 +56,9 @@ import org.mapstruct.ap.model.TypeConversion;
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;
import org.mapstruct.ap.model.common.TypeFactory; import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.model.source.BasicMethod;
import org.mapstruct.ap.model.source.Mapping; import org.mapstruct.ap.model.source.Mapping;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.MethodMatcher;
import org.mapstruct.ap.option.Options; import org.mapstruct.ap.option.Options;
import org.mapstruct.ap.option.ReportingPolicy; import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.MapperPrism; import org.mapstruct.ap.prism.MapperPrism;
@ -82,8 +82,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private TypeFactory typeFactory; private TypeFactory typeFactory;
private Conversions conversions; private Conversions conversions;
private BuiltInMappingMethods builtInMethods; private BuiltInMappingMethods builtInMethods;
private Set<BuiltInMethod> usedBuiltInMethods;
private Set<BuiltInMappingMethod> usedBuiltInMethods;
@Override @Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceModel) { public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceModel) {
@ -95,11 +94,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
this.typeFactory = context.getTypeFactory(); this.typeFactory = context.getTypeFactory();
this.conversions = new Conversions( elementUtils, typeFactory ); this.conversions = new Conversions( elementUtils, typeFactory );
this.builtInMethods = new BuiltInMappingMethods(messager, typeFactory ); this.builtInMethods = new BuiltInMappingMethods( typeFactory );
this.usedBuiltInMethods = new HashSet<BuiltInMappingMethod>(); this.usedBuiltInMethods = new HashSet<BuiltInMethod>();
this.builtInMethods = new BuiltInMappingMethods(messager, typeFactory );
this.usedBuiltInMethods = new HashSet<BuiltInMappingMethod>();
return getMapper( mapperTypeElement, sourceModel ); return getMapper( mapperTypeElement, sourceModel );
} }
@ -121,7 +117,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
.suppressGeneratorTimestamp( options.isSuppressGeneratorTimestamp() ) .suppressGeneratorTimestamp( options.isSuppressGeneratorTimestamp() )
.typeFactory( typeFactory ) .typeFactory( typeFactory )
.elementUtils( elementUtils ) .elementUtils( elementUtils )
.builtInMethods( new HashSet<BuiltInMappingMethod>( usedBuiltInMethods ) ) .builtInMethods( new HashSet<BuiltInMethod>( usedBuiltInMethods ) )
.build(); .build();
usedBuiltInMethods.clear(); usedBuiltInMethods.clear();
@ -129,7 +125,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
/** /**
* Returns the effective policy for reporting unmapped target properties. If * Returns the effective policy for reporting unmapped getReturnType properties. If
* explicitly set via {@code Mapper}, this value will be returned. Otherwise * explicitly set via {@code Mapper}, this value will be returned. Otherwise
* the value from the corresponding processor option will be returned. If * the value from the corresponding processor option will be returned. If
* that is not set either, the default value from * that is not set either, the default value from
@ -137,7 +133,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
* *
* @param element The type declaring the generated mapper type * @param element The type declaring the generated mapper type
* *
* @return The effective policy for reporting unmapped target properties. * @return The effective policy for reporting unmapped getReturnType properties.
*/ */
private ReportingPolicy getEffectiveUnmappedTargetPolicy(TypeElement element) { private ReportingPolicy getEffectiveUnmappedTargetPolicy(TypeElement element) {
MapperPrism mapperPrism = MapperPrism.getInstanceOn( element ); MapperPrism mapperPrism = MapperPrism.getInstanceOn( element );
@ -570,15 +566,20 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
targetType = typeFactory.getReturnType( targetAcessor ); targetType = typeFactory.getReturnType( targetAcessor );
} }
// first try the SourceMethods
String mappedElement = "property '" + Executables.getPropertyName( sourceAccessor ) + "'";
MethodReference propertyMappingMethod = getMappingMethodReference( MethodReference propertyMappingMethod = getMappingMethodReference(
method, getBestMatch( method, mappedElement, methods, sourceType, targetType ),
"property '" + Executables.getPropertyName( sourceAccessor ) + "'", mapperReferences );
mapperReferences,
methods, // then try BuiltInMethods
sourceType, if ( propertyMappingMethod == null ) {
targetType, propertyMappingMethod = getMappingMethodReference(
dateFormat getBestMatch( method, mappedElement, builtInMethods, sourceType, targetType ),
); targetType,
dateFormat );
}
TypeConversion conversion = getConversion( TypeConversion conversion = getConversion(
sourceType, sourceType,
targetType, targetType,
@ -622,16 +623,19 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
) )
); );
MethodReference elementMappingMethod = // first try the SourceMethods
getMappingMethodReference( MethodReference elementMappingMethod = getMappingMethodReference(
method, getBestMatch( method, "collection element", methods, sourceElementType, targetElementType ),
"collection element", mapperReferences );
mapperReferences,
methods, // then try BuiltInMethods
sourceElementType, if ( elementMappingMethod == null ) {
targetElementType, elementMappingMethod = getMappingMethodReference(
dateFormat getBestMatch( method, "collection element", builtInMethods, sourceElementType,
); targetElementType ),
targetElementType,
dateFormat );
}
if ( !sourceElementType.isAssignableTo( targetElementType ) && conversion == null && if ( !sourceElementType.isAssignableTo( targetElementType ) && conversion == null &&
elementMappingMethod == null ) { elementMappingMethod == null ) {
@ -676,24 +680,30 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
"entry.getValue()" "entry.getValue()"
); );
// first try the SourceMethods
MethodReference keyMappingMethod = getMappingMethodReference( MethodReference keyMappingMethod = getMappingMethodReference(
method, getBestMatch( method, "map key", methods, sourceKeyType, targetKeyType ), mapperReferences );
"map key",
mapperReferences, // then try BuiltInMethods
methods, if ( keyMappingMethod == null ) {
sourceKeyType, keyMappingMethod = getMappingMethodReference(
targetKeyType, getBestMatch( method, "map key", builtInMethods, sourceKeyType, targetKeyType ),
keyDateFormat targetKeyType,
); keyDateFormat );
}
// first try the SourceMethods
MethodReference valueMappingMethod = getMappingMethodReference( MethodReference valueMappingMethod = getMappingMethodReference(
method, getBestMatch( method, "map value", methods, sourceValueType, targetValueType ),
"map value", mapperReferences );
mapperReferences,
methods, // then try BuiltInMethods
sourceValueType, if ( valueMappingMethod == null ) {
targetValueType, valueMappingMethod = getMappingMethodReference(
valueDateFormat getBestMatch( method, "map value", builtInMethods, sourceValueType, targetValueType ),
); targetValueType,
valueDateFormat );
}
if ( !sourceKeyType.isAssignableTo( targetKeyType ) && keyConversion == null && keyMappingMethod == null ) { if ( !sourceKeyType.isAssignableTo( targetKeyType ) && keyConversion == null && keyMappingMethod == null ) {
messager.printMessage( messager.printMessage(
@ -742,28 +752,26 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private MethodReference getMappingMethodReference(Method mappingMethod, String mappedElement, private <T extends BasicMethod> T getBestMatch(Method mappingMethod, String mappedElement,
List<MapperReference> mapperReferences, Iterable<T> methods, Type parameterType,
Iterable<Method> methods, Type parameterType, Type returnType) {
Type returnType, String dateFormat) { List<T> candidatesWithMathingTargetType = new ArrayList<T>();
List<Method> candidatesWithMathingTargetType = new ArrayList<Method>();
for ( Method method : methods ) { for ( T method : methods ) {
if ( method.getSourceParameters().size() != 1 ) { if ( method.getSourceParameters().size() != 1 ) {
continue; continue;
} }
MethodMatcher m = new MethodMatcher( typeUtils, method, returnType, parameterType ); if ( method.matches( parameterType, returnType ) ) {
if ( m.matches() ) {
candidatesWithMathingTargetType.add( method ); candidatesWithMathingTargetType.add( method );
} }
} }
List<Method> candidatesWithBestMatchingSourceType = new ArrayList<Method>(); List<T> candidatesWithBestMatchingSourceType = new ArrayList<T>();
int bestMatchingSourceTypeDistance = Integer.MAX_VALUE; int bestMatchingSourceTypeDistance = Integer.MAX_VALUE;
// find the methods with the minimum distance regarding source parameter type // find the methods with the minimum distance regarding getParameter getParameter type
for ( Method method : candidatesWithMathingTargetType ) { for ( T method : candidatesWithMathingTargetType ) {
Parameter singleSourceParam = method.getSourceParameters().iterator().next(); Parameter singleSourceParam = method.getSourceParameters().iterator().next();
int sourceTypeDistance = parameterType.distanceTo( singleSourceParam.getType() ); int sourceTypeDistance = parameterType.distanceTo( singleSourceParam.getType() );
@ -776,7 +784,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
); );
} }
// print a warning if we find more than one method with minimum source type distance // print a warning if we find more than one method with minimum getParameter type distance
if ( candidatesWithBestMatchingSourceType.size() > 1 ) { if ( candidatesWithBestMatchingSourceType.size() > 1 ) {
messager.printMessage( messager.printMessage(
Kind.ERROR, Kind.ERROR,
@ -791,32 +799,15 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
if ( !candidatesWithBestMatchingSourceType.isEmpty() ) { if ( !candidatesWithBestMatchingSourceType.isEmpty() ) {
Method method = candidatesWithBestMatchingSourceType.get( 0 ); return candidatesWithBestMatchingSourceType.get( 0 );
MapperReference mapperReference = null; }
for ( MapperReference ref : mapperReferences ) {
if ( ref.getMapperType().equals( method.getDeclaringMapper() ) ) {
mapperReference = ref;
break;
}
}
return new MethodReference( method, mapperReference );
}
else {
BuiltInMappingMethod builtInMehtod = builtInMethods.getConversion( parameterType, returnType );
if (builtInMehtod != null ) {
ConversionContext context =
new DefaultConversionContext( typeFactory, returnType, dateFormat );
builtInMehtod.setConversionContext( context );
usedBuiltInMethods.add( builtInMehtod );
return builtInMehtod.createMethodReference();
}
}
return null; return null;
} }
private int addToCandidateListIfMinimal(List<Method> candidatesWithBestMathingType, int bestMatchingTypeDistance,
Method method, int currentTypeDistance) { private <T extends BasicMethod> int addToCandidateListIfMinimal(List<T> candidatesWithBestMathingType,
int bestMatchingTypeDistance, T method, int currentTypeDistance) {
if ( currentTypeDistance == bestMatchingTypeDistance ) { if ( currentTypeDistance == bestMatchingTypeDistance ) {
candidatesWithBestMathingType.add( method ); candidatesWithBestMathingType.add( method );
} }
@ -829,6 +820,29 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return bestMatchingTypeDistance; return bestMatchingTypeDistance;
} }
private MethodReference getMappingMethodReference( Method method, List<MapperReference> mapperReferences ) {
if ( method != null ) {
MapperReference mapperReference = null;
for ( MapperReference ref : mapperReferences ) {
if ( ref.getMapperType().equals( method.getDeclaringMapper() ) ) {
mapperReference = ref;
break;
}
}
return new MethodReference( method, mapperReference );
}
return null;
}
private MethodReference getMappingMethodReference( BuiltInMethod method, Type returnType, String dateFormat ) {
if ( method != null ) {
usedBuiltInMethods.add( method );
ConversionContext ctx = new DefaultConversionContext( typeFactory, returnType, dateFormat );
return new MethodReference( method, ctx );
}
return null;
}
/** /**
* Reports an error if source the property can't be mapped from source to target. A mapping if possible if one of * Reports an error if source the property can't be mapped from source to target. A mapping if possible if one of
* the following conditions is true: * the following conditions is true:
@ -952,10 +966,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
/** /**
* A getter could be an alternative target-accessor if a setter is not available, and the * A getter could be an alternative getReturnType-accessor if a setter is not available, and the
* target is a collection. getReturnType is a collection.
* *
* Provided such a getter is initialized lazy by the target class, e.g. in generated JAXB beans. * Provided such a getter is initialized lazy by the getReturnType class, e.g. in generated JAXB beans.
* *
* @param elements * @param elements
* *

View File

@ -46,6 +46,7 @@ import org.mapstruct.ap.prism.MappingsPrism;
import org.mapstruct.ap.util.AnnotationProcessingException; import org.mapstruct.ap.util.AnnotationProcessingException;
import static javax.lang.model.util.ElementFilter.methodsIn; import static javax.lang.model.util.ElementFilter.methodsIn;
import javax.lang.model.util.Types;
/** /**
* A {@link ModelElementProcessor} which retrieves a list of {@link Method}s * A {@link ModelElementProcessor} which retrieves a list of {@link Method}s
@ -59,12 +60,13 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
private Messager messager; private Messager messager;
private TypeFactory typeFactory; private TypeFactory typeFactory;
private Types typeUtils;
@Override @Override
public List<Method> process(ProcessorContext context, TypeElement mapperTypeElement, Void sourceModel) { public List<Method> process(ProcessorContext context, TypeElement mapperTypeElement, Void sourceModel) {
this.messager = context.getMessager(); this.messager = context.getMessager();
this.typeFactory = context.getTypeFactory(); this.typeFactory = context.getTypeFactory();
this.typeUtils = context.getTypeUtils();
return retrieveMethods( mapperTypeElement, true ); return retrieveMethods( mapperTypeElement, true );
} }
@ -138,7 +140,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
returnType, returnType,
getMappings( method ), getMappings( method ),
IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ), IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ),
MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ) ) MapMapping.fromPrism( MapMappingPrism.getInstanceOn( method ) ),
typeUtils
); );
} }
else { else {
@ -152,7 +155,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
mapperRequiresImplementation ? null : typeFactory.getType( element ), mapperRequiresImplementation ? null : typeFactory.getType( element ),
method, method,
parameters, parameters,
returnType returnType,
typeUtils
); );
} }
//create factory method //create factory method
@ -161,7 +165,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
Method.forFactoryMethod( Method.forFactoryMethod(
mapperRequiresImplementation ? null : typeFactory.getType( element ), mapperRequiresImplementation ? null : typeFactory.getType( element ),
method, method,
returnType returnType,
typeUtils
); );
} }
else { else {