#32 Establishing processing chain for model creation and transformation

This commit is contained in:
Gunnar Morling 2013-06-27 22:56:49 +02:00
parent 9a4e51801f
commit a565bed0c7
12 changed files with 692 additions and 380 deletions

View File

@ -18,9 +18,11 @@
*/ */
package org.mapstruct.ap; package org.mapstruct.ap;
import java.util.Arrays;
import java.util.Set; import java.util.Set;
import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions; 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.Element;
import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement; 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.GeneratePrism;
import net.java.dev.hickory.prism.GeneratePrisms; import net.java.dev.hickory.prism.GeneratePrisms;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.Mappings; import org.mapstruct.Mappings;
import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.Options; import org.mapstruct.ap.model.Options;
import org.mapstruct.ap.model.ReportingPolicy; 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}.
* </p>
* Implementation notes:
* </p>
* 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.
* </p>
* The model instantiation and processing happens in several phases/passes by applying
* a sequence of {@link ModelElementProcessor}s.
* </p>
* For reading annotation attributes, prisms as generated with help of the <a
* href="https://java.net/projects/hickory">Hickory</a> 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") @SupportedAnnotationTypes("org.mapstruct.Mapper")
@GeneratePrisms({ @GeneratePrisms({
@GeneratePrism(value = Mapper.class, publicAccess = true), @GeneratePrism(value = org.mapstruct.Mapper.class, publicAccess = true),
@GeneratePrism(value = Mapping.class, publicAccess = true), @GeneratePrism(value = Mapping.class, publicAccess = true),
@GeneratePrism(value = Mappings.class, publicAccess = true) @GeneratePrism(value = Mappings.class, publicAccess = true)
}) })
@ -63,32 +92,6 @@ public class MappingProcessor extends AbstractProcessor {
options = createOptions(); 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() { private Options createOptions() {
String unmappedTargetPolicy = processingEnv.getOptions().get( UNMAPPED_TARGET_POLICY ); String unmappedTargetPolicy = processingEnv.getOptions().get( UNMAPPED_TARGET_POLICY );
@ -97,4 +100,65 @@ public class MappingProcessor extends AbstractProcessor {
unmappedTargetPolicy != null ? ReportingPolicy.valueOf( unmappedTargetPolicy ) : null 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 <P, R> R process(ProcessorContext context, ModelElementProcessor<P, R> processor,
TypeElement mapperTypeElement, Object modelElement) {
@SuppressWarnings("unchecked")
P sourceElement = (P) modelElement;
return processor.process( context, mapperTypeElement, sourceElement );
}
//TODO Retrieve via service loader
private Iterable<ModelElementProcessor<?, ?>> getProcessors() {
return Arrays.<ModelElementProcessor<?, ?>>asList(
new MethodRetrievalProcessor(),
new MapperCreationProcessor(),
new MapperRenderingProcessor()
);
}
private TypeElement asTypeElement(Element element) {
return element.accept(
new ElementKindVisitor6<TypeElement, Void>() {
@Override
public TypeElement visitTypeAsInterface(TypeElement e, Void p) {
return e;
}
}, null
);
}
} }

View File

@ -28,12 +28,12 @@ import java.util.Set;
* *
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class SimpleMappingMethod extends MappingMethod { public class BeanMappingMethod extends MappingMethod {
private final List<PropertyMapping> propertyMappings; private final List<PropertyMapping> propertyMappings;
public SimpleMappingMethod(String name, String parameterName, Type sourceType, Type targetType, public BeanMappingMethod(String name, String parameterName, Type sourceType, Type targetType,
List<PropertyMapping> propertyMappings) { List<PropertyMapping> propertyMappings) {
super( name, parameterName, sourceType, targetType ); super( name, parameterName, sourceType, targetType );
this.propertyMappings = propertyMappings; this.propertyMappings = propertyMappings;
} }

View File

@ -33,10 +33,11 @@ public class Mapper extends AbstractModelElement {
private final List<Type> usedMapperTypes; private final List<Type> usedMapperTypes;
private final Options options; private final Options options;
private final SortedSet<Type> importedTypes; private final SortedSet<Type> importedTypes;
private final boolean isErroneous;
public Mapper(String packageName, String interfaceName, public Mapper(String packageName, String interfaceName,
String implementationName, List<MappingMethod> mappingMethods, List<Type> usedMapperTypes, String implementationName, List<MappingMethod> mappingMethods, List<Type> usedMapperTypes,
Options options) { Options options, boolean isErroneous) {
this.packageName = packageName; this.packageName = packageName;
this.interfaceName = interfaceName; this.interfaceName = interfaceName;
this.implementationName = implementationName; this.implementationName = implementationName;
@ -44,6 +45,7 @@ public class Mapper extends AbstractModelElement {
this.usedMapperTypes = usedMapperTypes; this.usedMapperTypes = usedMapperTypes;
this.options = options; this.options = options;
this.importedTypes = determineImportedTypes(); this.importedTypes = determineImportedTypes();
this.isErroneous = isErroneous;
} }
private SortedSet<Type> determineImportedTypes() { private SortedSet<Type> determineImportedTypes() {
@ -122,4 +124,8 @@ public class Mapper extends AbstractModelElement {
public SortedSet<Type> getImportedTypes() { public SortedSet<Type> getImportedTypes() {
return importedTypes; return importedTypes;
} }
public boolean isErroneous() {
return isErroneous;
}
} }

View File

@ -83,6 +83,10 @@ public class Type implements Comparable<Type> {
this( null, name, null, false, false, false ); 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, public Type(String packageName, String name, Type elementType, boolean isEnumType, boolean isCollectionType,
boolean isIterableType) { boolean isIterableType) {
this.packageName = packageName; this.packageName = packageName;

View File

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

View File

@ -16,10 +16,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap; package org.mapstruct.ap.processor;
import java.beans.Introspector; import java.beans.Introspector;
import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -28,23 +27,20 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; 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.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ElementVisitor;
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.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementKindVisitor6;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types; 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.Conversion;
import org.mapstruct.ap.conversion.Conversions; import org.mapstruct.ap.conversion.Conversions;
import org.mapstruct.ap.model.BeanMappingMethod;
import org.mapstruct.ap.model.IterableMappingMethod; import org.mapstruct.ap.model.IterableMappingMethod;
import org.mapstruct.ap.model.Mapper; import org.mapstruct.ap.model.Mapper;
import org.mapstruct.ap.model.MappingMethod; 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.Options;
import org.mapstruct.ap.model.PropertyMapping; import org.mapstruct.ap.model.PropertyMapping;
import org.mapstruct.ap.model.ReportingPolicy; import org.mapstruct.ap.model.ReportingPolicy;
import org.mapstruct.ap.model.SimpleMappingMethod;
import org.mapstruct.ap.model.Type; import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.source.Mapping; import org.mapstruct.ap.model.source.Mapping;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.Parameter;
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.TypeUtil;
import org.mapstruct.ap.writer.ModelWriter;
import static javax.lang.model.util.ElementFilter.methodsIn;
/** /**
* An {@link ElementVisitor} which generates the implementations for mapper * A {@link ModelElementProcessor} which creates a {@link Mapper} from the given
* interfaces (interfaces annotated with {@code @Mapper}. * list of {@link Method}s.
* </p>
* Implementation notes:
* </p>
* 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.
* </p>
* The model instantiation happens in two phases/passes: The first one retrieves
* the mapping methods of the given interfaces and their configuration (the
* <i>source</i> model). In the second pass the individual methods are
* aggregated into the <i>target</i> 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.
* </p>
* For reading annotation attributes, prisms as generated with help of the <a
* href="https://java.net/projects/hickory">Hickory</a> tool are used. These
* prisms allow a comfortable access to annotations and their attributes without
* depending on their class objects.
* *
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> { public class MapperCreationProcessor implements ModelElementProcessor<List<Method>, Mapper> {
private static final String IMPLEMENTATION_SUFFIX = "Impl"; private static final String IMPLEMENTATION_SUFFIX = "Impl";
private final ProcessingEnvironment processingEnvironment; private Elements elementUtils;
private final Types typeUtils; private Types typeUtils;
private final Elements elementUtils; private Messager messager;
private final TypeUtil typeUtil; private Options options;
private final Conversions conversions;
private final Options options;
private boolean mappingErroneous = false; private TypeUtil typeUtil;
private Conversions conversions;
private Executables executables;
public MapperGenerationVisitor(ProcessingEnvironment processingEnvironment, Options options) { private boolean isErroneous = false;
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;
}
@Override @Override
public Void visitTypeAsInterface(TypeElement element, Void p) { public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceElement) {
Mapper model = retrieveModel( element ); this.elementUtils = context.getElementUtils();
this.typeUtils = context.getTypeUtils();
this.messager = context.getMessager();
this.options = context.getOptions();
if ( !mappingErroneous ) { this.typeUtil = new TypeUtil( context.getElementUtils(), context.getTypeUtils() );
String sourceFileName = element.getQualifiedName() + IMPLEMENTATION_SUFFIX; this.conversions = new Conversions( elementUtils, typeUtils, typeUtil );
writeModelToSourceFile( sourceFileName, model ); this.executables = new Executables( typeUtil );
}
return null; return getMapper( mapperTypeElement, sourceElement );
} }
private void writeModelToSourceFile(String fileName, Mapper model) { public Mapper getMapper(TypeElement element, List<Method> methods) {
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<Method> methods = retrieveMethods( element, true );
//2.) build up aggregated "target" model
List<MappingMethod> mappings = getMappingMethods(
methods,
getEffectiveUnmappedTargetPolicy( element )
);
List<Type> usedMapperTypes = getUsedMapperTypes( element ); List<Type> usedMapperTypes = getUsedMapperTypes( element );
Mapper mapper = new Mapper( ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element );
return new Mapper(
elementUtils.getPackageOf( element ).getQualifiedName().toString(), elementUtils.getPackageOf( element ).getQualifiedName().toString(),
element.getSimpleName().toString(), element.getSimpleName().toString(),
element.getSimpleName() + IMPLEMENTATION_SUFFIX, element.getSimpleName() + IMPLEMENTATION_SUFFIX,
mappings, getMappingMethods( methods, unmappedTargetPolicy ),
usedMapperTypes, usedMapperTypes,
options options,
isErroneous
); );
return mapper;
} }
/** /**
@ -184,6 +132,15 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
} }
} }
private List<Type> getUsedMapperTypes(TypeElement element) {
List<Type> usedMapperTypes = new LinkedList<Type>();
MapperPrism mapperPrism = MapperPrism.getInstanceOn( element );
for ( TypeMirror usedMapper : mapperPrism.uses() ) {
usedMapperTypes.add( typeUtil.retrieveType( usedMapper ) );
}
return usedMapperTypes;
}
private List<MappingMethod> getMappingMethods(List<Method> methods, ReportingPolicy unmappedTargetPolicy) { private List<MappingMethod> getMappingMethods(List<Method> methods, ReportingPolicy unmappedTargetPolicy) {
List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>(); List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>();
@ -203,7 +160,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
mappingMethods.add( getIterableMappingMethod( methods, method ) ); mappingMethods.add( getIterableMappingMethod( methods, method ) );
} }
else { else {
mappingMethods.add( getSimpleMappingMethod( methods, method, unmappedTargetPolicy ) ); mappingMethods.add( getBeanMappingMethod( methods, method, unmappedTargetPolicy ) );
} }
} }
return mappingMethods; return mappingMethods;
@ -218,8 +175,8 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
return reversed; return reversed;
} }
private MappingMethod getSimpleMappingMethod(List<Method> methods, Method method, private MappingMethod getBeanMappingMethod(List<Method> methods, Method method,
ReportingPolicy unmappedTargetPolicy) { ReportingPolicy unmappedTargetPolicy) {
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>(); List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
Set<String> mappedTargetProperties = new HashSet<String>(); Set<String> mappedTargetProperties = new HashSet<String>();
@ -240,21 +197,21 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
elementUtils.getAllMembers( returnTypeElement ) elementUtils.getAllMembers( returnTypeElement )
); );
Set<String> sourceProperties = Executables.getPropertyNames( Set<String> sourceProperties = executables.getPropertyNames(
Filters.getterMethodsIn( sourceGetters ) Filters.getterMethodsIn( sourceGetters )
); );
Set<String> targetProperties = Executables.getPropertyNames( Set<String> targetProperties = executables.getPropertyNames(
Filters.setterMethodsIn( targetSetters ) Filters.setterMethodsIn( targetSetters )
); );
reportErrorIfMappedPropertiesDontExist( method, sourceProperties, targetProperties ); reportErrorIfMappedPropertiesDontExist( method, sourceProperties, targetProperties );
for ( ExecutableElement getterMethod : sourceGetters ) { for ( ExecutableElement getterMethod : sourceGetters ) {
String sourcePropertyName = Executables.getPropertyName( getterMethod ); String sourcePropertyName = executables.getPropertyName( getterMethod );
Mapping mapping = mappings.get( sourcePropertyName ); Mapping mapping = mappings.get( sourcePropertyName );
for ( ExecutableElement setterMethod : targetSetters ) { for ( ExecutableElement setterMethod : targetSetters ) {
String targetPropertyName = Executables.getPropertyName( setterMethod ); String targetPropertyName = executables.getPropertyName( setterMethod );
if ( targetPropertyName.equals( mapping != null ? mapping.getTargetName() : sourcePropertyName ) ) { if ( targetPropertyName.equals( mapping != null ? mapping.getTargetName() : sourcePropertyName ) ) {
PropertyMapping property = getPropertyMapping( PropertyMapping property = getPropertyMapping(
@ -278,7 +235,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
mappedTargetProperties mappedTargetProperties
); );
return new SimpleMappingMethod( return new BeanMappingMethod(
method.getName(), method.getName(),
method.getParameterName(), method.getParameterName(),
method.getSourceType(), method.getSourceType(),
@ -287,10 +244,65 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
); );
} }
private void reportErrorForUnmappedTargetPropertiesIfRequired(Method method,
ReportingPolicy unmappedTargetPolicy,
Set<String> targetProperties,
Set<String> mappedTargetProperties) {
if ( targetProperties.size() > mappedTargetProperties.size() &&
unmappedTargetPolicy.requiresReport() ) {
targetProperties.removeAll( mappedTargetProperties );
printMessage(
unmappedTargetPolicy,
MessageFormat.format(
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
targetProperties.size(),
Strings.join( targetProperties, ", " )
),
method.getExecutable()
);
}
}
private Method getReverseMappingMethod(List<Method> rawMethods, Method method) {
for ( Method oneMethod : rawMethods ) {
if ( oneMethod.reverses( method ) ) {
return oneMethod;
}
}
return null;
}
private void reportErrorIfMappedPropertiesDontExist(Method method, Set<String> sourceProperties,
Set<String> 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<Method> methods, Method method, ExecutableElement getterMethod, private PropertyMapping getPropertyMapping(List<Method> methods, Method method, ExecutableElement getterMethod,
ExecutableElement setterMethod) { ExecutableElement setterMethod) {
Type sourceType = retrieveReturnType( getterMethod ); Type sourceType = executables.retrieveReturnType( getterMethod );
Type targetType = retrieveParameter( setterMethod ).getType(); Type targetType = executables.retrieveParameter( setterMethod ).getType();
MappingMethodReference propertyMappingMethod = getMappingMethodReference( methods, sourceType, targetType ); MappingMethodReference propertyMappingMethod = getMappingMethodReference( methods, sourceType, targetType );
Conversion conversion = conversions.getConversion( Conversion conversion = conversions.getConversion(
@ -301,10 +313,10 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
PropertyMapping property = new PropertyMapping( PropertyMapping property = new PropertyMapping(
method.getParameterName(), method.getParameterName(),
Introspector.decapitalize( method.getTargetType().getName() ), Introspector.decapitalize( method.getTargetType().getName() ),
Executables.getPropertyName( getterMethod ), executables.getPropertyName( getterMethod ),
getterMethod.getSimpleName().toString(), getterMethod.getSimpleName().toString(),
sourceType, sourceType,
Executables.getPropertyName( setterMethod ), executables.getPropertyName( setterMethod ),
setterMethod.getSimpleName().toString(), setterMethod.getSimpleName().toString(),
targetType, targetType,
propertyMappingMethod, propertyMappingMethod,
@ -344,24 +356,35 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
); );
} }
private void reportErrorForUnmappedTargetPropertiesIfRequired(Method method, private String getIterableConversionString(Conversions conversions, Type sourceElementType, Type targetElementType,
ReportingPolicy unmappedTargetPolicy, boolean isToConversion) {
Set<String> targetProperties, Conversion conversion = conversions.getConversion( sourceElementType, targetElementType );
Set<String> mappedTargetProperties) {
if ( targetProperties.size() > mappedTargetProperties.size() && if ( conversion == null ) {
unmappedTargetPolicy.requiresReport() ) { return null;
targetProperties.removeAll( mappedTargetProperties );
printMessage(
unmappedTargetPolicy,
MessageFormat.format(
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
targetProperties.size(),
Strings.join( targetProperties, ", " )
),
method.getExecutable()
);
} }
return conversion.to(
Introspector.decapitalize( sourceElementType.getName() ),
targetElementType
);
}
private MappingMethodReference getMappingMethodReference(Iterable<Method> 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) { private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) {
@ -390,239 +413,16 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
} }
} }
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<Type> getUsedMapperTypes(TypeElement element) {
List<Type> usedMapperTypes = new LinkedList<Type>();
MapperPrism mapperPrism = MapperPrism.getInstanceOn( element );
for ( TypeMirror usedMapper : mapperPrism.uses() ) {
usedMapperTypes.add( typeUtil.retrieveType( usedMapper ) );
}
return usedMapperTypes;
}
private MappingMethodReference getMappingMethodReference(Iterable<Method> 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<Method> 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<Method> retrieveMethods(TypeElement element, boolean implementationRequired) {
List<Method> methods = new ArrayList<Method>();
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<String> sourceProperties,
Set<String> 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<String, Mapping> getMappings(ExecutableElement method) {
Map<String, Mapping> mappings = new HashMap<String, Mapping>();
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) { 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() ) { if ( reportingPolicy.failsBuild() ) {
mappingErroneous = true; isErroneous = true;
} }
} }
private void printMessage(ReportingPolicy reportingPolicy, String message, Element element, private void printMessage(ReportingPolicy reportingPolicy, String message, Element element,
AnnotationMirror annotationMirror, AnnotationValue annotationValue) { AnnotationMirror annotationMirror, AnnotationValue annotationValue) {
processingEnvironment.getMessager() messager
.printMessage( .printMessage(
reportingPolicy.getDiagnosticKind(), reportingPolicy.getDiagnosticKind(),
message, message,
@ -631,7 +431,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
annotationValue annotationValue
); );
if ( reportingPolicy.failsBuild() ) { if ( reportingPolicy.failsBuild() ) {
mappingErroneous = true; isErroneous = true;
} }
} }
} }

View File

@ -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<Mapper, Void> {
@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 );
}
}

View File

@ -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<TypeElement, List<Method>> {
private Types typeUtils;
private Messager messager;
private TypeUtil typeUtil;
private Executables executables;
@Override
public List<Method> 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<Method> retrieveMethods(TypeElement element, boolean implementationRequired) {
List<Method> methods = new ArrayList<Method>();
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<String, Mapping> getMappings(ExecutableElement method) {
Map<String, Mapping> mappings = new HashMap<String, Mapping>();
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 );
}
}

View File

@ -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 <P> The parameter type processed by this processor
* @param <R> The return type created by this processor
*
* @author Gunnar Morling
*/
public interface ModelElementProcessor<P, R> {
/**
* 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);
}

View File

@ -23,8 +23,12 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind; 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. * Provides functionality around {@link ExecutableElement}s.
* *
@ -32,14 +36,17 @@ import javax.lang.model.type.TypeKind;
*/ */
public class Executables { 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 ); return isNonBooleanGetterMethod( method ) || isBooleanGetterMethod( method );
} }
private static boolean isNonBooleanGetterMethod(ExecutableElement method) { private boolean isNonBooleanGetterMethod(ExecutableElement method) {
String name = method.getSimpleName().toString(); String name = method.getSimpleName().toString();
return method.getParameters().isEmpty() && return method.getParameters().isEmpty() &&
@ -48,7 +55,7 @@ public class Executables {
method.getReturnType().getKind() != TypeKind.VOID; method.getReturnType().getKind() != TypeKind.VOID;
} }
private static boolean isBooleanGetterMethod(ExecutableElement method) { private boolean isBooleanGetterMethod(ExecutableElement method) {
String name = method.getSimpleName().toString(); String name = method.getSimpleName().toString();
return method.getParameters().isEmpty() && return method.getParameters().isEmpty() &&
@ -57,7 +64,7 @@ public class Executables {
method.getReturnType().getKind() == TypeKind.BOOLEAN; method.getReturnType().getKind() == TypeKind.BOOLEAN;
} }
public static boolean isSetterMethod(ExecutableElement method) { public boolean isSetterMethod(ExecutableElement method) {
String name = method.getSimpleName().toString(); String name = method.getSimpleName().toString();
if ( name.startsWith( "set" ) && name.length() > 3 && method.getParameters() if ( name.startsWith( "set" ) && name.length() > 3 && method.getParameters()
@ -68,7 +75,7 @@ public class Executables {
return false; return false;
} }
public static String getPropertyName(ExecutableElement getterOrSetterMethod) { public String getPropertyName(ExecutableElement getterOrSetterMethod) {
if ( isNonBooleanGetterMethod( getterOrSetterMethod ) ) { if ( isNonBooleanGetterMethod( getterOrSetterMethod ) ) {
return Introspector.decapitalize( return Introspector.decapitalize(
getterOrSetterMethod.getSimpleName().toString().substring( 3 ) getterOrSetterMethod.getSimpleName().toString().substring( 3 )
@ -88,13 +95,33 @@ public class Executables {
throw new IllegalArgumentException( "Executable " + getterOrSetterMethod + " is not getter or setter method." ); throw new IllegalArgumentException( "Executable " + getterOrSetterMethod + " is not getter or setter method." );
} }
public static Set<String> getPropertyNames(List<ExecutableElement> propertyAccessors) { public Set<String> getPropertyNames(List<ExecutableElement> propertyAccessors) {
Set<String> propertyNames = new HashSet<String>(); Set<String> propertyNames = new HashSet<String>();
for ( ExecutableElement executableElement : propertyAccessors ) { for ( ExecutableElement executableElement : propertyAccessors ) {
propertyNames.add( Executables.getPropertyName( executableElement ) ); propertyNames.add( getPropertyName( executableElement ) );
} }
return propertyNames; 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() );
}
} }

View File

@ -32,6 +32,9 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
*/ */
public class Filters { public class Filters {
//TODO
private static Executables executables = new Executables( null );
private Filters() { private Filters() {
} }
@ -39,7 +42,7 @@ public class Filters {
List<ExecutableElement> getterMethods = new LinkedList<ExecutableElement>(); List<ExecutableElement> getterMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) { for ( ExecutableElement method : methodsIn( elements ) ) {
if ( Executables.isGetterMethod( method ) ) { if ( executables.isGetterMethod( method ) ) {
getterMethods.add( method ); getterMethods.add( method );
} }
} }
@ -51,7 +54,7 @@ public class Filters {
List<ExecutableElement> setterMethods = new LinkedList<ExecutableElement>(); List<ExecutableElement> setterMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) { for ( ExecutableElement method : methodsIn( elements ) ) {
if ( Executables.isSetterMethod( method ) ) { if ( executables.isSetterMethod( method ) ) {
setterMethods.add( method ); setterMethods.add( method );
} }
} }