From 55b74ae384a323b8bc618a56cf7daa3d51943dee Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 17 Dec 2015 23:29:56 +0100 Subject: [PATCH] #710 Deferring processing of mappers with incomplete source/target types to a later processing round --- .../itest/tests/SuperTypeGenerationTest.java | 34 ++++++ .../superTypeGenerationTest/generator/pom.xml | 39 +++++++ .../BaseGenerationProcessor.java | 88 +++++++++++++++ .../itest/supertypegeneration/GenBase.java | 30 ++++++ .../javax.annotation.processing.Processor | 17 +++ .../resources/superTypeGenerationTest/pom.xml | 40 +++++++ .../superTypeGenerationTest/usage/pom.xml | 47 ++++++++ .../usage/AnotherOrderMapper.java | 35 ++++++ .../supertypegeneration/usage/Order.java | 38 +++++++ .../supertypegeneration/usage/OrderDto.java | 38 +++++++ .../usage/OrderMapper.java | 35 ++++++ .../usage/GeneratedBasesTest.java | 54 ++++++++++ .../org/mapstruct/ap/MappingProcessor.java | 102 +++++++++++++----- .../ap/internal/util/Executables.java | 10 +- .../util/TypeHierarchyErroneousException.java | 40 +++++++ 15 files changed, 619 insertions(+), 28 deletions(-) create mode 100644 integrationtest/src/test/java/org/mapstruct/itest/tests/SuperTypeGenerationTest.java create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/BaseGenerationProcessor.java create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/GenBase.java create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/pom.xml create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/AnotherOrderMapper.java create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/Order.java create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/OrderDto.java create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/OrderMapper.java create mode 100644 integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/TypeHierarchyErroneousException.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/SuperTypeGenerationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/SuperTypeGenerationTest.java new file mode 100644 index 000000000..86722b3a4 --- /dev/null +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/SuperTypeGenerationTest.java @@ -0,0 +1,34 @@ +/** + * 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.itest.tests; + +import org.junit.runner.RunWith; +import org.mapstruct.itest.testutil.runner.ProcessorSuite; +import org.mapstruct.itest.testutil.runner.ProcessorSuite.ProcessorType; +import org.mapstruct.itest.testutil.runner.ProcessorSuiteRunner; + +/** + * Tests usage of MapStruct with another processor that generates supertypes of mapping source/target types. + * + * @author Gunnar Morling + */ +@RunWith( ProcessorSuiteRunner.class ) +@ProcessorSuite(baseDir = "superTypeGenerationTest", processorTypes = ProcessorType.ORACLE_JAVA_8) +public class SuperTypeGenerationTest { +} diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml new file mode 100644 index 000000000..9e7281483 --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/generator/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + + org.mapstruct.itest + itest-supertypegeneration-aggregator + 1.0.0 + ../pom.xml + + + itest-supertypegeneration-generator + jar + + + + junit + junit + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.6 + 1.6 + + -proc:none + + + + + + diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/BaseGenerationProcessor.java b/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/BaseGenerationProcessor.java new file mode 100644 index 000000000..5e45ae274 --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/BaseGenerationProcessor.java @@ -0,0 +1,88 @@ +/** + * 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.itest.supertypegeneration; + +import java.io.IOException; +import java.io.Writer; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.Name; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.JavaFileObject; + +/** + * Generates base classes. + * + * @author Gunnar Morling + * + */ +@SupportedAnnotationTypes("org.mapstruct.itest.supertypegeneration.GenBase") +public class BaseGenerationProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (TypeElement annotation : annotations) { + Set annotated = roundEnv.getElementsAnnotatedWith( annotation ); + for (Element element : annotated) { + AnnotationMirror annotationMirror = element.getAnnotationMirrors().get(0); + Integer level = (Integer) annotationMirror.getElementValues().values().iterator().next().getValue(); + + if ( level >= 1 ) { + try { + PackageElement packageElement = processingEnv.getElementUtils().getPackageOf( element ); + Name name = element.getSimpleName(); + String baseName = name.toString() + "Base"; + String baseBaseName = level > 1 ? baseName + "Base" : null; + + JavaFileObject baseGen = processingEnv.getFiler().createSourceFile( packageElement.getQualifiedName() + "." + baseName ); + Writer writer = baseGen.openWriter(); + writer.append( "package " + packageElement.getQualifiedName() + ";\n" ); + writer.append( "\n" ); + writer.append( "import org.mapstruct.itest.supertypegeneration.GenBase;\n" ); + writer.append( "\n" ); + if ( level > 1 ) { + writer.append( "@GenBase( " + (level - 1) + " )\n" ); + writer.append( "public class " + baseName + " extends " + baseBaseName + " {\n" ); + } + else { + writer.append( "public class " + baseName + " {\n" ); + } + writer.append( " private String baseName" + level + ";\n" ); + writer.append( " public String getBaseName" + level + "() { return baseName" + level + "; }\n" ); + writer.append( " public void setBaseName" + level + "(String baseName" + level + ") { this.baseName" + level + " = baseName" + level + ";}\n" ); + writer.append( "}\n" ); + writer.flush(); + writer.close(); + } + catch (IOException e) { + throw new RuntimeException( e ); + } + } + } + } + + return false; + } +} diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/GenBase.java b/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/GenBase.java new file mode 100644 index 000000000..163de08b5 --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/java/org/mapstruct/itest/supertypegeneration/GenBase.java @@ -0,0 +1,30 @@ +/** + * 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.itest.supertypegeneration; + +/** + * Triggers generation of base-classes. + * + * @author Gunnar Morling + * + */ +public @interface GenBase { + + int value(); +} diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 000000000..4b7bdb8d4 --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/generator/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1,17 @@ +# 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. +org.mapstruct.itest.supertypegeneration.BaseGenerationProcessor \ No newline at end of file diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/pom.xml new file mode 100644 index 000000000..01fbb286f --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + + org.mapstruct + mapstruct-it-parent + 1.0.0 + ../pom.xml + + + org.mapstruct.itest + itest-supertypegeneration-aggregator + pom + + + generator + usage + + diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml new file mode 100644 index 000000000..3f2d54ec6 --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + + org.mapstruct.itest + itest-supertypegeneration-aggregator + 1.0.0 + ../pom.xml + + + itest-supertypegeneration-usage + jar + + + + junit + junit + 4.12 + test + + + org.mapstruct.itest + itest-supertypegeneration-generator + 1.0.0 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.6 + 1.6 + + -XprintProcessorInfo + -XprintRounds + + + + + + diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/AnotherOrderMapper.java b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/AnotherOrderMapper.java new file mode 100644 index 000000000..9df121b7b --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/AnotherOrderMapper.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.itest.supertypegeneration.usage; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * + * @author Gunnar Morling + * + */ +@Mapper +public abstract class AnotherOrderMapper { + + public static final AnotherOrderMapper INSTANCE = Mappers.getMapper( AnotherOrderMapper.class ); + + public abstract OrderDto orderToDto(Order order); +} diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/Order.java b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/Order.java new file mode 100644 index 000000000..dbca50641 --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/Order.java @@ -0,0 +1,38 @@ +/** + * 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.itest.supertypegeneration.usage; + +import org.mapstruct.itest.supertypegeneration.GenBase; + +/** + * @author Gunnar Morling + */ +@GenBase(2) +public class Order extends OrderBase { + + private String item; + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } +} diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/OrderDto.java b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/OrderDto.java new file mode 100644 index 000000000..7db0cae35 --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/OrderDto.java @@ -0,0 +1,38 @@ +/** + * 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.itest.supertypegeneration.usage; + +import org.mapstruct.itest.supertypegeneration.GenBase; + +/** + * @author Gunnar Morling + */ +@GenBase(2) +public class OrderDto extends OrderDtoBase { + + private String item; + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } +} diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/OrderMapper.java b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/OrderMapper.java new file mode 100644 index 000000000..b4efc5953 --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/main/java/org/mapstruct/itest/supertypegeneration/usage/OrderMapper.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.itest.supertypegeneration.usage; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * + * @author Gunnar Morling + * + */ +@Mapper +public abstract class OrderMapper { + + public static final OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); + + public abstract OrderDto orderToDto(Order order); +} diff --git a/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java new file mode 100644 index 000000000..16a7a0bdc --- /dev/null +++ b/integrationtest/src/test/resources/superTypeGenerationTest/usage/src/test/java/org/mapstruct/itest/supertypegeneration/usage/GeneratedBasesTest.java @@ -0,0 +1,54 @@ +/** + * 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.itest.supertypegeneration.usage; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.mapstruct.itest.supertypegeneration.usage.Order; +import org.mapstruct.itest.supertypegeneration.usage.OrderDto; +import org.mapstruct.itest.supertypegeneration.usage.OrderMapper; + +/** + * Integration test for using MapStruct with another annotation processor that generates super-types of mapping source + * and target types. + * + * @author Gunnar Morling + */ +public class GeneratedBasesTest { + + @Test + public void considersPropertiesOnGeneratedSourceAndTargetTypes() { + Order order = new Order(); + order.setItem( "my item" ); + order.setBaseName2( "my base name 2" ); + order.setBaseName1( "my base name 1" ); + + OrderDto dto = OrderMapper.INSTANCE.orderToDto( order ); + assertEquals( order.getItem(), dto.getItem() ); + assertEquals( order.getBaseName2(), dto.getBaseName2() ); + assertEquals( order.getBaseName1(), dto.getBaseName1() ); + + // Let's make sure several mappers can be generated + dto = AnotherOrderMapper.INSTANCE.orderToDto( order ); + assertEquals( order.getItem(), dto.getItem() ); + assertEquals( order.getBaseName2(), dto.getBaseName2() ); + assertEquals( order.getBaseName1(), dto.getBaseName1() ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 12cddbf9a..9212632be 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -23,6 +23,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; @@ -49,6 +50,7 @@ import org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext; import org.mapstruct.ap.internal.processor.ModelElementProcessor; import org.mapstruct.ap.internal.processor.ModelElementProcessor.ProcessorContext; import org.mapstruct.ap.internal.util.AnnotationProcessingException; +import org.mapstruct.ap.internal.util.TypeHierarchyErroneousException; /** * A JSR 269 annotation {@link Processor} which generates the implementations for mapper interfaces (interfaces @@ -105,6 +107,17 @@ public class MappingProcessor extends AbstractProcessor { private Options options; + /** + * Any mappers for which an implementation cannot be generated in the current round because they have source/target + * types with incomplete hierarchies (as super-types are to be generated by other processors). They will be + * processed in subsequent rounds. + *

