mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
Adding initial support for native mappers (work in progress)
This commit is contained in:
parent
069e4f3a80
commit
df25e12b0d
@ -15,6 +15,26 @@
|
||||
*/
|
||||
package de.moapa.maple;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks an interface as mapper interface and activates the generation of a
|
||||
* mapper implementation for that interface.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Mapper {
|
||||
|
||||
/**
|
||||
* Specifies the type of the mapper implementation to be generated.
|
||||
* Currently supported values are {@code native} and {@code dozer}.
|
||||
*
|
||||
* @return The type of the mapper implementation to be generated.
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
|
@ -18,8 +18,12 @@ package de.moapa.maple.ap;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
@ -28,6 +32,7 @@ import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.ElementKindVisitor6;
|
||||
import javax.lang.model.util.Elements;
|
||||
@ -43,6 +48,7 @@ import de.moapa.maple.ap.model.Parameter;
|
||||
import de.moapa.maple.ap.model.Type;
|
||||
import de.moapa.maple.ap.writer.DozerModelWriter;
|
||||
import de.moapa.maple.ap.writer.ModelWriter;
|
||||
import de.moapa.maple.ap.writer.NativeModelWriter;
|
||||
|
||||
import static javax.lang.model.util.ElementFilter.methodsIn;
|
||||
|
||||
@ -50,12 +56,13 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
|
||||
private final static String IMPLEMENTATION_SUFFIX = "Impl";
|
||||
|
||||
private final static String MAPPER_ANNOTATION = "de.moapa.maple.Mapper";
|
||||
private final static String MAPPING_ANNOTATION = "de.moapa.maple.Mapping";
|
||||
|
||||
private final static String MAPPINGS_ANNOTATION = "de.moapa.maple.Mappings";
|
||||
|
||||
private final static String CONVERTER_TYPE = "de.moapa.maple.converter.Converter";
|
||||
|
||||
private final static String DEFAULT_MAPPER_TYPE = "dozer";
|
||||
|
||||
private final ProcessingEnvironment processingEnvironment;
|
||||
|
||||
private final Types typeUtils;
|
||||
@ -90,13 +97,14 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
|
||||
ModelWriter modelWriter = new DozerModelWriter();
|
||||
ModelWriter modelWriter = model.getMapperType()
|
||||
.equals( "native" ) ? new NativeModelWriter() : new DozerModelWriter();
|
||||
modelWriter.writeModel( sourceFile, model );
|
||||
}
|
||||
|
||||
private Mapper retrieveModel(TypeElement element) {
|
||||
|
||||
return new Mapper(
|
||||
retrieveMapperType( element ),
|
||||
elementUtils.getPackageOf( element ).getQualifiedName().toString(),
|
||||
element.getSimpleName() + IMPLEMENTATION_SUFFIX,
|
||||
element.getSimpleName().toString(),
|
||||
@ -104,6 +112,14 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
);
|
||||
}
|
||||
|
||||
private String retrieveMapperType(TypeElement element) {
|
||||
|
||||
AnnotationMirror mapperAnnotation = getAnnotation( element, MAPPER_ANNOTATION );
|
||||
String mapperType = getStringValue( mapperAnnotation, "value" );
|
||||
|
||||
return mapperType != null && !mapperType.isEmpty() ? mapperType : DEFAULT_MAPPER_TYPE;
|
||||
}
|
||||
|
||||
private List<MapperMethod> retrieveMethods(TypeElement element) {
|
||||
|
||||
List<MapperMethod> methods = new ArrayList<MapperMethod>();
|
||||
@ -112,7 +128,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
|
||||
Type returnType = retrieveReturnType( oneMethod );
|
||||
Parameter parameter = retrieveParameter( oneMethod );
|
||||
List<Binding> bindings = retrieveBindings( oneMethod );
|
||||
Map<String, Binding> bindings = retrieveBindings( oneMethod );
|
||||
methods.add(
|
||||
new MapperMethod(
|
||||
oneMethod.getSimpleName().toString(),
|
||||
@ -126,9 +142,11 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
return methods;
|
||||
}
|
||||
|
||||
private List<Binding> retrieveBindings(ExecutableElement method) {
|
||||
private Map<String, Binding> retrieveBindings(ExecutableElement method) {
|
||||
|
||||
List<Binding> bindings = new ArrayList<Binding>();
|
||||
Map<String, Binding> bindings = new LinkedHashMap<String, Binding>();
|
||||
|
||||
retrieveDefaultBindings( method, bindings );
|
||||
|
||||
for ( AnnotationMirror annotationMirror : method.getAnnotationMirrors() ) {
|
||||
|
||||
@ -137,76 +155,92 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
.accept( new NameDeterminationVisitor(), null );
|
||||
|
||||
if ( MAPPING_ANNOTATION.equals( annotationName ) ) {
|
||||
bindings.add( retrieveBinding( annotationMirror ) );
|
||||
retrieveBinding( annotationMirror, bindings );
|
||||
}
|
||||
else if ( MAPPINGS_ANNOTATION.equals( annotationName ) ) {
|
||||
bindings.addAll( retrieveBindings( annotationMirror ) );
|
||||
retrieveBindings( annotationMirror, bindings );
|
||||
}
|
||||
}
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
private List<Binding> retrieveBindings(AnnotationMirror annotationMirror) {
|
||||
private void retrieveDefaultBindings(ExecutableElement method, Map<String, Binding> bindings) {
|
||||
|
||||
List<Binding> bindings = new ArrayList<Binding>();
|
||||
Element returnTypeElement = typeUtils.asElement( method.getReturnType() );
|
||||
|
||||
for ( Entry<? extends ExecutableElement, ? extends AnnotationValue> oneAttribute : annotationMirror.getElementValues()
|
||||
.entrySet() ) {
|
||||
Set<String> writableTargetProperties = new LinkedHashSet<String>();
|
||||
|
||||
if ( oneAttribute.getKey().getSimpleName().contentEquals( "value" ) ) {
|
||||
List<? extends AnnotationValue> values = oneAttribute.getValue()
|
||||
.accept( new AnnotationValueListRetrievingVisitor(), null );
|
||||
//collect writable properties of the target type
|
||||
for ( ExecutableElement oneMethod : methodsIn( returnTypeElement.getEnclosedElements() ) ) {
|
||||
if ( oneMethod.getSimpleName().toString().startsWith( "set" ) &&
|
||||
oneMethod.getParameters().size() == 1 ) {
|
||||
|
||||
for ( AnnotationValue oneAnnotationValue : values ) {
|
||||
AnnotationMirror oneAnnotation = oneAnnotationValue.accept(
|
||||
new AnnotationRetrievingVisitor(),
|
||||
null
|
||||
);
|
||||
bindings.add( retrieveBinding( oneAnnotation ) );
|
||||
}
|
||||
writableTargetProperties.add( oneMethod.getSimpleName().toString().substring( 3 ) );
|
||||
}
|
||||
}
|
||||
|
||||
return bindings;
|
||||
//collect readable properties of the source type
|
||||
Element parameterElement = typeUtils.asElement( method.getParameters().get( 0 ).asType() );
|
||||
|
||||
Set<String> readableSourceProperties = new LinkedHashSet<String>();
|
||||
|
||||
for ( ExecutableElement oneMethod : methodsIn( parameterElement.getEnclosedElements() ) ) {
|
||||
if ( oneMethod.getSimpleName().toString().startsWith( "get" ) &&
|
||||
oneMethod.getParameters().isEmpty() &&
|
||||
oneMethod.getReturnType().getKind() != TypeKind.VOID ) {
|
||||
|
||||
readableSourceProperties.add( oneMethod.getSimpleName().toString().substring( 3 ) );
|
||||
}
|
||||
}
|
||||
|
||||
writableTargetProperties.retainAll( readableSourceProperties );
|
||||
|
||||
for ( String oneWritableProperty : writableTargetProperties ) {
|
||||
bindings.put( oneWritableProperty, new Binding( oneWritableProperty, oneWritableProperty ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Binding retrieveBinding(AnnotationMirror annotationMirror) {
|
||||
private void retrieveBindings(AnnotationMirror annotationMirror, Map<String, Binding> bindings) {
|
||||
|
||||
List<? extends AnnotationValue> values = getAnnotationValueListValue( annotationMirror, "value" );
|
||||
|
||||
for ( AnnotationValue oneAnnotationValue : values ) {
|
||||
AnnotationMirror oneAnnotation = oneAnnotationValue.accept(
|
||||
new AnnotationRetrievingVisitor(),
|
||||
null
|
||||
);
|
||||
retrieveBinding( oneAnnotation, bindings );
|
||||
}
|
||||
}
|
||||
|
||||
private void retrieveBinding(AnnotationMirror annotationMirror, Map<String, Binding> bindings) {
|
||||
|
||||
String sourcePropertyName = getStringValue( annotationMirror, "source" );
|
||||
String targetPropertyName = getStringValue( annotationMirror, "target" );
|
||||
TypeMirror converterTypeMirror = getTypeMirrorValue( annotationMirror, "converter" );
|
||||
|
||||
String sourcePropertyName = null;
|
||||
String targetPropertyName = null;
|
||||
Type converterType = null;
|
||||
Type sourceType = null;
|
||||
Type targetType = null;
|
||||
|
||||
for ( Entry<? extends ExecutableElement, ? extends AnnotationValue> oneAttribute : annotationMirror.getElementValues()
|
||||
.entrySet() ) {
|
||||
if ( converterTypeMirror != null ) {
|
||||
converterType = getType( typeUtils.asElement( converterTypeMirror ) );
|
||||
|
||||
if ( oneAttribute.getKey().getSimpleName().contentEquals( "source" ) ) {
|
||||
sourcePropertyName = oneAttribute.getValue().accept( new StringValueRetrievingVisitor(), null );
|
||||
}
|
||||
else if ( oneAttribute.getKey().getSimpleName().contentEquals( "target" ) ) {
|
||||
targetPropertyName = oneAttribute.getValue().accept( new StringValueRetrievingVisitor(), null );
|
||||
}
|
||||
else if ( oneAttribute.getKey().getSimpleName().contentEquals( "converter" ) ) {
|
||||
TypeMirror converterTypeMirror = oneAttribute.getValue()
|
||||
.accept(
|
||||
new TypeRetrievingVisitor(), null
|
||||
);
|
||||
List<? extends TypeMirror> converterTypeParameters = getTypeParameters(
|
||||
converterTypeMirror,
|
||||
CONVERTER_TYPE
|
||||
);
|
||||
|
||||
converterType = getType( typeUtils.asElement( converterTypeMirror ) );
|
||||
|
||||
List<? extends TypeMirror> converterTypeParameters = getTypeParameters(
|
||||
converterTypeMirror,
|
||||
CONVERTER_TYPE
|
||||
);
|
||||
|
||||
sourceType = getType( typeUtils.asElement( converterTypeParameters.get( 0 ) ) );
|
||||
targetType = getType( typeUtils.asElement( converterTypeParameters.get( 1 ) ) );
|
||||
}
|
||||
sourceType = getType( typeUtils.asElement( converterTypeParameters.get( 0 ) ) );
|
||||
targetType = getType( typeUtils.asElement( converterTypeParameters.get( 1 ) ) );
|
||||
}
|
||||
|
||||
return new Binding( sourceType, sourcePropertyName, targetType, targetPropertyName, converterType );
|
||||
bindings.put(
|
||||
sourcePropertyName,
|
||||
new Binding( sourceType, sourcePropertyName, targetType, targetPropertyName, converterType )
|
||||
);
|
||||
}
|
||||
|
||||
private Type getType(Element sourceTypeElement) {
|
||||
@ -261,6 +295,60 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||
);
|
||||
}
|
||||
|
||||
private AnnotationMirror getAnnotation(TypeElement element, String annotationName) {
|
||||
|
||||
for ( AnnotationMirror annotationMirror : element.getAnnotationMirrors() ) {
|
||||
|
||||
if ( annotationName.equals(
|
||||
annotationMirror.getAnnotationType().asElement().accept( new NameDeterminationVisitor(), null )
|
||||
) ) {
|
||||
|
||||
return annotationMirror;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getStringValue(AnnotationMirror annotationMirror, String attributeName) {
|
||||
|
||||
for ( Entry<? extends ExecutableElement, ? extends AnnotationValue> oneAttribute : annotationMirror.getElementValues()
|
||||
.entrySet() ) {
|
||||
|
||||
if ( oneAttribute.getKey().getSimpleName().contentEquals( attributeName ) ) {
|
||||
return oneAttribute.getValue().accept( new StringValueRetrievingVisitor(), null );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeMirror getTypeMirrorValue(AnnotationMirror annotationMirror, String attributeName) {
|
||||
|
||||
for ( Entry<? extends ExecutableElement, ? extends AnnotationValue> oneAttribute : annotationMirror.getElementValues()
|
||||
.entrySet() ) {
|
||||
|
||||
if ( oneAttribute.getKey().getSimpleName().contentEquals( attributeName ) ) {
|
||||
return oneAttribute.getValue().accept( new TypeRetrievingVisitor(), null );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<? extends AnnotationValue> getAnnotationValueListValue(AnnotationMirror annotationMirror, String attributeName) {
|
||||
|
||||
for ( Entry<? extends ExecutableElement, ? extends AnnotationValue> oneAttribute : annotationMirror.getElementValues()
|
||||
.entrySet() ) {
|
||||
|
||||
if ( oneAttribute.getKey().getSimpleName().contentEquals( "value" ) ) {
|
||||
return oneAttribute.getValue().accept( new AnnotationValueListRetrievingVisitor(), null );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class TypeParameterDeterminationVisitor extends TypeKindVisitor6<List<? extends TypeMirror>, Void> {
|
||||
|
||||
@Override
|
||||
|
@ -18,15 +18,15 @@ package de.moapa.maple.ap.model;
|
||||
public class Binding {
|
||||
|
||||
private final Type sourceType;
|
||||
|
||||
private final String sourceProperty;
|
||||
|
||||
private final Type targetType;
|
||||
|
||||
private final String targetProperty;
|
||||
|
||||
private final Type converterType;
|
||||
|
||||
public Binding(String sourceProperty, String targetProperty) {
|
||||
this( null, sourceProperty, null, targetProperty, null );
|
||||
}
|
||||
|
||||
public Binding(Type sourceType, String sourceProperty, Type targetType, String targetProperty, Type converterType) {
|
||||
|
||||
this.sourceType = sourceType;
|
||||
@ -55,4 +55,8 @@ public class Binding {
|
||||
public Type getConverterType() {
|
||||
return converterType;
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return !sourceProperty.equals( targetProperty ) || ( sourceType != null && !sourceType.equals( targetType ) ) ? false : true;
|
||||
}
|
||||
}
|
||||
|
@ -20,17 +20,15 @@ import java.util.List;
|
||||
|
||||
public class Mapper {
|
||||
|
||||
private final String mapperType;
|
||||
private final String packageName;
|
||||
|
||||
private final String implementationType;
|
||||
|
||||
private final String interfaceType;
|
||||
|
||||
private final List<MapperMethod> mapperMethods;
|
||||
|
||||
private final List<Converter> converters;
|
||||
|
||||
public Mapper(String packageName, String implementationType, String interfaceType, List<MapperMethod> mapperMethods) {
|
||||
public Mapper(String mapperType, String packageName, String implementationType, String interfaceType, List<MapperMethod> mapperMethods) {
|
||||
this.mapperType = mapperType;
|
||||
this.packageName = packageName;
|
||||
this.implementationType = implementationType;
|
||||
this.interfaceType = interfaceType;
|
||||
@ -43,7 +41,7 @@ public class Mapper {
|
||||
List<Converter> converters = new ArrayList<Converter>();
|
||||
|
||||
for ( MapperMethod oneMapperMethod : mapperMethods ) {
|
||||
for ( Binding oneBinding : oneMapperMethod.getBindings() ) {
|
||||
for ( Binding oneBinding : oneMapperMethod.getBindings().values() ) {
|
||||
if ( oneBinding.getConverterType() != null ) {
|
||||
converters.add(
|
||||
new Converter(
|
||||
@ -59,6 +57,10 @@ public class Mapper {
|
||||
return converters;
|
||||
}
|
||||
|
||||
public String getMapperType() {
|
||||
return mapperType;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
@ -15,16 +15,21 @@
|
||||
*/
|
||||
package de.moapa.maple.ap.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MapperMethod {
|
||||
|
||||
private final String name;
|
||||
private final Type returnType;
|
||||
private final Parameter parameter;
|
||||
private final List<Binding> bindings;
|
||||
|
||||
public MapperMethod(String name, Type returnType, Parameter parameter, List<Binding> bindings) {
|
||||
/**
|
||||
* Bindings for the properties of this method's converted object. Keyed by
|
||||
* property name of the source type.
|
||||
*/
|
||||
private final Map<String, Binding> bindings;
|
||||
|
||||
public MapperMethod(String name, Type returnType, Parameter parameter, Map<String, Binding> bindings) {
|
||||
this.name = name;
|
||||
this.returnType = returnType;
|
||||
this.parameter = parameter;
|
||||
@ -43,7 +48,17 @@ public class MapperMethod {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public List<Binding> getBindings() {
|
||||
public Map<String, Binding> getBindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
public boolean getNonDefaultBindingExisting() {
|
||||
for ( Binding oneBinding : bindings.values() ) {
|
||||
if ( !oneBinding.isDefault() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -55,5 +55,4 @@ public class DozerModelWriter implements ModelWriter {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright 2012 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
*
|
||||
* 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 de.moapa.maple.ap.writer;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import de.moapa.maple.ap.model.Mapper;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.DefaultObjectWrapper;
|
||||
import freemarker.template.Template;
|
||||
|
||||
public class NativeModelWriter implements ModelWriter {
|
||||
|
||||
private final static String TEMPLATE_NAME = "native-mapper-implementation.ftl";
|
||||
|
||||
private static final Configuration configuration;
|
||||
|
||||
static {
|
||||
|
||||
configuration = new Configuration();
|
||||
configuration.setClassForTemplateLoading( NativeModelWriter.class, "/" );
|
||||
configuration.setObjectWrapper( new DefaultObjectWrapper() );
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeModel(JavaFileObject sourceFile, Mapper model) {
|
||||
|
||||
try {
|
||||
BufferedWriter writer = new BufferedWriter( sourceFile.openWriter() );
|
||||
|
||||
Template template = configuration.getTemplate( TEMPLATE_NAME );
|
||||
template.process( model, writer );
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
catch ( RuntimeException e ) {
|
||||
throw e;
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
}
|
@ -35,11 +35,11 @@ public class ${implementationType} implements ${interfaceType} {
|
||||
BeanMappingBuilder builder = null;
|
||||
|
||||
<#list mapperMethods as oneMethod>
|
||||
<#if oneMethod.bindings?has_content>
|
||||
<#if oneMethod.nonDefaultBindingExisting>
|
||||
builder = new BeanMappingBuilder() {
|
||||
protected void configure() {
|
||||
mapping( ${oneMethod.parameter.type.name}.class, ${oneMethod.returnType.name}.class )
|
||||
<#list oneMethod.bindings as oneBinding>
|
||||
<#list oneMethod.bindings?values as oneBinding>
|
||||
.fields( "${oneBinding.sourceProperty}", "${oneBinding.targetProperty}"<#if oneBinding.converterType??>, customConverter( ${oneBinding.converterType.name}DozerAdapter.class )</#if> )
|
||||
</#list>
|
||||
;
|
||||
|
@ -0,0 +1,38 @@
|
||||
<#--
|
||||
|
||||
Copyright 2012 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
|
||||
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 ${packageName};
|
||||
|
||||
public class ${implementationType} implements ${interfaceType} {
|
||||
|
||||
<#list mapperMethods as oneMethod>
|
||||
public ${oneMethod.returnType.name} ${oneMethod.name}(${oneMethod.parameter.type.name} ${oneMethod.parameter.name}) {
|
||||
|
||||
${oneMethod.returnType.name} convertedObject = new ${oneMethod.returnType.name}();
|
||||
|
||||
<#list oneMethod.bindings?values as oneBinding>
|
||||
<#if oneBinding.converterType??>
|
||||
convertedObject.set${oneBinding.targetProperty?cap_first}( new ${oneBinding.converterType.name}().from( ${oneMethod.parameter.name}.get${oneBinding.sourceProperty?cap_first}() ) );
|
||||
<#else>
|
||||
convertedObject.set${oneBinding.targetProperty?cap_first}( ${oneMethod.parameter.name}.get${oneBinding.sourceProperty?cap_first}() );
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
return convertedObject;
|
||||
}
|
||||
</#list>
|
||||
}
|
@ -16,68 +16,24 @@
|
||||
package de.moapa.maple.ap.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaCompiler.CompilationTask;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import de.moapa.maple.ap.MappingProcessor;
|
||||
import de.moapa.maple.ap.test.model.Car;
|
||||
import de.moapa.maple.ap.test.model.CarDto;
|
||||
import de.moapa.maple.ap.test.model.CarMapper;
|
||||
import de.moapa.maple.ap.test.model.IntToStringConverter;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
public class CarMapperTest {
|
||||
public class CarMapperTest extends MapperTestBase {
|
||||
|
||||
private DiagnosticCollector<JavaFileObject> diagnostics;
|
||||
private JavaCompiler compiler;
|
||||
|
||||
private String sourceDir;
|
||||
private String classOutputDir;
|
||||
private String sourceOutputDir;
|
||||
private List<File> classPath;
|
||||
|
||||
@BeforeClass
|
||||
public void setup() throws Exception {
|
||||
|
||||
compiler = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
String basePath = getBasePath();
|
||||
|
||||
sourceDir = basePath + "/src/test/java";
|
||||
classOutputDir = basePath + "/target/compilation-tests/classes";
|
||||
sourceOutputDir = basePath + "/target/compilation-tests/generated-sources/mapping";
|
||||
|
||||
String testDependenciesDir = basePath + "/target/test-dependencies/";
|
||||
classPath = Arrays.asList(
|
||||
new File( testDependenciesDir, "maple.jar" ),
|
||||
new File( testDependenciesDir, "dozer.jar" ),
|
||||
new File( testDependenciesDir, "slf4j-api.jar" ),
|
||||
new File( testDependenciesDir, "slf4j-jdk14.jar" )
|
||||
);
|
||||
|
||||
createOutputDirs();
|
||||
|
||||
Thread.currentThread().setContextClassLoader(
|
||||
new URLClassLoader(
|
||||
new URL[] { new File( classOutputDir ).toURI().toURL() },
|
||||
Thread.currentThread().getContextClassLoader()
|
||||
)
|
||||
);
|
||||
public CarMapperTest() {
|
||||
super( "maple.jar", "dozer.jar", "slf4j-api.jar", "slf4j-jdk14.jar" );
|
||||
}
|
||||
|
||||
@BeforeMethod
|
||||
@ -167,86 +123,4 @@ public class CarMapperTest {
|
||||
assertThat( car ).isNotNull();
|
||||
assertThat( car.getYearOfManufacture() ).isEqualTo( 1980 );
|
||||
}
|
||||
|
||||
private File[] getSourceFiles(Class<?>... clazz) {
|
||||
|
||||
File[] sourceFiles = new File[clazz.length];
|
||||
|
||||
for ( int i = 0; i < clazz.length; i++ ) {
|
||||
|
||||
sourceFiles[i] = new File(
|
||||
sourceDir +
|
||||
File.separator +
|
||||
clazz[i].getName().replace( ".", File.separator ) +
|
||||
".java"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return sourceFiles;
|
||||
}
|
||||
|
||||
public boolean compile(DiagnosticCollector<JavaFileObject> diagnostics, File... sourceFiles) {
|
||||
|
||||
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
|
||||
|
||||
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects( sourceFiles );
|
||||
|
||||
try {
|
||||
fileManager.setLocation( StandardLocation.CLASS_PATH, classPath );
|
||||
fileManager.setLocation( StandardLocation.CLASS_OUTPUT, Arrays.asList( new File( classOutputDir ) ) );
|
||||
fileManager.setLocation( StandardLocation.SOURCE_OUTPUT, Arrays.asList( new File( sourceOutputDir ) ) );
|
||||
}
|
||||
catch ( IOException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
|
||||
CompilationTask task = compiler.getTask(
|
||||
null,
|
||||
fileManager,
|
||||
diagnostics,
|
||||
Collections.<String>emptyList(),
|
||||
null,
|
||||
compilationUnits
|
||||
);
|
||||
task.setProcessors( Arrays.asList( new MappingProcessor() ) );
|
||||
|
||||
return task.call();
|
||||
}
|
||||
|
||||
private String getBasePath() {
|
||||
|
||||
try {
|
||||
return new File( "." ).getCanonicalPath();
|
||||
}
|
||||
catch ( IOException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
private void createOutputDirs() {
|
||||
|
||||
File directory = new File( classOutputDir );
|
||||
deleteDirectory( directory );
|
||||
directory.mkdirs();
|
||||
|
||||
directory = new File( sourceOutputDir );
|
||||
deleteDirectory( directory );
|
||||
directory.mkdirs();
|
||||
}
|
||||
|
||||
private void deleteDirectory(File path) {
|
||||
if ( path.exists() ) {
|
||||
File[] files = path.listFiles();
|
||||
for ( int i = 0; i < files.length; i++ ) {
|
||||
if ( files[i].isDirectory() ) {
|
||||
deleteDirectory( files[i] );
|
||||
}
|
||||
else {
|
||||
files[i].delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
path.delete();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Copyright 2012 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
*
|
||||
* 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 de.moapa.maple.ap.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaCompiler.CompilationTask;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import de.moapa.maple.ap.MappingProcessor;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
|
||||
public abstract class MapperTestBase {
|
||||
|
||||
private JavaCompiler compiler;
|
||||
private String sourceDir;
|
||||
private String classOutputDir;
|
||||
private String sourceOutputDir;
|
||||
private List<File> classPath;
|
||||
private List<String> libraries;
|
||||
|
||||
public MapperTestBase(String... libraries) {
|
||||
this.libraries = Arrays.asList( libraries );
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public void setup() throws Exception {
|
||||
|
||||
compiler = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
String basePath = getBasePath();
|
||||
|
||||
sourceDir = basePath + "/src/test/java";
|
||||
classOutputDir = basePath + "/target/compilation-tests/classes";
|
||||
sourceOutputDir = basePath + "/target/compilation-tests/generated-sources/mapping";
|
||||
|
||||
String testDependenciesDir = basePath + "/target/test-dependencies/";
|
||||
|
||||
classPath = new ArrayList<File>();
|
||||
for ( String oneLibrary : libraries ) {
|
||||
classPath.add( new File( testDependenciesDir, oneLibrary ) );
|
||||
}
|
||||
|
||||
createOutputDirs();
|
||||
|
||||
Thread.currentThread().setContextClassLoader(
|
||||
new URLClassLoader(
|
||||
new URL[] { new File( classOutputDir ).toURI().toURL() },
|
||||
Thread.currentThread().getContextClassLoader()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected File[] getSourceFiles(Class<?>... clazz) {
|
||||
|
||||
File[] sourceFiles = new File[clazz.length];
|
||||
|
||||
for ( int i = 0; i < clazz.length; i++ ) {
|
||||
|
||||
sourceFiles[i] = new File(
|
||||
sourceDir +
|
||||
File.separator +
|
||||
clazz[i].getName().replace( ".", File.separator ) +
|
||||
".java"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return sourceFiles;
|
||||
}
|
||||
|
||||
protected boolean compile(DiagnosticCollector<JavaFileObject> diagnostics, File... sourceFiles) {
|
||||
|
||||
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
|
||||
|
||||
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects( sourceFiles );
|
||||
|
||||
try {
|
||||
fileManager.setLocation( StandardLocation.CLASS_PATH, classPath );
|
||||
fileManager.setLocation( StandardLocation.CLASS_OUTPUT, Arrays.asList( new File( classOutputDir ) ) );
|
||||
fileManager.setLocation( StandardLocation.SOURCE_OUTPUT, Arrays.asList( new File( sourceOutputDir ) ) );
|
||||
}
|
||||
catch ( IOException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
|
||||
CompilationTask task = compiler.getTask(
|
||||
null,
|
||||
fileManager,
|
||||
diagnostics,
|
||||
Collections.<String>emptyList(),
|
||||
null,
|
||||
compilationUnits
|
||||
);
|
||||
task.setProcessors( Arrays.asList( new MappingProcessor() ) );
|
||||
|
||||
return task.call();
|
||||
}
|
||||
|
||||
private String getBasePath() {
|
||||
|
||||
try {
|
||||
return new File( "." ).getCanonicalPath();
|
||||
}
|
||||
catch ( IOException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
private void createOutputDirs() {
|
||||
|
||||
File directory = new File( classOutputDir );
|
||||
deleteDirectory( directory );
|
||||
directory.mkdirs();
|
||||
|
||||
directory = new File( sourceOutputDir );
|
||||
deleteDirectory( directory );
|
||||
directory.mkdirs();
|
||||
}
|
||||
|
||||
private void deleteDirectory(File path) {
|
||||
if ( path.exists() ) {
|
||||
File[] files = path.listFiles();
|
||||
for ( int i = 0; i < files.length; i++ ) {
|
||||
if ( files[i].isDirectory() ) {
|
||||
deleteDirectory( files[i] );
|
||||
}
|
||||
else {
|
||||
files[i].delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
path.delete();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright 2012 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
*
|
||||
* 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 de.moapa.maple.ap.test;
|
||||
|
||||
import java.io.File;
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import de.moapa.maple.ap.test.model.Car;
|
||||
import de.moapa.maple.ap.test.model.CarDto;
|
||||
import de.moapa.maple.ap.test.model.IntToStringConverter;
|
||||
import de.moapa.maple.ap.test.model.NativeCarMapper;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
public class NativeCarMapperTest extends MapperTestBase {
|
||||
|
||||
private DiagnosticCollector<JavaFileObject> diagnostics;
|
||||
|
||||
public NativeCarMapperTest() {
|
||||
super( "maple.jar" );
|
||||
}
|
||||
|
||||
@BeforeMethod
|
||||
public void generateMapperImplementation() {
|
||||
|
||||
diagnostics = new DiagnosticCollector<JavaFileObject>();
|
||||
File[] sourceFiles = getSourceFiles(
|
||||
Car.class,
|
||||
CarDto.class,
|
||||
NativeCarMapper.class,
|
||||
IntToStringConverter.class
|
||||
);
|
||||
|
||||
boolean compilationSuccessful = compile( diagnostics, sourceFiles );
|
||||
|
||||
assertThat( compilationSuccessful ).describedAs( "Compilation failed: " + diagnostics.getDiagnostics() )
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldProvideMapperInstance() throws Exception {
|
||||
|
||||
assertThat( NativeCarMapper.INSTANCE ).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapAttributeByName() {
|
||||
|
||||
//given
|
||||
Car car = new Car( "Morris", 2, 1980 );
|
||||
|
||||
//when
|
||||
CarDto carDto = NativeCarMapper.INSTANCE.carToCarDto( car );
|
||||
|
||||
//then
|
||||
assertThat( carDto ).isNotNull();
|
||||
assertThat( carDto.getMake() ).isEqualTo( car.getMake() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapAttributeWithCustomMapping() {
|
||||
|
||||
//given
|
||||
Car car = new Car( "Morris", 2, 1980 );
|
||||
|
||||
//when
|
||||
CarDto carDto = NativeCarMapper.INSTANCE.carToCarDto( car );
|
||||
|
||||
//then
|
||||
assertThat( carDto ).isNotNull();
|
||||
assertThat( carDto.getSeatCount() ).isEqualTo( car.getNumberOfSeats() );
|
||||
}
|
||||
|
||||
@Test(enabled = false)
|
||||
public void shouldConsiderCustomMappingForReverseMapping() {
|
||||
|
||||
//given
|
||||
CarDto carDto = new CarDto( "Morris", 2, "1980" );
|
||||
|
||||
//when
|
||||
Car car = NativeCarMapper.INSTANCE.carDtoToCar( carDto );
|
||||
|
||||
//then
|
||||
assertThat( car ).isNotNull();
|
||||
assertThat( car.getNumberOfSeats() ).isEqualTo( carDto.getSeatCount() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldApplyConverter() {
|
||||
|
||||
//given
|
||||
Car car = new Car( "Morris", 2, 1980 );
|
||||
|
||||
//when
|
||||
CarDto carDto = NativeCarMapper.INSTANCE.carToCarDto( car );
|
||||
|
||||
//then
|
||||
assertThat( carDto ).isNotNull();
|
||||
assertThat( carDto.getManufacturingYear() ).isEqualTo( "1980" );
|
||||
}
|
||||
|
||||
@Test(enabled = false)
|
||||
public void shouldApplyConverterForReverseMapping() {
|
||||
|
||||
//given
|
||||
CarDto carDto = new CarDto( "Morris", 2, "1980" );
|
||||
|
||||
//when
|
||||
Car car = NativeCarMapper.INSTANCE.carDtoToCar( carDto );
|
||||
|
||||
//then
|
||||
assertThat( car ).isNotNull();
|
||||
assertThat( car.getYearOfManufacture() ).isEqualTo( 1980 );
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright 2012 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
*
|
||||
* 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 de.moapa.maple.ap.test.model;
|
||||
|
||||
import de.moapa.maple.Mapper;
|
||||
import de.moapa.maple.Mappers;
|
||||
import de.moapa.maple.Mapping;
|
||||
import de.moapa.maple.Mappings;
|
||||
|
||||
@Mapper("native")
|
||||
public interface NativeCarMapper {
|
||||
|
||||
NativeCarMapper INSTANCE = Mappers.getMapper( NativeCarMapper.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "numberOfSeats", target = "seatCount"),
|
||||
@Mapping(source = "yearOfManufacture", target = "manufacturingYear", converter = IntToStringConverter.class)
|
||||
})
|
||||
CarDto carToCarDto(Car car);
|
||||
|
||||
Car carDtoToCar(CarDto carDto);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user