Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ public interface CarMapper {
* Between `java.net.URL` and `String`.
** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/URL[URL] otherwise a `MalformedURLException` is thrown.

* Between `java.util.Optional<T>` and `T`.

[[mapping-object-references]]
=== Mapping object references

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

import org.mapstruct.ap.internal.model.common.Type;
Expand All @@ -41,13 +42,17 @@ public class Conversions {
private final Map<Key, ConversionProvider> conversions = new HashMap<>();
private final Type enumType;
private final Type stringType;
private final Type objectType;
private final Type optionalType;
private final TypeFactory typeFactory;

public Conversions(TypeFactory typeFactory) {
this.typeFactory = typeFactory;

this.enumType = typeFactory.getType( Enum.class );
this.stringType = typeFactory.getType( String.class );
this.objectType = typeFactory.getType( Object.class );
this.optionalType = typeFactory.getType( Optional.class );

//native types <> native types, including wrappers
registerNativeTypeConversion( byte.class, Byte.class );
Expand Down Expand Up @@ -195,6 +200,8 @@ public Conversions(TypeFactory typeFactory) {

register( UUID.class, String.class, new UUIDToStringConversion() );

register( Optional.class, Object.class, new OptionalToObjectConversion() );

registerURLConversion();
}

Expand Down Expand Up @@ -330,10 +337,24 @@ public ConversionProvider getConversion(Type sourceType, Type targetType) {
else if ( targetType.isEnumType() && sourceType.equals( stringType ) ) {
targetType = enumType;
}
else if ( isOptionalConvertable( sourceType, targetType ) ) {
sourceType = optionalType;
targetType = objectType;
}
else if ( isOptionalConvertable( targetType, sourceType ) ) {
sourceType = objectType;
targetType = optionalType;
}

return conversions.get( new Key( sourceType, targetType ) );
}

private boolean isOptionalConvertable(Type type1, Type type2) {
return type1.isOptionalType()
&& !type2.isOptionalType()
&& type1.getTypeParameters().get( 0 ).isAssignableTo( type2 );
}

private static class Key {
private final Type sourceType;
private final Type targetType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.internal.conversion;

import java.util.Optional;
import java.util.Set;

import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.util.Collections;

/**
* Conversion between {@link Optional} and {@link Object}.
*
* @author Iaroslav Bogdanchikov
*/
public class OptionalToObjectConversion extends SimpleConversion {

@Override
protected String getToExpression(ConversionContext conversionContext) {
return "<SOURCE>.get()";
}

@Override
protected String getFromExpression(ConversionContext conversionContext) {
return "Optional.ofNullable( <SOURCE> )";
}

@Override
protected Set<Type> getFromConversionImportTypes(final ConversionContext conversionContext) {
return Collections.asSet( conversionContext.getTypeFactory().getType( Optional.class ) );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,14 @@ private boolean setterWrapperNeedsSourceNullCheck(Assignment rhs, Type targetTyp
return true;
}

if ( rhs.getSourceType().isOptionalType() && !targetType.isOptionalType() ) {
return true;
}

if ( !rhs.getSourceType().isOptionalType() && targetType.isOptionalType() ) {
return false;
}

if ( rhs.getType().isConverted() ) {
// A type conversion is applied, so a null check is required
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -93,6 +94,7 @@ public class Type extends ModelElement implements Comparable<Type> {
private final boolean isVoid;
private final boolean isStream;
private final boolean isLiteral;
private final boolean isOptional;

private final boolean loggingVerbose;

Expand Down Expand Up @@ -132,7 +134,7 @@ public Type(TypeUtils typeUtils, ElementUtils elementUtils, TypeFactory typeFact
Map<String, String> toBeImportedTypes,
Map<String, String> notToBeImportedTypes,
Boolean isToBeImported,
boolean isLiteral, boolean loggingVerbose) {
boolean isLiteral, boolean isOptional, boolean loggingVerbose) {

this.typeUtils = typeUtils;
this.elementUtils = elementUtils;
Expand All @@ -157,6 +159,7 @@ public Type(TypeUtils typeUtils, ElementUtils elementUtils, TypeFactory typeFact
this.isStream = isStreamType;
this.isVoid = typeMirror.getKind() == TypeKind.VOID;
this.isLiteral = isLiteral;
this.isOptional = isOptional;

if ( isEnumType ) {
enumConstants = new ArrayList<>();
Expand Down Expand Up @@ -371,6 +374,15 @@ public boolean isStreamType() {
return isStream;
}

/**
* Whether this type is an {@link Optional}.
*
* @return true if this type is an {@link Optional}, false otherwise
*/
public boolean isOptionalType() {
return isOptional;
}

/**
* A wild card type can have two types of bounds (mutual exclusive): extends and super.
*
Expand Down Expand Up @@ -537,6 +549,7 @@ public Type erasure() {
notToBeImportedTypes,
isToBeImported,
isLiteral,
isOptional,
loggingVerbose
);
}
Expand Down Expand Up @@ -580,6 +593,7 @@ public Type withoutBounds() {
notToBeImportedTypes,
isToBeImported,
isLiteral,
isOptional,
loggingVerbose
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Optional;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
Expand All @@ -38,19 +39,19 @@
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import org.mapstruct.ap.internal.util.ElementUtils;
import org.mapstruct.ap.internal.util.TypeUtils;

import org.mapstruct.ap.internal.gem.BuilderGem;
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
import org.mapstruct.ap.internal.util.Collections;
import org.mapstruct.ap.internal.util.ElementUtils;
import org.mapstruct.ap.internal.util.Extractor;
import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.JavaStreamConstants;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.NativeTypes;
import org.mapstruct.ap.internal.util.RoundContext;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
import org.mapstruct.ap.spi.BuilderInfo;
Expand Down Expand Up @@ -92,6 +93,7 @@ public class TypeFactory {
private final TypeMirror collectionType;
private final TypeMirror mapType;
private final TypeMirror streamType;
private final TypeMirror optionalType;

private final Map<String, ImplementationType> implementationTypes = new HashMap<>();
private final Map<String, String> toBeImportedTypes = new HashMap<>();
Expand All @@ -113,6 +115,7 @@ public TypeFactory(ElementUtils elementUtils, TypeUtils typeUtils, FormattingMes
mapType = typeUtils.erasure( elementUtils.getTypeElement( Map.class.getCanonicalName() ).asType() );
TypeElement streamTypeElement = elementUtils.getTypeElement( JavaStreamConstants.STREAM_FQN );
streamType = streamTypeElement == null ? null : typeUtils.erasure( streamTypeElement.asType() );
optionalType = typeUtils.erasure( elementUtils.getTypeElement( Optional.class.getCanonicalName() ).asType() );

implementationTypes.put( Iterable.class.getName(), withInitialCapacity( getType( ArrayList.class ) ) );
implementationTypes.put( Collection.class.getName(), withInitialCapacity( getType( ArrayList.class ) ) );
Expand Down Expand Up @@ -221,6 +224,7 @@ private Type getType(TypeMirror mirror, boolean isLiteral, Boolean alwaysImport)
boolean isCollectionType = typeUtils.isSubtypeErased( mirror, collectionType );
boolean isMapType = typeUtils.isSubtypeErased( mirror, mapType );
boolean isStreamType = streamType != null && typeUtils.isSubtypeErased( mirror, streamType );
boolean isOptionalType = typeUtils.isSubtypeErased( mirror, optionalType );

boolean isEnumType;
boolean isInterface;
Expand Down Expand Up @@ -334,6 +338,7 @@ else if ( mirror.getKind() == TypeKind.TYPEVAR ) {
notToBeImportedTypes,
toBeImported,
isLiteral,
isOptionalType,
loggingVerbose
);
}
Expand Down Expand Up @@ -560,6 +565,7 @@ private ImplementationType getImplementationType(TypeMirror mirror) {
notToBeImportedTypes,
null,
implementationType.isLiteral(),
implementationType.isOptionalType(),
loggingVerbose
);
return implementation.createNew( replacement );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}
<@elseDefaultAssignment/>
<#elseif includeSourceNullCheck || ext.defaultValueAssignment??>
if ( <#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference}</#if> != null ) {
if ( <#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference}</#if><#if getSourceType().isOptionalType()>.isPresent()<#else> != null</#if> ) {
<#nested>
}
<@elseDefaultAssignment/>
Expand Down Expand Up @@ -183,4 +183,4 @@ Performs a default assignment with a default value.
<#if sourceLocalVarName??>
<@includeModel object=sourceType/> ${sourceLocalVarName} = ${sourceReference};
</#if>
</#macro>
</#macro>
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@
*/
package org.mapstruct.ap.internal.model.common;

import static org.assertj.core.api.Assertions.assertThat;

import java.lang.annotation.Annotation;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.List;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
Expand All @@ -24,6 +21,8 @@
import org.mapstruct.ap.internal.util.JodaTimeConstants;
import org.mapstruct.ap.testutil.IssueKey;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link org.mapstruct.ap.internal.model.common.DateFormatValidatorFactory}.
*
Expand Down Expand Up @@ -177,7 +176,9 @@ private Type typeWithFQN(String fullQualifiedName) {
new HashMap<>( ),
new HashMap<>( ),
false,
false, false
false,
false,
false
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@
*/
package org.mapstruct.ap.internal.model.common;

import static org.assertj.core.api.Assertions.assertThat;

import java.lang.annotation.Annotation;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.List;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
Expand All @@ -25,6 +22,8 @@
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.testutil.IssueKey;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Testing DefaultConversionContext for dateFormat
*
Expand Down Expand Up @@ -125,7 +124,9 @@ private Type typeWithFQN(String fullQualifiedName) {
new HashMap<>( ),
new HashMap<>( ),
false,
false, false
false,
false,
false
);
}

Expand Down
Loading