From 2ebfd04fe93a52c9a0256c456888046cd3b13003 Mon Sep 17 00:00:00 2001 From: Ewald Volkert Date: Sun, 1 Feb 2015 17:25:59 +0100 Subject: [PATCH] #209, #412 Adding support for mapping static inner classes and enums by adding import declarations for static inner types also if declared in the same package --- .../org/mapstruct/ap/model/GeneratedType.java | 30 +++++- .../test/imports/InnerClassesImportsTest.java | 92 +++++++++++++++++++ .../test/imports/innerclasses/BeanFacade.java | 41 +++++++++ .../innerclasses/BeanWithInnerEnum.java | 45 +++++++++ .../innerclasses/BeanWithInnerEnumMapper.java | 32 +++++++ .../innerclasses/InnerClassMapper.java | 35 +++++++ .../innerclasses/SourceWithInnerClass.java | 52 +++++++++++ .../innerclasses/TargetWithInnerClass.java | 45 +++++++++ .../testutil/assertions/JavaFileAssert.java | 22 ++++- 9 files changed, 387 insertions(+), 7 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanFacade.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnum.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnumMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/InnerClassMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/SourceWithInnerClass.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/TargetWithInnerClass.java diff --git a/processor/src/main/java/org/mapstruct/ap/model/GeneratedType.java b/processor/src/main/java/org/mapstruct/ap/model/GeneratedType.java index d6d896bbd..1124b8403 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/GeneratedType.java +++ b/processor/src/main/java/org/mapstruct/ap/model/GeneratedType.java @@ -40,6 +40,8 @@ import org.mapstruct.ap.version.VersionInformation; */ public abstract class GeneratedType extends ModelElement { + private static final String JAVA_LANG_PACKAGE = "java.lang"; + private final String packageName; private final String name; private final String superClassName; @@ -170,11 +172,7 @@ public abstract class GeneratedType extends ModelElement { return; } - if ( typeToAdd.isImported() && - typeToAdd.getPackageName() != null && - !typeToAdd.getPackageName().equals( packageName ) && - !typeToAdd.getPackageName().startsWith( "java.lang" ) ) { - + if ( needsImportDeclaration( typeToAdd ) ) { if ( typeToAdd.isArrayType() ) { collection.add( typeToAdd.getComponentType() ); } @@ -187,4 +185,26 @@ public abstract class GeneratedType extends ModelElement { addWithDependents( collection, type ); } } + + private boolean needsImportDeclaration(Type typeToAdd) { + if ( !typeToAdd.isImported() ) { + return false; + } + + if ( typeToAdd.getPackageName() == null ) { + return false; + } + + if ( typeToAdd.getPackageName().startsWith( JAVA_LANG_PACKAGE ) ) { + return false; + } + + if ( typeToAdd.getPackageName().equals( packageName ) ) { + if ( !typeToAdd.getTypeElement().getNestingKind().isNested() ) { + return false; + } + } + + return true; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java b/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java new file mode 100644 index 000000000..fefaca82a --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/InnerClassesImportsTest.java @@ -0,0 +1,92 @@ +/** + * Copyright 2012-2015 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.imports; + +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.imports.innerclasses.BeanFacade; +import org.mapstruct.ap.test.imports.innerclasses.BeanWithInnerEnum; +import org.mapstruct.ap.test.imports.innerclasses.BeanWithInnerEnum.InnerEnum; +import org.mapstruct.ap.test.imports.innerclasses.BeanWithInnerEnumMapper; +import org.mapstruct.ap.test.imports.innerclasses.InnerClassMapper; +import org.mapstruct.ap.test.imports.innerclasses.SourceWithInnerClass; +import org.mapstruct.ap.test.imports.innerclasses.SourceWithInnerClass.SourceInnerClass; +import org.mapstruct.ap.test.imports.innerclasses.TargetWithInnerClass; +import org.mapstruct.ap.test.imports.innerclasses.TargetWithInnerClass.TargetInnerClass; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +/** + * Test for generating a mapper which references nested types (static inner classes). + * + * @author Ewald Volkert + */ +@WithClasses({ + SourceWithInnerClass.class, TargetWithInnerClass.class, InnerClassMapper.class, // + BeanFacade.class, BeanWithInnerEnum.class, BeanWithInnerEnumMapper.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class InnerClassesImportsTest { + + private final GeneratedSource generatedSource = new GeneratedSource(); + + @Rule + public GeneratedSource getGeneratedSource() { + return generatedSource; + } + + @Test + @IssueKey( "412" ) + public void mapperRequiresInnerClassImports() { + SourceWithInnerClass source = new SourceWithInnerClass(); + source.setInnerClassMember( new SourceInnerClass( 412 ) ); + + TargetWithInnerClass target = InnerClassMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getInnerClassMember().getValue() ).isEqualTo( 412 ); + generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( SourceInnerClass.class ); + generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( TargetInnerClass.class ); + } + + @Test + @IssueKey( "209" ) + public void mapperRequiresInnerEnumImports() { + BeanWithInnerEnum source = new BeanWithInnerEnum(); + source.setTest( "whatever" ); + source.setInnerEnum( InnerEnum.A ); + + BeanFacade target = BeanWithInnerEnumMapper.INSTANCE.toFacade( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getInnerEnum() ).isEqualTo( "A" ); + + BeanWithInnerEnum sourceAgain = BeanWithInnerEnumMapper.INSTANCE.fromFacade( target ); + + assertThat( sourceAgain ).isNotNull(); + assertThat( sourceAgain.getInnerEnum() ).isEqualTo( InnerEnum.A ); + + generatedSource.forMapper( BeanWithInnerEnumMapper.class ).containsImportFor( InnerEnum.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanFacade.java b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanFacade.java new file mode 100644 index 000000000..0d08e71ff --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanFacade.java @@ -0,0 +1,41 @@ +/** + * Copyright 2012-2015 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.imports.innerclasses; + +public class BeanFacade { + + private String test; + private String innerEnum; + + public String getTest() { + return test; + } + + public void setTest(String test) { + this.test = test; + } + + public String getInnerEnum() { + return innerEnum; + } + + public void setInnerEnum(String innerEnum) { + this.innerEnum = innerEnum; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnum.java b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnum.java new file mode 100644 index 000000000..40cc399bf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnum.java @@ -0,0 +1,45 @@ +/** + * Copyright 2012-2015 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.imports.innerclasses; + +public class BeanWithInnerEnum { + + private String test; + private InnerEnum innerEnum; + + public enum InnerEnum { + A, B; + } + + public String getTest() { + return test; + } + + public void setTest(String test) { + this.test = test; + } + + public InnerEnum getInnerEnum() { + return innerEnum; + } + + public void setInnerEnum(InnerEnum innerEnum) { + this.innerEnum = innerEnum; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnumMapper.java b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnumMapper.java new file mode 100644 index 000000000..5e2fdf7fa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/BeanWithInnerEnumMapper.java @@ -0,0 +1,32 @@ +/** + * Copyright 2012-2015 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.imports.innerclasses; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface BeanWithInnerEnumMapper { + + BeanWithInnerEnumMapper INSTANCE = Mappers.getMapper( BeanWithInnerEnumMapper.class ); + + BeanWithInnerEnum fromFacade(BeanFacade beanFacade); + + BeanFacade toFacade(BeanWithInnerEnum beanWithInnerEnum); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/InnerClassMapper.java b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/InnerClassMapper.java new file mode 100644 index 000000000..89a38ad7b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/InnerClassMapper.java @@ -0,0 +1,35 @@ +/** + * Copyright 2012-2015 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.imports.innerclasses; + +import org.mapstruct.Mapper; +import org.mapstruct.ap.test.imports.innerclasses.SourceWithInnerClass.SourceInnerClass; +import org.mapstruct.ap.test.imports.innerclasses.TargetWithInnerClass.TargetInnerClass; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface InnerClassMapper { + + InnerClassMapper INSTANCE = Mappers.getMapper( InnerClassMapper.class ); + + TargetWithInnerClass sourceToTarget(SourceWithInnerClass source); + + TargetInnerClass innerSourceToInnerTarget(SourceInnerClass source); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/SourceWithInnerClass.java b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/SourceWithInnerClass.java new file mode 100644 index 000000000..e8d60f33d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/SourceWithInnerClass.java @@ -0,0 +1,52 @@ +/** + * Copyright 2012-2015 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.imports.innerclasses; + +public class SourceWithInnerClass { + + private SourceInnerClass innerClassMember; + + public SourceInnerClass getInnerClassMember() { + return innerClassMember; + } + + public void setInnerClassMember(SourceInnerClass innerClassMember) { + this.innerClassMember = innerClassMember; + } + + public static class SourceInnerClass { + private int value; + + public SourceInnerClass() { + } + + public SourceInnerClass(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/TargetWithInnerClass.java b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/TargetWithInnerClass.java new file mode 100644 index 000000000..2ea5b6288 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/imports/innerclasses/TargetWithInnerClass.java @@ -0,0 +1,45 @@ +/** + * Copyright 2012-2015 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.imports.innerclasses; + +public class TargetWithInnerClass { + + private TargetInnerClass innerClassMember; + + public TargetInnerClass getInnerClassMember() { + return innerClassMember; + } + + public void setInnerClassMember(TargetInnerClass innerClassMember) { + this.innerClassMember = innerClassMember; + } + + public static class TargetInnerClass { + private int value; + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java b/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java index 6e8889240..2384b8f62 100644 --- a/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java +++ b/processor/src/test/java/org/mapstruct/ap/testutil/assertions/JavaFileAssert.java @@ -63,7 +63,8 @@ public class JavaFileAssert extends FileAssert { * @param importedClass the class expected to be imported in this Java file */ public void containsImportFor(Class importedClass) { - content().contains( "import " + importedClass.getName() + ";" ); + + content().contains( getClassImportDeclaration( importedClass ) ); } /** @@ -72,6 +73,23 @@ public class JavaFileAssert extends FileAssert { * @param importedClass the class expected not to be imported in this Java file */ public void containsNoImportFor(Class importedClass) { - content().doesNotContain( "import " + importedClass.getName() + ";" ); + content().doesNotContain( getClassImportDeclaration( importedClass ) ); + } + + /** + * Build a class import declaration string. + * + * @param importedClass + * @return + */ + private String getClassImportDeclaration(Class importedClass) { + String classname = importedClass.getName(); + if ( importedClass.isMemberClass() ) { + // Member-Class name: a.b.Outer$Inner + // Import declaration: import a.b.Outer.Inner + classname = classname.replace( '$', '.' ); + } + + return "import " + classname + ";"; } }