+ * If the hierarchy of a mapper's source/target types is never completed (i.e. the missing super-types are not + * generated by other processors), this mapper will not be generated; That's fine, the compiler will raise an error + * due to the inconsistent Java types used as source or target anyways. + */ + private Set deferredMappers = new HashSet(); + @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init( processingEnv ); @@ -132,50 +145,85 @@ public class MappingProcessor extends AbstractProcessor { @Override public boolean process(final Set annotations, final RoundEnvironment roundEnvironment) { + // nothing to do in the last round + if ( !roundEnvironment.processingOver() ) { + // process any mappers left over from previous rounds + Set deferredMappers = getAndResetDeferredMappers(); + processMapperElements( deferredMappers ); + + // get and process any mappers from this round + Set mappers = getMappers( annotations, roundEnvironment ); + processMapperElements( mappers ); + } + + return ANNOTATIONS_CLAIMED_EXCLUSIVELY; + } + + /** + * Gets fresh copies of all mappers deferred from previous rounds (the originals may contain references to + * erroneous source/target type elements). + */ + private Set getAndResetDeferredMappers() { + Set deferred = new HashSet( deferredMappers.size() ); + + for (TypeElement element : deferredMappers ) { + deferred.add( processingEnv.getElementUtils().getTypeElement( element.getQualifiedName() ) ); + } + + deferredMappers.clear(); + return deferred; + } + + private Set getMappers(final Set annotations, + final RoundEnvironment roundEnvironment) { + Set mapperTypes = new HashSet(); for ( TypeElement annotation : annotations ) { - //Indicates that the annotation's type isn't on the class path of the compiled //project. Let the compiler deal with that and print an appropriate error. if ( annotation.getKind() != ElementKind.ANNOTATION_TYPE ) { continue; } - Set elementsWithAnnotation; try { - elementsWithAnnotation = roundEnvironment.getElementsAnnotatedWith( annotation ); + Set annotatedMappers = roundEnvironment.getElementsAnnotatedWith( annotation ); + for (Element mapperElement : annotatedMappers) { + TypeElement mapperTypeElement = asTypeElement( mapperElement ); + + // on some JDKs, RoundEnvironment.getElementsAnnotatedWith( ... ) returns types with + // annotations unknown to the compiler, even though they are not declared Mappers + if ( mapperTypeElement != null && MapperPrism.getInstanceOn( mapperTypeElement ) != null ) { + mapperTypes.add( mapperTypeElement ); + } + } } catch ( Throwable t ) { // whenever that may happen, but just to stay on the save side handleUncaughtError( annotation, t ); continue; } + } + return mapperTypes; + } - for ( Element mapperElement : elementsWithAnnotation ) { - try { - TypeElement mapperTypeElement = asTypeElement( mapperElement ); - - // on some JDKs, RoundEnvironment.getElementsAnnotatedWith( ... ) returns types with - // annotations unknown to the compiler, even though they are not declared Mappers - if ( mapperTypeElement == null || MapperPrism.getInstanceOn( mapperTypeElement ) == null ) { - continue; - } - - // create a new context for each generated mapper in order to have imports of referenced types - // correctly managed; - // note that this assumes that a new source file is created for each mapper which must not - // necessarily be the case, e.g. in case of several mapper interfaces declared as inner types - // of one outer interface - ProcessorContext context = new DefaultModelElementProcessorContext( processingEnv, options ); - processMapperTypeElement( context, mapperTypeElement ); - } - catch ( Throwable t ) { - handleUncaughtError( mapperElement, t ); - break; - } + private void processMapperElements(Set mapperElements) { + for ( TypeElement mapperElement : mapperElements ) { + try { + // create a new context for each generated mapper in order to have imports of referenced types + // correctly managed; + // note that this assumes that a new source file is created for each mapper which must not + // necessarily be the case, e.g. in case of several mapper interfaces declared as inner types + // of one outer interface + ProcessorContext context = new DefaultModelElementProcessorContext( processingEnv, options ); + processMapperTypeElement( context, mapperElement ); + } + catch ( TypeHierarchyErroneousException thie ) { + deferredMappers.add( mapperElement ); + } + catch ( Throwable t ) { + handleUncaughtError( mapperElement, t ); + break; } } - - return ANNOTATIONS_CLAIMED_EXCLUSIVELY; } private void handleUncaughtError(Element element, Throwable thrown) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java index 36619ffd9..696dc7f2a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java @@ -155,6 +155,10 @@ public class Executables { element = replaceTypeElementIfNecessary( elementUtils, element ); } + if ( element.asType().getKind() == TypeKind.ERROR ) { + throw new TypeHierarchyErroneousException( element ); + } + addNotYetOverridden( elementUtils, alreadyAdded, methodsIn( element.getEnclosedElements() ), parentType ); if ( hasNonObjectSuperclass( element ) ) { @@ -244,8 +248,12 @@ public class Executables { * @return {@code true}, iff the type has a super-class that is not java.lang.Object */ private static boolean hasNonObjectSuperclass(TypeElement element) { + if ( element.getSuperclass().getKind() == TypeKind.ERROR ) { + throw new TypeHierarchyErroneousException( element ); + } + return element.getSuperclass().getKind() == TypeKind.DECLARED - && asTypeElement( element.getSuperclass() ).getSuperclass().getKind() == TypeKind.DECLARED; + && !asTypeElement( element.getSuperclass() ).getQualifiedName().toString().equals( "java.lang.Object" ); } /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/TypeHierarchyErroneousException.java b/processor/src/main/java/org/mapstruct/ap/internal/util/TypeHierarchyErroneousException.java new file mode 100644 index 000000000..b30273253 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/TypeHierarchyErroneousException.java @@ -0,0 +1,40 @@ +/** + * 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.internal.util; + +import javax.lang.model.element.TypeElement; + +/** + * Indicates a type element was visited whose hierarchy was erroneous, because it has a non-existing super-type. + * + * @author Gunnar Morling + * + */ +public class TypeHierarchyErroneousException extends RuntimeException { + + private TypeElement element; + + public TypeHierarchyErroneousException(TypeElement element) { + this.element = element; + } + + public TypeElement getElement() { + return element; + } +}