#838 check for suitable constructors when reversing

This commit is contained in:
sjaakd 2016-07-20 21:55:04 +02:00
parent 70ba92b229
commit 3d43d022f4
5 changed files with 84 additions and 12 deletions

View File

@ -39,6 +39,7 @@ import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
@ -95,6 +96,7 @@ public class Type extends ModelElement implements Comparable<Type> {
private Type boundingBase = null;
private Boolean hasEmptyAccessibleContructor;
//CHECKSTYLE:OFF
public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory,
@ -770,4 +772,22 @@ public class Type extends ModelElement implements Comparable<Type> {
return boundingBase;
}
public boolean hasEmptyAccessibleContructor() {
if ( this.hasEmptyAccessibleContructor == null ) {
hasEmptyAccessibleContructor = false;
List<ExecutableElement> constructors = ElementFilter.constructorsIn( typeElement.getEnclosedElements() );
for ( ExecutableElement constructor : constructors ) {
if ( (constructor.getModifiers().contains( Modifier.PUBLIC )
|| constructor.getModifiers().contains( Modifier.PROTECTED ) )
&& constructor.getParameters().isEmpty() ) {
hasEmptyAccessibleContructor = true;
break;
}
}
}
return hasEmptyAccessibleContructor;
}
}

View File

@ -73,6 +73,12 @@ public class SourceMethod implements Method {
private List<SourceMethod> applicablePrototypeMethods;
private Boolean isBeanMapping;
private Boolean isEnumMapping;
private Boolean isValueMapping;
private Boolean isIterableMapping;
private Boolean isMapMapping;
public static class Builder {
private Type declaringMapper = null;
@ -367,18 +373,40 @@ public class SourceMethod implements Method {
}
public boolean isIterableMapping() {
return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isIterableType()
&& getResultType().isIterableType();
if ( isIterableMapping == null ) {
isIterableMapping = getSourceParameters().size() == 1
&& first( getSourceParameters() ).getType().isIterableType()
&& getResultType().isIterableType();
}
return isIterableMapping;
}
public boolean isMapMapping() {
return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isMapType()
&& getResultType().isMapType();
if ( isMapMapping == null ) {
isMapMapping = getSourceParameters().size() == 1
&& first( getSourceParameters() ).getType().isMapType()
&& getResultType().isMapType();
}
return isMapMapping;
}
public boolean isEnumMapping() {
return getSourceParameters().size() == 1 && first( getSourceParameters() ).getType().isEnumType()
&& getResultType().isEnumType();
if ( isEnumMapping == null ) {
isEnumMapping = getSourceParameters().size() == 1
&& first( getSourceParameters() ).getType().isEnumType()
&& getResultType().isEnumType();
}
return isEnumMapping;
}
public boolean isBeanMapping() {
if ( isBeanMapping == null ) {
isBeanMapping = !isIterableMapping()
&& !isMapMapping()
&& !isEnumMapping()
&& !isValueMapping();
}
return isBeanMapping;
}
/**
@ -388,7 +416,11 @@ public class SourceMethod implements Method {
* @return whether (true) or not (false) to execute value mappings
*/
public boolean isValueMapping() {
return isEnumMapping() && mappingOptions.getMappings().isEmpty();
if ( isValueMapping == null ) {
isValueMapping = isEnumMapping() && mappingOptions.getMappings().isEmpty();
}
return isValueMapping;
}
private boolean equals(Object o1, Object o2) {

View File

@ -458,6 +458,14 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
if ( reversePrism != null ) {
// is there a suitable constructor
if ( method.isBeanMapping()
&& !method.getResultType().isCollectionOrMapType()
&& !method.getResultType().hasEmptyAccessibleContructor() ) {
reportErrorWhenNoSuitableConstrutor( method, reversePrism );
return null;
}
// method is configured as being reverse method, collect candidates
List<SourceMethod> candidates = new ArrayList<SourceMethod>();
for ( SourceMethod oneMethod : rawMethods ) {
@ -621,6 +629,17 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
}
private void reportErrorWhenNoSuitableConstrutor( SourceMethod method,
InheritInverseConfigurationPrism reversePrism) {
messager.printMessage( method.getExecutable(),
reversePrism.mirror,
Message.INHERITINVERSECONFIGURATION_NO_SUITABLE_CONSTRUCTOR,
reversePrism.name()
);
}
private void reportErrorWhenSeveralNamesMatch(List<SourceMethod> candidates, SourceMethod method,
InheritInverseConfigurationPrism reversePrism) {

View File

@ -48,7 +48,7 @@ public enum Message {
PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_VALUE_BOTH_DEFINED( "Expression and default value are both defined in @Mapping, either define a defaultValue or an expression." ),
PROPERTYMAPPING_CONSTANT_AND_DEFAULT_VALUE_BOTH_DEFINED( "Constant and default value are both defined in @Mapping, either define a defaultValue or a constant." ),
PROPERTYMAPPING_INVALID_EXPRESSION( "Value must be given in the form \"java(<EXPRESSION>)\"." ),
PROPERTYMAPPING_REVERSAL_PROBLEM( "Parameter %s cannot be reversed." ),
PROPERTYMAPPING_REVERSAL_PROBLEM( "Parameter %s cannot be reversed.", Diagnostic.Kind.NOTE ),
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no parameter named \"%s\"." ),
PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ),
PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s)." ),
@ -102,6 +102,7 @@ public enum Message {
INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ),
INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ),
INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s." ),
INHERITINVERSECONFIGURATION_NO_SUITABLE_CONSTRUCTOR( "There is no suitable result type constructor for reversing this method." ),
INHERITINVERSECONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ),
INHERITCONFIGURATION_DUPLICATES( "Several matching methods exist: %s(). Specify a name explicitly." ),
INHERITCONFIGURATION_INVALIDNAME( "None of the candidates %s() matches given name: \"%s\"." ),

View File

@ -171,17 +171,17 @@ public class NestedSourcePropertiesTest {
}
@Test
@IssueKey( "337" )
@IssueKey( "838" )
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic( type = ArtistToChartEntryErroneous.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 47,
messageRegExp = "Parameter java.lang.Integer position cannot be reversed" )
line = 46,
messageRegExp = "There is no suitable result type constructor for reversing this method\\." )
}
)
@WithClasses({ ArtistToChartEntryErroneous.class })
public void reverseShouldIgnoreParameter() {
public void reverseShouldRaiseErrorForEmptyContructor() {
}
}