/** * Copyright 2012-2017 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.model; import static org.mapstruct.ap.internal.util.Collections.first; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.source.EnumMapping; import org.mapstruct.ap.internal.model.source.Mapping; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.prism.BeanMappingPrism; import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Strings; /** * A {@link MappingMethod} which maps one enum type to another, optionally configured by one or more * {@link EnumMapping}s. * * @author Gunnar Morling */ public class EnumMappingMethod extends MappingMethod { private final List<EnumMapping> enumMappings; public static class Builder { private SourceMethod method; private MappingBuilderContext ctx; public Builder mappingContext(MappingBuilderContext mappingContext) { this.ctx = mappingContext; return this; } public Builder souceMethod(SourceMethod sourceMethod) { this.method = sourceMethod; return this; } public EnumMappingMethod build() { if ( !reportErrorIfMappedEnumConstantsDontExist( method ) || !reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped( method ) ) { return null; } List<EnumMapping> enumMappings = new ArrayList<EnumMapping>(); List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants(); for ( String enumConstant : sourceEnumConstants ) { List<Mapping> mappedConstants = method.getMappingBySourcePropertyName( enumConstant ); if ( mappedConstants.isEmpty() ) { enumMappings.add( new EnumMapping( enumConstant, enumConstant ) ); } else if ( mappedConstants.size() == 1 ) { enumMappings.add( new EnumMapping( enumConstant, first( mappedConstants ).getTargetName() ) ); } else { List<String> targetConstants = new ArrayList<String>( mappedConstants.size() ); for ( Mapping mapping : mappedConstants ) { targetConstants.add( mapping.getTargetName() ); } ctx.getMessager().printMessage( method.getExecutable(), Message.ENUMMAPPING_MULTIPLE_SOURCES, enumConstant, Strings.join( targetConstants, ", " ) ); } } SelectionParameters selectionParameters = getSelecionParameters( method ); Set<String> existingVariables = new HashSet<String>( method.getParameterNames() ); List<LifecycleCallbackMethodReference> beforeMappingMethods = LifecycleCallbackFactory.beforeMappingMethods( method, selectionParameters, ctx, existingVariables ); List<LifecycleCallbackMethodReference> afterMappingMethods = LifecycleCallbackFactory.afterMappingMethods( method, selectionParameters, ctx, existingVariables ); return new EnumMappingMethod( method, enumMappings, beforeMappingMethods, afterMappingMethods ); } private static SelectionParameters getSelecionParameters(SourceMethod method) { BeanMappingPrism beanMappingPrism = BeanMappingPrism.getInstanceOn( method.getExecutable() ); if ( beanMappingPrism != null ) { List<TypeMirror> qualifiers = beanMappingPrism.qualifiedBy(); List<String> qualifyingNames = beanMappingPrism.qualifiedByName(); TypeMirror resultType = beanMappingPrism.resultType(); return new SelectionParameters( qualifiers, qualifyingNames, resultType ); } return null; } private boolean reportErrorIfMappedEnumConstantsDontExist(SourceMethod method) { List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants(); List<String> targetEnumConstants = method.getReturnType().getEnumConstants(); boolean foundIncorrectMapping = false; for ( List<Mapping> mappedConstants : method.getMappingOptions().getMappings().values() ) { for ( Mapping mappedConstant : mappedConstants ) { if ( mappedConstant.getSourceName() == null ) { ctx.getMessager().printMessage( method.getExecutable(), mappedConstant.getMirror(), Message.ENUMMAPPING_UNDEFINED_SOURCE ); foundIncorrectMapping = true; } else if ( !sourceEnumConstants.contains( mappedConstant.getSourceName() ) ) { ctx.getMessager().printMessage( method.getExecutable(), mappedConstant.getMirror(), mappedConstant.getSourceAnnotationValue(), Message.ENUMMAPPING_NON_EXISTING_CONSTANT, mappedConstant.getSourceName(), first( method.getSourceParameters() ).getType() ); foundIncorrectMapping = true; } if ( mappedConstant.getTargetName() == null ) { ctx.getMessager().printMessage( method.getExecutable(), mappedConstant.getMirror(), Message.ENUMMAPPING_UNDEFINED_TARGET ); foundIncorrectMapping = true; } else if ( !targetEnumConstants.contains( mappedConstant.getTargetName() ) ) { ctx.getMessager().printMessage( method.getExecutable(), mappedConstant.getMirror(), mappedConstant.getTargetAnnotationValue(), Message.ENUMMAPPING_NON_EXISTING_CONSTANT, mappedConstant.getTargetName(), method.getReturnType() ); foundIncorrectMapping = true; } } } return !foundIncorrectMapping; } private boolean reportErrorIfSourceEnumConstantsWithoutCorrespondingTargetConstantAreNotMapped( SourceMethod method) { List<String> sourceEnumConstants = first( method.getSourceParameters() ).getType().getEnumConstants(); List<String> targetEnumConstants = method.getReturnType().getEnumConstants(); List<String> unmappedSourceEnumConstants = new ArrayList<String>(); for ( String sourceEnumConstant : sourceEnumConstants ) { if ( !targetEnumConstants.contains( sourceEnumConstant ) && method.getMappingBySourcePropertyName( sourceEnumConstant ).isEmpty() ) { unmappedSourceEnumConstants.add( sourceEnumConstant ); } } if ( !unmappedSourceEnumConstants.isEmpty() ) { ctx.getMessager().printMessage( method.getExecutable(), Message.ENUMMAPPING_UNMAPPED_SOURCES, Strings.join( unmappedSourceEnumConstants, ", " ) ); } return unmappedSourceEnumConstants.isEmpty(); } } private EnumMappingMethod(Method method, List<EnumMapping> enumMappings, List<LifecycleCallbackMethodReference> beforeMappingMethods, List<LifecycleCallbackMethodReference> afterMappingMethods) { super( method, beforeMappingMethods, afterMappingMethods ); this.enumMappings = enumMappings; } public List<EnumMapping> getEnumMappings() { return enumMappings; } public Parameter getSourceParameter() { return first( getParameters() ); } }