org.autorefactor.refactoring.rules.AndroidWakeLockRefactoring.java Source code

Java tutorial

Introduction

Here is the source code for org.autorefactor.refactoring.rules.AndroidWakeLockRefactoring.java

Source

/*
 * AutoRefactor - Eclipse plugin to automatically refactor Java code bases.
 *
 * Copyright (C) 2016 Luis Cruz - Android Refactoring
 * Copyright (C) 2016 Jean-Nol Rouvignac - code cleanups
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program under LICENSE-GNUGPL.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution under LICENSE-ECLIPSE, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.autorefactor.refactoring.rules;

import static org.autorefactor.refactoring.ASTHelper.DO_NOT_VISIT_SUBTREE;
import static org.autorefactor.refactoring.ASTHelper.VISIT_SUBTREE;
import static org.autorefactor.refactoring.ASTHelper.getAncestor;
import static org.autorefactor.refactoring.ASTHelper.isMethod;

import org.autorefactor.preferences.Preferences;
import org.autorefactor.refactoring.ASTBuilder;
import org.autorefactor.refactoring.FinderVisitor;
import org.autorefactor.refactoring.Refactorings;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.TypeDeclaration;

/** See {@link #getDescription()} method. */
public class AndroidWakeLockRefactoring extends AbstractRefactoringRule {
    /**
     * Get the name.
     *
     * @return the name.
     */
    public String getName() {
        return "Android WakeLock";
    }

    /**
     * Get the description.
     *
     * @return the description.
     */
    public String getDescription() {
        return "Android - Failing to release a wakelock properly can keep the Android device "
                + "in a high power mode, which reduces battery life. "
                + "There are several causes for this, such as "
                + "releasing the wake lock in onDestroy() instead of in onPause(), "
                + "failing to call release() in all possible code paths after an acquire(), " + "and so on.";
    }

    /**
     * Get the reason.
     *
     * @return the reason.
     */
    public String getReason() {
        return "It improves the performance.";
    }

    @Override
    public boolean isEnabled(Preferences preferences) {
        // FIXME enable only when android libraries are detected
        return super.isEnabled(preferences);
    }

    @Override
    public boolean visit(MethodInvocation node) {
        if (isMethod(node, "android.os.PowerManager.WakeLock", "release")) {
            // check whether it is being called in onDestroy()
            MethodDeclaration enclosingMethod = getAncestor(node, MethodDeclaration.class);
            if (isMethod(enclosingMethod, "android.app.Activity", "onDestroy")) {
                final Refactorings r = ctx.getRefactorings();
                TypeDeclaration typeDeclaration = getAncestor(enclosingMethod, TypeDeclaration.class);
                MethodDeclaration onPauseMethod = findMethod(typeDeclaration, "onPause");
                if (onPauseMethod != null && node.getParent().getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
                    r.remove(node.getParent());
                    r.insertLast(onPauseMethod.getBody(), Block.STATEMENTS_PROPERTY,
                            createWakelockReleaseStmt(node));
                } else {
                    // Add the missing onPause() method to the class.
                    r.insertAfter(createOnPauseMethodDeclaration(), enclosingMethod);
                }
                return DO_NOT_VISIT_SUBTREE;
            }
        } else if (isMethod(node, "android.os.PowerManager.WakeLock", "acquire")) {
            final Refactorings r = ctx.getRefactorings();
            TypeDeclaration typeDeclaration = getAncestor(node, TypeDeclaration.class);
            ReleasePresenceChecker releasePresenceChecker = new ReleasePresenceChecker();
            if (!releasePresenceChecker.findOrDefault(typeDeclaration, false)) {
                MethodDeclaration onPauseMethod = findMethod(typeDeclaration, "onPause");
                if (onPauseMethod != null && node.getParent().getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
                    r.insertLast(onPauseMethod.getBody(), Block.STATEMENTS_PROPERTY,
                            createWakelockReleaseStmt(node));
                } else {
                    r.insertLast(typeDeclaration, typeDeclaration.getBodyDeclarationsProperty(),
                            createOnPauseMethodDeclaration());
                }
                return DO_NOT_VISIT_SUBTREE;
            }
        }
        return VISIT_SUBTREE;
    }

    private Statement createWakelockReleaseStmt(MethodInvocation methodInvocation) {
        final ASTBuilder b = ctx.getASTBuilder();
        return b.if0(b.not(b.invoke(b.copyExpression(methodInvocation), "isHeld")),
                b.block(b.toStmt(b.invoke(b.copyExpression(methodInvocation), "release"))));
    }

    private MethodDeclaration createOnPauseMethodDeclaration() {
        final ASTBuilder b = ctx.getASTBuilder();
        return b.method(b.extendedModifiers(b.annotation("Override"), b.protected0()), "onPause", b.parameters(),
                b.block(b.toStmt(b.superInvoke("onPause"))));
    }

    private MethodDeclaration findMethod(TypeDeclaration typeDeclaration, String methodToFind) {
        if (typeDeclaration != null) {
            for (MethodDeclaration method : typeDeclaration.getMethods()) {
                IMethodBinding methodBinding = method.resolveBinding();
                if (methodBinding != null && methodToFind.equals(methodBinding.getName())
                        && method.parameters().isEmpty()) {
                    return method;
                }
            }
        }
        return null;
    }

    private static class ReleasePresenceChecker extends FinderVisitor<Boolean> {
        @Override
        public boolean visit(MethodInvocation node) {
            if (isMethod(node, "android.os.PowerManager.WakeLock", "release")) {
                setResult(true);
                return DO_NOT_VISIT_SUBTREE;
            }
            return VISIT_SUBTREE;
        }
    }
}