edu.illinois.jflow.core.transformations.code.ExtractClosureAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for edu.illinois.jflow.core.transformations.code.ExtractClosureAnalyzer.java

Source

/**
 * This class derives
 * from {@link org.eclipse.jdt.internal.corext.refactoring.code.ExtractMethodAnalyzer} and is
 * licensed under the Eclipse Public License.
 */
package edu.illinois.jflow.core.transformations.code;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.compiler.ITerminalSymbols;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.ContinueStatement;
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.FieldAccess;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.Message;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
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.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
import org.eclipse.jdt.internal.corext.dom.Selection;
import org.eclipse.jdt.internal.corext.refactoring.Checks;
import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
import org.eclipse.jdt.internal.corext.refactoring.code.LocalTypeAnalyzer;
import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowContext;
import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
import org.eclipse.jdt.internal.corext.refactoring.code.flow.InOutFlowAnalyzer;
import org.eclipse.jdt.internal.corext.refactoring.code.flow.InputFlowAnalyzer;
import org.eclipse.jdt.internal.corext.refactoring.util.CodeAnalyzer;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;

/**
 * Analyzes the "flow" in the selected statements to find potential reads/writes.
 * 
 * @author Nicholas Chen
 * 
 */
@SuppressWarnings("restriction")
class ExtractClosureAnalyzer extends CodeAnalyzer {

    public static final int ERROR = -2;

    public static final int UNDEFINED = -1;

    public static final int NO = 0;

    public static final int EXPRESSION = 1;

    public static final int ACCESS_TO_LOCAL = 2;

    public static final int RETURN_STATEMENT_VOID = 3;

    public static final int RETURN_STATEMENT_VALUE = 4;

    public static final int MULTIPLE = 5;

    /** This is either a method declaration or an initializer */
    private BodyDeclaration fEnclosingBodyDeclaration;

    private IMethodBinding fEnclosingMethodBinding;

    private int fMaxVariableId;

    private int fReturnKind;

    private FlowInfo fInputFlowInfo;

    private FlowContext fInputFlowContext;

    private IVariableBinding[] fArguments;

    private IVariableBinding[] fMethodLocals;

    private ITypeBinding[] fTypeVariables;

    private IVariableBinding fReturnValue;

    private IVariableBinding[] fCallerLocals;

    private IVariableBinding fReturnLocal;

    private ITypeBinding[] fAllExceptions;

    private ITypeBinding fExpressionBinding;

    private boolean fForceStatic;

    private boolean fIsLastStatementSelected;

    private SimpleName fEnclosingLoopLabel;

    private IVariableBinding[] fPotentialReadsOutsideOfClosure;

    public ExtractClosureAnalyzer(ICompilationUnit unit, Selection selection) throws CoreException {
        super(unit, selection, false);
    }

    public BodyDeclaration getEnclosingBodyDeclaration() {
        return fEnclosingBodyDeclaration;
    }

    public int getReturnKind() {
        return fReturnKind;
    }

    public boolean extractsExpression() {
        return fReturnKind == EXPRESSION;
    }

    public boolean generateImport() {
        switch (fReturnKind) {
        case EXPRESSION:
            return true;
        default:
            return false;
        }
    }

    public IVariableBinding[] getArguments() {
        return fArguments;
    }

    public IVariableBinding[] getMethodLocals() {
        return fMethodLocals;
    }

    public IVariableBinding getReturnValue() {
        return fReturnValue;
    }

    public IVariableBinding[] getCallerLocals() {
        return fCallerLocals;
    }

    public IVariableBinding getReturnLocal() {
        return fReturnLocal;
    }

    public ITypeBinding getExpressionBinding() {
        return fExpressionBinding;
    }

    public boolean getForceStatic() {
        return fForceStatic;
    }

    public ITypeBinding[] getTypeVariables() {
        return fTypeVariables;
    }

    public IVariableBinding[] getPotentialReadsOutsideOfClosure() {
        return fPotentialReadsOutsideOfClosure;
    }

    //---- Activation checking ---------------------------------------------------------------------------

