#32 Simplifying messaging

This commit is contained in:
Gunnar Morling 2013-07-01 23:39:55 +02:00
parent 17f106c6ea
commit 1972f36ec1
8 changed files with 140 additions and 105 deletions

View File

@ -129,10 +129,18 @@ public class MappingProcessor extends AbstractProcessor {
return ANNOTATIONS_CLAIMED_EXCLUSIVELY; return ANNOTATIONS_CLAIMED_EXCLUSIVELY;
} }
/**
* Applies all registered {@link ModelElementProcessor}s to the given mapper
* type.
*
* @param context The processor context.
* @param mapperTypeElement The mapper type element.
*/
private void processMapperTypeElement(ProcessorContext context, TypeElement mapperTypeElement) { private void processMapperTypeElement(ProcessorContext context, TypeElement mapperTypeElement) {
Object mapper = mapperTypeElement; Object model = null;
for ( ModelElementProcessor<?, ?> processor : getProcessors() ) { for ( ModelElementProcessor<?, ?> processor : getProcessors() ) {
mapper = process( context, processor, mapperTypeElement, mapper ); model = process( context, processor, mapperTypeElement, model );
} }
} }

View File

@ -34,11 +34,9 @@ public class Mapper extends AbstractModelElement {
private final List<MappingMethod> mappingMethods; private final List<MappingMethod> mappingMethods;
private final List<MapperReference> referencedMappers; private final List<MapperReference> referencedMappers;
private final Options options; private final Options options;
private final boolean isErroneous;
public Mapper(String packageName, String interfaceName, String implementationName, public Mapper(String packageName, String interfaceName, String implementationName,
List<MappingMethod> mappingMethods, List<MapperReference> referencedMappers, Options options, List<MappingMethod> mappingMethods, List<MapperReference> referencedMappers, Options options) {
boolean isErroneous) {
this.packageName = packageName; this.packageName = packageName;
this.interfaceName = interfaceName; this.interfaceName = interfaceName;
this.implementationName = implementationName; this.implementationName = implementationName;
@ -46,7 +44,6 @@ public class Mapper extends AbstractModelElement {
this.mappingMethods = mappingMethods; this.mappingMethods = mappingMethods;
this.referencedMappers = referencedMappers; this.referencedMappers = referencedMappers;
this.options = options; this.options = options;
this.isErroneous = isErroneous;
} }
@Override @Override
@ -132,10 +129,6 @@ public class Mapper extends AbstractModelElement {
return options; return options;
} }
public boolean isErroneous() {
return isErroneous;
}
public void addAnnotation(Annotation annotation) { public void addAnnotation(Annotation annotation) {
annotations.add( annotation ); annotations.add( annotation );
} }

View File

@ -21,8 +21,12 @@ package org.mapstruct.ap.processor;
import javax.annotation.processing.Filer; import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager; import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
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.Diagnostic.Kind;
import org.mapstruct.ap.model.Options; import org.mapstruct.ap.model.Options;
@ -33,11 +37,13 @@ import org.mapstruct.ap.model.Options;
*/ */
public class DefaultModelElementProcessorContext implements ModelElementProcessor.ProcessorContext { public class DefaultModelElementProcessorContext implements ModelElementProcessor.ProcessorContext {
private ProcessingEnvironment processingEnvironment; private final ProcessingEnvironment processingEnvironment;
private Options options; private final DelegatingMessager messager;
private final Options options;
public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options) { public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options) {
this.processingEnvironment = processingEnvironment; this.processingEnvironment = processingEnvironment;
this.messager = new DelegatingMessager( processingEnvironment.getMessager() );
this.options = options; this.options = options;
} }
@ -58,11 +64,58 @@ public class DefaultModelElementProcessorContext implements ModelElementProcesso
@Override @Override
public Messager getMessager() { public Messager getMessager() {
return processingEnvironment.getMessager(); return messager;
} }
@Override @Override
public Options getOptions() { public Options getOptions() {
return options; return options;
} }
@Override
public boolean isErroneous() {
return messager.isErroneous();
}
private static class DelegatingMessager implements Messager {
private final Messager delegate;
private boolean isErroneous = false;
public DelegatingMessager(Messager delegate) {
this.delegate = delegate;
}
public void printMessage(Kind kind, CharSequence msg) {
delegate.printMessage( kind, msg );
if ( kind == Kind.ERROR ) {
isErroneous = true;
}
}
public void printMessage(Kind kind, CharSequence msg, Element e) {
delegate.printMessage( kind, msg, e );
if ( kind == Kind.ERROR ) {
isErroneous = true;
}
}
public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a) {
delegate.printMessage( kind, msg, e, a );
if ( kind == Kind.ERROR ) {
isErroneous = true;
}
}
public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v) {
delegate.printMessage( kind, msg, e, a, v );
if ( kind == Kind.ERROR ) {
isErroneous = true;
}
}
public boolean isErroneous() {
return isErroneous;
}
}
} }

