#61 Refactoring Type into a wrapper around TypeMirror

This commit is contained in:
Gunnar Morling 2013-08-14 21:36:37 +02:00
parent 86a8e72692
commit 585f944574
23 changed files with 395 additions and 416 deletions

View File

@ -20,6 +20,7 @@ package org.mapstruct.ap.conversion;
import org.mapstruct.ap.model.Type; import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.TypeConversion; import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.util.TypeFactory;
/** /**
* Implementations create inline {@link TypeConversion}s such as * Implementations create inline {@link TypeConversion}s such as
@ -80,5 +81,7 @@ public interface ConversionProvider {
* @return The date format if this conversion. * @return The date format if this conversion.
*/ */
String getDateFormat(); String getDateFormat();
TypeFactory getTypeFactory();
} }
} }

View File

@ -22,12 +22,10 @@ import java.math.BigInteger;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.model.Type; import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.util.TypeUtil; import org.mapstruct.ap.util.TypeFactory;
import static org.mapstruct.ap.conversion.ReverseConversion.reverse; import static org.mapstruct.ap.conversion.ReverseConversion.reverse;
@ -38,16 +36,16 @@ import static org.mapstruct.ap.conversion.ReverseConversion.reverse;
*/ */
public class Conversions { public class Conversions {
private TypeUtil typeUtil;
private final Map<Key, ConversionProvider> conversions = new HashMap<Conversions.Key, ConversionProvider>(); private final Map<Key, ConversionProvider> conversions = new HashMap<Conversions.Key, ConversionProvider>();
private final DeclaredType enumType; private final Type enumType;
private final DeclaredType stringType; private final Type stringType;
private final TypeFactory typeFactory;
public Conversions(Elements elementUtils, Types typeUtils, TypeUtil typeUtil) { public Conversions(Elements elementUtils, TypeFactory typeFactory) {
this.typeUtil = typeUtil; this.typeFactory = typeFactory;
this.enumType = typeUtils.getDeclaredType( elementUtils.getTypeElement( Enum.class.getCanonicalName() ) ); this.enumType = typeFactory.getType( Enum.class );
this.stringType = typeUtils.getDeclaredType( elementUtils.getTypeElement( String.class.getCanonicalName() ) ); this.stringType = typeFactory.getType( String.class );
//native types <> native types, including wrappers //native types <> native types, including wrappers
registerNativeTypeConversion( byte.class, Byte.class ); registerNativeTypeConversion( byte.class, Byte.class );
@ -202,28 +200,32 @@ public class Conversions {
} }
private void register(Class<?> sourceType, Class<?> targetType, ConversionProvider conversion) { private void register(Class<?> sourceType, Class<?> targetType, ConversionProvider conversion) {
conversions.put( Key.forClasses( sourceType, targetType ), conversion ); conversions.put( forClasses( sourceType, targetType ), conversion );
conversions.put( Key.forClasses( targetType, sourceType ), reverse( conversion ) ); conversions.put( forClasses( targetType, sourceType ), reverse( conversion ) );
} }
public ConversionProvider getConversion(Type sourceType, Type targetType) { public ConversionProvider getConversion(Type sourceType, Type targetType) {
if ( sourceType.isEnumType() && targetType.equals( typeUtil.getType( stringType ) ) ) { if ( sourceType.isEnumType() && targetType.equals( stringType ) ) {
sourceType = typeUtil.getType( enumType ); sourceType = enumType;
} }
else if ( targetType.isEnumType() && sourceType.equals( typeUtil.getType( stringType ) ) ) { else if ( targetType.isEnumType() && sourceType.equals( stringType ) ) {
targetType = typeUtil.getType( enumType ); targetType = enumType;
} }
return conversions.get( new Key( sourceType, targetType ) ); return conversions.get( new Key( sourceType, targetType ) );
} }
private Key forClasses(Class<?> sourceClass, Class<?> targetClass) {
Type sourceType = typeFactory.getType( sourceClass );
Type targetType = typeFactory.getType( targetClass );
return new Key( sourceType, targetType );
}
private static class Key { private static class Key {
private final Type sourceType; private final Type sourceType;
private final Type targetType; private final Type targetType;
private static Key forClasses(Class<?> sourceType, Class<?> targetType) {
return new Key( Type.forClass( sourceType ), Type.forClass( targetType ) );
}
private Key(Type sourceType, Type targetType) { private Key(Type sourceType, Type targetType) {
this.sourceType = sourceType; this.sourceType = sourceType;
@ -240,10 +242,8 @@ public class Conversions {
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result result = prime * result + ( ( sourceType == null ) ? 0 : sourceType.hashCode() );
+ ( ( sourceType == null ) ? 0 : sourceType.hashCode() ); result = prime * result + ( ( targetType == null ) ? 0 : targetType.hashCode() );
result = prime * result
+ ( ( targetType == null ) ? 0 : targetType.hashCode() );
return result; return result;
} }

View File

@ -39,7 +39,7 @@ public class DateToStringConversion implements ConversionProvider {
@Override @Override
public TypeConversion to(String sourceReference, Context conversionContext) { public TypeConversion to(String sourceReference, Context conversionContext) {
return new TypeConversion( return new TypeConversion(
asSet( Type.forClass( SimpleDateFormat.class ) ), asSet( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ),
Collections.<Type>emptyList(), Collections.<Type>emptyList(),
getConversionString( sourceReference, conversionContext, "format" ) getConversionString( sourceReference, conversionContext, "format" )
); );
@ -48,8 +48,8 @@ public class DateToStringConversion implements ConversionProvider {
@Override @Override
public TypeConversion from(String targetReference, Context conversionContext) { public TypeConversion from(String targetReference, Context conversionContext) {
return new TypeConversion( return new TypeConversion(
asSet( Type.forClass( SimpleDateFormat.class ) ), asSet( conversionContext.getTypeFactory().getType( SimpleDateFormat.class ) ),
Arrays.asList( Type.forClass( ParseException.class ) ), Arrays.asList( conversionContext.getTypeFactory().getType( ParseException.class ) ),
getConversionString( targetReference, conversionContext, "parse" ) getConversionString( targetReference, conversionContext, "parse" )
); );
} }

View File

@ -20,6 +20,7 @@ package org.mapstruct.ap.conversion;
import org.mapstruct.ap.conversion.ConversionProvider.Context; import org.mapstruct.ap.conversion.ConversionProvider.Context;
import org.mapstruct.ap.model.Type; import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.util.TypeFactory;
/** /**
* Default implementation of the {@link Context} passed to conversion providers. * Default implementation of the {@link Context} passed to conversion providers.
@ -30,8 +31,10 @@ public class DefaultConversionContext implements ConversionProvider.Context {
private final Type targetType; private final Type targetType;
private final String format; private final String format;
private final TypeFactory typeFactory;
public DefaultConversionContext(Type targetType, String format) { public DefaultConversionContext(TypeFactory typeFactory, Type targetType, String format) {
this.typeFactory = typeFactory;
this.targetType = targetType; this.targetType = targetType;
this.format = format; this.format = format;
} }
@ -45,4 +48,9 @@ public class DefaultConversionContext implements ConversionProvider.Context {
public String getDateFormat() { public String getDateFormat() {
return format; return format;
} }
@Override
public TypeFactory getTypeFactory() {
return typeFactory;
}
} }

View File

@ -25,6 +25,8 @@ import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import javax.annotation.Generated; import javax.annotation.Generated;
import org.mapstruct.ap.util.TypeFactory;
/** /**
* Represents a type implementing a mapper interface (annotated with {@code @Mapper}. This is the root object of the * Represents a type implementing a mapper interface (annotated with {@code @Mapper}. This is the root object of the
* mapper model. * mapper model.
@ -33,6 +35,7 @@ import javax.annotation.Generated;
*/ */
public class Mapper extends AbstractModelElement { public class Mapper extends AbstractModelElement {
private final TypeFactory typeFactory;
private final String packageName; private final String packageName;
private final String interfaceName; private final String interfaceName;
private final String implementationName; private final String implementationName;
@ -41,7 +44,7 @@ public class Mapper extends AbstractModelElement {
private final List<MapperReference> referencedMappers; private final List<MapperReference> referencedMappers;
private final Options options; private final Options options;
public Mapper(String packageName, String interfaceName, String implementationName, public Mapper(TypeFactory typeFactory, String packageName, String interfaceName, String implementationName,
List<MappingMethod> mappingMethods, List<MapperReference> referencedMappers, Options options) { List<MappingMethod> mappingMethods, List<MapperReference> referencedMappers, Options options) {
this.packageName = packageName; this.packageName = packageName;
this.interfaceName = interfaceName; this.interfaceName = interfaceName;
@ -50,12 +53,13 @@ public class Mapper extends AbstractModelElement {
this.mappingMethods = mappingMethods; this.mappingMethods = mappingMethods;
this.referencedMappers = referencedMappers; this.referencedMappers = referencedMappers;
this.options = options; this.options = options;
this.typeFactory = typeFactory;
} }
@Override @Override
public SortedSet<Type> getImportTypes() { public SortedSet<Type> getImportTypes() {
SortedSet<Type> importedTypes = new TreeSet<Type>(); SortedSet<Type> importedTypes = new TreeSet<Type>();
importedTypes.add( Type.forClass( Generated.class ) ); importedTypes.add( typeFactory.getType( Generated.class ) );
for ( MappingMethod mappingMethod : mappingMethods ) { for ( MappingMethod mappingMethod : mappingMethods ) {
for ( Type type : mappingMethod.getImportTypes() ) { for ( Type type : mappingMethod.getImportTypes() ) {
@ -87,9 +91,7 @@ public class Mapper extends AbstractModelElement {
collection.add( typeToAdd ); collection.add( typeToAdd );
} }
addWithDependents( collection, typeToAdd.getCollectionImplementationType() ); addWithDependents( collection, typeToAdd.getImplementationType() );
addWithDependents( collection, typeToAdd.getIterableImplementationType() );
addWithDependents( collection, typeToAdd.getMapImplementationType() );
for ( Type type : typeToAdd.getTypeParameters() ) { for ( Type type : typeToAdd.getTypeParameters() ) {
addWithDependents( collection, type ); addWithDependents( collection, type );

View File

@ -92,6 +92,7 @@ public abstract class MappingMethod extends AbstractModelElement {
} }
types.add( getReturnType() ); types.add( getReturnType() );
types.addAll( getReturnType().getImportTypes() );
return types; return types;
} }

View File

@ -18,176 +18,88 @@
*/ */
package org.mapstruct.ap.model; package org.mapstruct.ap.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Set; import java.util.Set;
import java.util.SortedMap; import javax.lang.model.element.ElementKind;
import java.util.SortedSet; import javax.lang.model.element.TypeElement;
import java.util.TreeMap; import javax.lang.model.type.DeclaredType;
import java.util.TreeSet; import javax.lang.model.type.TypeKind;
import java.util.concurrent.ConcurrentHashMap; import javax.lang.model.type.TypeMirror;
import java.util.concurrent.ConcurrentMap; import javax.lang.model.util.Elements;
import java.util.concurrent.ConcurrentNavigableMap; import javax.lang.model.util.SimpleElementVisitor6;
import java.util.concurrent.ConcurrentSkipListMap; import javax.lang.model.util.Types;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.TypeFactory;
/** /**
* Represents the type of a bean property, parameter etc. * Represents the type of a bean property, parameter etc. Each type corresponds to a {@link TypeMirror}, i.e. there are
* different instances for e.g. {@code Set<String>} and {@code Set<Integer>}.
* <p>
* Allows for a unified handling of declared and primitive types and usage within templates. Instances are obtained
* through {@link TypeFactory}.
* *
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class Type extends AbstractModelElement implements Comparable<Type> { public class Type extends AbstractModelElement implements Comparable<Type> {
/**
* Type representing {@code void}
*/
public static final Type VOID = new Type( "void" );
private static final Set<String> PRIMITIVE_TYPE_NAMES = new HashSet<String>(
Arrays.asList( "boolean", "char", "byte", "short", "int", "long", "float", "double" )
);
private static final ConcurrentMap<String, Type> DEFAULT_ITERABLE_IMPLEMENTATION_TYPES =
new ConcurrentHashMap<String, Type>();
private static final ConcurrentMap<String, Type> DEFAULT_COLLECTION_IMPLEMENTATION_TYPES =
new ConcurrentHashMap<String, Type>();
private static final ConcurrentMap<String, Type> DEFAULT_MAP_IMPLEMENTATION_TYPES =
new ConcurrentHashMap<String, Type>();
static {
//base
DEFAULT_ITERABLE_IMPLEMENTATION_TYPES.put( Iterable.class.getName(), forClass( ArrayList.class ) );
DEFAULT_COLLECTION_IMPLEMENTATION_TYPES.put( Collection.class.getName(), forClass( ArrayList.class ) );
//list
DEFAULT_COLLECTION_IMPLEMENTATION_TYPES.put( List.class.getName(), forClass( ArrayList.class ) );
//set
DEFAULT_COLLECTION_IMPLEMENTATION_TYPES.put( Set.class.getName(), forClass( HashSet.class ) );
DEFAULT_COLLECTION_IMPLEMENTATION_TYPES.put( SortedSet.class.getName(), forClass( TreeSet.class ) );
DEFAULT_COLLECTION_IMPLEMENTATION_TYPES.put( NavigableSet.class.getName(), forClass( TreeSet.class ) );
DEFAULT_ITERABLE_IMPLEMENTATION_TYPES.putAll( DEFAULT_COLLECTION_IMPLEMENTATION_TYPES );
//map
DEFAULT_MAP_IMPLEMENTATION_TYPES.put( Map.class.getName(), forClass( HashMap.class ) );
DEFAULT_MAP_IMPLEMENTATION_TYPES.put( SortedMap.class.getName(), forClass( TreeMap.class ) );
DEFAULT_MAP_IMPLEMENTATION_TYPES.put( NavigableMap.class.getName(), forClass( TreeMap.class ) );
DEFAULT_MAP_IMPLEMENTATION_TYPES.put( ConcurrentMap.class.getName(), forClass( ConcurrentHashMap.class ) );
DEFAULT_MAP_IMPLEMENTATION_TYPES.put(
ConcurrentNavigableMap.class.getName(),
forClass( ConcurrentSkipListMap.class )
);
}
private final String canonicalName;
private final String packageName; private final String packageName;
private final String name; private final String name;
private final String qualifiedName;
private final List<Type> typeParameters; private final List<Type> typeParameters;
private final boolean isInterface; private final boolean isInterface;
private final boolean isEnumType; private final boolean isEnumType;
private final boolean isCollectionType;
private final boolean isIterableType; private final boolean isIterableType;
private final boolean isMapType; private final boolean isMapType;
private final Type collectionImplementationType; private final Type implementationType;
private final Type iterableImplementationType; private final TypeMirror typeMirror;
private final Type mapImplementationType; private final Types typeUtils;
private final TypeElement typeElement;
public static Type forClass(Class<?> clazz) { public Type(TypeMirror typeMirror, List<Type> typeParameters, Type implementationType, boolean isIterableType,
Package pakkage = clazz.getPackage(); boolean isMapType,
Types typeUtils, Elements elementUtils) {
if ( pakkage != null ) { this.typeMirror = typeMirror;
return new Type( this.implementationType = implementationType;
clazz.getCanonicalName(), this.typeParameters = typeParameters;
pakkage.getName(),
clazz.getSimpleName(),
clazz.isInterface(),
clazz.isEnum(),
Collection.class.isAssignableFrom( clazz ),
Iterable.class.isAssignableFrom( clazz ),
Map.class.isAssignableFrom( clazz ),
Collections.<Type>emptyList()
);
}
else {
return new Type( clazz.getSimpleName() );
}
}
public Type(String name) {
this( name, null, name, false, false, false, false, false, Collections.<Type>emptyList() );
}
public Type(String packageName, String name) {
this(
packageName + "." + name,
packageName,
name,
false,
false,
false,
false,
false,
Collections.<Type>emptyList()
);
}
public Type(String canonicalName, String packageName, String name, boolean isInterface, boolean isEnumType,
boolean isCollectionType,
boolean isIterableType, boolean isMapType, List<Type> typeParameters) {
this.canonicalName = canonicalName;
this.packageName = packageName;
this.name = name;
this.isInterface = isInterface;
this.isEnumType = isEnumType;
this.isCollectionType = isCollectionType;
this.isIterableType = isIterableType; this.isIterableType = isIterableType;
this.isMapType = isMapType; this.isMapType = isMapType;
this.typeParameters = typeParameters; this.typeUtils = typeUtils;
if ( isCollectionType ) { DeclaredType declaredType = typeMirror.getKind() == TypeKind.DECLARED ? (DeclaredType) typeMirror : null;
collectionImplementationType = DEFAULT_COLLECTION_IMPLEMENTATION_TYPES.get( packageName + "." + name );
if ( declaredType != null ) {
isEnumType = declaredType.asElement().getKind() == ElementKind.ENUM;
isInterface = declaredType.asElement().getKind() == ElementKind.INTERFACE;
name = declaredType.asElement().getSimpleName().toString();
typeElement = declaredType.asElement().accept( new TypeElementRetrievalVisitor(), null );
if ( typeElement != null ) {
packageName = elementUtils.getPackageOf( typeElement ).getQualifiedName().toString();
qualifiedName = typeElement.getQualifiedName().toString();
}
else {
packageName = null;
qualifiedName = name;
}
} }
else { else {
collectionImplementationType = null; isEnumType = false;
} isInterface = false;
typeElement = null;
if ( isIterableType ) { name = typeMirror.toString();
iterableImplementationType = DEFAULT_ITERABLE_IMPLEMENTATION_TYPES.get( packageName + "." + name ); packageName = null;
} qualifiedName = name;
else {
iterableImplementationType = null;
}
if ( isMapType ) {
Type mapType = DEFAULT_MAP_IMPLEMENTATION_TYPES.get( packageName + "." + name );
mapImplementationType = mapType != null ? new Type(
mapType.getPackageName() + "." + mapType.getName(),
mapType.getPackageName(),
mapType.getName(),
mapType.isInterface(),
mapType.isEnumType(),
mapType.isCollectionType(),
mapType.isIterableType(),
true,
typeParameters
) : null;
}
else {
mapImplementationType = null;
} }
} }
public String getCanonicalName() { public TypeMirror getTypeMirror() {
return canonicalName; return typeMirror;
}
public TypeElement getTypeElement() {
return typeElement;
} }
public String getPackageName() { public String getPackageName() {
@ -203,7 +115,7 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
} }
public boolean isPrimitive() { public boolean isPrimitive() {
return packageName == null && PRIMITIVE_TYPE_NAMES.contains( name ); return typeMirror.getKind().isPrimitive();
} }
public boolean isInterface() { public boolean isInterface() {
@ -214,26 +126,8 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
return isEnumType; return isEnumType;
} }
public Type getCollectionImplementationType() {
return collectionImplementationType;
}
public Type getIterableImplementationType() {
return iterableImplementationType;
}
public Type getMapImplementationType() {
return mapImplementationType;
}
public boolean isCollectionType() {
return isCollectionType;
}
public Type getImplementationType() { public Type getImplementationType() {
return collectionImplementationType != null ? collectionImplementationType : return implementationType;
iterableImplementationType != null ? iterableImplementationType :
mapImplementationType;
} }
public boolean isIterableType() { public boolean isIterableType() {
@ -245,12 +139,13 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
} }
public String getFullyQualifiedName() { public String getFullyQualifiedName() {
return packageName == null ? name : packageName + "." + name; return qualifiedName;
} }
@Override @Override
public Set<Type> getImportTypes() { public Set<Type> getImportTypes() {
return Collections.emptySet(); return implementationType != null ? org.mapstruct.ap.util.Collections.<Type>asSet( implementationType ) :
Collections.<Type>emptySet();
} }
@Override @Override
@ -259,7 +154,6 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
int result = 1; int result = 1;
result = prime * result + ( ( name == null ) ? 0 : name.hashCode() ); result = prime * result + ( ( name == null ) ? 0 : name.hashCode() );
result = prime * result + ( ( packageName == null ) ? 0 : packageName.hashCode() ); result = prime * result + ( ( packageName == null ) ? 0 : packageName.hashCode() );
result = prime * result + ( ( typeParameters == null ) ? 0 : typeParameters.hashCode() );
return result; return result;
} }
@ -275,31 +169,8 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
return false; return false;
} }
Type other = (Type) obj; Type other = (Type) obj;
if ( name == null ) {
if ( other.name != null ) { return typeUtils.isSameType( typeMirror, other.typeMirror );
return false;
}
}
else if ( !name.equals( other.name ) ) {
return false;
}
if ( packageName == null ) {
if ( other.packageName != null ) {
return false;
}
}
else if ( !packageName.equals( other.packageName ) ) {
return false;
}
if ( typeParameters == null ) {
if ( other.typeParameters != null ) {
return false;
}
}
else if ( !typeParameters.equals( other.typeParameters ) ) {
return false;
}
return true;
} }
@Override @Override
@ -309,11 +180,13 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
@Override @Override
public String toString() { public String toString() {
if ( !typeParameters.isEmpty() ) { return typeMirror.toString();
return name + "<" + Strings.join( typeParameters, ", " ) + ">"; }
}
else { private static class TypeElementRetrievalVisitor extends SimpleElementVisitor6<TypeElement, Void> {
return name; @Override
public TypeElement visitType(TypeElement e, Void p) {
return e;
} }
} }
} }

View File

@ -27,6 +27,7 @@ import org.mapstruct.ap.model.AnnotationMapperReference;
import org.mapstruct.ap.model.Mapper; import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MapperReference; import org.mapstruct.ap.model.MapperReference;
import org.mapstruct.ap.util.OptionsHelper; import org.mapstruct.ap.util.OptionsHelper;
import org.mapstruct.ap.util.TypeFactory;
/** /**
* An {@link ModelElementProcessor} which converts the given {@link Mapper} * An {@link ModelElementProcessor} which converts the given {@link Mapper}
@ -38,8 +39,12 @@ import org.mapstruct.ap.util.OptionsHelper;
*/ */
public abstract class AnnotationBasedComponentModelProcessor implements ModelElementProcessor<Mapper, Mapper> { public abstract class AnnotationBasedComponentModelProcessor implements ModelElementProcessor<Mapper, Mapper> {
private TypeFactory typeFactory;
@Override @Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) { public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) {
this.typeFactory = context.getTypeFactory();
String componentModel = MapperPrism.getInstanceOn( mapperTypeElement ).componentModel(); String componentModel = MapperPrism.getInstanceOn( mapperTypeElement ).componentModel();
String effectiveComponentModel = OptionsHelper.getEffectiveComponentModel( String effectiveComponentModel = OptionsHelper.getEffectiveComponentModel(
context.getOptions(), context.getOptions(),
@ -68,7 +73,10 @@ public abstract class AnnotationBasedComponentModelProcessor implements ModelEle
* @return the mapper reference replacing the original one * @return the mapper reference replacing the original one
*/ */
protected MapperReference replacementMapperReference(MapperReference originalReference) { protected MapperReference replacementMapperReference(MapperReference originalReference) {
return new AnnotationMapperReference( getMapperReferenceAnnotation(), originalReference.getMapperType() ); return new AnnotationMapperReference(
getMapperReferenceAnnotation(),
originalReference.getMapperType()
);
} }
/** /**
@ -90,4 +98,8 @@ public abstract class AnnotationBasedComponentModelProcessor implements ModelEle
public int getPriority() { public int getPriority() {
return 1100; return 1100;
} }
protected TypeFactory getTypeFactory() {
return typeFactory;
}
} }

View File

@ -20,7 +20,6 @@ package org.mapstruct.ap.processor;
import org.mapstruct.ap.model.Annotation; import org.mapstruct.ap.model.Annotation;
import org.mapstruct.ap.model.Mapper; import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.Type;
/** /**
* A {@link ModelElementProcessor} which converts the given {@link Mapper} * A {@link ModelElementProcessor} which converts the given {@link Mapper}
@ -38,11 +37,11 @@ public class CdiComponentProcessor extends AnnotationBasedComponentModelProcesso
@Override @Override
protected Annotation getTypeAnnotation() { protected Annotation getTypeAnnotation() {
return new Annotation( new Type( "javax.enterprise.context", "ApplicationScoped" ) ); return new Annotation( getTypeFactory().getType( "javax.enterprise.context.ApplicationScoped" ) );
} }
@Override @Override
protected Annotation getMapperReferenceAnnotation() { protected Annotation getMapperReferenceAnnotation() {
return new Annotation( new Type( "javax.inject", "Inject" ) ); return new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) );
} }
} }

View File

@ -29,6 +29,7 @@ import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.model.Options; import org.mapstruct.ap.model.Options;
import org.mapstruct.ap.util.TypeFactory;
/** /**
* Default implementation of the processor context. * Default implementation of the processor context.
@ -40,10 +41,15 @@ public class DefaultModelElementProcessorContext implements ModelElementProcesso
private final ProcessingEnvironment processingEnvironment; private final ProcessingEnvironment processingEnvironment;
private final DelegatingMessager messager; private final DelegatingMessager messager;
private final Options options; private final Options options;
private final TypeFactory typeFactory;
public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options) { public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options) {
this.processingEnvironment = processingEnvironment; this.processingEnvironment = processingEnvironment;
this.messager = new DelegatingMessager( processingEnvironment.getMessager() ); this.messager = new DelegatingMessager( processingEnvironment.getMessager() );
this.typeFactory = new TypeFactory(
processingEnvironment.getElementUtils(),
processingEnvironment.getTypeUtils()
);
this.options = options; this.options = options;
} }
@ -62,6 +68,11 @@ public class DefaultModelElementProcessorContext implements ModelElementProcesso
return processingEnvironment.getElementUtils(); return processingEnvironment.getElementUtils();
} }
@Override
public TypeFactory getTypeFactory() {
return typeFactory;
}
@Override @Override
public Messager getMessager() { public Messager getMessager() {
return messager; return messager;
@ -86,6 +97,7 @@ public class DefaultModelElementProcessorContext implements ModelElementProcesso
this.delegate = delegate; this.delegate = delegate;
} }
@Override
public void printMessage(Kind kind, CharSequence msg) { public void printMessage(Kind kind, CharSequence msg) {
delegate.printMessage( kind, msg ); delegate.printMessage( kind, msg );
if ( kind == Kind.ERROR ) { if ( kind == Kind.ERROR ) {
@ -93,6 +105,7 @@ public class DefaultModelElementProcessorContext implements ModelElementProcesso
} }
} }
@Override
public void printMessage(Kind kind, CharSequence msg, Element e) { public void printMessage(Kind kind, CharSequence msg, Element e) {
delegate.printMessage( kind, msg, e ); delegate.printMessage( kind, msg, e );
if ( kind == Kind.ERROR ) { if ( kind == Kind.ERROR ) {
@ -100,6 +113,7 @@ public class DefaultModelElementProcessorContext implements ModelElementProcesso
} }
} }
@Override
public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a) { public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a) {
delegate.printMessage( kind, msg, e, a ); delegate.printMessage( kind, msg, e, a );
if ( kind == Kind.ERROR ) { if ( kind == Kind.ERROR ) {
@ -107,6 +121,7 @@ public class DefaultModelElementProcessorContext implements ModelElementProcesso
} }
} }
@Override
public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v) { public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v) {
delegate.printMessage( kind, msg, e, a, v ); delegate.printMessage( kind, msg, e, a, v );
if ( kind == Kind.ERROR ) { if ( kind == Kind.ERROR ) {

View File

@ -20,7 +20,6 @@ package org.mapstruct.ap.processor;
import org.mapstruct.ap.model.Annotation; import org.mapstruct.ap.model.Annotation;
import org.mapstruct.ap.model.Mapper; import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.Type;
/** /**
* A {@link ModelElementProcessor} which converts the given {@link Mapper} * A {@link ModelElementProcessor} which converts the given {@link Mapper}
@ -38,11 +37,11 @@ public class Jsr330ComponentProcessor extends AnnotationBasedComponentModelProce
@Override @Override
protected Annotation getTypeAnnotation() { protected Annotation getTypeAnnotation() {
return new Annotation( new Type( "javax.inject", "Named" ) ); return new Annotation( getTypeFactory().getType( "javax.inject.Named" ) );
} }
@Override @Override
protected Annotation getMapperReferenceAnnotation() { protected Annotation getMapperReferenceAnnotation() {
return new Annotation( new Type( "javax.inject", "Inject" ) ); return new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) );
} }
} }

View File

@ -30,9 +30,9 @@ import java.util.Set;
import javax.annotation.processing.Messager; import javax.annotation.processing.Messager;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.MapperPrism; import org.mapstruct.ap.MapperPrism;
@ -58,7 +58,7 @@ import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.util.Executables; import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.Filters; import org.mapstruct.ap.util.Filters;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
import org.mapstruct.ap.util.TypeUtil; import org.mapstruct.ap.util.TypeFactory;
/** /**
* A {@link ModelElementProcessor} which creates a {@link Mapper} from the given * A {@link ModelElementProcessor} which creates a {@link Mapper} from the given
@ -71,24 +71,24 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private static final String IMPLEMENTATION_SUFFIX = "Impl"; private static final String IMPLEMENTATION_SUFFIX = "Impl";
private Elements elementUtils; private Elements elementUtils;
private Types typeUtils;
private Messager messager; private Messager messager;
private Options options; private Options options;
private TypeUtil typeUtil; private TypeFactory typeFactory;
private Conversions conversions; private Conversions conversions;
private Executables executables; private Executables executables;
private Filters filters;
@Override @Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceModel) { public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceModel) {
this.elementUtils = context.getElementUtils(); this.elementUtils = context.getElementUtils();
this.typeUtils = context.getTypeUtils();
this.messager = context.getMessager(); this.messager = context.getMessager();
this.options = context.getOptions(); this.options = context.getOptions();
this.typeUtil = new TypeUtil( context.getElementUtils(), context.getTypeUtils() ); this.typeFactory = context.getTypeFactory();
this.conversions = new Conversions( elementUtils, typeUtils, typeUtil ); this.conversions = new Conversions( elementUtils, typeFactory );
this.executables = new Executables( typeUtil ); this.executables = new Executables( typeFactory );
this.filters = new Filters( executables );
return getMapper( mapperTypeElement, sourceModel ); return getMapper( mapperTypeElement, sourceModel );
} }
@ -104,6 +104,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
List<MapperReference> mapperReferences = getReferencedMappers( element ); List<MapperReference> mapperReferences = getReferencedMappers( element );
return new Mapper( return new Mapper(
typeFactory,
elementUtils.getPackageOf( element ).getQualifiedName().toString(), elementUtils.getPackageOf( element ).getQualifiedName().toString(),
element.getSimpleName().toString(), element.getSimpleName().toString(),
element.getSimpleName() + IMPLEMENTATION_SUFFIX, element.getSimpleName() + IMPLEMENTATION_SUFFIX,
@ -143,7 +144,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
MapperPrism mapperPrism = MapperPrism.getInstanceOn( element ); MapperPrism mapperPrism = MapperPrism.getInstanceOn( element );
for ( TypeMirror usedMapper : mapperPrism.uses() ) { for ( TypeMirror usedMapper : mapperPrism.uses() ) {
mapperReferences.add( new DefaultMapperReference( typeUtil.retrieveType( usedMapper ) ) ); mapperReferences.add( new DefaultMapperReference( typeFactory.getType( usedMapper ) ) );
} }
return mapperReferences; return mapperReferences;
@ -191,7 +192,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
private void reportErrorIfNoImplementationTypeIsRegisteredForInterfaceReturnType(Method method) { private void reportErrorIfNoImplementationTypeIsRegisteredForInterfaceReturnType(Method method) {
if ( method.getReturnType() != Type.VOID && method.getReturnType().isInterface() && if ( method.getReturnType().getTypeMirror().getKind() != TypeKind.VOID &&
method.getReturnType().isInterface() &&
method.getReturnType().getImplementationType() == null ) { method.getReturnType().getImplementationType() == null ) {
messager.printMessage( messager.printMessage(
Kind.ERROR, Kind.ERROR,
@ -219,8 +221,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
Mapping mapping = method.getMapping( targetPropertyName ); Mapping mapping = method.getMapping( targetPropertyName );
String dateFormat = mapping != null ? mapping.getDateFormat() : null; String dateFormat = mapping != null ? mapping.getDateFormat() : null;
String sourcePropertyName = mapping != null ? mapping.getSourcePropertyName() : targetPropertyName; String sourcePropertyName = mapping != null ? mapping.getSourcePropertyName() : targetPropertyName;
TypeElement parameterElement = elementUtils.getTypeElement( parameter.getType().getCanonicalName() ); TypeElement parameterElement = parameter.getType().getTypeElement();
List<ExecutableElement> sourceGetters = Filters.getterMethodsIn( List<ExecutableElement> sourceGetters = filters.getterMethodsIn(
elementUtils.getAllMembers( parameterElement ) elementUtils.getAllMembers( parameterElement )
); );
@ -252,8 +254,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return null; return null;
} }
TypeElement resultTypeElement = elementUtils.getTypeElement( method.getResultType().getCanonicalName() ); TypeElement resultTypeElement = method.getResultType().getTypeElement();
List<ExecutableElement> targetSetters = Filters.setterMethodsIn( List<ExecutableElement> targetSetters = filters.setterMethodsIn(
elementUtils.getAllMembers( resultTypeElement ) elementUtils.getAllMembers( resultTypeElement )
); );
@ -348,8 +350,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
private boolean hasProperty(Parameter parameter, String propertyName) { private boolean hasProperty(Parameter parameter, String propertyName) {
TypeElement parameterTypeElement = elementUtils.getTypeElement( parameter.getType().getCanonicalName() ); TypeElement parameterTypeElement = parameter.getType().getTypeElement();
List<ExecutableElement> getters = Filters.setterMethodsIn( List<ExecutableElement> getters = filters.setterMethodsIn(
elementUtils.getAllMembers( parameterTypeElement ) elementUtils.getAllMembers( parameterTypeElement )
); );
@ -357,8 +359,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
private boolean reportErrorIfMappedPropertiesDontExist(Method method) { private boolean reportErrorIfMappedPropertiesDontExist(Method method) {
TypeElement resultTypeElement = elementUtils.getTypeElement( method.getResultType().getCanonicalName() ); TypeElement resultTypeElement = method.getResultType().getTypeElement();
List<ExecutableElement> targetSetters = Filters.setterMethodsIn( List<ExecutableElement> targetSetters = filters.setterMethodsIn(
elementUtils.getAllMembers( resultTypeElement ) elementUtils.getAllMembers( resultTypeElement )
); );
@ -528,7 +530,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return null; return null;
} }
return conversionProvider.to( sourceReference, new DefaultConversionContext( targetType, dateFormat ) ); return conversionProvider.to(
sourceReference,
new DefaultConversionContext( typeFactory, targetType, dateFormat )
);
} }
private MappingMethodReference getMappingMethodReference(Iterable<Method> methods, Type parameterType, private MappingMethodReference getMappingMethodReference(Iterable<Method> methods, Type parameterType,
@ -540,8 +545,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
Parameter singleSourceParam = method.getSourceParameters().iterator().next(); Parameter singleSourceParam = method.getSourceParameters().iterator().next();
if ( singleSourceParam.getType().equals( parameterType ) && if ( singleSourceParam.getType().equals( parameterType ) && method.getResultType().equals( returnType ) ) {
method.getResultType().equals( returnType ) ) {
return new MappingMethodReference( method ); return new MappingMethodReference( method );
} }
} }
@ -561,8 +565,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
if ( property.getSourceType().equals( property.getTargetType() ) || if ( property.getSourceType().equals( property.getTargetType() ) ||
property.getMappingMethod() != null || property.getMappingMethod() != null ||
property.getConversion() != null || property.getConversion() != null ||
( property.getTargetType().isCollectionType() && property.getTargetType().getImplementationType() != null ) {
property.getTargetType().getCollectionImplementationType() != null ) ) {
return; return;
} }

View File

@ -26,8 +26,8 @@ import javax.annotation.processing.Messager;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.IterableMappingPrism; import org.mapstruct.ap.IterableMappingPrism;
@ -42,7 +42,7 @@ import org.mapstruct.ap.model.source.MapMapping;
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.util.Executables; import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.TypeUtil; import org.mapstruct.ap.util.TypeFactory;
import static javax.lang.model.util.ElementFilter.methodsIn; import static javax.lang.model.util.ElementFilter.methodsIn;
@ -57,16 +57,14 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
public class MethodRetrievalProcessor implements ModelElementProcessor<Void, List<Method>> { public class MethodRetrievalProcessor implements ModelElementProcessor<Void, List<Method>> {
private Messager messager; private Messager messager;
private Types typeUtils; private TypeFactory typeFactory;
private TypeUtil typeUtil;
private Executables executables; private Executables executables;
@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.typeUtils = context.getTypeUtils(); this.typeFactory = context.getTypeFactory();
this.typeUtil = new TypeUtil( context.getElementUtils(), typeUtils ); this.executables = new Executables( typeFactory );
this.executables = new Executables( typeUtil );
return retrieveMethods( mapperTypeElement, true ); return retrieveMethods( mapperTypeElement, true );
} }
@ -146,7 +144,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
else if ( parameters.size() == 1 ) { else if ( parameters.size() == 1 ) {
return return
Method.forReferencedMethod( Method.forReferencedMethod(
typeUtil.getType( typeUtils.getDeclaredType( element ) ), typeFactory.getType( element ),
method, method,
parameters, parameters,
returnType returnType
@ -203,12 +201,13 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
return false; return false;
} }
if ( resultType == Type.VOID ) { if ( resultType.getTypeMirror().getKind() == TypeKind.VOID ) {
messager.printMessage( Kind.ERROR, "Can't generate mapping method with return type void.", method ); messager.printMessage( Kind.ERROR, "Can't generate mapping method with return type void.", method );
return false; return false;
} }
if ( returnType != Type.VOID && !typeUtil.isAssignable( resultType, returnType ) ) { if ( returnType.getTypeMirror().getKind() != TypeKind.VOID &&
!typeFactory.isAssignable( resultType, returnType ) ) {
messager.printMessage( messager.printMessage(
Kind.ERROR, Kind.ERROR,
"The result type is not assignable to the the return type.", "The result type is not assignable to the the return type.",

View File

@ -26,6 +26,7 @@ import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.model.Options; import org.mapstruct.ap.model.Options;
import org.mapstruct.ap.util.TypeFactory;
/** /**
* A processor which performs one task of the mapper generation, e.g. retrieving * A processor which performs one task of the mapper generation, e.g. retrieving
@ -55,6 +56,8 @@ public interface ModelElementProcessor<P, R> {
Elements getElementUtils(); Elements getElementUtils();
TypeFactory getTypeFactory();
Messager getMessager(); Messager getMessager();
Options getOptions(); Options getOptions();

View File

@ -20,7 +20,6 @@ package org.mapstruct.ap.processor;
import org.mapstruct.ap.model.Annotation; import org.mapstruct.ap.model.Annotation;
import org.mapstruct.ap.model.Mapper; import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.Type;
/** /**
* A {@link ModelElementProcessor} which converts the given {@link Mapper} * A {@link ModelElementProcessor} which converts the given {@link Mapper}
@ -38,11 +37,11 @@ public class SpringComponentProcessor extends AnnotationBasedComponentModelProce
@Override @Override
protected Annotation getTypeAnnotation() { protected Annotation getTypeAnnotation() {
return new Annotation( new Type( "org.springframework.stereotype", "Component" ) ); return new Annotation( getTypeFactory().getType( "org.springframework.stereotype.Component" ) );
} }
@Override @Override
protected Annotation getMapperReferenceAnnotation() { protected Annotation getMapperReferenceAnnotation() {
return new Annotation( new Type( "org.springframework.beans.factory.annotation", "Autowired" ) ); return new Annotation( getTypeFactory().getType( "org.springframework.beans.factory.annotation.Autowired" ) );
} }
} }

View File

@ -38,10 +38,10 @@ import org.mapstruct.ap.model.Type;
*/ */
public class Executables { public class Executables {
private final TypeUtil typeUtil; private final TypeFactory typeFactory;
public Executables(TypeUtil typeUtil) { public Executables(TypeFactory typeFactory) {
this.typeUtil = typeUtil; this.typeFactory = typeFactory;
} }
public boolean isGetterMethod(ExecutableElement method) { public boolean isGetterMethod(ExecutableElement method) {
@ -119,7 +119,7 @@ public class Executables {
return new Parameter( return new Parameter(
parameter.getSimpleName().toString(), parameter.getSimpleName().toString(),
typeUtil.retrieveType( parameter.asType() ), typeFactory.getType( parameter.asType() ),
false false
); );
} }
@ -133,7 +133,7 @@ public class Executables {
.add( .add(
new Parameter( new Parameter(
parameter.getSimpleName().toString(), parameter.getSimpleName().toString(),
typeUtil.retrieveType( parameter.asType() ), typeFactory.getType( parameter.asType() ),
MappingTargetPrism.getInstanceOn( parameter ) != null MappingTargetPrism.getInstanceOn( parameter ) != null
) )
); );
@ -143,6 +143,6 @@ public class Executables {
} }
public Type retrieveReturnType(ExecutableElement method) { public Type retrieveReturnType(ExecutableElement method) {
return typeUtil.retrieveType( method.getReturnType() ); return typeFactory.getType( method.getReturnType() );
} }
} }

View File

@ -32,13 +32,13 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
*/ */
public class Filters { public class Filters {
//TODO private final Executables executables;
private static Executables executables = new Executables( null );
private Filters() { public Filters(Executables executables) {
this.executables = executables;
} }
public static List<ExecutableElement> getterMethodsIn(Iterable<? extends Element> elements) { public List<ExecutableElement> getterMethodsIn(Iterable<? extends Element> elements) {
List<ExecutableElement> getterMethods = new LinkedList<ExecutableElement>(); List<ExecutableElement> getterMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) { for ( ExecutableElement method : methodsIn( elements ) ) {
@ -50,7 +50,7 @@ public class Filters {
return getterMethods; return getterMethods;
} }
public static List<ExecutableElement> setterMethodsIn(Iterable<? extends Element> elements) { public List<ExecutableElement> setterMethodsIn(Iterable<? extends Element> elements) {
List<ExecutableElement> setterMethods = new LinkedList<ExecutableElement>(); List<ExecutableElement> setterMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) { for ( ExecutableElement method : methodsIn( elements ) ) {

View File

@ -0,0 +1,192 @@
/**
* Copyright 2012-2013 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.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.model.Type;
/**
* Factory creating {@link Type} instances.
*
* @author Gunnar Morling
*/
public class TypeFactory {
private final Elements elementUtils;
private final Types typeUtils;
private final TypeMirror iterableType;
private final TypeMirror mapType;
private final Map<String, Type> implementationTypes = new HashMap<String, Type>();
public TypeFactory(Elements elementUtils, Types typeUtils) {
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
iterableType = typeUtils.erasure( elementUtils.getTypeElement( Iterable.class.getCanonicalName() ).asType() );
mapType = typeUtils.erasure( elementUtils.getTypeElement( Map.class.getCanonicalName() ).asType() );
implementationTypes.put( Iterable.class.getName(), getType( ArrayList.class ) );
implementationTypes.put( Collection.class.getName(), getType( ArrayList.class ) );
implementationTypes.put( List.class.getName(), getType( ArrayList.class ) );
implementationTypes.put( Set.class.getName(), getType( HashSet.class ) );
implementationTypes.put( SortedSet.class.getName(), getType( TreeSet.class ) );
implementationTypes.put( NavigableSet.class.getName(), getType( TreeSet.class ) );
implementationTypes.put( Map.class.getName(), getType( HashMap.class ) );
implementationTypes.put( SortedMap.class.getName(), getType( TreeMap.class ) );
implementationTypes.put( NavigableMap.class.getName(), getType( TreeMap.class ) );
implementationTypes.put( ConcurrentMap.class.getName(), getType( ConcurrentHashMap.class ) );
implementationTypes.put( ConcurrentNavigableMap.class.getName(), getType( ConcurrentSkipListMap.class ) );
}
public Type getType(Class<?> type) {
return type.isPrimitive() ? getType( getPrimitiveType( type ) ) : getType( type.getCanonicalName() );
}
public Type getType(String canonicalName) {
TypeElement typeElement = elementUtils.getTypeElement( canonicalName );
return getType( typeElement );
}
public Type getType(TypeElement typeElement) {
return getType( typeElement.asType() );
}
public Type getType(TypeMirror mirror) {
Type implementationType = getImplementationType( mirror );
boolean isIterableType = typeUtils.isSubtype(
mirror,
iterableType
);
boolean isMapType = typeUtils.isSubtype(
mirror,
mapType
);
return new Type(
mirror,
getTypeParameters( mirror ),
implementationType,
isIterableType,
isMapType,
typeUtils,
elementUtils
);
}
private List<Type> getTypeParameters(TypeMirror mirror) {
if ( mirror.getKind() != TypeKind.DECLARED ) {
return java.util.Collections.emptyList();
}
DeclaredType declaredType = (DeclaredType) mirror;
ArrayList<Type> typeParameters = new ArrayList<Type>( declaredType.getTypeArguments().size() );
for ( TypeMirror typeParameter : declaredType.getTypeArguments() ) {
typeParameters.add( getType( typeParameter ) );
}
return typeParameters;
}
private TypeMirror getPrimitiveType(Class<?> primitiveType) {
return primitiveType == byte.class ? typeUtils.getPrimitiveType( TypeKind.BYTE ) :
primitiveType == short.class ? typeUtils.getPrimitiveType( TypeKind.SHORT ) :
primitiveType == int.class ? typeUtils.getPrimitiveType( TypeKind.INT ) :
primitiveType == long.class ? typeUtils.getPrimitiveType( TypeKind.LONG ) :
primitiveType == float.class ? typeUtils.getPrimitiveType( TypeKind.FLOAT ) :
primitiveType == double.class ? typeUtils.getPrimitiveType( TypeKind.DOUBLE ) :
primitiveType == boolean.class ? typeUtils.getPrimitiveType( TypeKind.BOOLEAN ) :
primitiveType == char.class ? typeUtils.getPrimitiveType( TypeKind.CHAR ) :
typeUtils.getPrimitiveType( TypeKind.BYTE );
}
private Type getImplementationType(TypeMirror mirror) {
if ( mirror.getKind() != TypeKind.DECLARED ) {
return null;
}
DeclaredType declaredType = (DeclaredType) mirror;
Type implementationType = implementationTypes.get(
( (TypeElement) declaredType.asElement() ).getQualifiedName()
.toString()
);
if ( implementationType != null ) {
return new Type(
typeUtils.getDeclaredType(
implementationType.getTypeElement(),
declaredType.getTypeArguments().toArray( new TypeMirror[] { } )
),
getTypeParameters( mirror ),
null,
implementationType.isIterableType(),
implementationType.isMapType(),
typeUtils,
elementUtils
);
}
return null;
}
/**
* @param type1 first type
* @param type2 second type
*
* @return {@code true} if and only if the first type is assignable to the second
*/
public boolean isAssignable(Type type1, Type type2) {
if ( type1.equals( type2 ) ) {
return true;
}
TypeMirror mirror1 = type1.getTypeMirror();
TypeMirror mirror2 = type2.getTypeMirror();
return null != mirror1 && null != mirror2 && typeUtils.isAssignable( mirror1, mirror2 );
}
}

View File

@ -1,129 +0,0 @@
/**
* Copyright 2012-2013 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.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.model.Type;
public class TypeUtil {
private final Elements elementUtils;
private final Types typeUtils;
private final TypeMirror collectionType;
private final TypeMirror iterableType;
private final TypeMirror mapType;
public TypeUtil(Elements elementUtils, Types typeUtils) {
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
collectionType = elementUtils.getTypeElement( Collection.class.getCanonicalName() ).asType();
iterableType = elementUtils.getTypeElement( Iterable.class.getCanonicalName() ).asType();
mapType = elementUtils.getTypeElement( Map.class.getCanonicalName() ).asType();
}
public Type getType(DeclaredType type) {
List<Type> typeParameters = new ArrayList<Type>();
boolean isIterableType = isIterableType( type );
for ( TypeMirror mirror : type.getTypeArguments() ) {
typeParameters.add( retrieveType( mirror ) );
}
return new Type(
( (TypeElement) type.asElement() ).getQualifiedName().toString(),
elementUtils.getPackageOf( type.asElement() ).toString(),
type.asElement().getSimpleName().toString(),
type.asElement().getKind() == ElementKind.INTERFACE,
type.asElement().getKind() == ElementKind.ENUM,
isCollectionType( type ),
isIterableType,
isMapType( type ),
typeParameters
);
}
private boolean isIterableType(DeclaredType type) {
return typeUtils.isAssignable( typeUtils.erasure( type ), typeUtils.erasure( iterableType ) );
}
private boolean isCollectionType(DeclaredType type) {
return typeUtils.isAssignable( typeUtils.erasure( type ), typeUtils.erasure( collectionType ) );
}
private boolean isMapType(DeclaredType type) {
return typeUtils.isAssignable( typeUtils.erasure( type ), typeUtils.erasure( mapType ) );
}
public Type retrieveType(TypeMirror mirror) {
if ( mirror == null ) {
return null;
}
else if ( mirror.getKind() == TypeKind.DECLARED ) {
return getType( ( (DeclaredType) mirror ) );
}
else if ( mirror.getKind() == TypeKind.VOID ) {
return Type.VOID;
}
else {
return new Type( mirror.toString() );
}
}
/**
* @param type1 first type
* @param type2 second type
*
* @return {@code true} if and only if the first type is assignable to the second
*/
public boolean isAssignable(Type type1, Type type2) {
if ( type1.equals( type2 ) ) {
return true;
}
TypeMirror mirror1 = toTypeMirror( type1 );
TypeMirror mirror2 = toTypeMirror( type2 );
return null != mirror1 && null != mirror2 && typeUtils.isAssignable( mirror1, mirror2 );
}
private TypeMirror toTypeMirror(Type type) {
TypeElement rawType = elementUtils.getTypeElement( type.getCanonicalName() );
if ( null == rawType ) {
return null;
}
TypeMirror[] parameters = new TypeMirror[type.getTypeParameters().size()];
for ( int i = 0; i < type.getTypeParameters().size(); i++ ) {
parameters[i] = toTypeMirror( type.getTypeParameters().get( i ) );
}
return typeUtils.getDeclaredType( rawType, parameters );
}
}

View File

@ -28,7 +28,7 @@
${resultName}.clear(); ${resultName}.clear();
<#else> <#else>
<#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side --> <#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side -->
<#if resultType.name == "Iterable" && resultType.packageName == "java.lang">${resultType.iterableImplementationType.name}<#else>${resultType.name}</#if><<@includeModel object=resultType.typeParameters[0]/>> ${resultName} = new <#if resultType.iterableImplementationType??>${resultType.iterableImplementationType.name}<#else>${resultType.name}</#if><<@includeModel object=resultType.typeParameters[0]/>>(); <#if resultType.fullyQualifiedName == "java.lang.Iterable">${resultType.implementationType.name}<#else>${resultType.name}</#if><<@includeModel object=resultType.typeParameters[0]/>> ${resultName} = new <#if resultType.implementationType??>${resultType.implementationType.name}<#else>${resultType.name}</#if><<@includeModel object=resultType.typeParameters[0]/>>();
</#if> </#if>
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) { for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {

View File

@ -27,7 +27,7 @@
<#if existingInstanceMapping> <#if existingInstanceMapping>
${resultName}.clear(); ${resultName}.clear();
<#else> <#else>
<@includeModel object=resultType /> ${resultName} = new <#if resultType.mapImplementationType??><@includeModel object=resultType.mapImplementationType /><#else><@includeModel object=resultType /></#if>(); <@includeModel object=resultType /> ${resultName} = new <#if resultType.implementationType??><@includeModel object=resultType.implementationType /><#else><@includeModel object=resultType /></#if>();
</#if> </#if>
for ( Map.Entry<<#list sourceParameter.type.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> ${entryVariableName} : ${sourceParameter.name}.entrySet() ) { for ( Map.Entry<<#list sourceParameter.type.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> ${entryVariableName} : ${sourceParameter.name}.entrySet() ) {

View File

@ -18,4 +18,4 @@
limitations under the License. limitations under the License.
--> -->
${type} ${name} <@includeModel object=type/> ${name}

View File

@ -32,9 +32,9 @@
</#if> </#if>
<#-- c) simply set --> <#-- c) simply set -->
<#else> <#else>
<#if targetType.collectionType == true> <#if targetType.implementationType??>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) { if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
${ext.targetBeanName}.${targetAccessorName}( new <#if targetType.collectionImplementationType??>${targetType.collectionImplementationType.name}<#else>${targetType.name}</#if><#if targetType.elementType??><${targetType.elementType.name}></#if>( ${sourceBeanName}.${sourceAccessorName}() ) ); ${ext.targetBeanName}.${targetAccessorName}( new <#if targetType.implementationType??>${targetType.implementationType.name}<#else>${targetType.name}</#if><#if targetType.elementType??><${targetType.elementType.name}></#if>( ${sourceBeanName}.${sourceAccessorName}() ) );
} }
<#else> <#else>
${ext.targetBeanName}.${targetAccessorName}( ${sourceBeanName}.${sourceAccessorName}() ); ${ext.targetBeanName}.${targetAccessorName}( ${sourceBeanName}.${sourceAccessorName}() );