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