    public RefactoringStatus checkInitialConditions() {
        RefactoringStatus result = getStatus();
        checkExpression(result);
        if (result.hasFatalError())
            return result;

        fReturnKind = UNDEFINED;
        fMaxVariableId = LocalVariableIndex.perform(fEnclosingBodyDeclaration);
        if (analyzeSelection(result).hasFatalError())
            return result;

        int returns = fReturnKind == NO ? 0 : 1;
        if (fReturnValue != null) {
            fReturnKind = ACCESS_TO_LOCAL;
            returns++;
        }
        if (isExpressionSelected()) {
            fReturnKind = EXPRESSION;
            returns++;
        }

        if (returns > 1) {
            result.addFatalError(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_ambiguous_return_value,
                    JavaStatusContext.create(fCUnit, getSelection()));
            fReturnKind = MULTIPLE;
            return result;
        }
        return result;
    }

    private void checkExpression(RefactoringStatus status) {
        ASTNode[] nodes = getSelectedNodes();
        if (nodes != null && nodes.length == 1) {
            ASTNode node = nodes[0];
            if (node instanceof Type) {
                status.addFatalError(
                        JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_type_reference,
                        JavaStatusContext.create(fCUnit, node));
            } else if (node.getLocationInParent() == SwitchCase.EXPRESSION_PROPERTY) {
                status.addFatalError(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_switch_case,
                        JavaStatusContext.create(fCUnit, node));
            } else if (node instanceof Annotation || ASTNodes.getParent(node, Annotation.class) != null) {
                status.addFatalError(
                        JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_from_annotation,
                        JavaStatusContext.create(fCUnit, node));
            }
        }
    }

    //    !!! -- +/- same as in ExtractTempRefactoring
    public boolean isLiteralNodeSelected() {
        ASTNode[] nodes = getSelectedNodes();
        if (nodes.length != 1)
            return false;
        ASTNode node = nodes[0];
        switch (node.getNodeType()) {
        case ASTNode.BOOLEAN_LITERAL:
        case ASTNode.CHARACTER_LITERAL:
        case ASTNode.NULL_LITERAL:
        case ASTNode.NUMBER_LITERAL:
            return true;

        default:
            return false;
        }
    }

    //---- Input checking -----------------------------------------------------------------------------------

    public void checkInput(RefactoringStatus status, String methodName, ASTNode destination) {
        ITypeBinding[] arguments = getArgumentTypes();
        ITypeBinding type = ASTNodes.getEnclosingType(destination);
        status.merge(Checks.checkMethodInType(type, methodName, arguments));
        status.merge(Checks.checkMethodInHierarchy(type.getSuperclass(), methodName, null, arguments));
    }

    private ITypeBinding[] getArgumentTypes() {
        ITypeBinding[] result = new ITypeBinding[fArguments.length];
        for (int i = 0; i < fArguments.length; i++) {
            result[i] = fArguments[i].getType();
        }
        return result;
    }

    private RefactoringStatus analyzeSelection(RefactoringStatus status) {
        fInputFlowContext = new FlowContext(0, fMaxVariableId + 1);
        fInputFlowContext.setConsiderAccessMode(true);
        fInputFlowContext.setComputeMode(FlowContext.ARGUMENTS);

        InOutFlowAnalyzer flowAnalyzer = new InOutFlowAnalyzer(fInputFlowContext);
        fInputFlowInfo = flowAnalyzer.perform(getSelectedNodes());

        if (fInputFlowInfo.branches()) {
            String canHandleBranchesProblem = canHandleBranches();
            if (canHandleBranchesProblem != null) {
                status.addFatalError(canHandleBranchesProblem, JavaStatusContext.create(fCUnit, getSelection()));
                fReturnKind = ERROR;
                return status;
            }
        }
        if (fInputFlowInfo.isValueReturn()) {
            fReturnKind = RETURN_STATEMENT_VALUE;
        } else if (fInputFlowInfo.isVoidReturn()
                || (fInputFlowInfo.isPartialReturn() && isVoidMethod() && isLastStatementSelected())) {
            fReturnKind = RETURN_STATEMENT_VOID;
        } else if (fInputFlowInfo.isNoReturn() || fInputFlowInfo.isThrow() || fInputFlowInfo.isUndefined()) {
            fReturnKind = NO;
        }

        if (fReturnKind == UNDEFINED) {
            status.addError(JFlowRefactoringCoreMessages.FlowAnalyzer_execution_flow,
                    JavaStatusContext.create(fCUnit, getSelection()));
            fReturnKind = NO;
        }
        computeInput();
        computeExceptions();
        computeOutput(status);
        if (status.hasFatalError())
            return status;

        adjustArgumentsAndMethodLocals();
        compressArrays();
        return status;
    }