View File

@ -28,14 +28,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.processing.Messager; 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.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.MapperPrism; import org.mapstruct.ap.MapperPrism;
import org.mapstruct.ap.conversion.Conversion; import org.mapstruct.ap.conversion.Conversion;
@ -77,10 +75,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private Conversions conversions; private Conversions conversions;
private Executables executables; private Executables executables;
private boolean isErroneous = false;
@Override @Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceElement) { public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<Method> sourceModel) {
this.elementUtils = context.getElementUtils(); this.elementUtils = context.getElementUtils();
this.typeUtils = context.getTypeUtils(); this.typeUtils = context.getTypeUtils();
this.messager = context.getMessager(); this.messager = context.getMessager();
@ -90,7 +86,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
this.conversions = new Conversions( elementUtils, typeUtils, typeUtil ); this.conversions = new Conversions( elementUtils, typeUtils, typeUtil );
this.executables = new Executables( typeUtil ); this.executables = new Executables( typeUtil );
return getMapper( mapperTypeElement, sourceElement ); return getMapper( mapperTypeElement, sourceModel );
} }
@Override @Override
@ -98,7 +94,6 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return 1000; return 1000;
} }
private Mapper getMapper(TypeElement element, List<Method> methods) { private Mapper getMapper(TypeElement element, List<Method> methods) {
ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element ); ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element );
List<MappingMethod> mappingMethods = getMappingMethods( methods, unmappedTargetPolicy ); List<MappingMethod> mappingMethods = getMappingMethods( methods, unmappedTargetPolicy );
@ -110,8 +105,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
element.getSimpleName() + IMPLEMENTATION_SUFFIX, element.getSimpleName() + IMPLEMENTATION_SUFFIX,
mappingMethods, mappingMethods,
mapperReferences, mapperReferences,
options, options
isErroneous
); );
} }
@ -262,8 +256,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
if ( targetProperties.size() > mappedTargetProperties.size() && if ( targetProperties.size() > mappedTargetProperties.size() &&
unmappedTargetPolicy.requiresReport() ) { unmappedTargetPolicy.requiresReport() ) {
targetProperties.removeAll( mappedTargetProperties ); targetProperties.removeAll( mappedTargetProperties );
printMessage( messager.printMessage(
unmappedTargetPolicy, unmappedTargetPolicy.getDiagnosticKind(),
MessageFormat.format( MessageFormat.format(
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"", "Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
targetProperties.size(), targetProperties.size(),
@ -287,23 +281,29 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
Set<String> targetProperties) { Set<String> targetProperties) {
for ( Mapping mappedProperty : method.getMappings().values() ) { for ( Mapping mappedProperty : method.getMappings().values() ) {
if ( !sourceProperties.contains( mappedProperty.getSourceName() ) ) { if ( !sourceProperties.contains( mappedProperty.getSourceName() ) ) {
printMessage( messager.printMessage(
ReportingPolicy.ERROR, Kind.ERROR,
String.format( String.format(
"Unknown property \"%s\" in parameter type %s.", "Unknown property \"%s\" in parameter type %s.",
mappedProperty.getSourceName(), mappedProperty.getSourceName(),
method.getSourceType() method.getSourceType()
), method.getExecutable(), mappedProperty.getMirror(), mappedProperty.getSourceAnnotationValue() ),
method.getExecutable(),
mappedProperty.getMirror(),
mappedProperty.getSourceAnnotationValue()
); );
} }
if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) { if ( !targetProperties.contains( mappedProperty.getTargetName() ) ) {
printMessage( messager.printMessage(
ReportingPolicy.ERROR, Kind.ERROR,
String.format( String.format(
"Unknown property \"%s\" in return type %s.", "Unknown property \"%s\" in return type %s.",
mappedProperty.getTargetName(), mappedProperty.getTargetName(),
method.getTargetType() method.getTargetType()
), method.getExecutable(), mappedProperty.getMirror(), mappedProperty.getTargetAnnotationValue() ),
method.getExecutable(),
mappedProperty.getMirror(),
mappedProperty.getTargetAnnotationValue()
); );
} }
} }
@ -397,20 +397,25 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return null; return null;
} }
/**
* Reports an error if source and target type of the property are different
* and neither a mapping method nor a conversion exists nor the property is
* of a collection type with default implementation
*
* @param method The mapping method owning the property mapping.
* @param property The property mapping to check.
*/
private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) { private void reportErrorIfPropertyCanNotBeMapped(Method method, PropertyMapping property) {
if ( property.getSourceType().equals( property.getTargetType() ) ) { if ( property.getSourceType().equals( property.getTargetType() ) ||
property.getMappingMethod() != null ||
property.getConversion() != null ||
( property.getTargetType().isCollectionType() &&
property.getTargetType().getCollectionImplementationType() != null ) ) {
return; return;
} }
//no mapping method nor conversion nor collection with default implementation messager.printMessage(
if ( !( Kind.ERROR,
property.getMappingMethod() != null ||
property.getConversion() != null ||
( property.getTargetType().isCollectionType() && property.getTargetType()
.getCollectionImplementationType() != null ) ) ) {
printMessage(
ReportingPolicy.ERROR,
String.format( String.format(
"Can't map property \"%s %s\" to \"%s %s\".", "Can't map property \"%s %s\" to \"%s %s\".",
property.getSourceType(), property.getSourceType(),
@ -422,26 +427,3 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
); );
} }
} }
private void printMessage(ReportingPolicy reportingPolicy, String message, Element element) {
messager.printMessage( reportingPolicy.getDiagnosticKind(), message, element );
if ( reportingPolicy.failsBuild() ) {
isErroneous = true;
}
}
private void printMessage(ReportingPolicy reportingPolicy, String message, Element element,
AnnotationMirror annotationMirror, AnnotationValue annotationValue) {
messager
.printMessage(
reportingPolicy.getDiagnosticKind(),
message,
element,
annotationMirror,
annotationValue
);
if ( reportingPolicy.failsBuild() ) {
isErroneous = true;
}
}
}

View File

@ -28,17 +28,18 @@ import org.mapstruct.ap.writer.ModelWriter;
/** /**
* A {@link ModelElementProcessor} which creates a Java source file representing * A {@link ModelElementProcessor} which creates a Java source file representing
* the given {@link Mapper} object. * the given {@link Mapper} object, unless the given mapper type is erroneous.
* *
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class MapperRenderingProcessor implements ModelElementProcessor<Mapper, Void> { public class MapperRenderingProcessor implements ModelElementProcessor<Mapper, Void> {
@Override @Override
public Void process(ProcessorContext context, TypeElement mapperTypeElement, Mapper sourceElement) { public Void process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) {
if ( !sourceElement.isErroneous() ) { if ( !context.isErroneous() ) {
writeToSourceFile( context.getFiler(), sourceElement ); writeToSourceFile( context.getFiler(), mapper );
} }
return null; return null;
} }

View File

@ -23,17 +23,16 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.processing.Messager; import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.MapperPrism; import org.mapstruct.ap.MapperPrism;
import org.mapstruct.ap.MappingPrism; import org.mapstruct.ap.MappingPrism;
import org.mapstruct.ap.MappingsPrism; import org.mapstruct.ap.MappingsPrism;
import org.mapstruct.ap.model.ReportingPolicy;
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;
@ -51,17 +50,17 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
* *
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class MethodRetrievalProcessor implements ModelElementProcessor<TypeElement, List<Method>> { public class MethodRetrievalProcessor implements ModelElementProcessor<Void, List<Method>> {
private Types typeUtils;
private Messager messager; private Messager messager;
private Types typeUtils;
private TypeUtil typeUtil; private TypeUtil typeUtil;
private Executables executables; private Executables executables;
@Override @Override
public List<Method> process(ProcessorContext context, TypeElement mapperTypeElement, TypeElement sourceElement) { public List<Method> process(ProcessorContext context, TypeElement mapperTypeElement, Void sourceModel) {
this.typeUtils = context.getTypeUtils();
this.messager = context.getMessager(); this.messager = context.getMessager();
this.typeUtils = context.getTypeUtils();
this.typeUtil = new TypeUtil( context.getElementUtils(), typeUtils ); this.typeUtil = new TypeUtil( context.getElementUtils(), typeUtils );
this.executables = new Executables( typeUtil ); this.executables = new Executables( typeUtil );
@ -148,8 +147,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<TypeEleme
private boolean checkParameterAndReturnType(ExecutableElement method, Type parameterType, Type returnType) { private boolean checkParameterAndReturnType(ExecutableElement method, Type parameterType, Type returnType) {
if ( parameterType.isIterableType() && !returnType.isIterableType() ) { if ( parameterType.isIterableType() && !returnType.isIterableType() ) {
printMessage( messager.printMessage(
ReportingPolicy.ERROR, Kind.ERROR,
"Can't generate mapping method from iterable type to non-iterable type.", "Can't generate mapping method from iterable type to non-iterable type.",
method method
); );
@ -157,8 +156,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<TypeEleme
} }
if ( !parameterType.isIterableType() && returnType.isIterableType() ) { if ( !parameterType.isIterableType() && returnType.isIterableType() ) {
printMessage( messager.printMessage(
ReportingPolicy.ERROR, Kind.ERROR,
"Can't generate mapping method from non-iterable type to iterable type.", "Can't generate mapping method from non-iterable type to iterable type.",
method method
); );
@ -166,20 +165,12 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<TypeEleme
} }
if ( parameterType.isPrimitive() ) { if ( parameterType.isPrimitive() ) {
printMessage( messager.printMessage( Kind.ERROR, "Can't generate mapping method with primitive parameter type.", method );
ReportingPolicy.ERROR,
"Can't generate mapping method with primitive parameter type.",
method
);
return false; return false;
} }
if ( returnType.isPrimitive() ) { if ( returnType.isPrimitive() ) {
printMessage( messager.printMessage( Kind.ERROR, "Can't generate mapping method with primitive return type.", method );
ReportingPolicy.ERROR,
"Can't generate mapping method with primitive return type.",
method
);
return false; return false;
} }
@ -210,8 +201,4 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<TypeEleme
return mappings; return mappings;
} }
private void printMessage(ReportingPolicy reportingPolicy, String message, Element element) {
messager.printMessage( reportingPolicy.getDiagnosticKind(), message, element );
}
} }

View File

@ -23,6 +23,7 @@ import javax.annotation.processing.Messager;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
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.Diagnostic.Kind;
import org.mapstruct.ap.model.Options; import org.mapstruct.ap.model.Options;
@ -57,6 +58,16 @@ public interface ModelElementProcessor<P, R> {
Messager getMessager(); Messager getMessager();
Options getOptions(); Options getOptions();
/**
* Whether the currently processed mapper type is erroneous which is the
* case if at least one diagnostic with {@link Kind#ERROR} is reported
* by any of the participating processors.
*
* @return {@code true} if the currently processed mapper type is
* erroneous, {@code false} otherwise.
*/
boolean isErroneous();
} }
/** /**
@ -66,7 +77,7 @@ public interface ModelElementProcessor<P, R> {
* @param context Context providing common infrastructure objects. * @param context Context providing common infrastructure objects.
* @param mapperTypeElement The original type element from which the given mapper object * @param mapperTypeElement The original type element from which the given mapper object
* is derived. * is derived.
* @param sourceElement The current representation of the bean mapper. Never * @param sourceModel The current representation of the bean mapper. Never
* {@code null} (the very first processor receives the original * {@code null} (the very first processor receives the original
* type element). * type element).
* *
@ -76,7 +87,7 @@ public interface ModelElementProcessor<P, R> {
* return {@code null} except for the very last processor which * return {@code null} except for the very last processor which
* generates the resulting Java source file. * generates the resulting Java source file.
*/ */
R process(ProcessorContext context, TypeElement mapperTypeElement, P sourceElement); R process(ProcessorContext context, TypeElement mapperTypeElement, P sourceModel);
/** /**
* Returns the priority value of this processor which must be between 1 * Returns the priority value of this processor which must be between 1

View File

@ -150,7 +150,7 @@ public abstract class MapperTestBase {
while ( actualIterator.hasNext() ) { while ( actualIterator.hasNext() ) {
assertThat( expectedIterator.hasNext() ).describedAs( assertThat( expectedIterator.hasNext() ).describedAs(
String.format( String.format(
"Found less diagnostics than expected. Actual: %s; Expected: %s.", "Found more diagnostics than expected. Actual: %s; Expected: %s.",
actualDiagnostics, actualDiagnostics,
expectedDiagnostics expectedDiagnostics
) )
@ -174,7 +174,7 @@ public abstract class MapperTestBase {
assertThat( expectedIterator.hasNext() ).describedAs( assertThat( expectedIterator.hasNext() ).describedAs(
String.format( String.format(
"Found more diagnostics than expected. Actual: %s; Expected: %s.", "Found less diagnostics than expected. Actual: %s; Expected: %s.",
actualDiagnostics, actualDiagnostics,
expectedDiagnostics expectedDiagnostics
) )