diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java index 0eaeaec8d..84083d49a 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; + import javax.annotation.processing.Messager; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -30,6 +31,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; @@ -62,12 +64,16 @@ public class MethodRetrievalProcessor implements ModelElementProcessor process(ProcessorContext context, TypeElement mapperTypeElement, Void sourceModel) { this.messager = context.getMessager(); this.typeFactory = context.getTypeFactory(); this.typeUtils = context.getTypeUtils(); + this.elementUtils = context.getElementUtils(); + this.javaLangObjectTypeMirror = typeFactory.getType( Object.class ).getTypeMirror(); return retrieveMethods( mapperTypeElement, mapperTypeElement ); } @@ -88,9 +94,11 @@ public class MethodRetrievalProcessor implements ModelElementProcessor methods = new ArrayList(); for ( ExecutableElement executable : methodsIn( allEnclosingElementsIncludeSuper( usedMapper ) ) ) { - SourceMethod method = getMethod( usedMapper, executable, mapperToImplement ); - if ( method != null ) { - methods.add( method ); + if ( isNotObjectEquals( executable ) && wasNotYetOverridden( methods, executable ) ) { + SourceMethod method = getMethod( usedMapper, executable, mapperToImplement ); + if ( method != null ) { + methods.add( method ); + } } } @@ -111,6 +119,38 @@ public class MethodRetrievalProcessor implements ModelElementProcessortrue, iff the executable does not represent {@link java.lang.Object#equals(Object)} or an + * overridden version of it + */ + private boolean isNotObjectEquals(ExecutableElement executable) { + if ( "equals".equals( executable.getSimpleName().toString() ) + && executable.getParameters().size() == 1 + && executable.getParameters().get( 0 ).asType().equals( javaLangObjectTypeMirror ) ) { + return false; + } + return true; + } + + /** + * @param methods the list of already collected methods of one type hierarchy (order is from sub-types to + * super-types) + * @param executable the method to check + * @return true, iff the given executable was not yet overridden by a method in the given list. + */ + private boolean wasNotYetOverridden(List methods, ExecutableElement executable) { + for ( SourceMethod alreadyAdded : methods ) { + ExecutableElement executableInSubtype = alreadyAdded.getExecutable(); + TypeElement declaringType = (TypeElement) executableInSubtype.getEnclosingElement(); + + if ( elementUtils.overrides( executableInSubtype, executable, declaringType ) ) { + return false; + } + } + return true; + } + private TypeElement asTypeElement(TypeMirror usedMapper) { return (TypeElement) ( (DeclaredType) usedMapper ).asElement(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractClassTest.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractClassTest.java index 904447a12..fad6406f8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractClassTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractClassTest.java @@ -18,21 +18,24 @@ */ package org.mapstruct.ap.test.abstractclass; -import static org.fest.assertions.Assertions.assertThat; - 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.fest.assertions.Assertions.assertThat; + /** * Test for the generation of implementation of abstract base classes. * * @author Gunnar Morling */ @WithClasses( { Source.class, Target.class, SourceTargetMapper.class, AbstractBaseMapper.class, - BaseMapperInterface.class } ) + BaseMapperInterface.class, + ReferencedMapper.class, + AbstractReferencedMapper.class, + ReferencedMapperInterface.class } ) @RunWith( AnnotationProcessorTestRunner.class ) public class AbstractClassTest { @@ -63,6 +66,8 @@ public class AbstractClassTest { private void assertResult(Target target) { assertThat( target ).isNotNull(); assertThat( target.getSize() ).isEqualTo( Long.valueOf( 181 ) ); - assertThat( target.getBirthday() ).isEqualTo( "26.04.1948" ); + assertThat( target.getBirthday() ).isEqualTo( "Birthday: 26.04.1948" ); + assertThat( target.getManuallyConverted() ).isEqualTo( 42 ); + assertThat( target.isNotAttractingEqualsMethod() ).isTrue(); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractReferencedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractReferencedMapper.java new file mode 100644 index 000000000..3385ad97b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/AbstractReferencedMapper.java @@ -0,0 +1,50 @@ +/** + * Copyright 2012-2014 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.abstractclass; + +import javax.xml.ws.Holder; + +/** + * @author Andreas Gudian + * + */ +public abstract class AbstractReferencedMapper implements ReferencedMapperInterface { + @Override + public int holderToInt(Holder holder) { + return 41; + } + + public boolean objectToBoolean(Object obj) { + if ( obj instanceof String ) { + return true; + } + + return false; + } + + @Override + public boolean equals(Object obj) { + return super.equals( obj ); + } + + @Override + public int hashCode() { + return super.hashCode(); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/ReferencedMapper.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/ReferencedMapper.java new file mode 100644 index 000000000..f791dbdd4 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/ReferencedMapper.java @@ -0,0 +1,32 @@ +/** + * Copyright 2012-2014 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.abstractclass; + +import javax.xml.ws.Holder; + +/** + * @author Andreas Gudian + * + */ +public class ReferencedMapper extends AbstractReferencedMapper { + @Override + public int holderToInt(Holder holder) { + return 42; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/ReferencedMapperInterface.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/ReferencedMapperInterface.java new file mode 100644 index 000000000..d46c9b5cb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/ReferencedMapperInterface.java @@ -0,0 +1,29 @@ +/** + * Copyright 2012-2014 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.abstractclass; + +import javax.xml.ws.Holder; + +/** + * @author Andreas Gudian + * + */ +public interface ReferencedMapperInterface { + int holderToInt(Holder holder); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/Source.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/Source.java index 41c674d57..58fb8ae0d 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/Source.java @@ -20,10 +20,14 @@ package org.mapstruct.ap.test.abstractclass; import java.util.Calendar; +import javax.xml.ws.Holder; + public class Source { private final int size; private final Calendar birthday; + private final String notAttractingEqualsMethod = "no way"; + private final Holder manuallyConverted = new Holder("What is the answer?"); public Source() { size = 181; @@ -38,4 +42,12 @@ public class Source { public Calendar getBirthday() { return birthday; } + + public String getNotAttractingEqualsMethod() { + return notAttractingEqualsMethod; + } + + public Holder getManuallyConverted() { + return manuallyConverted; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/SourceTargetMapper.java index 846154fa6..8b2cf87be 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/SourceTargetMapper.java @@ -25,7 +25,7 @@ import java.util.Calendar; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -@Mapper +@Mapper( uses = ReferencedMapper.class ) public abstract class SourceTargetMapper extends AbstractBaseMapper { public static final SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @@ -34,6 +34,6 @@ public abstract class SourceTargetMapper extends AbstractBaseMapper { protected String calendarToString(Calendar calendar) { DateFormat format = new SimpleDateFormat( "dd.MM.yyyy" ); - return format.format( calendar.getTime() ); + return "Birthday: " + format.format( calendar.getTime() ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/Target.java b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/Target.java index f95ac4009..6b687d753 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/abstractclass/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/abstractclass/Target.java @@ -22,6 +22,8 @@ public class Target { private Long size; private String birthday; + private boolean notAttractingEqualsMethod; + private int manuallyConverted; public Long getSize() { return size; @@ -38,4 +40,20 @@ public class Target { public void setBirthday(String birthday) { this.birthday = birthday; } + + public boolean isNotAttractingEqualsMethod() { + return notAttractingEqualsMethod; + } + + public void setNotAttractingEqualsMethod(boolean notAttractingEqualsMethod) { + this.notAttractingEqualsMethod = notAttractingEqualsMethod; + } + + public int getManuallyConverted() { + return manuallyConverted; + } + + public void setManuallyConverted(int manuallyConverted) { + this.manuallyConverted = manuallyConverted; + } }