    private String canHandleBranches() {
        if (fReturnValue != null)
            return JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_branch_mismatch;

        ASTNode[] selectedNodes = getSelectedNodes();
        final ASTNode lastSelectedNode = selectedNodes[selectedNodes.length - 1];
        Statement body = getParentLoopBody(lastSelectedNode.getParent());
        if (!(body instanceof Block))
            return JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_branch_mismatch;

        if (body != lastSelectedNode) {
            Block block = (Block) body;
            List<Statement> statements = block.statements();
            ASTNode lastStatementInLoop = statements.get(statements.size() - 1);
            if (lastSelectedNode != lastStatementInLoop)
                return JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_branch_mismatch;
        }

        final String continueMatchesLoopProblem[] = { null };
        for (int i = 0; i < selectedNodes.length; i++) {
            final ASTNode astNode = selectedNodes[i];
            astNode.accept(new ASTVisitor() {
                ArrayList<String> fLocalLoopLabels = new ArrayList<String>();

                @Override
                public boolean visit(BreakStatement node) {
                    SimpleName label = node.getLabel();
                    if (label != null && !fLocalLoopLabels.contains(label.getIdentifier())) {
                        continueMatchesLoopProblem[0] = Messages.format(
                                JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_branch_break_mismatch,
                                new Object[] { ("break " + label.getIdentifier()) }); //$NON-NLS-1$
                    }
                    return false;
                }

                @Override
                public boolean visit(LabeledStatement node) {
                    SimpleName label = node.getLabel();
                    if (label != null)
                        fLocalLoopLabels.add(label.getIdentifier());
                    return true;
                }

                @Override
                public void endVisit(ContinueStatement node) {
                    SimpleName label = node.getLabel();
                    if (label != null && !fLocalLoopLabels.contains(label.getIdentifier())) {
                        if (fEnclosingLoopLabel == null
                                || !label.getIdentifier().equals(fEnclosingLoopLabel.getIdentifier())) {
                            continueMatchesLoopProblem[0] = Messages.format(
                                    JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_branch_continue_mismatch,
                                    new Object[] { "continue " + label.getIdentifier() }); //$NON-NLS-1$
                        }
                    }
                }
            });
        }
        return continueMatchesLoopProblem[0];
    }

    private Statement getParentLoopBody(ASTNode node) {
        Statement stmt = null;
        ASTNode start = node;
        while (start != null && !(start instanceof ForStatement) && !(start instanceof DoStatement)
                && !(start instanceof WhileStatement) && !(start instanceof EnhancedForStatement)) {
            start = start.getParent();
        }
        if (start instanceof ForStatement) {
            stmt = ((ForStatement) start).getBody();
        } else if (start instanceof DoStatement) {
            stmt = ((DoStatement) start).getBody();
        } else if (start instanceof WhileStatement) {
            stmt = ((WhileStatement) start).getBody();
        } else if (start instanceof EnhancedForStatement) {
            stmt = ((EnhancedForStatement) start).getBody();
        }
        if (start.getParent() instanceof LabeledStatement) {
            LabeledStatement labeledStatement = (LabeledStatement) start.getParent();
            fEnclosingLoopLabel = labeledStatement.getLabel();
        }
        return stmt;
    }

    private boolean isVoidMethod() {
        // if we have an initializer
        if (fEnclosingMethodBinding == null)
            return true;
        ITypeBinding binding = fEnclosingMethodBinding.getReturnType();
        if (fEnclosingBodyDeclaration.getAST().resolveWellKnownType("void").equals(binding)) //$NON-NLS-1$
            return true;
        return false;
    }

    public boolean isLastStatementSelected() {
        return fIsLastStatementSelected;
    }

