#148, #1386, #2593 Only import top level classes

Instead of importing all classes, inner classes will be used through their top level classes only.
This also fixes the problem in #2593 since inner classes are no longer imported but used through their top classes
This commit is contained in:
Filip Hrisafov 2021-10-25 08:22:26 +02:00 committed by GitHub
parent 564455ee45
commit 80d26a1a9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 776 additions and 9 deletions

View File

@ -128,6 +128,11 @@ public class MavenIntegrationTest {
void simpleTest() {
}
// for issue #2593
@ProcessorTest(baseDir = "defaultPackage")
void defaultPackageTest() {
}
@ProcessorTest(baseDir = "springTest")
void springTest() {
}

View File

@ -0,0 +1,97 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author Filip Hrisafov
*/
public class DefaultPackageObject {
public enum CarType {
SEDAN, CAMPER, X4, TRUCK;
}
static public class Car {
private String make;
private int numberOfSeats;
private CarType type;
public Car(String string, int numberOfSeats, CarType sedan) {
this.make = string;
this.numberOfSeats = numberOfSeats;
this.type = sedan;
}
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public int getNumberOfSeats() {
return numberOfSeats;
}
public void setNumberOfSeats(int numberOfSeats) {
this.numberOfSeats = numberOfSeats;
}
public CarType getType() {
return type;
}
public void setType(CarType type) {
this.type = type;
}
}
static public class CarDto {
private String make;
private int seatCount;
private String type;
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public int getSeatCount() {
return seatCount;
}
public void setSeatCount(int seatCount) {
this.seatCount = seatCount;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>defaultPackage</artifactId>
<packaging>jar</packaging>
</project>

View File

@ -0,0 +1,31 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Filip Hrisafov
*/
public class DefaultPackageTest {
@Test
public void shouldWorkCorrectlyInDefaultPackage() {
DefaultPackageObject.CarDto carDto = DefaultPackageObject.CarMapper.INSTANCE.carToCarDto(
new DefaultPackageObject.Car(
"Morris",
5,
DefaultPackageObject.CarType.SEDAN
) );
assertThat( carDto ).isNotNull();
assertThat( carDto.getMake() ).isEqualTo( "Morris" );
assertThat( carDto.getSeatCount() ).isEqualTo( 5 );
assertThat( carDto.getType() ).isEqualTo( "SEDAN" );
}
}

View File

@ -5,9 +5,11 @@
*/
package org.mapstruct.ap.internal.model.common;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@ -22,6 +24,7 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
@ -71,9 +74,11 @@ public class Type extends ModelElement implements Comparable<Type> {
private final ImplementationType implementationType;
private final Type componentType;
private final Type topLevelType;
private final String packageName;
private final String name;
private final String nameWithTopLevelTypeName;
private final String qualifiedName;
private final boolean isInterface;
@ -172,6 +177,9 @@ public class Type extends ModelElement implements Comparable<Type> {
this.filters = new Filters( accessorNaming, typeUtils, typeMirror );
this.loggingVerbose = loggingVerbose;
this.topLevelType = topLevelType( this.typeElement, this.typeFactory );
this.nameWithTopLevelTypeName = nameWithTopLevelTypeName( this.typeElement );
}
//CHECKSTYLE:ON
@ -199,11 +207,23 @@ public class Type extends ModelElement implements Comparable<Type> {
* <p>
* If the {@code java.time} variant is referred to first, the {@code java.time.LocalDateTime} will be imported
* and the {@code org.joda} variant will be referred to with its FQN.
* <p>
* If the type is nested and its top level type is to be imported
* then the name including its top level type will be returned.
*
* @return Just the name if this {@link Type} will be imported, otherwise the fully-qualified name.
* @return Just the name if this {@link Type} will be imported, the name up to the top level {@link Type}
* (if the top level type is important, otherwise the fully-qualified name.
*/
public String createReferenceName() {
return isToBeImported() ? name : ( shouldUseSimpleName() ? name : qualifiedName );
if ( isToBeImported() || shouldUseSimpleName() ) {
return name;
}
if ( isTopLevelTypeToBeImported() && nameWithTopLevelTypeName != null ) {
return nameWithTopLevelTypeName;
}
return qualifiedName;
}
public List<Type> getTypeParameters() {
@ -402,6 +422,10 @@ public class Type extends ModelElement implements Comparable<Type> {
result.addAll( componentType.getImportTypes() );
}
if ( topLevelType != null ) {
result.addAll( topLevelType.getImportTypes() );
}
for ( Type parameter : typeParameters ) {
result.addAll( parameter.getImportTypes() );
}
@ -413,6 +437,10 @@ public class Type extends ModelElement implements Comparable<Type> {
return result;
}
protected boolean isTopLevelTypeToBeImported() {
return topLevelType != null && topLevelType.isToBeImported();
}
/**
* Whether this type is to be imported by means of an import statement in the currently generated source file
* (it can be referenced in the generated source using its simple name) or not (referenced using the FQN).
@ -435,7 +463,7 @@ public class Type extends ModelElement implements Comparable<Type> {
isToBeImported = true;
}
}
else {
else if ( typeElement == null || !typeElement.getNestingKind().isNested() ) {
toBeImportedTypes.put( trimmedName, trimmedQualifiedName );
isToBeImported = true;
}
@ -1492,4 +1520,39 @@ public class Type extends ModelElement implements Comparable<Type> {
return trimmedClassName;
}
private static String nameWithTopLevelTypeName(TypeElement element) {
if ( element == null ) {
return null;
}
if ( !element.getNestingKind().isNested() ) {
return element.getSimpleName().toString();
}
Deque<CharSequence> elements = new ArrayDeque<>();
elements.addFirst( element.getSimpleName() );
Element parent = element.getEnclosingElement();
while ( parent != null && parent.getKind() != ElementKind.PACKAGE ) {
elements.addFirst( parent.getSimpleName() );
parent = parent.getEnclosingElement();
}
return String.join( ".", elements );
}
private static Type topLevelType(TypeElement typeElement, TypeFactory typeFactory) {
if ( typeElement == null || typeElement.getNestingKind() == NestingKind.TOP_LEVEL ) {
return null;
}
Element parent = typeElement.getEnclosingElement();
while ( parent != null ) {
if ( parent.getEnclosingElement() != null &&
parent.getEnclosingElement().getKind() == ElementKind.PACKAGE ) {
break;
}
parent = parent.getEnclosingElement();
}
return parent == null ? null : typeFactory.getType( parent.asType() );
}
}

View File

@ -14,7 +14,6 @@ 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.test.imports.innerclasses.TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
@ -47,8 +46,7 @@ public class InnerClassesImportsTest {
assertThat( target ).isNotNull();
assertThat( target.getInnerClassMember().getValue() ).isEqualTo( 412 );
generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( SourceInnerClass.class );
generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( TargetInnerClass.class );
generatedSource.addComparisonToFixtureFor( InnerClassMapper.class );
}
@ProcessorTest
@ -61,8 +59,7 @@ public class InnerClassesImportsTest {
assertThat( target ).isNotNull();
assertThat( target.getValue() ).isEqualTo( 412 );
generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( SourceInnerClass.class );
generatedSource.forMapper( InnerClassMapper.class ).containsImportFor( TargetInnerInnerClass.class );
generatedSource.addComparisonToFixtureFor( InnerClassMapper.class );
}
@ProcessorTest
@ -82,6 +79,6 @@ public class InnerClassesImportsTest {
assertThat( sourceAgain ).isNotNull();
assertThat( sourceAgain.getInnerEnum() ).isEqualTo( InnerEnum.A );
generatedSource.forMapper( BeanWithInnerEnumMapper.class ).containsImportFor( InnerEnum.class );
generatedSource.addComparisonToFixtureFor( BeanWithInnerEnumMapper.class );
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage;
import org.mapstruct.ap.test.imports.nested.other.TargetInOtherPackage;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.GeneratedSource;
/**
* @author Filip Hrisafov
*/
@WithClasses({
SourceInOtherPackage.class,
TargetInOtherPackage.class,
Source.class,
Target.class
})
@IssueKey("1386,148")
class NestedImportsTest {
@RegisterExtension
final GeneratedSource generatedSource = new GeneratedSource();
@ProcessorTest
@WithClasses( {
SourceInOtherPackageMapper.class
} )
void shouldGenerateNestedInnerClassesForSourceInOtherPackage() {
generatedSource.addComparisonToFixtureFor( SourceInOtherPackageMapper.class );
}
@ProcessorTest
@WithClasses( {
NestedSourceInOtherPackageMapper.class
} )
void shouldGenerateCorrectImportsForTopLevelClassesFromOnlyNestedInnerClasses() {
generatedSource.addComparisonToFixtureFor( NestedSourceInOtherPackageMapper.class );
}
@ProcessorTest
@WithClasses( {
TargetInOtherPackageMapper.class
} )
void shouldGenerateNestedInnerClassesForTargetInOtherPackage() {
generatedSource.addComparisonToFixtureFor( TargetInOtherPackageMapper.class );
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface NestedSourceInOtherPackageMapper {
Target.Nested map(SourceInOtherPackage.Nested source);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
/**
* @author Filip Hrisafov
*/
public class Source {
private Nested value;
public Nested getValue() {
return value;
}
public void setValue(Nested value) {
this.value = value;
}
public static class Nested {
private Inner inner;
public static class Inner {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public Inner getInner() {
return inner;
}
public void setInner(Inner inner) {
this.inner = inner;
}
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface SourceInOtherPackageMapper {
Target map(SourceInOtherPackage source);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
/**
* @author Filip Hrisafov
*/
public class Target {
private Nested value;
public Nested getValue() {
return value;
}
public void setValue(Nested value) {
this.value = value;
}
public static class Nested {
private Inner inner;
public static class Inner {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public Inner getInner() {
return inner;
}
public void setInner(Inner inner) {
this.inner = inner;
}
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
import org.mapstruct.Mapper;
import org.mapstruct.ap.test.imports.nested.other.TargetInOtherPackage;
/**
* @author Filip Hrisafov
*/
@Mapper
public interface TargetInOtherPackageMapper {
TargetInOtherPackage map(Source source);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested.other;
/**
* @author Filip Hrisafov
*/
public class SourceInOtherPackage {
private Nested value;
public Nested getValue() {
return value;
}
public void setValue(Nested value) {
this.value = value;
}
public static class Nested {
private Inner inner;
public static class Inner {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public Inner getInner() {
return inner;
}
public void setInner(Inner inner) {
this.inner = inner;
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested.other;
/**
* @author Filip Hrisafov
*/
public class TargetInOtherPackage {
private Nested value;
public Nested getValue() {
return value;
}
public void setValue(Nested value) {
this.value = value;
}
public static class Nested {
private Inner inner;
public static class Inner {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public Inner getInner() {
return inner;
}
public void setInner(Inner inner) {
this.inner = inner;
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.innerclasses;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-10-16T21:06:53+0200",
comments = "version: , compiler: javac, environment: Java 17 (Oracle Corporation)"
)
public class BeanWithInnerEnumMapperImpl implements BeanWithInnerEnumMapper {
@Override
public BeanWithInnerEnum fromFacade(BeanFacade beanFacade) {
if ( beanFacade == null ) {
return null;
}
BeanWithInnerEnum beanWithInnerEnum = new BeanWithInnerEnum();
beanWithInnerEnum.setTest( beanFacade.getTest() );
if ( beanFacade.getInnerEnum() != null ) {
beanWithInnerEnum.setInnerEnum( Enum.valueOf( BeanWithInnerEnum.InnerEnum.class, beanFacade.getInnerEnum() ) );
}
return beanWithInnerEnum;
}
@Override
public BeanFacade toFacade(BeanWithInnerEnum beanWithInnerEnum) {
if ( beanWithInnerEnum == null ) {
return null;
}
BeanFacade beanFacade = new BeanFacade();
beanFacade.setTest( beanWithInnerEnum.getTest() );
if ( beanWithInnerEnum.getInnerEnum() != null ) {
beanFacade.setInnerEnum( beanWithInnerEnum.getInnerEnum().name() );
}
return beanFacade;
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.innerclasses;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-10-16T21:06:53+0200",
comments = "version: , compiler: javac, environment: Java 17 (Oracle Corporation)"
)
public class InnerClassMapperImpl implements InnerClassMapper {
@Override
public TargetWithInnerClass sourceToTarget(SourceWithInnerClass source) {
if ( source == null ) {
return null;
}
TargetWithInnerClass targetWithInnerClass = new TargetWithInnerClass();
targetWithInnerClass.setInnerClassMember( innerSourceToInnerTarget( source.getInnerClassMember() ) );
return targetWithInnerClass;
}
@Override
public TargetWithInnerClass.TargetInnerClass innerSourceToInnerTarget(SourceWithInnerClass.SourceInnerClass source) {
if ( source == null ) {
return null;
}
TargetWithInnerClass.TargetInnerClass targetInnerClass = new TargetWithInnerClass.TargetInnerClass();
targetInnerClass.setValue( source.getValue() );
return targetInnerClass;
}
@Override
public TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass innerSourceToInnerInnerTarget(SourceWithInnerClass.SourceInnerClass source) {
if ( source == null ) {
return null;
}
TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass targetInnerInnerClass = new TargetWithInnerClass.TargetInnerClass.TargetInnerInnerClass();
targetInnerInnerClass.setValue( source.getValue() );
return targetInnerInnerClass;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
import javax.annotation.processing.Generated;
import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-10-24T19:26:14+0200",
comments = "version: , compiler: javac, environment: Java 1.8.0_161 (Oracle Corporation)"
)
public class NestedSourceInOtherPackageMapperImpl implements NestedSourceInOtherPackageMapper {
@Override
public Target.Nested map(SourceInOtherPackage.Nested source) {
if ( source == null ) {
return null;
}
Target.Nested nested = new Target.Nested();
nested.setInner( innerToInner( source.getInner() ) );
return nested;
}
protected Target.Nested.Inner innerToInner(SourceInOtherPackage.Nested.Inner inner) {
if ( inner == null ) {
return null;
}
Target.Nested.Inner inner1 = new Target.Nested.Inner();
inner1.setValue( inner.getValue() );
return inner1;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
import javax.annotation.Generated;
import org.mapstruct.ap.test.imports.nested.other.SourceInOtherPackage;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2018-08-19T19:13:35+0200",
comments = "version: , compiler: javac, environment: Java 1.8.0_161 (Oracle Corporation)"
)
public class SourceInOtherPackageMapperImpl implements SourceInOtherPackageMapper {
@Override
public Target map(SourceInOtherPackage source) {
if ( source == null ) {
return null;
}
Target target = new Target();
target.setValue( nestedToNested( source.getValue() ) );
return target;
}
protected Target.Nested.Inner innerToInner(SourceInOtherPackage.Nested.Inner inner) {
if ( inner == null ) {
return null;
}
Target.Nested.Inner inner1 = new Target.Nested.Inner();
inner1.setValue( inner.getValue() );
return inner1;
}
protected Target.Nested nestedToNested(SourceInOtherPackage.Nested nested) {
if ( nested == null ) {
return null;
}
Target.Nested nested1 = new Target.Nested();
nested1.setInner( innerToInner( nested.getInner() ) );
return nested1;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.imports.nested;
import javax.annotation.processing.Generated;
import org.mapstruct.ap.test.imports.nested.other.TargetInOtherPackage;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-10-24T13:45:02+0200",
comments = "version: , compiler: javac, environment: Java 1.8.0_161 (Oracle Corporation)"
)
public class TargetInOtherPackageMapperImpl implements TargetInOtherPackageMapper {
@Override
public TargetInOtherPackage map(Source source) {
if ( source == null ) {
return null;
}
TargetInOtherPackage targetInOtherPackage = new TargetInOtherPackage();
targetInOtherPackage.setValue( nestedToNested( source.getValue() ) );
return targetInOtherPackage;
}
protected TargetInOtherPackage.Nested.Inner innerToInner(Source.Nested.Inner inner) {
if ( inner == null ) {
return null;
}
TargetInOtherPackage.Nested.Inner inner1 = new TargetInOtherPackage.Nested.Inner();
inner1.setValue( inner.getValue() );
return inner1;
}
protected TargetInOtherPackage.Nested nestedToNested(Source.Nested nested) {
if ( nested == null ) {
return null;
}
TargetInOtherPackage.Nested nested1 = new TargetInOtherPackage.Nested();
nested1.setInner( innerToInner( nested.getInner() ) );
return nested1;
}
}