From 00385a1cdb1fc548b3d1f079577be52adf6ce821 Mon Sep 17 00:00:00 2001 From: Tillerino Date: Fri, 7 Jul 2017 19:03:56 +0200 Subject: [PATCH] #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. --- .../org/mapstruct/factory/MappersTest.java | 11 +++ .../test/model/SomeClass$FooImpl.java | 33 +++++++++ .../model/SomeClass$NestedClass$FooImpl.java | 33 +++++++++ .../org/mapstruct/test/model/SomeClass.java | 32 +++++++++ .../ap/internal/model/Decorator.java | 2 +- .../mapstruct/ap/internal/model/Mapper.java | 19 ++++- .../ap/test/bugs/_611/Issue611Test.java | 71 +++++++++++++++++++ .../ap/test/bugs/_611/SomeClass.java | 55 ++++++++++++++ .../ap/test/bugs/_611/SomeOtherClass.java | 40 +++++++++++ 9 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 core-common/src/test/java/org/mapstruct/test/model/SomeClass$FooImpl.java create mode 100644 core-common/src/test/java/org/mapstruct/test/model/SomeClass$NestedClass$FooImpl.java create mode 100644 core-common/src/test/java/org/mapstruct/test/model/SomeClass.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeClass.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeOtherClass.java diff --git a/core-common/src/test/java/org/mapstruct/factory/MappersTest.java b/core-common/src/test/java/org/mapstruct/factory/MappersTest.java index 10ce0e8c6..990907fe0 100644 --- a/core-common/src/test/java/org/mapstruct/factory/MappersTest.java +++ b/core-common/src/test/java/org/mapstruct/factory/MappersTest.java @@ -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(); + } } diff --git a/core-common/src/test/java/org/mapstruct/test/model/SomeClass$FooImpl.java b/core-common/src/test/java/org/mapstruct/test/model/SomeClass$FooImpl.java new file mode 100644 index 000000000..0e2e0dd18 --- /dev/null +++ b/core-common/src/test/java/org/mapstruct/test/model/SomeClass$FooImpl.java @@ -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 { + +} diff --git a/core-common/src/test/java/org/mapstruct/test/model/SomeClass$NestedClass$FooImpl.java b/core-common/src/test/java/org/mapstruct/test/model/SomeClass$NestedClass$FooImpl.java new file mode 100644 index 000000000..27b1fec37 --- /dev/null +++ b/core-common/src/test/java/org/mapstruct/test/model/SomeClass$NestedClass$FooImpl.java @@ -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 { + +} diff --git a/core-common/src/test/java/org/mapstruct/test/model/SomeClass.java b/core-common/src/test/java/org/mapstruct/test/model/SomeClass.java new file mode 100644 index 000000000..f8fc1437e --- /dev/null +++ b/core-common/src/test/java/org/mapstruct/test/model/SomeClass.java @@ -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 { } + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java index ecc868d02..ae67c4ff2 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Decorator.java @@ -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( diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java index f89ef45a9..efe8e49d0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/Mapper.java @@ -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(); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java new file mode 100644 index 000000000..f8e96ec75 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/Issue611Test.java @@ -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 ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeClass.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeClass.java new file mode 100644 index 000000000..ea9bd23f9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeClass.java @@ -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 { + } + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeOtherClass.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeOtherClass.java new file mode 100644 index 000000000..aa0c56002 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_611/SomeOtherClass.java @@ -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 { + } + } +}