    private void computeLastStatementSelected() {
        ASTNode[] nodes = getSelectedNodes();
        if (nodes.length == 0) {
            fIsLastStatementSelected = false;
        } else {
            Block body = null;
            if (fEnclosingBodyDeclaration instanceof MethodDeclaration) {
                body = ((MethodDeclaration) fEnclosingBodyDeclaration).getBody();
            } else if (fEnclosingBodyDeclaration instanceof Initializer) {
                body = ((Initializer) fEnclosingBodyDeclaration).getBody();
            }
            if (body != null) {
                List<Statement> statements = body.statements();
                fIsLastStatementSelected = nodes[nodes.length - 1] == statements.get(statements.size() - 1);
            }
        }
    }

    private void computeInput() {
        int argumentMode = FlowInfo.READ | FlowInfo.READ_POTENTIAL | FlowInfo.WRITE_POTENTIAL | FlowInfo.UNKNOWN;
        fArguments = removeSelectedDeclarations(fInputFlowInfo.get(fInputFlowContext, argumentMode));
        fMethodLocals = removeSelectedDeclarations(
                fInputFlowInfo.get(fInputFlowContext, FlowInfo.WRITE | FlowInfo.WRITE_POTENTIAL));
        fTypeVariables = computeTypeVariables(fInputFlowInfo.getTypeVariables());
    }

    private IVariableBinding[] removeSelectedDeclarations(IVariableBinding[] bindings) {
        List<IVariableBinding> result = new ArrayList<IVariableBinding>(bindings.length);
        Selection selection = getSelection();
        for (int i = 0; i < bindings.length; i++) {
            ASTNode decl = ((CompilationUnit) fEnclosingBodyDeclaration.getRoot()).findDeclaringNode(bindings[i]);
            if (!selection.covers(decl))
                result.add(bindings[i]);
        }
        return result.toArray(new IVariableBinding[result.size()]);
    }

    private ITypeBinding[] computeTypeVariables(ITypeBinding[] bindings) {
        Selection selection = getSelection();
        Set<ITypeBinding> result = new HashSet<ITypeBinding>();
        // first remove all type variables that come from outside of the method
        // or are covered by the selection
        CompilationUnit compilationUnit = (CompilationUnit) fEnclosingBodyDeclaration.getRoot();
        for (int i = 0; i < bindings.length; i++) {
            ASTNode decl = compilationUnit.findDeclaringNode(bindings[i]);
            if (decl == null || (!selection.covers(decl) && decl.getParent() instanceof MethodDeclaration))
                result.add(bindings[i]);
        }
        // all all type variables which are needed since a local variable uses it
        for (int i = 0; i < fArguments.length; i++) {
            IVariableBinding arg = fArguments[i];
            ITypeBinding type = arg.getType();
            if (type != null && type.isTypeVariable()) {
                ASTNode decl = compilationUnit.findDeclaringNode(type);
                if (decl == null || (!selection.covers(decl) && decl.getParent() instanceof MethodDeclaration))
                    result.add(type);
            }
        }
        return result.toArray(new ITypeBinding[result.size()]);
    }

