blob: f29138d00360318fea059519e95158663e359825 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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 com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.text.IDocument;
import org.eclipse.swt.graphics.Image;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMarkerResolution2;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import com.motorola.studio.android.common.utilities.EclipseUtils;
import com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix.i18n.MessagesNLS;
/**
* MarkerResolution responsible for moving the findViewByID call outside the loop.
*/
public class FindViewByIdMarkerResolution implements IMarkerResolution2
{
/*
* (non-Javadoc)
* @see org.eclipse.ui.IMarkerResolution#getLabel()
*/
public String getLabel()
{
return MessagesNLS.FindViewByIdMarkerResolution_Label;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.IMarkerResolution#run(org.eclipse.core.resources.IMarker)
*/
public void run(IMarker marker)
{
IResource resource = marker.getResource();
final ICompilationUnit iCompilationUnit =
JavaCore.createCompilationUnitFrom((IFile) resource);
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setProject(JavaCore.create(resource.getProject()));
parser.setResolveBindings(true);
parser.setSource(iCompilationUnit);
parser.setStatementsRecovery(true);
parser.setBindingsRecovery(true);
final CompilationUnit compUnit = (CompilationUnit) parser.createAST(null);
try
{
final int lineNumber = marker.getAttribute(IMarker.LINE_NUMBER, -1);
MethodInvocation invokedMethod = null;
//Look for the invokedMethod that shall be moved
invokedMethod = getMethodInvocation(compUnit, lineNumber, invokedMethod);
IEditorPart editor = openEditor(iCompilationUnit);
ASTNode loopNode = getLoopNode(invokedMethod);
//Retrieve block parent for the loop statement.
Block targetBlock = (Block) loopNode.getParent();
List<Statement> statements = targetBlock.statements();
int i = getLoopStatementIndex(loopNode, statements);
//Add the node before the loop.
compUnit.recordModifications();
ASTNode invokedMethodStatement = getInvokedStatement(invokedMethod);
final VariableDeclarationStatement varDeclarationStatement[] =
new VariableDeclarationStatement[1];
//Verify if the invoke statement contains a variable attribution
if (invokedMethodStatement instanceof ExpressionStatement)
{
ExpressionStatement expressionStatement =
(ExpressionStatement) invokedMethodStatement;
Expression expression = expressionStatement.getExpression();
if (expression instanceof Assignment)
{
Expression leftHandSide = ((Assignment) expression).getLeftHandSide();
if (leftHandSide instanceof SimpleName) //Search for the variable declaration
{
SimpleName simpleName = (SimpleName) leftHandSide;
final String varName = simpleName.getIdentifier();
loopNode.accept(new ASTVisitor()
{
@Override
public boolean visit(VariableDeclarationStatement node) //Visit all variable declarations inside the loop looking for the variable which receives the findViewById result
{
List<VariableDeclarationFragment> fragments = node.fragments();
for (VariableDeclarationFragment fragment : fragments)
{
if (fragment.getName().getIdentifier().equals(varName))
{
varDeclarationStatement[0] = node;
break;
}
}
return super.visit(node);
}
});
}
}
}
//Variable is declared inside the loop, now let's move the variable declaration if needed
if (varDeclarationStatement[0] != null)
{
ASTNode varDeclarationSubTree =
ASTNode.copySubtree(targetBlock.getAST(), varDeclarationStatement[0]);
statements.add(i, (Statement) varDeclarationSubTree.getRoot());
//Delete the node inside loop.
varDeclarationStatement[0].delete();
i++;
}
ASTNode copySubtree = ASTNode.copySubtree(targetBlock.getAST(), invokedMethodStatement);
statements.add(i, (Statement) copySubtree.getRoot());
//Delete the node inside loop.
invokedMethodStatement.delete();
// apply changes to file
final Map<?, ?> mapOptions = JavaCore.create(resource.getProject()).getOptions(true);
final IDocument document =
((AbstractTextEditor) editor).getDocumentProvider().getDocument(
editor.getEditorInput());
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
{
public void run()
{
try
{
TextEdit edit = compUnit.rewrite(document, mapOptions);
iCompilationUnit.applyTextEdit(edit, new NullProgressMonitor());
}
catch (JavaModelException e)
{
EclipseUtils.showErrorDialog(
MessagesNLS.FindViewByIdMarkerResolution_Error_Msg_Title,
MessagesNLS.FindViewByIdMarkerResolution_Error_Aplying_Changes
+ e.getMessage());
}
}
});
marker.delete();
}
catch (Exception e)
{
EclipseUtils.showErrorDialog(MessagesNLS.FindViewByIdMarkerResolution_Error_Msg_Title,
MessagesNLS.FindViewByIdMarkerResolution_Error_Could_Not_Fix_Code);
}
}
private IEditorPart openEditor(final ICompilationUnit iCompilationUnit)
{
IEditorPart editor = null;
try
{
editor = JavaUI.openInEditor(iCompilationUnit);
}
catch (Exception e)
{
EclipseUtils.showErrorDialog(MessagesNLS.FindViewByIdMarkerResolution_Error_Msg_Title,
MessagesNLS.FindViewByIdMarkerResolution_Error_Unable_To_Open_Editor);
}
return editor;
}
private MethodInvocation getMethodInvocation(final CompilationUnit compUnit,
final int lineNumber, MethodInvocation invokedMethod)
{
final MethodInvocation[] tempMethodInvocation = new MethodInvocation[1];
compUnit.accept(new ASTVisitor()
{
@Override
public boolean visit(MethodInvocation node)
{
if (compUnit.getLineNumber(node.getStartPosition()) == lineNumber)
{
tempMethodInvocation[0] = node;
}
return super.visit(node);
};
});
if (tempMethodInvocation[0] != null)
{
invokedMethod = tempMethodInvocation[0];
}
return invokedMethod;
}
/*
* Look for Statement containing the invokedMethod
*/
private ASTNode getInvokedStatement(MethodInvocation invokedMethod)
{
boolean found = false;
ASTNode parent = invokedMethod.getParent();
do
{
if ((parent instanceof Statement))
{
found = true;
}
else
{
parent = parent.getParent();
}
}
while (!found);
return parent;
}
/*
* Resturns the index of the loop statement, inside a list of statements
*/
private int getLoopStatementIndex(ASTNode loopNode, List<Statement> statements)
{
int i = 0;
for (i = 0; i < statements.size(); i++)
{
Statement statement = statements.get(i);
if (statement.equals(loopNode))
{
break;
}
}
return i;
}
/*
* Find the loop node that contains the called method
*/
private ASTNode getLoopNode(MethodInvocation invokedMethod)
{
boolean found = false;
ASTNode parent = invokedMethod.getParent();
do
{
parent = parent.getParent();
if ((parent instanceof ForStatement) || (parent instanceof DoStatement)
|| (parent instanceof WhileStatement)
|| (parent instanceof EnhancedForStatement))
{
found = true;
}
}
while (!found);
return parent;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.IMarkerResolution2#getDescription()
*/
public String getDescription()
{
return MessagesNLS.FindViewByIdMarkerResolution_Description;
}
public Image getImage()
{
// TODO Auto-generated method stub
return null;
}
}