#611 Allow nested declaration of Mappers

* #611 Allow nested declaration of Mappers

Up until now, if a Mapper was declared as a nested interface, say
EnclosingClass.NestedMapper, the implementation of the mapper was
generated as NestedMapperImpl in the same package. The Mappers factory
class then tried to load EnclosingClass$NestedMapperImpl, which would
fail.
This commit is contained in:
Tillerino 2017-07-07 19:03:56 +02:00 committed by Filip Hrisafov
parent 3b9584ff13
commit 00385a1cdb
9 changed files with 294 additions and 2 deletions

View File

@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.mapstruct.test.model.Foo;
import org.mapstruct.test.model.SomeClass;
/**
* Unit test for {@link Mappers}.
@ -36,4 +37,14 @@ public class MappersTest {
Foo mapper = Mappers.getMapper( Foo.class );
assertThat( mapper ).isNotNull();
}
/**
* Checks if an implementation of a nested mapper can be found. This is a special case since
* it is named
*/
@Test
public void findsNestedMapperImpl() throws Exception {
assertThat( Mappers.getMapper( SomeClass.Foo.class ) ).isNotNull();
assertThat( Mappers.getMapper( SomeClass.NestedClass.Foo.class ) ).isNotNull();
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright 2012-2017 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.test.model;
/**
* For testing naming of implementations of nested mappers (issue 611).
*
* @author Tillmann Gaida
*/
/*
* Turn off checkstyle since the underscore introduced by issue 611 violates the class naming
* convention.
*/
// CHECKSTYLE:OFF
public class SomeClass$FooImpl implements SomeClass.Foo {
}

View File

@ -0,0 +1,33 @@
/**
* Copyright 2012-2017 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.test.model;
/**
* For testing naming of implementations of nested mappers (issue 611).
*
* @author Tillmann Gaida
*/
/*
* Turn off checkstyle since the underscore introduced by issue 611 violates the class naming
* convention.
*/
// CHECKSTYLE:OFF
public class SomeClass$NestedClass$FooImpl implements SomeClass.NestedClass.Foo {
}

View File

@ -0,0 +1,32 @@
/**
* Copyright 2012-2017 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.test.model;
/**
* The sole purpose of this class is to have a place to nest the Foo interface.
*
* @author Tillmann Gaida
*/
public class SomeClass {
public interface Foo { }
public static class NestedClass {
public interface Foo { }
}
}

View File

@ -111,7 +111,7 @@ public class Decorator extends GeneratedType {
public Decorator build() {
String implementationName = implName.replace( Mapper.CLASS_NAME_PLACEHOLDER,
mapperElement.getSimpleName() );
Mapper.getFlatName( mapperElement ) );
Type decoratorType = typeFactory.getType( decoratorPrism.value() );
DecoratorConstructor decoratorConstructor = new DecoratorConstructor(

View File

@ -21,6 +21,7 @@ package org.mapstruct.ap.internal.model;
import java.util.List;
import java.util.SortedSet;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
@ -153,7 +154,7 @@ public class Mapper extends GeneratedType {
}
public Mapper build() {
String implementationName = implName.replace( CLASS_NAME_PLACEHOLDER, element.getSimpleName() ) +
String implementationName = implName.replace( CLASS_NAME_PLACEHOLDER, getFlatName( element ) ) +
( decorator == null ? "" : "_" );
String elementPackage = elementUtils.getPackageOf( element ).getQualifiedName().toString();
@ -199,4 +200,20 @@ public class Mapper extends GeneratedType {
protected String getTemplateName() {
return getTemplateNameForClass( GeneratedType.class );
}
/**
* Returns the same as {@link Class#getName()} but without the package declaration.
*/
public static String getFlatName(TypeElement element) {
if (!(element.getEnclosingElement() instanceof TypeElement)) {
return element.getSimpleName().toString();
}
StringBuilder nameBuilder = new StringBuilder( element.getSimpleName().toString() );
for (Element enclosing = element.getEnclosingElement(); enclosing instanceof TypeElement; enclosing =
enclosing.getEnclosingElement()) {
nameBuilder.insert( 0, '$' );
nameBuilder.insert( 0, enclosing.getSimpleName().toString() );
}
return nameBuilder.toString();
}
}

View File

@ -0,0 +1,71 @@
/**
* Copyright 2012-2017 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.bugs._611;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Tillmann Gaida
*/
@IssueKey("611")
@RunWith(AnnotationProcessorTestRunner.class)
@WithClasses({
SomeClass.class,
SomeOtherClass.class
})
public class Issue611Test {
/**
* Checks if an implementation of a nested mapper can be loaded at all.
*/
@Test
public void mapperIsFound() {
assertThat( SomeClass.InnerMapper.INSTANCE ).isNotNull();
}
/**
* Checks if an implementation of a nested mapper can be loaded which is nested into an already
* nested class.
*/
@Test
public void mapperNestedInsideNestedClassIsFound() {
assertThat( SomeClass.SomeInnerClass.InnerMapper.INSTANCE ).isNotNull();
}
/**
* Checks if it is possible to load two mapper implementations which have equal simple names
* in the same package.
*/
@Test
public void rightMapperIsFound() {
SomeClass.InnerMapper.Source source1 = new SomeClass.InnerMapper.Source();
SomeOtherClass.InnerMapper.Source source2 = new SomeOtherClass.InnerMapper.Source();
SomeClass.InnerMapper.Target target1 = SomeClass.InnerMapper.INSTANCE.toTarget( source1 );
SomeOtherClass.InnerMapper.Target target2 = SomeOtherClass.InnerMapper.INSTANCE.toTarget( source2 );
assertThat( target1 ).isExactlyInstanceOf( SomeClass.InnerMapper.Target.class );
assertThat( target2 ).isExactlyInstanceOf( SomeOtherClass.InnerMapper.Target.class );
}
}

View File

@ -0,0 +1,55 @@
/**
* Copyright 2012-2017 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.bugs._611;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Tillmann Gaida
*/
public class SomeClass {
@Mapper
public interface InnerMapper {
InnerMapper INSTANCE = Mappers.getMapper( InnerMapper.class );
Target toTarget(Source in);
class Source {
}
class Target {
}
}
public static class SomeInnerClass {
@Mapper
public interface InnerMapper {
InnerMapper INSTANCE = Mappers.getMapper( InnerMapper.class );
Target toTarget(Source in);
class Source {
}
class Target {
}
}
}
}

View File

@ -0,0 +1,40 @@
/**
* Copyright 2012-2017 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.bugs._611;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Tillmann Gaida
*/
public class SomeOtherClass {
@Mapper
public interface InnerMapper {
InnerMapper INSTANCE = Mappers.getMapper( InnerMapper.class );
Target toTarget(Source in);
class Source {
}
class Target {
}
}
}