/**
* 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;
import java.util.Iterator;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
import org.eclipse.jdt.ui.SharedASTProvider;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IMarkerResolution2;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.mapstruct.eclipse.internal.quickfix.visitors.FindMethodByPositionVisitor;
/**
* Base class for quick fixes
*
* @author Andreas Gudian
*/
@SuppressWarnings("restriction")
public abstract class MapStructQuickFix implements IMarkerResolution2 {
@Override
public void run(IMarker marker) {
ICompilationUnit compilationUnit = null;
try {
IResource resource = marker.getResource();
IJavaElement javaElement = JavaCore.create( resource );
compilationUnit = (ICompilationUnit) javaElement.getAdapter( ICompilationUnit.class );
IEditorInput input = EditorUtility.getEditorInput( compilationUnit );
if ( input != null ) {
CompilationUnit astCompilationUnit = toAST( compilationUnit );
ASTNode locatedNode =
locateASTNodeForSartingOffset( findProblemStart( input, marker ), astCompilationUnit );
if ( locatedNode != null ) {
ASTRewrite rewrite = getASTRewrite( astCompilationUnit, locatedNode, marker );
if ( rewrite != null ) {
compilationUnit.applyTextEdit( rewrite.rewriteAST(), null );
compilationUnit.becomeWorkingCopy( null );
compilationUnit.commitWorkingCopy( true, null );
compilationUnit.discardWorkingCopy();
marker.delete();
}
}
}
}
catch ( CoreException e ) {
throw new RuntimeException( e );
}
}
/**
* @param input editor input
* @param marker the marker
* @return the offset of the problem, either as currently computed in an open (dirty) editor, or as originally
* stated in the marker
* @throws CoreException
*/
private static int findProblemStart(IEditorInput input, IMarker marker) throws CoreException {
IAnnotationModel model =
JavaPlugin.getDefault().getCompilationUnitDocumentProvider().getAnnotationModel( input );
if ( model != null ) {
Iterator<Annotation> iter = model.getAnnotationIterator();
while ( iter.hasNext() ) {
Annotation curr = iter.next();
if ( curr instanceof MarkerAnnotation ) {
MarkerAnnotation annot = (MarkerAnnotation) curr;
if ( marker.equals( annot.getMarker() ) ) {
Position pos = model.getPosition( annot );
if ( pos != null ) {
return pos.getOffset();
}
}
}
}
}
Integer charStart = (Integer) marker.getAttribute( IMarker.CHAR_START );
return charStart != null ? charStart : -1;
}
private ASTNode locateASTNodeForSartingOffset(int problemOffset, CompilationUnit astCompilationUnit)
throws CoreException {
FindMethodByPositionVisitor visitor = new FindMethodByPositionVisitor( problemOffset );
astCompilationUnit.accept( visitor );
return visitor.getLocatedNode();
}
/**
* @param unit the compilation unit
* @param nodeWithMarker the ASTNode that is located at the start position of the marker
* @param marker the marker
* @return the rewrite to be performed, or <code>null</code> in case no changes are to be performed
*/
protected abstract ASTRewrite getASTRewrite(CompilationUnit unit, ASTNode nodeWithMarker, IMarker marker);
/**
* Add an import statement for the fullyQualifiedName if it is not yet imported
*
* @param compilationUnit the compilation unit
* @param rewrite the rewrite to modify
* @param fullyQualifiedName the fully qualified name of the type to add an import for
*/
protected void addImportIfRequired(CompilationUnit compilationUnit, ASTRewrite rewrite, String fullyQualifiedName) {
if ( !hasImport( compilationUnit, fullyQualifiedName )
&& requiresImport( compilationUnit, fullyQualifiedName ) ) {
AST ast = compilationUnit.getAST();
ImportDeclaration declaration = ast.newImportDeclaration();
declaration.setName( ast.newName( fullyQualifiedName ) );
rewrite.getListRewrite( compilationUnit, CompilationUnit.IMPORTS_PROPERTY ).insertLast( declaration, null );
}
}
private static boolean requiresImport(CompilationUnit compilationUnit, String fullyQualifiedName) {
if ( isPrimitive( fullyQualifiedName )
|| fullyQualifiedName.startsWith( "java.lang." ) ) {
return false;
}
if ( compilationUnit.getPackage().getName().getFullyQualifiedName()
.equals( toSimpleName( fullyQualifiedName ) ) ) {
return false;
}
return true;
}
private static boolean hasImport(CompilationUnit compilationUnit, String fullyQualifiedName) {
for ( Object obj : compilationUnit.imports() ) {
ImportDeclaration importDec = (ImportDeclaration) obj;
if ( importDec.getName().getFullyQualifiedName().equals( fullyQualifiedName ) ) {
return true;
}
}
return false;
}
protected static String toSimpleName(String fqTypeName) {
int lastIndex = fqTypeName.lastIndexOf( '.' );
if ( lastIndex >= 0 ) {
return fqTypeName.substring( lastIndex + 1 );
}
return fqTypeName;
}
protected static String capitalize(String string) {
return firstChar( string ).toUpperCase() + afterFirstChar( string );
}
protected static String uncapitalize(String string) {
return firstChar( string ).toLowerCase() + afterFirstChar( string );
}
private static String firstChar(String string) {
return string.length() > 0 ? string.substring( 0, 1 ) : "";
}
private static String afterFirstChar(String string) {
return string.length() > 1 ? string.substring( 1 ) : "";
}
protected static boolean isPrimitive(String type) {
return null != PrimitiveType.toCode( type );
}
@Override
public String getDescription() {
return null;
}
@Override
public Image getImage() {
return JavaPluginImages.get( JavaPluginImages.IMG_CORRECTION_CHANGE );
}
private static CompilationUnit toAST(ICompilationUnit unit) {
CompilationUnit astRoot = SharedASTProvider.getAST( unit, SharedASTProvider.WAIT_YES, null );
if ( astRoot == null ) {
astRoot = ASTResolving.createQuickFixAST( unit, null );
}
return astRoot;
}
}