/** * 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.processor; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; import javax.lang.model.element.TypeElement; import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.AnnotationMapperReference; import org.mapstruct.ap.internal.model.Decorator; import org.mapstruct.ap.internal.model.Field; import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.MapperReference; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.util.MapperConfiguration; /** * An {@link ModelElementProcessor} which converts the given {@link Mapper} * object into an annotation based component model in case a matching model is selected as * target component model for this mapper. * * @author Gunnar Morling * @author Andreas Gudian */ public abstract class AnnotationBasedComponentModelProcessor implements ModelElementProcessor<Mapper, Mapper> { private TypeFactory typeFactory; @Override public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, Mapper mapper) { this.typeFactory = context.getTypeFactory(); String componentModel = MapperConfiguration.getInstanceOn( mapperTypeElement ) .componentModel( context.getOptions() ); if ( !getComponentModelIdentifier().equalsIgnoreCase( componentModel ) ) { return mapper; } for ( Annotation typeAnnotation : getTypeAnnotations( mapper ) ) { mapper.addAnnotation( typeAnnotation ); } if ( !requiresGenerationOfDecoratorClass() ) { mapper.removeDecorator(); } else if ( mapper.getDecorator() != null ) { adjustDecorator( mapper ); } List<Annotation> annotations = getMapperReferenceAnnotations(); ListIterator<MapperReference> iterator = mapper.getReferencedMappers().listIterator(); while ( iterator.hasNext() ) { MapperReference reference = iterator.next(); iterator.remove(); iterator.add( replacementMapperReference( reference, annotations ) ); } return mapper; } protected void adjustDecorator(Mapper mapper) { Decorator decorator = mapper.getDecorator(); for ( Annotation typeAnnotation : getDecoratorAnnotations() ) { decorator.addAnnotation( typeAnnotation ); } decorator.removeConstructor(); List<Annotation> annotations = getDelegatorReferenceAnnotations( mapper ); List<Field> replacement = new ArrayList<Field>(); if ( !decorator.getMethods().isEmpty() ) { for ( Field field : decorator.getFields() ) { replacement.add( replacementMapperReference( field, annotations ) ); } } decorator.setFields( replacement ); } protected List<Annotation> getDelegatorReferenceAnnotations(Mapper mapper) { return Collections.emptyList(); } /** * @param originalReference the reference to be replaced * @param annotations the list of annotations * * @return the mapper reference replacing the original one */ protected MapperReference replacementMapperReference(Field originalReference, List<Annotation> annotations) { return new AnnotationMapperReference( originalReference.getType(), originalReference.getVariableName(), annotations, originalReference.isUsed() ); } /** * @return the component model identifier */ protected abstract String getComponentModelIdentifier(); /** * @param mapper the mapper * * @return the annotation(s) to be added at the mapper type implementation */ protected abstract List<Annotation> getTypeAnnotations(Mapper mapper); /** * @return the annotation(s) to be added at the decorator of the mapper */ protected List<Annotation> getDecoratorAnnotations() { return Collections.emptyList(); } /** * @return the annotation of the field for the mapper reference */ protected abstract List<Annotation> getMapperReferenceAnnotations(); /** * @return if a decorator (sub-)class needs to be generated or not */ protected abstract boolean requiresGenerationOfDecoratorClass(); @Override public int getPriority() { return 1100; } protected TypeFactory getTypeFactory() { return typeFactory; } }