From a565bed0c787f210f4af874f4c84dc52df8bf22e Mon Sep 17 00:00:00 2001
From: Gunnar Morling
Date: Thu, 27 Jun 2013 22:56:49 +0200
Subject: [PATCH] #32 Establishing processing chain for model creation and
transformation
---
.../org/mapstruct/ap/MappingProcessor.java | 120 +++--
...pingMethod.java => BeanMappingMethod.java} | 6 +-
.../java/org/mapstruct/ap/model/Mapper.java | 8 +-
.../java/org/mapstruct/ap/model/Type.java | 4 +
.../DefaultModelElementProcessorContext.java | 68 +++
.../MapperCreationProcessor.java} | 476 +++++-------------
.../processor/MapperRenderingProcessor.java | 58 +++
.../processor/MethodRetrievalProcessor.java | 202 ++++++++
.../ap/processor/ModelElementProcessor.java | 80 +++
.../org/mapstruct/ap/util/Executables.java | 43 +-
.../java/org/mapstruct/ap/util/Filters.java | 7 +-
....mapstruct.ap.model.BeanMappingMethod.ftl} | 0
12 files changed, 692 insertions(+), 380 deletions(-)
rename processor/src/main/java/org/mapstruct/ap/model/{SimpleMappingMethod.java => BeanMappingMethod.java} (88%)
create mode 100644 processor/src/main/java/org/mapstruct/ap/processor/DefaultModelElementProcessorContext.java
rename processor/src/main/java/org/mapstruct/ap/{MapperGenerationVisitor.java => processor/MapperCreationProcessor.java} (58%)
create mode 100644 processor/src/main/java/org/mapstruct/ap/processor/MapperRenderingProcessor.java
create mode 100644 processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
create mode 100644 processor/src/main/java/org/mapstruct/ap/processor/ModelElementProcessor.java
rename processor/src/main/resources/{org.mapstruct.ap.model.SimpleMappingMethod.ftl => org.mapstruct.ap.model.BeanMappingMethod.ftl} (100%)
diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
index 60715995e..e3f42776a 100644
--- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
@@ -18,9 +18,11 @@
*/
package org.mapstruct.ap;
+import java.util.Arrays;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
@@ -28,18 +30,45 @@ import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementKindVisitor6;
import net.java.dev.hickory.prism.GeneratePrism;
import net.java.dev.hickory.prism.GeneratePrisms;
-import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
+import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.Options;
import org.mapstruct.ap.model.ReportingPolicy;
+import org.mapstruct.ap.processor.DefaultModelElementProcessorContext;
+import org.mapstruct.ap.processor.MapperCreationProcessor;
+import org.mapstruct.ap.processor.MapperRenderingProcessor;
+import org.mapstruct.ap.processor.MethodRetrievalProcessor;
+import org.mapstruct.ap.processor.ModelElementProcessor;
+import org.mapstruct.ap.processor.ModelElementProcessor.ProcessorContext;
+/**
+ * A {@link Processor} which generates the implementations for mapper interfaces
+ * (interfaces annotated with {@code @Mapper}.
+ *
+ * Implementation notes:
+ *
+ * The generation happens by incrementally building up a model representation of
+ * each mapper to be generated (a {@link Mapper} object), which is then written
+ * into the resulting Java source file using the FreeMarker template engine.
+ *
+ * The model instantiation and processing happens in several phases/passes by applying
+ * a sequence of {@link ModelElementProcessor}s.
+ *
+ * For reading annotation attributes, prisms as generated with help of the Hickory tool are used. These
+ * prisms allow a comfortable access to annotations and their attributes without
+ * depending on their class objects.
+ *
+ * @author Gunnar Morling
+ */
@SupportedAnnotationTypes("org.mapstruct.Mapper")
@GeneratePrisms({
- @GeneratePrism(value = Mapper.class, publicAccess = true),
+ @GeneratePrism(value = org.mapstruct.Mapper.class, publicAccess = true),
@GeneratePrism(value = Mapping.class, publicAccess = true),
@GeneratePrism(value = Mappings.class, publicAccess = true)
})
@@ -63,32 +92,6 @@ public class MappingProcessor extends AbstractProcessor {
options = createOptions();
}
- @Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
-
- @Override
- public boolean process(
- final Set extends TypeElement> annotations,
- final RoundEnvironment roundEnvironment) {
-
- for ( TypeElement oneAnnotation : annotations ) {
-
- //Indicates that the annotation's type isn't on the class path of the compiled
- //project. Let the compiler deal with that and print an appropriate error.
- if ( oneAnnotation.getKind() != ElementKind.ANNOTATION_TYPE ) {
- continue;
- }
-
- for ( Element oneAnnotatedElement : roundEnvironment.getElementsAnnotatedWith( oneAnnotation ) ) {
- oneAnnotatedElement.accept( new MapperGenerationVisitor( processingEnv, options ), null );
- }
- }
-
- return ANNOTATIONS_CLAIMED_EXCLUSIVELY;
- }
-
private Options createOptions() {
String unmappedTargetPolicy = processingEnv.getOptions().get( UNMAPPED_TARGET_POLICY );
@@ -97,4 +100,65 @@ public class MappingProcessor extends AbstractProcessor {
unmappedTargetPolicy != null ? ReportingPolicy.valueOf( unmappedTargetPolicy ) : null
);
}
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(final Set extends TypeElement> annotations, final RoundEnvironment roundEnvironment) {
+ ProcessorContext context = new DefaultModelElementProcessorContext( processingEnv, options );
+
+ for ( TypeElement annotation : annotations ) {
+
+ //Indicates that the annotation's type isn't on the class path of the compiled
+ //project. Let the compiler deal with that and print an appropriate error.
+ if ( annotation.getKind() != ElementKind.ANNOTATION_TYPE ) {
+ continue;
+ }
+
+ for ( Element mapperElement : roundEnvironment.getElementsAnnotatedWith( annotation ) ) {
+ TypeElement mapperTypeElement = asTypeElement( mapperElement );
+ processMapperTypeElement( context, mapperTypeElement );
+ }
+ }
+
+ return ANNOTATIONS_CLAIMED_EXCLUSIVELY;
+ }
+
+ private void processMapperTypeElement(ProcessorContext context, TypeElement mapperTypeElement) {
+ Object mapper = mapperTypeElement;
+ for ( ModelElementProcessor, ?> processor : getProcessors() ) {
+ mapper = process( context, processor, mapperTypeElement, mapper );
+ }
+ }
+
+ private R process(ProcessorContext context, ModelElementProcessor
processor,
+ TypeElement mapperTypeElement, Object modelElement) {
+ @SuppressWarnings("unchecked")
+ P sourceElement = (P) modelElement;
+ return processor.process( context, mapperTypeElement, sourceElement );
+ }
+
+ //TODO Retrieve via service loader
+ private Iterable> getProcessors() {
+ return Arrays.>asList(
+ new MethodRetrievalProcessor(),
+ new MapperCreationProcessor(),
+ new MapperRenderingProcessor()
+ );
+ }
+
+ private TypeElement asTypeElement(Element element) {
+ return element.accept(
+ new ElementKindVisitor6() {
+ @Override
+ public TypeElement visitTypeAsInterface(TypeElement e, Void p) {
+ return e;
+ }
+
+ }, null
+ );
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/SimpleMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
similarity index 88%
rename from processor/src/main/java/org/mapstruct/ap/model/SimpleMappingMethod.java
rename to processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
index 088104f99..619ac6e82 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/SimpleMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
@@ -28,12 +28,12 @@ import java.util.Set;
*
* @author Gunnar Morling
*/
-public class SimpleMappingMethod extends MappingMethod {
+public class BeanMappingMethod extends MappingMethod {
private final List propertyMappings;
- public SimpleMappingMethod(String name, String parameterName, Type sourceType, Type targetType,
- List propertyMappings) {
+ public BeanMappingMethod(String name, String parameterName, Type sourceType, Type targetType,
+ List propertyMappings) {
super( name, parameterName, sourceType, targetType );
this.propertyMappings = propertyMappings;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/model/Mapper.java
index 83db622cc..d330504b1 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/Mapper.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/Mapper.java
@@ -33,10 +33,11 @@ public class Mapper extends AbstractModelElement {
private final List usedMapperTypes;
private final Options options;
private final SortedSet importedTypes;
+ private final boolean isErroneous;
public Mapper(String packageName, String interfaceName,
String implementationName, List mappingMethods, List usedMapperTypes,
- Options options) {
+ Options options, boolean isErroneous) {
this.packageName = packageName;
this.interfaceName = interfaceName;
this.implementationName = implementationName;
@@ -44,6 +45,7 @@ public class Mapper extends AbstractModelElement {
this.usedMapperTypes = usedMapperTypes;
this.options = options;
this.importedTypes = determineImportedTypes();
+ this.isErroneous = isErroneous;
}
private SortedSet determineImportedTypes() {
@@ -122,4 +124,8 @@ public class Mapper extends AbstractModelElement {
public SortedSet getImportedTypes() {
return importedTypes;
}
+
+ public boolean isErroneous() {
+ return isErroneous;
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/Type.java b/processor/src/main/java/org/mapstruct/ap/model/Type.java
index 07d726838..5068fda49 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/Type.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/Type.java
@@ -83,6 +83,10 @@ public class Type implements Comparable {
this( null, name, null, false, false, false );
}
+ public Type(String packageName, String name) {
+ this( packageName, name, null, false, false, false );
+ }
+
public Type(String packageName, String name, Type elementType, boolean isEnumType, boolean isCollectionType,
boolean isIterableType) {
this.packageName = packageName;
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/processor/DefaultModelElementProcessorContext.java
new file mode 100644
index 000000000..a7e3019ae
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/processor/DefaultModelElementProcessorContext.java
@@ -0,0 +1,68 @@
+/**
+ * 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.processor;
+
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import org.mapstruct.ap.model.Options;
+
+/**
+ * Default implementation of the processor context.
+ *
+ * @author Gunnar Morling
+ */
+public class DefaultModelElementProcessorContext implements ModelElementProcessor.ProcessorContext {
+
+ private ProcessingEnvironment processingEnvironment;
+ private Options options;
+
+ public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options) {
+ this.processingEnvironment = processingEnvironment;
+ this.options = options;
+ }
+
+ @Override
+ public Filer getFiler() {
+ return processingEnvironment.getFiler();
+ }
+
+ @Override
+ public Types getTypeUtils() {
+ return processingEnvironment.getTypeUtils();
+ }
+
+ @Override
+ public Elements getElementUtils() {
+ return processingEnvironment.getElementUtils();
+ }
+
+ @Override
+ public Messager getMessager() {
+ return processingEnvironment.getMessager();
+ }
+
+ @Override
+ public Options getOptions() {
+ return options;
+ }
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
similarity index 58%
rename from processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java
rename to processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
index 6eac511b0..185012996 100644
--- a/processor/src/main/java/org/mapstruct/ap/MapperGenerationVisitor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
@@ -16,10 +16,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.mapstruct.ap;
+package org.mapstruct.ap.processor;
import java.beans.Introspector;
-import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
@@ -28,23 +27,20 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Messager;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementVisitor;
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.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor6;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
-import javax.tools.JavaFileObject;
+import org.mapstruct.ap.MapperPrism;
import org.mapstruct.ap.conversion.Conversion;
import org.mapstruct.ap.conversion.Conversions;
+import org.mapstruct.ap.model.BeanMappingMethod;
import org.mapstruct.ap.model.IterableMappingMethod;
import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MappingMethod;
@@ -52,111 +48,63 @@ import org.mapstruct.ap.model.MappingMethodReference;
import org.mapstruct.ap.model.Options;
import org.mapstruct.ap.model.PropertyMapping;
import org.mapstruct.ap.model.ReportingPolicy;
-import org.mapstruct.ap.model.SimpleMappingMethod;
import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.source.Mapping;
import org.mapstruct.ap.model.source.Method;
-import org.mapstruct.ap.model.source.Parameter;
import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.Filters;
import org.mapstruct.ap.util.Strings;
import org.mapstruct.ap.util.TypeUtil;
-import org.mapstruct.ap.writer.ModelWriter;
-
-import static javax.lang.model.util.ElementFilter.methodsIn;
/**
- * An {@link ElementVisitor} which generates the implementations for mapper
- * interfaces (interfaces annotated with {@code @Mapper}.
- *
- * Implementation notes:
- *
- * The mapper generation happens by building up a model representation of
- * the mapper to be generated (a {@link Mapper} object), which is then written
- * into a file using the FreeMarker template engine.
- *
- * The model instantiation happens in two phases/passes: The first one retrieves
- * the mapping methods of the given interfaces and their configuration (the
- * source model). In the second pass the individual methods are
- * aggregated into the target model, which contains a {@link BeanMapping}
- * each pair of source and target type which has references to forward and
- * reverse mapping methods as well as the methods for mapping the element types
- * (if it is a collection mapping) and {@link Conversion}s if applicable.
- *
- * For reading annotation attributes, prisms as generated with help of the Hickory tool are used. These
- * prisms allow a comfortable access to annotations and their attributes without
- * depending on their class objects.
+ * A {@link ModelElementProcessor} which creates a {@link Mapper} from the given
+ * list of {@link Method}s.
*
* @author Gunnar Morling
*/
-public class MapperGenerationVisitor extends ElementKindVisitor6 {
+public class MapperCreationProcessor implements ModelElementProcessor, Mapper> {
private static final String IMPLEMENTATION_SUFFIX = "Impl";
- private final ProcessingEnvironment processingEnvironment;
- private final Types typeUtils;
- private final Elements elementUtils;
- private final TypeUtil typeUtil;
- private final Conversions conversions;
- private final Options options;
+ private Elements elementUtils;
+ private Types typeUtils;
+ private Messager messager;
+ private Options options;
- private boolean mappingErroneous = false;
+ private TypeUtil typeUtil;
+ private Conversions conversions;
+ private Executables executables;
- public MapperGenerationVisitor(ProcessingEnvironment processingEnvironment, Options options) {
- this.processingEnvironment = processingEnvironment;
- this.typeUtils = processingEnvironment.getTypeUtils();
- this.elementUtils = processingEnvironment.getElementUtils();
- this.typeUtil = new TypeUtil( elementUtils, typeUtils );
- this.conversions = new Conversions( elementUtils, typeUtils, typeUtil );
- this.options = options;
- }
+ private boolean isErroneous = false;
@Override
- public Void visitTypeAsInterface(TypeElement element, Void p) {
- Mapper model = retrieveModel( element );
+ public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List sourceElement) {
+ this.elementUtils = context.getElementUtils();
+ this.typeUtils = context.getTypeUtils();
+ this.messager = context.getMessager();
+ this.options = context.getOptions();
- if ( !mappingErroneous ) {
- String sourceFileName = element.getQualifiedName() + IMPLEMENTATION_SUFFIX;
- writeModelToSourceFile( sourceFileName, model );
- }
+ this.typeUtil = new TypeUtil( context.getElementUtils(), context.getTypeUtils() );
+ this.conversions = new Conversions( elementUtils, typeUtils, typeUtil );
+ this.executables = new Executables( typeUtil );
- return null;
+ return getMapper( mapperTypeElement, sourceElement );
}
- private void writeModelToSourceFile(String fileName, Mapper model) {
- JavaFileObject sourceFile;
- try {
- sourceFile = processingEnvironment.getFiler().createSourceFile( fileName );
- }
- catch ( IOException e ) {
- throw new RuntimeException( e );
- }
-
- new ModelWriter().writeModel( sourceFile, model );
- }
-
- private Mapper retrieveModel(TypeElement element) {
- //1.) build up "source" model
- List methods = retrieveMethods( element, true );
-
- //2.) build up aggregated "target" model
- List mappings = getMappingMethods(
- methods,
- getEffectiveUnmappedTargetPolicy( element )
- );
+ public Mapper getMapper(TypeElement element, List methods) {
List usedMapperTypes = getUsedMapperTypes( element );
- Mapper mapper = new Mapper(
+ ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element );
+
+ return new Mapper(
elementUtils.getPackageOf( element ).getQualifiedName().toString(),
element.getSimpleName().toString(),
element.getSimpleName() + IMPLEMENTATION_SUFFIX,
- mappings,
+ getMappingMethods( methods, unmappedTargetPolicy ),
usedMapperTypes,
- options
+ options,
+ isErroneous
);
-
- return mapper;
}
/**
@@ -184,6 +132,15 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
}
}
+ private List getUsedMapperTypes(TypeElement element) {
+ List usedMapperTypes = new LinkedList();
+ MapperPrism mapperPrism = MapperPrism.getInstanceOn( element );
+ for ( TypeMirror usedMapper : mapperPrism.uses() ) {
+ usedMapperTypes.add( typeUtil.retrieveType( usedMapper ) );
+ }
+ return usedMapperTypes;
+ }
+
private List getMappingMethods(List methods, ReportingPolicy unmappedTargetPolicy) {
List mappingMethods = new ArrayList();
@@ -203,7 +160,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
mappingMethods.add( getIterableMappingMethod( methods, method ) );
}
else {
- mappingMethods.add( getSimpleMappingMethod( methods, method, unmappedTargetPolicy ) );
+ mappingMethods.add( getBeanMappingMethod( methods, method, unmappedTargetPolicy ) );
}
}
return mappingMethods;
@@ -218,8 +175,8 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
return reversed;
}
- private MappingMethod getSimpleMappingMethod(List methods, Method method,
- ReportingPolicy unmappedTargetPolicy) {
+ private MappingMethod getBeanMappingMethod(List methods, Method method,
+ ReportingPolicy unmappedTargetPolicy) {
List propertyMappings = new ArrayList();
Set mappedTargetProperties = new HashSet();
@@ -240,21 +197,21 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
elementUtils.getAllMembers( returnTypeElement )
);
- Set sourceProperties = Executables.getPropertyNames(
+ Set sourceProperties = executables.getPropertyNames(
Filters.getterMethodsIn( sourceGetters )
);
- Set targetProperties = Executables.getPropertyNames(
+ Set targetProperties = executables.getPropertyNames(
Filters.setterMethodsIn( targetSetters )
);
reportErrorIfMappedPropertiesDontExist( method, sourceProperties, targetProperties );
for ( ExecutableElement getterMethod : sourceGetters ) {
- String sourcePropertyName = Executables.getPropertyName( getterMethod );
+ String sourcePropertyName = executables.getPropertyName( getterMethod );
Mapping mapping = mappings.get( sourcePropertyName );
for ( ExecutableElement setterMethod : targetSetters ) {
- String targetPropertyName = Executables.getPropertyName( setterMethod );
+ String targetPropertyName = executables.getPropertyName( setterMethod );
if ( targetPropertyName.equals( mapping != null ? mapping.getTargetName() : sourcePropertyName ) ) {
PropertyMapping property = getPropertyMapping(
@@ -278,7 +235,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
mappedTargetProperties
);
- return new SimpleMappingMethod(
+ return new BeanMappingMethod(
method.getName(),
method.getParameterName(),
method.getSourceType(),
@@ -287,10 +244,65 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
);
}
+ private void reportErrorForUnmappedTargetPropertiesIfRequired(Method method,
+ ReportingPolicy unmappedTargetPolicy,
+ Set targetProperties,
+ Set mappedTargetProperties) {
+
+ if ( targetProperties.size() > mappedTargetProperties.size() &&
+ unmappedTargetPolicy.requiresReport() ) {
+ targetProperties.removeAll( mappedTargetProperties );
+ printMessage(
+ unmappedTargetPolicy,
+ MessageFormat.format(
+ "Unmapped target {0,choice,1#property|1 rawMethods, Method method) {
+ for ( Method oneMethod : rawMethods ) {
+ if ( oneMethod.reverses( method ) ) {
+ return oneMethod;
+ }
+ }
+ return null;
+ }
+
+ private void reportErrorIfMappedPropertiesDontExist(Method method, Set sourceProperties,
+ Set targetProperties) {
+ for ( Mapping mappedProperty : method.getMappings().values() ) {
+ if ( !sourceProperties.contains( mappedProperty.getSourceName() ) ) {
+ printMessage(
+ ReportingPolicy.ERROR,
+ String.format(
+ "Unknown property \"%s\" in parameter type %s.",
+ mappedProperty.getSourceName(),
+ method.getSourceType()
+ ), method.getExecutable(), mappedProperty.getMirror(), mappedProperty.getSourceAnnotationValue()
+ );
+ }
+ if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) {
+ printMessage(
+ ReportingPolicy.ERROR,
+ String.format(
+ "Unknown property \"%s\" in return type %s.",
+ mappedProperty.getTargetName(),
+ method.getTargetType()
+ ), method.getExecutable(), mappedProperty.getMirror(), mappedProperty.getTargetAnnotationValue()
+ );
+ }
+ }
+ }
+
private PropertyMapping getPropertyMapping(List methods, Method method, ExecutableElement getterMethod,
ExecutableElement setterMethod) {
- Type sourceType = retrieveReturnType( getterMethod );
- Type targetType = retrieveParameter( setterMethod ).getType();
+ Type sourceType = executables.retrieveReturnType( getterMethod );
+ Type targetType = executables.retrieveParameter( setterMethod ).getType();
MappingMethodReference propertyMappingMethod = getMappingMethodReference( methods, sourceType, targetType );
Conversion conversion = conversions.getConversion(
@@ -301,10 +313,10 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
PropertyMapping property = new PropertyMapping(
method.getParameterName(),
Introspector.decapitalize( method.getTargetType().getName() ),
- Executables.getPropertyName( getterMethod ),
+ executables.getPropertyName( getterMethod ),
getterMethod.getSimpleName().toString(),
sourceType,
- Executables.getPropertyName( setterMethod ),
+ executables.getPropertyName( setterMethod ),
setterMethod.getSimpleName().toString(),
targetType,
propertyMappingMethod,
@@ -344,24 +356,35 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
);
}
- private void reportErrorForUnmappedTargetPropertiesIfRequired(Method method,
- ReportingPolicy unmappedTargetPolicy,
- Set targetProperties,
- Set mappedTargetProperties) {
+ private String getIterableConversionString(Conversions conversions, Type sourceElementType, Type targetElementType,
+ boolean isToConversion) {
+ Conversion conversion = conversions.getConversion( sourceElementType, targetElementType );
- if ( targetProperties.size() > mappedTargetProperties.size() &&
- unmappedTargetPolicy.requiresReport() ) {
- targetProperties.removeAll( mappedTargetProperties );
- printMessage(
- unmappedTargetPolicy,
- MessageFormat.format(
- "Unmapped target {0,choice,1#property|1 methods, Type parameterType,
+ Type returnType) {
+ for ( Method oneMethod : methods ) {
+ if ( oneMethod.getSourceType().equals( parameterType ) && oneMethod.getTargetType().equals( returnType ) ) {
+ return new MappingMethodReference(
+ oneMethod.getDeclaringMapper(),
+ oneMethod.getName(),
+ oneMethod.getParameterName(),
+ oneMethod.getSourceType(),
+ oneMethod.getTargetType()
+ );
+ }
+ }
+
+ return null;
}
private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) {
@@ -390,239 +413,16 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
}
}
- private String getIterableConversionString(Conversions conversions, Type sourceElementType, Type targetElementType,
- boolean isToConversion) {
- Conversion conversion = conversions.getConversion( sourceElementType, targetElementType );
-
- if ( conversion == null ) {
- return null;
- }
-
- return conversion.to(
- Introspector.decapitalize( sourceElementType.getName() ),
- targetElementType
- );
- }
-
- private List getUsedMapperTypes(TypeElement element) {
- List usedMapperTypes = new LinkedList();
- MapperPrism mapperPrism = MapperPrism.getInstanceOn( element );
- for ( TypeMirror usedMapper : mapperPrism.uses() ) {
- usedMapperTypes.add( typeUtil.retrieveType( usedMapper ) );
- }
- return usedMapperTypes;
- }
-
- private MappingMethodReference getMappingMethodReference(Iterable methods, Type parameterType,
- Type returnType) {
- for ( Method oneMethod : methods ) {
- if ( oneMethod.getSourceType().equals( parameterType ) && oneMethod.getTargetType().equals( returnType ) ) {
- return new MappingMethodReference(
- oneMethod.getDeclaringMapper(),
- oneMethod.getName(),
- oneMethod.getParameterName(),
- oneMethod.getSourceType(),
- oneMethod.getTargetType()
- );
- }
- }
-
- return null;
- }
-
- private Method getReverseMappingMethod(List rawMethods, Method method) {
- for ( Method oneMethod : rawMethods ) {
- if ( oneMethod.reverses( method ) ) {
- return oneMethod;
- }
- }
- return null;
- }
-
- /**
- * Retrieves the mapping methods declared by the given mapper type.
- *
- * @param element The type of interest
- * @param implementationRequired Whether an implementation of this type must be generated or
- * not. {@code true} if the type is the currently processed
- * mapper interface, {@code false} if the given type is one
- * referred to via {@code Mapper#uses()}.
- *
- * @return All mapping methods declared by the given type
- */
- private List retrieveMethods(TypeElement element, boolean implementationRequired) {
- List methods = new ArrayList();
-
- MapperPrism mapperPrism = implementationRequired ? MapperPrism.getInstanceOn( element ) : null;
-
- //TODO Extract to separate method
- for ( ExecutableElement method : methodsIn( element.getEnclosedElements() ) ) {
- Parameter parameter = retrieveParameter( method );
- Type returnType = retrieveReturnType( method );
-
- boolean mappingErroneous = false;
-
- if ( implementationRequired ) {
- if ( parameter.getType().isIterableType() && !returnType.isIterableType() ) {
- printMessage(
- ReportingPolicy.ERROR,
- "Can't generate mapping method from iterable type to non-iterable type.",
- method
- );
- mappingErroneous = true;
- }
- if ( !parameter.getType().isIterableType() && returnType.isIterableType() ) {
- printMessage(
- ReportingPolicy.ERROR,
- "Can't generate mapping method from non-iterable type to iterable type.",
- method
- );
- mappingErroneous = true;
- }
- if ( parameter.getType().isPrimitive() ) {
- printMessage(
- ReportingPolicy.ERROR,
- "Can't generate mapping method with primitive parameter type.",
- method
- );
- mappingErroneous = true;
- }
- if ( returnType.isPrimitive() ) {
- printMessage(
- ReportingPolicy.ERROR,
- "Can't generate mapping method with primitive return type.",
- method
- );
- mappingErroneous = true;
- }
-
- if ( mappingErroneous ) {
- continue;
- }
- }
-
- //add method with property mappings if an implementation needs to be generated
- if ( implementationRequired ) {
- methods.add(
- Method.forMethodRequiringImplementation(
- method,
- parameter.getName(),
- parameter.getType(),
- returnType,
- getMappings( method )
- )
- );
- }
- //otherwise add reference to existing mapper method
- else {
- methods.add(
- Method.forReferencedMethod(
- typeUtil.getType( typeUtils.getDeclaredType( element ) ),
- method,
- parameter.getName(),
- parameter.getType(),
- returnType
- )
- );
- }
- }
-
- //Add all methods of used mappers in order to reference them in the aggregated model
- if ( implementationRequired ) {
- for ( TypeMirror usedMapper : mapperPrism.uses() ) {
- methods.addAll(
- retrieveMethods(
- (TypeElement) ( (DeclaredType) usedMapper ).asElement(),
- false
- )
- );
- }
- }
-
- return methods;
- }
-
- private void reportErrorIfMappedPropertiesDontExist(Method method, Set sourceProperties,
- Set targetProperties) {
- for ( Mapping mappedProperty : method.getMappings().values() ) {
- if ( !sourceProperties.contains( mappedProperty.getSourceName() ) ) {
- printMessage(
- ReportingPolicy.ERROR,
- String.format(
- "Unknown property \"%s\" in parameter type %s.",
- mappedProperty.getSourceName(),
- method.getSourceType()
- ), method.getExecutable(), mappedProperty.getMirror(), mappedProperty.getSourceAnnotationValue()
- );
- }
- if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) {
- printMessage(
- ReportingPolicy.ERROR,
- String.format(
- "Unknown property \"%s\" in return type %s.",
- mappedProperty.getTargetName(),
- method.getTargetType()
- ), method.getExecutable(), mappedProperty.getMirror(), mappedProperty.getTargetAnnotationValue()
- );
- }
- }
- }
-
- /**
- * Retrieves the mappings configured via {@code @Mapping} from the given
- * method.
- *
- * @param method The method of interest
- *
- * @return The mappings for the given method, keyed by source property name
- */
- private Map getMappings(ExecutableElement method) {
- Map mappings = new HashMap();
-
- MappingPrism mappingAnnotation = MappingPrism.getInstanceOn( method );
- MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( method );
-
- if ( mappingAnnotation != null ) {
- mappings.put( mappingAnnotation.source(), Mapping.fromMappingPrism( mappingAnnotation ) );
- }
-
- if ( mappingsAnnotation != null ) {
- mappings.putAll( Mapping.fromMappingsPrism( mappingsAnnotation ) );
- }
-
- return mappings;
- }
-
- private Parameter retrieveParameter(ExecutableElement method) {
- List extends VariableElement> parameters = method.getParameters();
-
- if ( parameters.size() != 1 ) {
- //TODO: Log error
- return null;
- }
-
- VariableElement parameter = parameters.get( 0 );
-
- return new Parameter(
- parameter.getSimpleName().toString(),
- typeUtil.retrieveType( parameter.asType() )
- );
- }
-
- private Type retrieveReturnType(ExecutableElement method) {
- return typeUtil.retrieveType( method.getReturnType() );
- }
-
private void printMessage(ReportingPolicy reportingPolicy, String message, Element element) {
- processingEnvironment.getMessager().printMessage( reportingPolicy.getDiagnosticKind(), message, element );
+ messager.printMessage( reportingPolicy.getDiagnosticKind(), message, element );
if ( reportingPolicy.failsBuild() ) {
- mappingErroneous = true;
+ isErroneous = true;
}
}
private void printMessage(ReportingPolicy reportingPolicy, String message, Element element,
AnnotationMirror annotationMirror, AnnotationValue annotationValue) {
- processingEnvironment.getMessager()
+ messager
.printMessage(
reportingPolicy.getDiagnosticKind(),
message,
@@ -631,7 +431,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6 {
annotationValue
);
if ( reportingPolicy.failsBuild() ) {
- mappingErroneous = true;
+ isErroneous = true;
}
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperRenderingProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperRenderingProcessor.java
new file mode 100644
index 000000000..34056dbdb
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperRenderingProcessor.java
@@ -0,0 +1,58 @@
+/**
+ * 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.processor;
+
+import java.io.IOException;
+import javax.annotation.processing.Filer;
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaFileObject;
+
+import org.mapstruct.ap.model.Mapper;
+import org.mapstruct.ap.writer.ModelWriter;
+
+/**
+ * A {@link ModelElementProcessor} which creates a Java source file representing
+ * the given {@link Mapper} object.
+ *
+ * @author Gunnar Morling
+ */
+public class MapperRenderingProcessor implements ModelElementProcessor {
+
+ @Override
+ public Void process(ProcessorContext context, TypeElement mapperTypeElement, Mapper sourceElement) {
+ if ( !sourceElement.isErroneous() ) {
+ writeToSourceFile( context.getFiler(), sourceElement );
+ }
+ return null;
+ }
+
+ private void writeToSourceFile(Filer filer, Mapper model) {
+ String fileName = model.getPackageName() + "." + model.getImplementationName();
+
+ JavaFileObject sourceFile;
+ try {
+ sourceFile = filer.createSourceFile( fileName );
+ }
+ catch ( IOException e ) {
+ throw new RuntimeException( e );
+ }
+
+ new ModelWriter().writeModel( sourceFile, model );
+ }
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
new file mode 100644
index 000000000..34604ba97
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
@@ -0,0 +1,202 @@
+/**
+ * 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.processor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+
+import org.mapstruct.ap.MapperPrism;
+import org.mapstruct.ap.MappingPrism;
+import org.mapstruct.ap.MappingsPrism;
+import org.mapstruct.ap.model.ReportingPolicy;
+import org.mapstruct.ap.model.Type;
+import org.mapstruct.ap.model.source.Mapping;
+import org.mapstruct.ap.model.source.Method;
+import org.mapstruct.ap.model.source.Parameter;
+import org.mapstruct.ap.util.Executables;
+import org.mapstruct.ap.util.TypeUtil;
+
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+/**
+ * A {@link ModelElementProcessor} which retrieves a list of {@link Method}s
+ * representing all the mapping methods of the given bean mapper type as well as
+ * all referenced mapper methods declared by other mappers referenced by the
+ * current mapper.
+ *
+ * @author Gunnar Morling
+ */
+public class MethodRetrievalProcessor implements ModelElementProcessor> {
+
+ private Types typeUtils;
+ private Messager messager;
+ private TypeUtil typeUtil;
+ private Executables executables;
+
+ @Override
+ public List process(ProcessorContext context, TypeElement mapperTypeElement, TypeElement sourceElement) {
+ this.typeUtils = context.getTypeUtils();
+ this.messager = context.getMessager();
+ this.typeUtil = new TypeUtil( context.getElementUtils(), typeUtils );
+ this.executables = new Executables( typeUtil );
+
+ return retrieveMethods( mapperTypeElement, true );
+ }
+
+ /**
+ * Retrieves the mapping methods declared by the given mapper type.
+ *
+ * @param element The type of interest
+ * @param implementationRequired Whether an implementation of this type must be generated or
+ * not. {@code true} if the type is the currently processed
+ * mapper interface, {@code false} if the given type is one
+ * referred to via {@code Mapper#uses()}.
+ *
+ * @return All mapping methods declared by the given type
+ */
+ private List retrieveMethods(TypeElement element, boolean implementationRequired) {
+ List methods = new ArrayList();
+
+ MapperPrism mapperPrism = implementationRequired ? MapperPrism.getInstanceOn( element ) : null;
+
+ //TODO Extract to separate method
+ for ( ExecutableElement method : methodsIn( element.getEnclosedElements() ) ) {
+ Parameter parameter = executables.retrieveParameter( method );
+ Type returnType = executables.retrieveReturnType( method );
+
+ boolean mappingErroneous = false;
+
+ if ( implementationRequired ) {
+ if ( parameter.getType().isIterableType() && !returnType.isIterableType() ) {
+ printMessage(
+ ReportingPolicy.ERROR,
+ "Can't generate mapping method from iterable type to non-iterable type.",
+ method
+ );
+ mappingErroneous = true;
+ }
+ if ( !parameter.getType().isIterableType() && returnType.isIterableType() ) {
+ printMessage(
+ ReportingPolicy.ERROR,
+ "Can't generate mapping method from non-iterable type to iterable type.",
+ method
+ );
+ mappingErroneous = true;
+ }
+ if ( parameter.getType().isPrimitive() ) {
+ printMessage(
+ ReportingPolicy.ERROR,
+ "Can't generate mapping method with primitive parameter type.",
+ method
+ );
+ mappingErroneous = true;
+ }
+ if ( returnType.isPrimitive() ) {
+ printMessage(
+ ReportingPolicy.ERROR,
+ "Can't generate mapping method with primitive return type.",
+ method
+ );
+ mappingErroneous = true;
+ }
+
+ if ( mappingErroneous ) {
+ continue;
+ }
+ }
+
+ //add method with property mappings if an implementation needs to be generated
+ if ( implementationRequired ) {
+ methods.add(
+ Method.forMethodRequiringImplementation(
+ method,
+ parameter.getName(),
+ parameter.getType(),
+ returnType,
+ getMappings( method )
+ )
+ );
+ }
+ //otherwise add reference to existing mapper method
+ else {
+ methods.add(
+ Method.forReferencedMethod(
+ typeUtil.getType( typeUtils.getDeclaredType( element ) ),
+ method,
+ parameter.getName(),
+ parameter.getType(),
+ returnType
+ )
+ );
+ }
+ }
+
+ //Add all methods of used mappers in order to reference them in the aggregated model
+ if ( implementationRequired ) {
+ for ( TypeMirror usedMapper : mapperPrism.uses() ) {
+ methods.addAll(
+ retrieveMethods(
+ (TypeElement) ( (DeclaredType) usedMapper ).asElement(),
+ false
+ )
+ );
+ }
+ }
+
+ return methods;
+ }
+
+ /**
+ * Retrieves the mappings configured via {@code @Mapping} from the given
+ * method.
+ *
+ * @param method The method of interest
+ *
+ * @return The mappings for the given method, keyed by source property name
+ */
+ private Map getMappings(ExecutableElement method) {
+ Map mappings = new HashMap();
+
+ MappingPrism mappingAnnotation = MappingPrism.getInstanceOn( method );
+ MappingsPrism mappingsAnnotation = MappingsPrism.getInstanceOn( method );
+
+ if ( mappingAnnotation != null ) {
+ mappings.put( mappingAnnotation.source(), Mapping.fromMappingPrism( mappingAnnotation ) );
+ }
+
+ if ( mappingsAnnotation != null ) {
+ mappings.putAll( Mapping.fromMappingsPrism( mappingsAnnotation ) );
+ }
+
+ return mappings;
+ }
+
+ private void printMessage(ReportingPolicy reportingPolicy, String message, Element element) {
+ messager.printMessage( reportingPolicy.getDiagnosticKind(), message, element );
+ }
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/ModelElementProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/ModelElementProcessor.java
new file mode 100644
index 000000000..cd9872544
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/processor/ModelElementProcessor.java
@@ -0,0 +1,80 @@
+/**
+ * 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.processor;
+
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import org.mapstruct.ap.model.Options;
+
+/**
+ * A processor which performs one task of the mapper generation, e.g. retrieving
+ * methods from the source {@link TypeElement}, performing validity checks or
+ * generating the output source file.
+ *
+ * @param The parameter type processed by this processor
+ * @param The return type created by this processor
+ *
+ * @author Gunnar Morling
+ */
+public interface ModelElementProcessor {
+
+ /**
+ * Context object passed to
+ * {@link ModelElementProcessor#process(ProcessorContext, TypeElement, Object)}
+ * providing access to common infrastructure objects such as {@link Types}
+ * etc.
+ *
+ * @author Gunnar Morling
+ */
+ public interface ProcessorContext {
+
+ Filer getFiler();
+
+ Types getTypeUtils();
+
+ Elements getElementUtils();
+
+ Messager getMessager();
+
+ Options getOptions();
+ }
+
+ /**
+ * Processes the given source element, representing a Java bean mapper in
+ * one form or another.
+ *
+ * @param context Context providing common infrastructure objects.
+ * @param mapperTypeElement The original type element from which the given mapper object
+ * is derived.
+ * @param sourceElement The current representation of the bean mapper. Never
+ * {@code null} (the very first processor receives the original
+ * type element).
+ *
+ * @return The resulting representation of the bean mapper; may be the same
+ * as the source representation, e.g. if a given implementation just
+ * performs some sort of validity check. Implementations must never
+ * return {@code null} except for the very last processor which
+ * generates the resulting Java source file.
+ */
+ R process(ProcessorContext context, TypeElement mapperTypeElement, P sourceElement);
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/util/Executables.java b/processor/src/main/java/org/mapstruct/ap/util/Executables.java
index 440db5c8a..47115fa8f 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/Executables.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/Executables.java
@@ -23,8 +23,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
+import org.mapstruct.ap.model.Type;
+import org.mapstruct.ap.model.source.Parameter;
+
/**
* Provides functionality around {@link ExecutableElement}s.
*
@@ -32,14 +36,17 @@ import javax.lang.model.type.TypeKind;
*/
public class Executables {
- private Executables() {
+ private final TypeUtil typeUtil;
+
+ public Executables(TypeUtil typeUtil) {
+ this.typeUtil = typeUtil;
}
- public static boolean isGetterMethod(ExecutableElement method) {
+ public boolean isGetterMethod(ExecutableElement method) {
return isNonBooleanGetterMethod( method ) || isBooleanGetterMethod( method );
}
- private static boolean isNonBooleanGetterMethod(ExecutableElement method) {
+ private boolean isNonBooleanGetterMethod(ExecutableElement method) {
String name = method.getSimpleName().toString();
return method.getParameters().isEmpty() &&
@@ -48,7 +55,7 @@ public class Executables {
method.getReturnType().getKind() != TypeKind.VOID;
}
- private static boolean isBooleanGetterMethod(ExecutableElement method) {
+ private boolean isBooleanGetterMethod(ExecutableElement method) {
String name = method.getSimpleName().toString();
return method.getParameters().isEmpty() &&
@@ -57,7 +64,7 @@ public class Executables {
method.getReturnType().getKind() == TypeKind.BOOLEAN;
}
- public static boolean isSetterMethod(ExecutableElement method) {
+ public boolean isSetterMethod(ExecutableElement method) {
String name = method.getSimpleName().toString();
if ( name.startsWith( "set" ) && name.length() > 3 && method.getParameters()
@@ -68,7 +75,7 @@ public class Executables {
return false;
}
- public static String getPropertyName(ExecutableElement getterOrSetterMethod) {
+ public String getPropertyName(ExecutableElement getterOrSetterMethod) {
if ( isNonBooleanGetterMethod( getterOrSetterMethod ) ) {
return Introspector.decapitalize(
getterOrSetterMethod.getSimpleName().toString().substring( 3 )
@@ -88,13 +95,33 @@ public class Executables {
throw new IllegalArgumentException( "Executable " + getterOrSetterMethod + " is not getter or setter method." );
}
- public static Set getPropertyNames(List propertyAccessors) {
+ public Set getPropertyNames(List propertyAccessors) {
Set propertyNames = new HashSet();
for ( ExecutableElement executableElement : propertyAccessors ) {
- propertyNames.add( Executables.getPropertyName( executableElement ) );
+ propertyNames.add( getPropertyName( executableElement ) );
}
return propertyNames;
}
+
+ public Parameter retrieveParameter(ExecutableElement method) {
+ List extends VariableElement> parameters = method.getParameters();
+
+ if ( parameters.size() != 1 ) {
+ //TODO: Log error
+ return null;
+ }
+
+ VariableElement parameter = parameters.get( 0 );
+
+ return new Parameter(
+ parameter.getSimpleName().toString(),
+ typeUtil.retrieveType( parameter.asType() )
+ );
+ }
+
+ public Type retrieveReturnType(ExecutableElement method) {
+ return typeUtil.retrieveType( method.getReturnType() );
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/util/Filters.java b/processor/src/main/java/org/mapstruct/ap/util/Filters.java
index 9f675f7bd..ba3187a4e 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/Filters.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/Filters.java
@@ -32,6 +32,9 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
*/
public class Filters {
+ //TODO
+ private static Executables executables = new Executables( null );
+
private Filters() {
}
@@ -39,7 +42,7 @@ public class Filters {
List getterMethods = new LinkedList();
for ( ExecutableElement method : methodsIn( elements ) ) {
- if ( Executables.isGetterMethod( method ) ) {
+ if ( executables.isGetterMethod( method ) ) {
getterMethods.add( method );
}
}
@@ -51,7 +54,7 @@ public class Filters {
List setterMethods = new LinkedList();
for ( ExecutableElement method : methodsIn( elements ) ) {
- if ( Executables.isSetterMethod( method ) ) {
+ if ( executables.isSetterMethod( method ) ) {
setterMethods.add( method );
}
}
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.SimpleMappingMethod.ftl b/processor/src/main/resources/org.mapstruct.ap.model.BeanMappingMethod.ftl
similarity index 100%
rename from processor/src/main/resources/org.mapstruct.ap.model.SimpleMappingMethod.ftl
rename to processor/src/main/resources/org.mapstruct.ap.model.BeanMappingMethod.ftl