    private void computeOutput(RefactoringStatus status) {
        // First find all writes inside the selection.
        FlowContext flowContext = new FlowContext(0, fMaxVariableId + 1);
        flowContext.setConsiderAccessMode(true);
        flowContext.setComputeMode(FlowContext.RETURN_VALUES);
        FlowInfo returnInfo = new InOutFlowAnalyzer(flowContext).perform(getSelectedNodes());
        IVariableBinding[] returnValues = returnInfo.get(flowContext,
                FlowInfo.WRITE | FlowInfo.WRITE_POTENTIAL | FlowInfo.UNKNOWN);

        // Compute a selection that exactly covers the selected nodes
        IRegion region = getSelectedNodeRange();
        Selection selection = Selection.createFromStartLength(region.getOffset(), region.getLength());

        List<IVariableBinding> localReads = new ArrayList<IVariableBinding>();
        flowContext.setComputeMode(FlowContext.ARGUMENTS);
        FlowInfo argInfo = new InputFlowAnalyzer(flowContext, selection, true).perform(fEnclosingBodyDeclaration);
        IVariableBinding[] reads = argInfo.get(flowContext,
                FlowInfo.READ | FlowInfo.READ_POTENTIAL | FlowInfo.UNKNOWN);
        outer: for (int i = 0; i < returnValues.length && localReads.size() < returnValues.length; i++) {
            IVariableBinding binding = returnValues[i];
            for (int x = 0; x < reads.length; x++) {
                if (reads[x] == binding) {
                    localReads.add(binding);
                    fReturnValue = binding;
                    continue outer;
                }
            }
        }

        fPotentialReadsOutsideOfClosure = localReads.toArray(new IVariableBinding[localReads.size()]);

        List<IVariableBinding> callerLocals = new ArrayList<IVariableBinding>(5);
        FlowInfo localInfo = new InputFlowAnalyzer(flowContext, selection, false)
                .perform(fEnclosingBodyDeclaration);
        IVariableBinding[] writes = localInfo.get(flowContext,
                FlowInfo.WRITE | FlowInfo.WRITE_POTENTIAL | FlowInfo.UNKNOWN);
        for (int i = 0; i < writes.length; i++) {
            IVariableBinding write = writes[i];
            if (getSelection().covers(ASTNodes.findDeclaration(write, fEnclosingBodyDeclaration)))
                callerLocals.add(write);
        }
        fCallerLocals = callerLocals.toArray(new IVariableBinding[callerLocals.size()]);
        if (fReturnValue != null
                && getSelection().covers(ASTNodes.findDeclaration(fReturnValue, fEnclosingBodyDeclaration)))
            fReturnLocal = fReturnValue;
    }

    private void adjustArgumentsAndMethodLocals() {
        for (int i = 0; i < fArguments.length; i++) {
            IVariableBinding argument = fArguments[i];
            // Both arguments and locals consider FlowInfo.WRITE_POTENTIAL. But at the end a variable
            // can either be a local of an argument. Fix this based on the compute return type which
            // didn't exist when we computed the locals and arguments (see computeInput())
            if (fInputFlowInfo.hasAccessMode(fInputFlowContext, argument, FlowInfo.WRITE_POTENTIAL)) {
                if (argument != fReturnValue)
                    fArguments[i] = null;
                // We didn't remove the argument. So we have to remove the local declaration
                if (fArguments[i] != null) {
                    for (int l = 0; l < fMethodLocals.length; l++) {
                        if (fMethodLocals[l] == argument)
                            fMethodLocals[l] = null;
                    }
                }
            }
        }
    }

    private void compressArrays() {
        fArguments = compressArray(fArguments);
        fCallerLocals = compressArray(fCallerLocals);
        fMethodLocals = compressArray(fMethodLocals);
    }

