com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix.FindViewByIdMarkerResolution.java Source code

Java tutorial

Introduction

Here is the source code for com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix.FindViewByIdMarkerResolution.java

Source

/*
 * 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;
    }

}