mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#644 Workaround for problems with the Eclipse implementation of asMemberOf. Refactored SpecificCompilerWorkarounds to be primarily used through a decorated variant of Types that is now used throughout MapStruct.
This commit is contained in:
parent
2291f2dedd
commit
8a3d5418df
@ -57,7 +57,12 @@
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>tycho-compiler-jdt</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@ -84,11 +89,7 @@
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>tycho-compiler-jdt</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- plexus-container-default is a runtime-dependency of the tyco-compiler -->
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
@ -238,6 +239,11 @@
|
||||
<artifactId>hickory</artifactId>
|
||||
<version>${com.jolira.hickory.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>tycho-compiler-jdt</artifactId>
|
||||
<version>${org.eclipse.tyco.compiler-jdt.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
@ -46,7 +46,6 @@ import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism;
|
||||
import org.mapstruct.ap.internal.util.Executables;
|
||||
import org.mapstruct.ap.internal.util.Filters;
|
||||
import org.mapstruct.ap.internal.util.Nouns;
|
||||
import org.mapstruct.ap.internal.util.SpecificCompilerWorkarounds;
|
||||
|
||||
/**
|
||||
* Represents (a reference to) the type of a bean property, parameter etc. Types are managed per generated source file.
|
||||
@ -312,8 +311,8 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return new Type(
|
||||
typeUtils,
|
||||
elementUtils,
|
||||
typeFactory,
|
||||
SpecificCompilerWorkarounds.erasure( typeUtils, typeMirror ),
|
||||
typeFactory,
|
||||
typeUtils.erasure( typeMirror ),
|
||||
typeElement,
|
||||
typeParameters,
|
||||
implementationType,
|
||||
@ -573,9 +572,8 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
|
||||
private boolean isSubType(TypeMirror candidate, Class<?> clazz) {
|
||||
String className = clazz.getCanonicalName();
|
||||
TypeMirror classType =
|
||||
SpecificCompilerWorkarounds.erasure( typeUtils, elementUtils.getTypeElement( className ).asType() );
|
||||
return SpecificCompilerWorkarounds.isSubType( typeUtils, candidate, classType );
|
||||
TypeMirror classType = typeUtils.erasure( elementUtils.getTypeElement( className ).asType() );
|
||||
return typeUtils.isSubtype( candidate, classType );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,18 +48,14 @@ import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.SimpleElementVisitor6;
|
||||
import javax.lang.model.util.SimpleTypeVisitor6;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
import org.mapstruct.ap.internal.prism.MappingTargetPrism;
|
||||
import org.mapstruct.ap.internal.prism.TargetTypePrism;
|
||||
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
|
||||
import org.mapstruct.ap.internal.util.Collections;
|
||||
import org.mapstruct.ap.internal.util.SpecificCompilerWorkarounds;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.SpecificCompilerWorkarounds.erasure;
|
||||
import static org.mapstruct.ap.internal.util.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary;
|
||||
import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary;
|
||||
|
||||
/**
|
||||
* Factory creating {@link Type} instances.
|
||||
@ -82,10 +78,10 @@ public class TypeFactory {
|
||||
this.elementUtils = elementUtils;
|
||||
this.typeUtils = typeUtils;
|
||||
|
||||
iterableType = erasure( typeUtils, elementUtils.getTypeElement( Iterable.class.getCanonicalName() ).asType() );
|
||||
iterableType = typeUtils.erasure( elementUtils.getTypeElement( Iterable.class.getCanonicalName() ).asType() );
|
||||
collectionType =
|
||||
erasure( typeUtils, elementUtils.getTypeElement( Collection.class.getCanonicalName() ).asType() );
|
||||
mapType = erasure( typeUtils, elementUtils.getTypeElement( Map.class.getCanonicalName() ).asType() );
|
||||
typeUtils.erasure( elementUtils.getTypeElement( Collection.class.getCanonicalName() ).asType() );
|
||||
mapType = typeUtils.erasure( elementUtils.getTypeElement( Map.class.getCanonicalName() ).asType() );
|
||||
|
||||
implementationTypes.put( Iterable.class.getName(), getType( ArrayList.class ) );
|
||||
implementationTypes.put( Collection.class.getName(), getType( ArrayList.class ) );
|
||||
@ -139,9 +135,9 @@ public class TypeFactory {
|
||||
|
||||
Type implementationType = getImplementationType( mirror );
|
||||
|
||||
boolean isIterableType = SpecificCompilerWorkarounds.isSubType( typeUtils, mirror, iterableType );
|
||||
boolean isCollectionType = SpecificCompilerWorkarounds.isSubType( typeUtils, mirror, collectionType );
|
||||
boolean isMapType = SpecificCompilerWorkarounds.isSubType( typeUtils, mirror, mapType );
|
||||
boolean isIterableType = typeUtils.isSubtype( mirror, iterableType );
|
||||
boolean isCollectionType = typeUtils.isSubtype( mirror, collectionType );
|
||||
boolean isMapType = typeUtils.isSubtype( mirror, mapType );
|
||||
|
||||
boolean isEnumType;
|
||||
boolean isInterface;
|
||||
@ -159,7 +155,7 @@ public class TypeFactory {
|
||||
isInterface = declaredType.asElement().getKind() == ElementKind.INTERFACE;
|
||||
name = declaredType.asElement().getSimpleName().toString();
|
||||
|
||||
typeElement = declaredType.asElement().accept( new TypeElementRetrievalVisitor(), null );
|
||||
typeElement = (TypeElement) declaredType.asElement();
|
||||
|
||||
if ( typeElement != null ) {
|
||||
packageName = elementUtils.getPackageOf( typeElement ).getQualifiedName().toString();
|
||||
@ -177,8 +173,7 @@ public class TypeFactory {
|
||||
|
||||
if ( componentTypeMirror.getKind() == TypeKind.DECLARED ) {
|
||||
DeclaredType declaredType = (DeclaredType) componentTypeMirror;
|
||||
TypeElement componentTypeElement =
|
||||
declaredType.asElement().accept( new TypeElementRetrievalVisitor(), null );
|
||||
TypeElement componentTypeElement = (TypeElement) declaredType.asElement();
|
||||
|
||||
name = componentTypeElement.getSimpleName().toString() + "[]";
|
||||
packageName = elementUtils.getPackageOf( componentTypeElement ).getQualifiedName().toString();
|
||||
@ -262,8 +257,7 @@ public class TypeFactory {
|
||||
public ExecutableType getMethodType(TypeElement includingType, ExecutableElement method) {
|
||||
DeclaredType asType = (DeclaredType) replaceTypeElementIfNecessary( elementUtils, includingType ).asType();
|
||||
TypeMirror asMemberOf = typeUtils.asMemberOf( asType, method );
|
||||
ExecutableType methodType = asMemberOf.accept( new ExecutableTypeRetrievalVisitor(), null );
|
||||
return methodType;
|
||||
return (ExecutableType) asMemberOf;
|
||||
}
|
||||
|
||||
public Parameter getSingleParameter(TypeElement includingType, ExecutableElement method) {
|
||||
@ -421,20 +415,6 @@ public class TypeFactory {
|
||||
return imported;
|
||||
}
|
||||
|
||||
private static class TypeElementRetrievalVisitor extends SimpleElementVisitor6<TypeElement, Void> {
|
||||
@Override
|
||||
public TypeElement visitType(TypeElement e, Void p) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExecutableTypeRetrievalVisitor extends SimpleTypeVisitor6<ExecutableType, Void> {
|
||||
@Override
|
||||
public ExecutableType visitExecutable(ExecutableType t, Void p) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts any collection type, e.g. {@code List<T>} to {@code Collection<T>} and any map type, e.g.
|
||||
* {@code HashMap<K,V>} to {@code Map<K,V>}.
|
||||
|
@ -39,7 +39,6 @@ import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.common.TypeFactory;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.hasNonNullElements;
|
||||
import static org.mapstruct.ap.internal.util.SpecificCompilerWorkarounds.isSubType;
|
||||
|
||||
/**
|
||||
* SourceMethodMatcher $8.4 of the JavaLanguage specification describes a method body as such:
|
||||
@ -232,7 +231,7 @@ public class MethodMatcher {
|
||||
private final Assignability assignability;
|
||||
private final Map<TypeVariable, TypeMirror> genericTypesMap;
|
||||
|
||||
public TypeMatcher(Assignability assignability, Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||
TypeMatcher(Assignability assignability, Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||
super( Boolean.FALSE ); // default value
|
||||
this.assignability = assignability;
|
||||
this.genericTypesMap = genericTypesMap;
|
||||
@ -302,8 +301,8 @@ public class MethodMatcher {
|
||||
// check if types are in bound
|
||||
TypeMirror lowerBound = t.getLowerBound();
|
||||
TypeMirror upperBound = t.getUpperBound();
|
||||
if ( ( isNullType( lowerBound ) || isSubType( typeUtils, lowerBound, p ) )
|
||||
&& ( isNullType( upperBound ) || isSubType( typeUtils, p, upperBound ) ) ) {
|
||||
if ( ( isNullType( lowerBound ) || typeUtils.isSubtype( lowerBound, p ) )
|
||||
&& ( isNullType( upperBound ) || typeUtils.isSubtype( p, upperBound ) ) ) {
|
||||
genericTypesMap.put( t, p );
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
@ -327,7 +326,7 @@ public class MethodMatcher {
|
||||
case DECLARED:
|
||||
// for example method: String method(? extends String)
|
||||
// isSubType checks range [subtype, type], e.g. isSubtype [Object, String]==true
|
||||
return isSubType( typeUtils, p, extendsBound );
|
||||
return typeUtils.isSubtype( p, extendsBound );
|
||||
|
||||
case TYPEVAR:
|
||||
// for example method: <T extends String & Serializable> T method(? extends T)
|
||||
@ -349,7 +348,7 @@ public class MethodMatcher {
|
||||
// for example method: String method(? super String)
|
||||
// to check super type, we can simply reverse the argument, but that would initially yield
|
||||
// a result: <type, superType] (so type not included) so we need to check sameType also.
|
||||
return isSubType( typeUtils, superBound, p ) || typeUtils.isSameType( p, superBound );
|
||||
return typeUtils.isSubtype( superBound, p ) || typeUtils.isSameType( p, superBound );
|
||||
|
||||
case TYPEVAR:
|
||||
|
||||
@ -367,7 +366,7 @@ public class MethodMatcher {
|
||||
// to check super type, we can simply reverse the argument, but that would initially yield
|
||||
// a result: <type, superType] (so type not included) so we need to check sameType also.
|
||||
TypeMirror superBoundAsDeclared = typeParameter.getBounds().get( 0 );
|
||||
return ( isSubType( typeUtils, superBoundAsDeclared, p ) || typeUtils.isSameType(
|
||||
return ( typeUtils.isSubtype( superBoundAsDeclared, p ) || typeUtils.isSameType(
|
||||
p,
|
||||
superBoundAsDeclared ) );
|
||||
default:
|
||||
@ -405,7 +404,7 @@ public class MethodMatcher {
|
||||
List<? extends TypeMirror> bounds = tpe.getBounds();
|
||||
if ( t != null && bounds != null ) {
|
||||
for ( TypeMirror bound : bounds ) {
|
||||
if ( !( bound.getKind() == TypeKind.DECLARED && isSubType( typeUtils, t, bound ) ) ) {
|
||||
if ( !( bound.getKind() == TypeKind.DECLARED && typeUtils.isSubtype( t, bound ) ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import org.mapstruct.ap.internal.option.Options;
|
||||
import org.mapstruct.ap.internal.processor.ModelElementProcessor.ProcessorContext;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.workarounds.TypesDecorator;
|
||||
import org.mapstruct.ap.internal.version.VersionInformation;
|
||||
|
||||
/**
|
||||
@ -47,13 +48,15 @@ public class DefaultModelElementProcessorContext implements ProcessorContext {
|
||||
private final Options options;
|
||||
private final TypeFactory typeFactory;
|
||||
private final VersionInformation versionInformation;
|
||||
private final Types delegatingTypes;
|
||||
|
||||
public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options) {
|
||||
this.processingEnvironment = processingEnvironment;
|
||||
this.messager = new DelegatingMessager( processingEnvironment.getMessager() );
|
||||
this.delegatingTypes = new TypesDecorator( processingEnvironment );
|
||||
this.typeFactory = new TypeFactory(
|
||||
processingEnvironment.getElementUtils(),
|
||||
processingEnvironment.getTypeUtils()
|
||||
delegatingTypes
|
||||
);
|
||||
this.options = options;
|
||||
this.versionInformation = DefaultVersionInformation.fromProcessingEnvironment( processingEnvironment );
|
||||
@ -66,7 +69,7 @@ public class DefaultModelElementProcessorContext implements ProcessorContext {
|
||||
|
||||
@Override
|
||||
public Types getTypeUtils() {
|
||||
return processingEnvironment.getTypeUtils();
|
||||
return delegatingTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,19 +102,19 @@ public class DefaultModelElementProcessorContext implements ProcessorContext {
|
||||
return messager.isErroneous();
|
||||
}
|
||||
|
||||
private static class DelegatingMessager implements FormattingMessager {
|
||||
private static final class DelegatingMessager implements FormattingMessager {
|
||||
|
||||
private final Messager delegate;
|
||||
private boolean isErroneous = false;
|
||||
|
||||
public DelegatingMessager(Messager delegate) {
|
||||
DelegatingMessager(Messager delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printMessage(Message msg, Object... args) {
|
||||
String message = String.format( msg.getDescription(), args );
|
||||
delegate.printMessage( msg.getDiagnosticKind(), message);
|
||||
delegate.printMessage( msg.getDiagnosticKind(), message );
|
||||
if ( msg.getDiagnosticKind() == Kind.ERROR ) {
|
||||
isErroneous = true;
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ import org.mapstruct.ap.internal.model.source.selector.MethodSelectors;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
|
||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.SpecificCompilerWorkarounds;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
|
||||
/**
|
||||
@ -559,7 +558,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
? typeFactory.getType( Object.class ).getTypeMirror()
|
||||
: targetType.getTypeParameters().get( 0 ).getTypeMirror();
|
||||
|
||||
return SpecificCompilerWorkarounds.isAssignable( typeUtils, sourceElementType, targetElementType );
|
||||
return typeUtils.isAssignable( sourceElementType, targetElementType );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ import org.mapstruct.ap.spi.AccessorNamingStrategy;
|
||||
import org.mapstruct.ap.spi.MethodType;
|
||||
|
||||
import static javax.lang.model.util.ElementFilter.methodsIn;
|
||||
import static org.mapstruct.ap.internal.util.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary;
|
||||
import static org.mapstruct.ap.internal.util.workarounds.SpecificCompilerWorkarounds.replaceTypeElementIfNecessary;
|
||||
|
||||
/**
|
||||
* Provides functionality around {@link ExecutableElement}s.
|
||||
|
@ -0,0 +1,196 @@
|
||||
/**
|
||||
* 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.workarounds;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
import org.eclipse.jdt.core.compiler.CharOperation;
|
||||
import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
|
||||
import org.eclipse.jdt.internal.compiler.apt.model.ElementImpl;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
|
||||
|
||||
/**
|
||||
* Contains the workaround for {@link Types#asMemberOf(DeclaredType, Element)} using Eclipse implementation types.
|
||||
* <p>
|
||||
* <strong>This class may only be loaded when running within Eclipse</strong>
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
final class EclipseAsMemberOfWorkaround {
|
||||
private EclipseAsMemberOfWorkaround() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Eclipse-specific implementation of {@link Types#asMemberOf(DeclaredType, Element)}.
|
||||
* <p>
|
||||
* Returns {@code null} if the implementation could not determine the result.
|
||||
*/
|
||||
static TypeMirror asMemberOf(ProcessingEnvironment environment, DeclaredType containing,
|
||||
Element element) {
|
||||
|
||||
ElementImpl elementImpl = tryCast( element, ElementImpl.class );
|
||||
BaseProcessingEnvImpl env = tryCast( environment, BaseProcessingEnvImpl.class );
|
||||
|
||||
if ( elementImpl == null || env == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ReferenceBinding referenceBinding =
|
||||
(ReferenceBinding) ( (ElementImpl) environment.getTypeUtils().asElement( containing ) )._binding;
|
||||
|
||||
MethodBinding methodBinding = (MethodBinding) elementImpl._binding;
|
||||
|
||||
// matches in super-classes have priority
|
||||
MethodBinding inSuperclassHiearchy = findInSuperclassHierarchy( methodBinding, referenceBinding );
|
||||
|
||||
if ( inSuperclassHiearchy != null ) {
|
||||
return env.getFactory().newTypeMirror( inSuperclassHiearchy );
|
||||
}
|
||||
|
||||
// if nothing was found, traverse the interfaces and collect all candidate methods that match
|
||||
List<MethodBinding> candidatesFromInterfaces = new ArrayList<MethodBinding>();
|
||||
|
||||
collectFromInterfaces(
|
||||
methodBinding,
|
||||
referenceBinding,
|
||||
new HashSet<ReferenceBinding>(),
|
||||
candidatesFromInterfaces );
|
||||
|
||||
// there can be multiple matches for the same method name from adjacent interface hierarchies.
|
||||
Collections.sort( candidatesFromInterfaces, MostSpecificMethodBindingComparator.INSTANCE );
|
||||
|
||||
if ( !candidatesFromInterfaces.isEmpty() ) {
|
||||
// return the most specific match
|
||||
return env.getFactory().newTypeMirror( candidatesFromInterfaces.get( 0 ) );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <T> T tryCast(Object instance, Class<T> type) {
|
||||
if ( instance != null && type.isInstance( instance ) ) {
|
||||
return type.cast( instance );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void collectFromInterfaces(MethodBinding methodBinding, ReferenceBinding typeBinding,
|
||||
Set<ReferenceBinding> visitedTypes, List<MethodBinding> found) {
|
||||
if ( typeBinding == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// also check the interfaces of the superclass hierarchy (the superclasses themselves don't contain a match,
|
||||
// we checked that already)
|
||||
collectFromInterfaces( methodBinding, typeBinding.superclass(), visitedTypes, found );
|
||||
|
||||
for ( ReferenceBinding ifc : typeBinding.superInterfaces() ) {
|
||||
if ( visitedTypes.contains( ifc ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visitedTypes.add( ifc );
|
||||
|
||||
// finding a match in one interface
|
||||
MethodBinding f = findMatchingMethodBinding( methodBinding, ifc.methods() );
|
||||
|
||||
if ( f == null ) {
|
||||
collectFromInterfaces( methodBinding, ifc, visitedTypes, found );
|
||||
}
|
||||
else {
|
||||
// no need for recursion if we found a candidate in this type already
|
||||
found.add( f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param baseMethod binding to compare against
|
||||
* @param methods the candidate methods
|
||||
* @return The method from the list of candidates that matches the name and original/erasure of
|
||||
* {@code methodBinding}, or {@code null} if none was found.
|
||||
*/
|
||||
private static MethodBinding findMatchingMethodBinding(MethodBinding baseMethod, MethodBinding[] methods) {
|
||||
for ( MethodBinding method : methods ) {
|
||||
if ( CharOperation.equals( method.selector, baseMethod.selector )
|
||||
&& ( method.original() == baseMethod || method.areParameterErasuresEqual( baseMethod ) ) ) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MethodBinding findInSuperclassHierarchy(MethodBinding baseMethod, ReferenceBinding typeBinding) {
|
||||
while ( typeBinding != null ) {
|
||||
MethodBinding matching = findMatchingMethodBinding( baseMethod, typeBinding.methods() );
|
||||
if ( matching != null ) {
|
||||
return matching;
|
||||
}
|
||||
|
||||
typeBinding = typeBinding.superclass();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares MethodBindings by their signature: the more specific method is considered <em>lower</em>.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
private static final class MostSpecificMethodBindingComparator implements Comparator<MethodBinding> {
|
||||
private static final MostSpecificMethodBindingComparator INSTANCE = new MostSpecificMethodBindingComparator();
|
||||
|
||||
@Override
|
||||
public int compare(MethodBinding first, MethodBinding second) {
|
||||
boolean firstParamsAssignableFromSecond =
|
||||
first.areParametersCompatibleWith( second.parameters );
|
||||
boolean secondParamsAssignableFromFirst =
|
||||
second.areParametersCompatibleWith( first.parameters );
|
||||
|
||||
if ( firstParamsAssignableFromSecond != secondParamsAssignableFromFirst ) {
|
||||
return firstParamsAssignableFromSecond ? 1 : -1;
|
||||
}
|
||||
|
||||
if ( TypeBinding.equalsEquals( first.returnType, second.returnType ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
boolean firstReturnTypeAssignableFromSecond =
|
||||
second.returnType.isCompatibleWith( first.returnType );
|
||||
|
||||
return firstReturnTypeAssignableFromSecond ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
@ -16,9 +16,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mapstruct.ap.internal.util;
|
||||
package org.mapstruct.ap.internal.util.workarounds;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
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;
|
||||
@ -31,14 +34,10 @@ import javax.lang.model.util.Types;
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class SpecificCompilerWorkarounds {
|
||||
|
||||
private SpecificCompilerWorkarounds() { }
|
||||
|
||||
/**
|
||||
* Tests whether one type is assignable to another.
|
||||
*
|
||||
* <p>
|
||||
* Work-around for a bug most likely related to problem solved with {@link #isSubType}
|
||||
/**
|
||||
* Tests whether one type is assignable to another, checking for VOID first.
|
||||
*
|
||||
* @param types the type utils
|
||||
* @param t1 the first type
|
||||
@ -46,12 +45,12 @@ public class SpecificCompilerWorkarounds {
|
||||
* @return {@code true} if and only if the first type is assignable to the second
|
||||
* @throws IllegalArgumentException if given an executable or package type
|
||||
*/
|
||||
public static boolean isAssignable(Types types, TypeMirror t1, TypeMirror t2) {
|
||||
static boolean isAssignable(Types types, TypeMirror t1, TypeMirror t2) {
|
||||
if ( t1.getKind() == TypeKind.VOID ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return types.isAssignable( erasure( types, t1 ), erasure( types, t2 ) );
|
||||
return types.isAssignable( t1, t2 );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,7 +65,7 @@ public class SpecificCompilerWorkarounds {
|
||||
* @return {@code true} if and only if the first type is a subtype of the second
|
||||
* @throws IllegalArgumentException if given an executable or package type
|
||||
*/
|
||||
public static boolean isSubType(Types types, TypeMirror t1, TypeMirror t2) {
|
||||
static boolean isSubtype(Types types, TypeMirror t1, TypeMirror t2) {
|
||||
if ( t1.getKind() == TypeKind.VOID ) {
|
||||
return false;
|
||||
}
|
||||
@ -86,7 +85,7 @@ public class SpecificCompilerWorkarounds {
|
||||
* @return the erasure of the given type
|
||||
* @throws IllegalArgumentException if given a package type
|
||||
*/
|
||||
public static TypeMirror erasure(Types types, TypeMirror t) {
|
||||
static TypeMirror erasure(Types types, TypeMirror t) {
|
||||
if ( t.getKind() == TypeKind.VOID || t.getKind() == TypeKind.NULL ) {
|
||||
return t;
|
||||
}
|
||||
@ -115,4 +114,34 @@ public class SpecificCompilerWorkarounds {
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround for Bugs in the Eclipse implementation of {@link Types#asMemberOf(DeclaredType, Element)}.
|
||||
*
|
||||
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=382590">Eclipse Bug 382590</a>
|
||||
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=481555">Eclipse Bug 481555</a>
|
||||
*/
|
||||
static TypeMirror asMemberOf(Types typeUtils, ProcessingEnvironment env, DeclaredType containing, Element element) {
|
||||
TypeMirror result = null;
|
||||
Exception lastException = null;
|
||||
try {
|
||||
try {
|
||||
result = typeUtils.asMemberOf( containing, element );
|
||||
}
|
||||
catch ( IllegalArgumentException e ) {
|
||||
lastException = e;
|
||||
|
||||
result = EclipseAsMemberOfWorkaround.asMemberOf( env, containing, element );
|
||||
}
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
lastException = e;
|
||||
}
|
||||
if ( null == result ) {
|
||||
throw new RuntimeException( "Fallback implementation of asMemberOf didn't work for "
|
||||
+ element + " in " + containing, lastException );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* 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.workarounds;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.ExecutableType;
|
||||
import javax.lang.model.type.NoType;
|
||||
import javax.lang.model.type.NullType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.type.WildcardType;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
/**
|
||||
* Replaces the usage of {@link Types} within MapStruct by delegating to the original implementation or to our specific
|
||||
* workarounds if necessary.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class TypesDecorator implements Types {
|
||||
private final Types delegate;
|
||||
private final ProcessingEnvironment processingEnv;
|
||||
|
||||
public TypesDecorator(ProcessingEnvironment processingEnv) {
|
||||
this.delegate = processingEnv.getTypeUtils();
|
||||
this.processingEnv = processingEnv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Element asElement(TypeMirror t) {
|
||||
return delegate.asElement( t );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameType(TypeMirror t1, TypeMirror t2) {
|
||||
return delegate.isSameType( t1, t2 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
|
||||
return SpecificCompilerWorkarounds.isSubtype( delegate, t1, t2 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
|
||||
return SpecificCompilerWorkarounds.isAssignable( delegate, t1, t2 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(TypeMirror t1, TypeMirror t2) {
|
||||
return delegate.contains( t1, t2 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
|
||||
return delegate.isSubsignature( m1, m2 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends TypeMirror> directSupertypes(TypeMirror t) {
|
||||
return delegate.directSupertypes( t );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeMirror erasure(TypeMirror t) {
|
||||
return SpecificCompilerWorkarounds.erasure( delegate, t );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeElement boxedClass(PrimitiveType p) {
|
||||
return delegate.boxedClass( p );
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimitiveType unboxedType(TypeMirror t) {
|
||||
return delegate.unboxedType( t );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeMirror capture(TypeMirror t) {
|
||||
return delegate.capture( t );
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimitiveType getPrimitiveType(TypeKind kind) {
|
||||
return delegate.getPrimitiveType( kind );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NullType getNullType() {
|
||||
return delegate.getNullType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoType getNoType(TypeKind kind) {
|
||||
return delegate.getNoType( kind );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayType getArrayType(TypeMirror componentType) {
|
||||
return delegate.getArrayType( componentType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public WildcardType getWildcardType(TypeMirror extendsBound, TypeMirror superBound) {
|
||||
return delegate.getWildcardType( extendsBound, superBound );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {
|
||||
return delegate.getDeclaredType( typeElem, typeArgs );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeclaredType getDeclaredType(DeclaredType containing, TypeElement typeElem, TypeMirror... typeArgs) {
|
||||
return delegate.getDeclaredType( containing, typeElem, typeArgs );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeMirror asMemberOf(DeclaredType containing, Element element) {
|
||||
return SpecificCompilerWorkarounds.asMemberOf( delegate, processingEnv, containing, element );
|
||||
}
|
||||
}
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.mapstruct.ap.test.inheritedmappingmethod;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.inheritedmappingmethod._target.CarDto;
|
||||
@ -30,6 +28,8 @@ 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;
|
||||
|
||||
@IssueKey( "274" )
|
||||
@WithClasses({
|
||||
Car.class, CarDto.class, UnboundMappable.class, CarMapper.class, //
|
||||
@ -39,7 +39,7 @@ import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
public class InheritedMappingMethodTest {
|
||||
|
||||
@Test
|
||||
public void shouldProvideUnboundedMapperInstance() throws Exception {
|
||||
public void shouldProvideUnboundedMapperInstance() {
|
||||
UnboundMappable<CarDto, Car> instance = CarMapper.INSTANCE;
|
||||
assertThat( instance ).isNotNull();
|
||||
}
|
||||
@ -59,7 +59,7 @@ public class InheritedMappingMethodTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldProvideBoundedMapperInstance() throws Exception {
|
||||
public void shouldProvideBoundedMapperInstance() {
|
||||
BoundMappable<? extends CarDto, ? extends Car> instance = FastCarMapper.INSTANCE;
|
||||
assertThat( instance ).isNotNull();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user