#53 Avoiding conlicting variable names

This commit is contained in:
Gunnar Morling 2013-07-15 23:43:11 +02:00
parent f9343bdc3f
commit 1b0447d404
19 changed files with 412 additions and 34 deletions

View File

@ -18,9 +18,11 @@
*/
package org.mapstruct.ap.model;
import java.beans.Introspector;
import java.util.Set;
import org.mapstruct.ap.util.Collections;
import org.mapstruct.ap.util.Strings;
/**
* Mapper reference which is retrieved via Annotation-based dependency injection.
@ -30,8 +32,8 @@ import org.mapstruct.ap.util.Collections;
*/
public class AnnotationMapperReference extends AbstractModelElement implements MapperReference {
private Annotation annotation;
private Type type;
private final Annotation annotation;
private final Type type;
public AnnotationMapperReference(Annotation annotation, Type type) {
this.annotation = annotation;
@ -51,4 +53,8 @@ public class AnnotationMapperReference extends AbstractModelElement implements M
public Set<Type> getImportTypes() {
return Collections.asSet( annotation.getImportTypes(), type );
}
public String getVariableName() {
return Strings.getSaveVariableName( Introspector.decapitalize( type.getName() ) );
}
}

View File

@ -18,20 +18,21 @@
*/
package org.mapstruct.ap.model;
import java.beans.Introspector;
import java.util.Set;
import org.mapstruct.ap.util.Collections;
import org.mapstruct.ap.util.Strings;
/**
* Mapper reference which is retrieved via the {@code Mappers#getMapper()}
* method. Used by default if no other component model is specified via
* {@code Mapper#uses()}.
* Mapper reference which is retrieved via the {@code Mappers#getMapper()} method. Used by default if no other component
* model is specified via {@code Mapper#uses()}.
*
* @author Gunnar Morling
*/
public class DefaultMapperReference extends AbstractModelElement implements MapperReference {
private Type type;
private final Type type;
public DefaultMapperReference(Type type) {
this.type = type;
@ -46,4 +47,8 @@ public class DefaultMapperReference extends AbstractModelElement implements Mapp
public Set<Type> getImportTypes() {
return Collections.asSet( type );
}
public String getVariableName() {
return Strings.getSaveVariableName( Introspector.decapitalize( type.getName() ) );
}
}

View File

@ -18,8 +18,11 @@
*/
package org.mapstruct.ap.model;
import java.beans.Introspector;
import java.util.Set;
import org.mapstruct.ap.util.Strings;
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one iterable type to another. The collection
* elements are mapped either by a {@link TypeConversion} or another mapping method.
@ -56,4 +59,14 @@ public class IterableMappingMethod extends MappingMethod {
return types;
}
public String getLoopVariableName() {
return Strings.getSaveVariableName(
Introspector.decapitalize(
getSourceType().getTypeParameters()
.get( 0 )
.getName()
), getParameterName()
);
}
}

View File

@ -20,6 +20,8 @@ package org.mapstruct.ap.model;
import java.util.Set;
import org.mapstruct.ap.util.Strings;
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and
* values are mapped either by a {@link TypeConversion} or another mapping method if required.
@ -29,8 +31,8 @@ import java.util.Set;
public class MapMappingMethod extends MappingMethod {
private final MappingMethodReference keyMappingMethod;
private final TypeConversion keyConversion;
private final MappingMethodReference valueMappingMethod;
private final TypeConversion keyConversion;
private final TypeConversion valueConversion;
public MapMappingMethod(String name, String parameterName, Type sourceType, Type targetType,
@ -69,7 +71,24 @@ public class MapMappingMethod extends MappingMethod {
return types;
}
public String getReturnValueName() {
return "map";
public String getKeyVariableName() {
return Strings.getSaveVariableName(
"key",
getParameterName()
);
}
public String getValueVariableName() {
return Strings.getSaveVariableName(
"value",
getParameterName()
);
}
public String getEntryVariableName() {
return Strings.getSaveVariableName(
"entry",
getParameterName()
);
}
}

View File

@ -18,9 +18,12 @@
*/
package org.mapstruct.ap.model;
import java.beans.Introspector;
import java.util.HashSet;
import java.util.Set;
import org.mapstruct.ap.util.Strings;
/**
* A method implemented or referenced by a {@link Mapper} class.
*
@ -65,6 +68,13 @@ public abstract class MappingMethod extends AbstractModelElement {
return types;
}
public String getReturnValueName() {
return Strings.getSaveVariableName(
Introspector.decapitalize( getTargetType().getName() ),
getParameterName()
);
}
@Override
public String toString() {
return "MappingMethod {" +

View File

@ -18,9 +18,12 @@
*/
package org.mapstruct.ap.model;
import java.beans.Introspector;
import java.util.HashSet;
import java.util.Set;
import org.mapstruct.ap.util.Strings;
/**
* Represents a reference to {@link MappingMethod}.
*
@ -40,6 +43,10 @@ public class MappingMethodReference extends MappingMethod {
return declaringMapper;
}
public String getMapperVariableName() {
return Strings.getSaveVariableName( Introspector.decapitalize( declaringMapper.getName() ) );
}
public Set<Type> getReferencedTypes() {
Set<Type> types = new HashSet<Type>();
types.add( getSourceType() );

View File

@ -340,7 +340,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
PropertyMapping property = new PropertyMapping(
method.getParameterName(),
Introspector.decapitalize( method.getTargetType().getName() ),
Strings.getSaveVariableName(
Introspector.decapitalize( method.getTargetType().getName() ),
method.getParameterName()
),
executables.getPropertyName( getterMethod ),
getterMethod.getSimpleName().toString(),
sourceType,
@ -367,7 +370,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
sourceElementType,
targetElementType,
method.getIterableMapping() != null ? method.getIterableMapping().getDateFormat() : null,
Introspector.decapitalize( sourceElementType.getName() )
Strings.getSaveVariableName(
Introspector.decapitalize( sourceElementType.getName() ),
method.getParameterName()
)
);
return new IterableMappingMethod(

View File

@ -18,6 +18,12 @@
*/
package org.mapstruct.ap.util;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.mapstruct.ap.util.Collections.asSet;
/**
* Helper class for dealing with strings.
*
@ -25,6 +31,59 @@ package org.mapstruct.ap.util;
*/
public class Strings {
private static final Set<String> KEYWORDS = asSet(
"abstract",
"continue",
"for",
"new",
"switch",
"assert",
"default",
"goto",
"package",
"synchronized",
"boolean",
"do",
"if",
"private",
"this",
"break",
"double",
"implements",
"protected",
"throw",
"byte",
"else",
"import",
"public",
"throws",
"case",
"enum",
"instanceof",
"return",
"transient",
"catch",
"extends",
"int",
"short",
"try",
"char",
"final",
"interface",
"static",
"void",
"class",
"finally",
"long",
"strictfp",
"volatile",
"const",
"float",
"native",
"super",
"while"
);
private Strings() {
}
@ -53,4 +112,15 @@ public class Strings {
public static boolean isEmpty(String string) {
return string == null || string.isEmpty();
}
public static String getSaveVariableName(String name, String... existingVariableNames) {
Set<String> conflictingNames = new HashSet<String>( KEYWORDS );
conflictingNames.addAll( Arrays.asList( existingVariableNames ) );
while ( conflictingNames.contains( name ) ) {
name = name + "_";
}
return name;
}
}

View File

@ -19,4 +19,4 @@
-->
<#nt><@includeModel object=annotation/>
private ${mapperType.name} ${mapperType.name?uncap_first};
private ${mapperType.name} ${variableName};

View File

@ -24,11 +24,11 @@
return null;
}
${targetType.name} ${targetType.name?uncap_first} = new ${targetType.name}();
${targetType.name} ${returnValueName} = new ${targetType.name}();
<#list propertyMappings as propertyMapping>
<@includeModel object=propertyMapping/>
</#list>
return ${targetType.name?uncap_first};
return ${returnValueName};
}

View File

@ -18,4 +18,4 @@
limitations under the License.
-->
private final ${mapperType.name} ${mapperType.name?uncap_first} = new ${mapperType.name}();
private final ${mapperType.name} ${variableName} = new ${mapperType.name}();

View File

@ -25,17 +25,17 @@
}
<#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side -->
<#if targetType.name == "Iterable" && targetType.packageName == "java.lang">${targetType.iterableImplementationType.name}<#else>${targetType.name}</#if><<@includeModel object=targetType.typeParameters[0]/>> ${targetType.name?uncap_first} = new <#if targetType.iterableImplementationType??>${targetType.iterableImplementationType.name}<#else>${targetType.name}</#if><<@includeModel object=targetType.typeParameters[0]/>>();
<#if targetType.name == "Iterable" && targetType.packageName == "java.lang">${targetType.iterableImplementationType.name}<#else>${targetType.name}</#if><<@includeModel object=targetType.typeParameters[0]/>> ${returnValueName} = new <#if targetType.iterableImplementationType??>${targetType.iterableImplementationType.name}<#else>${targetType.name}</#if><<@includeModel object=targetType.typeParameters[0]/>>();
for ( <@includeModel object=sourceType.typeParameters[0]/> ${sourceType.typeParameters[0].name?uncap_first} : ${parameterName} ) {
for ( <@includeModel object=sourceType.typeParameters[0]/> ${loopVariableName} : ${parameterName} ) {
<#if elementMappingMethod??>
${targetType.name?uncap_first}.add( <@includeModel object=elementMappingMethod input="${sourceType.typeParameters[0].name?uncap_first}"/> );
${returnValueName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> );
<#else>
<#if (conversion.exceptionTypes?size == 0) >
${targetType.name?uncap_first}.add( <@includeModel object=conversion/> );
${returnValueName}.add( <@includeModel object=conversion/> );
<#else>
try {
${targetType.name?uncap_first}.add( <@includeModel object=conversion/> );
${returnValueName}.add( <@includeModel object=conversion/> );
}
<#list conversion.exceptionTypes as exceptionType>
catch( ${exceptionType.name} e ) {
@ -46,5 +46,5 @@
</#if>
}
return ${targetType.name?uncap_first};
return ${returnValueName};
}

View File

@ -26,18 +26,18 @@
<@includeModel object=targetType /> ${returnValueName} = new <#if targetType.mapImplementationType??><@includeModel object=targetType.mapImplementationType /><#else><@includeModel object=targetType /></#if>();
for ( Map.Entry<<#list sourceType.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> entry : ${parameterName}.entrySet() ) {
for ( Map.Entry<<#list sourceType.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> ${entryVariableName} : ${parameterName}.entrySet() ) {
<#-- key -->
<#if keyMappingMethod??>
<@includeModel object=targetType.typeParameters[0]/> key = <@includeModel object=keyMappingMethod input="entry.getKey()"/>;
<@includeModel object=targetType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyMappingMethod input="entry.getKey()"/>;
<#elseif keyConversion??>
<#if (keyConversion.exceptionTypes?size == 0) >
<@includeModel object=targetType.typeParameters[0]/> key = <@includeModel object=keyConversion/>;
<@includeModel object=targetType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyConversion/>;
<#else>
<@includeModel object=targetType.typeParameters[0]/> key;
<@includeModel object=targetType.typeParameters[0]/> ${keyVariableName};
try {
key = <@includeModel object=keyConversion/>;
${keyVariableName} = <@includeModel object=keyConversion/>;
}
<#list keyConversion.exceptionTypes as exceptionType>
catch( ${exceptionType.name} e ) {
@ -46,18 +46,18 @@
</#list>
</#if>
<#else>
<@includeModel object=targetType.typeParameters[0]/> key = entry.getKey();
<@includeModel object=targetType.typeParameters[0]/> ${keyVariableName} = entry.getKey();
</#if>
<#-- value -->
<#if valueMappingMethod??>
<@includeModel object=targetType.typeParameters[1]/> value = <@includeModel object=valueMappingMethod input="entry.getValue()"/>;
<@includeModel object=targetType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueMappingMethod input="entry.getValue()"/>;
<#elseif valueConversion??>
<#if (valueConversion.exceptionTypes?size == 0) >
<@includeModel object=targetType.typeParameters[1]/> value = <@includeModel object=valueConversion/>;
<@includeModel object=targetType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueConversion/>;
<#else>
<@includeModel object=targetType.typeParameters[1]/> value;
<@includeModel object=targetType.typeParameters[1]/> ${valueVariableName};
try {
value = <@includeModel object=valueConversion/>;
${valueVariableName} = <@includeModel object=valueConversion/>;
}
<#list valueConversion.exceptionTypes as exceptionType>
catch( ${exceptionType.name} e ) {
@ -66,10 +66,10 @@
</#list>
</#if>
<#else>
<@includeModel object=targetType.typeParameters[1]/> value = entry.getValue();
<@includeModel object=targetType.typeParameters[1]/> ${valueVariableName} = entry.getValue();
</#if>
${returnValueName}.put( key, value );
${returnValueName}.put( ${keyVariableName}, ${valueVariableName} );
}
return ${returnValueName};

View File

@ -18,4 +18,4 @@
limitations under the License.
-->
<#if declaringMapper??>${declaringMapper.name?uncap_first}.</#if>${name}( ${ext.input} )
<#if declaringMapper??>${mapperVariableName}.</#if>${name}( ${ext.input} )

View File

@ -0,0 +1,54 @@
/**
* 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.test.naming;
import java.util.List;
import java.util.Map;
public class Break {
private List<String> values;
private String someNumber;
private Map<String, String> map;
public List<String> getValues() {
return values;
}
public void setValues(List<String> values) {
this.values = values;
}
public String getSomeNumber() {
return someNumber;
}
public void setSomeNumber(String someNumber) {
this.someNumber = someNumber;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}

View File

@ -0,0 +1,54 @@
/**
* 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.test.naming;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class Source {
private List<Long> values;
private int someNumber;
private Map<Long, Date> map;
public List<Long> getValues() {
return values;
}
public void setValues(List<Long> values) {
this.values = values;
}
public int getSomeNumber() {
return someNumber;
}
public void setSomeNumber(int someNumber) {
this.someNumber = someNumber;
}
public Map<Long, Date> getMap() {
return map;
}
public void setMap(Map<Long, Date> map) {
this.map = map;
}
}

View File

@ -0,0 +1,40 @@
/**
* 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.test.naming;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.mapstruct.MapMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mappers;
@Mapper(uses = While.class)
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
Break sourceToBreak(Source source);
List<String> longIterableToStringList(List<Long> long1);
@MapMapping(valueDateFormat = "dd.MM.yyyy")
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> value);
}

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.test.naming;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.MapperTestBase;
import org.mapstruct.ap.testutil.WithClasses;
import org.testng.annotations.Test;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.MapAssert.entry;
/**
* Test for aming of variables/members which conflict with keywords or parameter names.
*
* @author Gunnar Morling
*/
@WithClasses({ SourceTargetMapper.class, While.class, Break.class, Source.class })
@IssueKey("53")
public class VariableNamingTest extends MapperTestBase {
@Test
public void shouldGenerateImplementationsOfMethodsWithProblematicVariableNmes() {
Source source = new Source();
source.setSomeNumber( 42 );
source.setValues( Arrays.<Long>asList( 42L, 121L ) );
Map<Long, Date> map = new HashMap<Long, Date>();
map.put( 42L, new GregorianCalendar( 1980, 0, 1 ).getTime() );
map.put( 121L, new GregorianCalendar( 2013, 6, 20 ).getTime() );
source.setMap( map );
Break target = SourceTargetMapper.INSTANCE.sourceToBreak( source );
assertThat( target ).isNotNull();
assertThat( target.getValues() ).isNotNull();
assertThat( target.getValues() ).containsOnly( "42", "121" );
assertThat( target.getSomeNumber() ).isEqualTo( "42" );
assertThat( target.getMap() ).hasSize( 2 );
assertThat( target.getMap() ).includes(
entry( "42", "01.01.1980" ),
entry( "121", "20.07.2013" )
);
}
}

View File

@ -0,0 +1,26 @@
/**
* 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.test.naming;
public class While {
public String asString(int i) {
return String.valueOf( i );
}
}