    private IVariableBinding[] compressArray(IVariableBinding[] array) {
        if (array == null)
            return null;
        int size = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] != null)
                size++;
        }
        if (size == array.length)
            return array;
        IVariableBinding[] result = new IVariableBinding[size];
        for (int i = 0, r = 0; i < array.length; i++) {
            if (array[i] != null)
                result[r++] = array[i];
        }
        return result;
    }

    //---- Exceptions -----------------------------------------------------------------------------------------

    public ITypeBinding[] getExceptions(boolean includeRuntimeExceptions) {
        if (includeRuntimeExceptions)
            return fAllExceptions;
        List<ITypeBinding> result = new ArrayList<ITypeBinding>(fAllExceptions.length);
        for (int i = 0; i < fAllExceptions.length; i++) {
            ITypeBinding exception = fAllExceptions[i];
            if (!includeRuntimeExceptions && Bindings.isRuntimeException(exception))
                continue;
            result.add(exception);
        }
        return result.toArray(new ITypeBinding[result.size()]);
    }

    private void computeExceptions() {
        fAllExceptions = ExceptionAnalyzer.perform(getSelectedNodes());
    }

    //---- Special visitor methods ---------------------------------------------------------------------------

    @Override
    protected void handleNextSelectedNode(ASTNode node) {
        super.handleNextSelectedNode(node);
        checkParent(node);
    }

    @Override
    protected boolean handleSelectionEndsIn(ASTNode node) {
        invalidSelection(JFlowRefactoringCoreMessages.StatementAnalyzer_doesNotCover,
                JavaStatusContext.create(fCUnit, node));
        return super.handleSelectionEndsIn(node);
    }

    private void checkParent(ASTNode node) {
        ASTNode firstParent = getFirstSelectedNode().getParent();
        do {
            node = node.getParent();
            if (node == firstParent)
                return;
        } while (node != null);
        invalidSelection(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_parent_mismatch);
    }

    @Override
    public void endVisit(CompilationUnit node) {
        RefactoringStatus status = getStatus();
        superCall: {
            if (status.hasFatalError())
                break superCall;
            if (!hasSelectedNodes()) {
                ASTNode coveringNode = getLastCoveringNode();
                if (coveringNode instanceof Block && coveringNode.getParent() instanceof MethodDeclaration) {
                    MethodDeclaration methodDecl = (MethodDeclaration) coveringNode.getParent();
                    Message[] messages = ASTNodes.getMessages(methodDecl, ASTNodes.NODE_ONLY);
                    if (messages.length > 0) {
                        status.addFatalError(
                                Messages.format(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_compile_errors,
                                        BasicElementLabels
                                                .getJavaElementName(methodDecl.getName().getIdentifier())),
                                JavaStatusContext.create(fCUnit, methodDecl));
                        break superCall;
                    }
                }
                status.addFatalError(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_only_method_body);
                break superCall;
            }
            fEnclosingBodyDeclaration = (BodyDeclaration) ASTNodes.getParent(getFirstSelectedNode(),
                    BodyDeclaration.class);
            if (fEnclosingBodyDeclaration == null
                    || (fEnclosingBodyDeclaration.getNodeType() != ASTNode.METHOD_DECLARATION
                            && fEnclosingBodyDeclaration.getNodeType() != ASTNode.FIELD_DECLARATION
                            && fEnclosingBodyDeclaration.getNodeType() != ASTNode.INITIALIZER)) {
                status.addFatalError(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_only_method_body);
                break superCall;
            } else if (ASTNodes.getEnclosingType(fEnclosingBodyDeclaration) == null) {
                status.addFatalError(
                        JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_compile_errors_no_parent_binding);
                break superCall;
            } else if (fEnclosingBodyDeclaration.getNodeType() == ASTNode.METHOD_DECLARATION) {
                fEnclosingMethodBinding = ((MethodDeclaration) fEnclosingBodyDeclaration).resolveBinding();
            }
            if (!isSingleExpressionOrStatementSet()) {
                status.addFatalError(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_single_expression_or_set);
                break superCall;
            }
            if (isExpressionSelected()) {
                ASTNode expression = getFirstSelectedNode();
                if (expression instanceof Name) {
                    Name name = (Name) expression;
                    if (name.resolveBinding() instanceof ITypeBinding) {
                        status.addFatalError(
                                JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_type_reference);
                        break superCall;
                    }
                    if (name.resolveBinding() instanceof IMethodBinding) {
                        status.addFatalError(
                                JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_method_name_reference);
                        break superCall;
                    }
                    if (name.resolveBinding() instanceof IVariableBinding) {
                        StructuralPropertyDescriptor locationInParent = name.getLocationInParent();
                        if (locationInParent == QualifiedName.NAME_PROPERTY
                                || (locationInParent == FieldAccess.NAME_PROPERTY
                                        && !(((FieldAccess) name.getParent())
                                                .getExpression() instanceof ThisExpression))) {
                            status.addFatalError(
                                    JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_part_of_qualified_name);
                            break superCall;
                        }
                    }
                    if (name.isSimpleName() && ((SimpleName) name).isDeclaration()) {
                        status.addFatalError(
                                JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_name_in_declaration);
                        break superCall;
                    }
                }
                fForceStatic = ASTNodes.getParent(expression, ASTNode.SUPER_CONSTRUCTOR_INVOCATION) != null
                        || ASTNodes.getParent(expression, ASTNode.CONSTRUCTOR_INVOCATION) != null;
            }
            status.merge(LocalTypeAnalyzer.perform(fEnclosingBodyDeclaration, getSelection()));
            computeLastStatementSelected();
        }
        super.endVisit(node);
    }

    @Override
    public boolean visit(AnonymousClassDeclaration node) {
        boolean result = super.visit(node);
        if (isFirstSelectedNode(node)) {
            invalidSelection(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_anonymous_type,
                    JavaStatusContext.create(fCUnit, node));
            return false;
        }
        return result;
    }

    @Override
    public boolean visit(Assignment node) {
        boolean result = super.visit(node);
        if (getSelection().covers(node.getLeftHandSide()) || getSelection().coveredBy(node.getLeftHandSide())) {
            invalidSelection(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_leftHandSideOfAssignment,
                    JavaStatusContext.create(fCUnit, node));
            return false;
        }
        return result;
    }

    @Override
    public boolean visit(DoStatement node) {
        boolean result = super.visit(node);

        try {
            int actionStart = getTokenScanner().getTokenEndOffset(ITerminalSymbols.TokenNamedo,
                    node.getStartPosition());
            if (getSelection().getOffset() == actionStart) {
                invalidSelection(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_after_do_keyword,
                        JavaStatusContext.create(fCUnit, getSelection()));
                return false;
            }
        } catch (CoreException e) {
            // ignore
        }

        return result;
    }

    @Override
    public boolean visit(MethodDeclaration node) {
        Block body = node.getBody();
        if (body == null)
            return false;
        Selection selection = getSelection();
        int nodeStart = body.getStartPosition();
        int nodeExclusiveEnd = nodeStart + body.getLength();
        // if selection node inside of the method body ignore method
        if (!(nodeStart < selection.getOffset() && selection.getExclusiveEnd() < nodeExclusiveEnd))
            return false;
        return super.visit(node);
    }

    @Override
    public boolean visit(ConstructorInvocation node) {
        return visitConstructorInvocation(node, super.visit(node));
    }

    @Override
    public boolean visit(SuperConstructorInvocation node) {
        return visitConstructorInvocation(node, super.visit(node));
    }

    private boolean visitConstructorInvocation(ASTNode node, boolean superResult) {
        if (getSelection().getVisitSelectionMode(node) == Selection.SELECTED) {
            invalidSelection(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_super_or_this,
                    JavaStatusContext.create(fCUnit, node));
            return false;
        }
        return superResult;
    }

    @Override
    public boolean visit(VariableDeclarationFragment node) {
        boolean result = super.visit(node);
        if (isFirstSelectedNode(node)) {
            invalidSelection(
                    JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_variable_declaration_fragment,
                    JavaStatusContext.create(fCUnit, node));
            return false;
        }
        return result;
    }

    @Override
    public void endVisit(ForStatement node) {
        if (getSelection().getEndVisitSelectionMode(node) == Selection.AFTER) {
            if (node.initializers().contains(getFirstSelectedNode())) {
                invalidSelection(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_for_initializer,
                        JavaStatusContext.create(fCUnit, getSelection()));
            } else if (node.updaters().contains(getLastSelectedNode())) {
                invalidSelection(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_for_updater,
                        JavaStatusContext.create(fCUnit, getSelection()));
            }
        }
        super.endVisit(node);
    }

    @Override
    public void endVisit(VariableDeclarationExpression node) {
        if (getSelection().getEndVisitSelectionMode(node) == Selection.SELECTED && getFirstSelectedNode() == node) {
            if (node.getLocationInParent() == TryStatement.RESOURCES_PROPERTY) {
                invalidSelection(JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_resource_in_try_with_resources,
                        JavaStatusContext.create(fCUnit, getSelection()));
            }
        }
        checkTypeInDeclaration(node.getType());
        super.endVisit(node);
    }

    @Override
    public void endVisit(VariableDeclarationStatement node) {
        checkTypeInDeclaration(node.getType());
        super.endVisit(node);
    }

    private boolean isFirstSelectedNode(ASTNode node) {
        return getSelection().getVisitSelectionMode(node) == Selection.SELECTED && getFirstSelectedNode() == node;
    }

    private void checkTypeInDeclaration(Type node) {
        if (getSelection().getEndVisitSelectionMode(node) == Selection.SELECTED && getFirstSelectedNode() == node) {
            invalidSelection(
                    JFlowRefactoringCoreMessages.ExtractClosureAnalyzer_cannot_extract_variable_declaration,
                    JavaStatusContext.create(fCUnit, getSelection()));
        }
    }

    private boolean isSingleExpressionOrStatementSet() {
        ASTNode first = getFirstSelectedNode();
        if (first == null)
            return true;
        if (first instanceof Expression && getSelectedNodes().length != 1)
            return false;
        return true;
    }
}