/** * 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.eclipse.internal.quickfix.fixes; import static org.mapstruct.eclipse.internal.MapStructAPIConstants.MAPPINGS_FQ_NAME; import static org.mapstruct.eclipse.internal.MapStructAPIConstants.MAPPINGS_SIMPLE_NAME; import static org.mapstruct.eclipse.internal.MapStructAPIConstants.MAPPING_FQ_NAME; import static org.mapstruct.eclipse.internal.MapStructAPIConstants.MAPPING_MEMBER_IGNORE; import static org.mapstruct.eclipse.internal.MapStructAPIConstants.MAPPING_MEMBER_TARGET; import static org.mapstruct.eclipse.internal.MapStructAPIConstants.MAPPING_SIMPLE_NAME; import java.util.Collection; import java.util.LinkedList; import java.util.List; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ArrayInitializer; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.MemberValuePair; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; import org.mapstruct.eclipse.internal.quickfix.MapStructQuickFix; import org.mapstruct.eclipse.internal.quickfix.visitors.FindAnnotationByNameVisitor; /** * Quick fix that adds {@code @Mapping( target = "<property>", ignore = true)} to the method. * * @author Andreas Gudian */ public class AddIgnoreTargetMappingAnnotationQuickFix extends MapStructQuickFix { private final List<String> properties; public AddIgnoreTargetMappingAnnotationQuickFix(List<String> properties) { this.properties = properties; } @Override public String getLabel() { String suffix = ( properties.size() > 1 ? "ies." : "y " + properties.get( 0 ) ); return "Ignore unmapped target propert" + suffix; } @Override public String getDescription() { String suffix = ( properties.size() > 1 ? "ies." : "y " + properties.get( 0 ) ); StringBuilder sb = new StringBuilder( "<html>Ignore target propert" ); sb.append( suffix ); sb.append( "<br><br>" ); for ( String prop : properties ) { sb.append( "<b>@Mapping( target = \"" ).append( prop ).append( "\", ignore = true )</b><br>" ); } sb.append( "</html>" ); return sb.toString(); } @Override protected ASTRewrite getASTRewrite(CompilationUnit unit, ASTNode nodeWithMarker, IMarker marker) { AST ast = unit.getAST(); ASTRewrite rewrite = ASTRewrite.create( ast ); MethodDeclaration method = (MethodDeclaration) nodeWithMarker; ListRewrite mappingList = getListForAddingMappingAnnotations( unit, properties, ast, rewrite, method ); addMappingAnnotations( properties, ast, mappingList ); addImportIfRequired( unit, rewrite, MAPPING_FQ_NAME ); return rewrite; } private ListRewrite getListForAddingMappingAnnotations(CompilationUnit unit, Collection<String> properties, AST ast, ASTRewrite rewrite, MethodDeclaration method) { // if there is already an @Mappings annotation, add the new @Mapping's there Annotation mappingsAnnotation = findAnnotation( method, MAPPINGS_FQ_NAME ); if ( mappingsAnnotation != null ) { return rewrite.getListRewrite( ( (SingleMemberAnnotation) mappingsAnnotation ).getValue(), ArrayInitializer.EXPRESSIONS_PROPERTY ); } // if repeatable @Mapping's are supported, then add the annotations directly to the method if ( supportsRepeatableMapping( unit ) ) { return rewrite.getListRewrite( method, MethodDeclaration.MODIFIERS2_PROPERTY ); } // if we only need to add one @Mapping and there is none, yet, then add the single annotation directly Annotation singleMappingAnnotation = findAnnotation( method, MAPPING_FQ_NAME ); if ( singleMappingAnnotation == null && properties.size() == 1 ) { return rewrite.getListRewrite( method, MethodDeclaration.MODIFIERS2_PROPERTY ); } // create a new @Mappings annotation and add the @Mapping's there ListRewrite mappingList = addNewMappingsAnnotation( unit, rewrite, ast, method ); if ( singleMappingAnnotation != null ) { rewrite.getListRewrite( method, MethodDeclaration.MODIFIERS2_PROPERTY ) .remove( singleMappingAnnotation, null ); mappingList.insertFirst( singleMappingAnnotation, null ); } return mappingList; } private ListRewrite addNewMappingsAnnotation(CompilationUnit unit, ASTRewrite rewrite, AST ast, MethodDeclaration method) { SingleMemberAnnotation mappings = ast.newSingleMemberAnnotation(); mappings.setTypeName( ast.newName( MAPPINGS_SIMPLE_NAME ) ); ArrayInitializer mappingArray = ast.newArrayInitializer(); mappings.setValue( mappingArray ); ListRewrite annotations = rewrite.getListRewrite( method, MethodDeclaration.MODIFIERS2_PROPERTY ); annotations.insertFirst( mappings, null ); addImportIfRequired( unit, rewrite, MAPPINGS_FQ_NAME ); return rewrite.getListRewrite( mappingArray, ArrayInitializer.EXPRESSIONS_PROPERTY ); } private Annotation findAnnotation(MethodDeclaration method, String annotationName) { FindAnnotationByNameVisitor locatedAnnotation = new FindAnnotationByNameVisitor( annotationName ); method.accept( locatedAnnotation ); return locatedAnnotation.getLocatedNode(); } private boolean supportsRepeatableMapping(CompilationUnit unit) { try { IJavaElement javaElement = unit.getJavaElement(); if ( javaElement == null ) { return false; } IType mappingType = javaElement.getJavaProject().findType( MAPPING_FQ_NAME ); if ( mappingType == null ) { return false; } for ( IAnnotation annotation : mappingType.getAnnotations() ) { if ( annotation.getElementName().equals( "Repeatable" ) || annotation.getElementName().equals( "java.lang.annotation.Repeatable" ) ) { return true; } } } catch ( CoreException e ) { return false; } return false; } @SuppressWarnings("unchecked") private void addMappingAnnotations(Collection<String> properties, AST ast, ListRewrite mappingList) { LinkedList<NormalAnnotation> toAdd = new LinkedList<NormalAnnotation>(); for ( String property : properties ) { NormalAnnotation mapping = ast.newNormalAnnotation(); mapping.setTypeName( ast.newName( MAPPING_SIMPLE_NAME ) ); MemberValuePair valuePair = ast.newMemberValuePair(); valuePair.setName( ast.newSimpleName( MAPPING_MEMBER_TARGET ) ); StringLiteral literal = ast.newStringLiteral(); literal.setLiteralValue( property ); valuePair.setValue( literal ); mapping.values().add( valuePair ); valuePair = ast.newMemberValuePair(); valuePair.setName( ast.newSimpleName( MAPPING_MEMBER_IGNORE ) ); valuePair.setValue( ast.newBooleanLiteral( true ) ); mapping.values().add( valuePair ); toAdd.addFirst( mapping ); } for ( NormalAnnotation mapping : toAdd ) { mappingList.insertFirst( mapping, null ); } } }