Java tutorial
/** * Copyright 2012-2015 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 java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; 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; 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; /** * Quick fix for error/warning "Unmapped target property" / "Unmapped target properties" that adds * {@code @Mapping( target = "<property>", ignore = true)} to the method. * * @author Andreas Gudian */ public class AddIgnoreTargetMappingAnnotationQuickFix extends MapStructQuickFix { public AddIgnoreTargetMappingAnnotationQuickFix() { super("Ignore unmapped target properties"); } @Override public boolean canFix(IMarker marker) { String message = getMessage(marker); return message.startsWith("Unmapped target property:") || message.startsWith("Unmapped target properties:"); } @Override protected ASTRewrite getASTRewrite(CompilationUnit unit, ASTNode nodeWithMarker, IMarker marker) { Collection<String> properties = extractPropertiesFromMessage(getMessage(marker)); if (properties == null) { return null; } 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); } } private static Collection<String> extractPropertiesFromMessage(String msg) { Pattern p = Pattern.compile("Unmapped target (property|properties): \"([^\"]+)\"."); Matcher matcher = p.matcher(msg); if (matcher.matches()) { String props = matcher.group(2); return Arrays.asList(props.split(", ")); } return null; } }