From e1c9238c80c78f8da82e4b88e21445d3ae13ca3d Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Tue, 13 Mar 2012 17:48:26 -0700 Subject: [PATCH] Refactoring code for potential future use. --- .../ui/refactoring/utils/CodeAnalyzer.java | 41 ++++ .../ui/refactoring/utils/Messages.java | 10 + .../ui/refactoring/utils/Messages.properties | 10 + .../refactoring/utils/SelectionAnalyzer.java | 146 ++++++++++++++ .../refactoring/utils/StatementAnalyzer.java | 186 ++++++++++++++++++ 5 files changed, 393 insertions(+) create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/CodeAnalyzer.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionAnalyzer.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/StatementAnalyzer.java diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/CodeAnalyzer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/CodeAnalyzer.java new file mode 100644 index 00000000000..ef1f852f804 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/CodeAnalyzer.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2012 IBM Corporation and others. + * 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, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; + +import org.eclipse.cdt.core.dom.ast.IASTInitializerList; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.model.ITranslationUnit; + +import org.eclipse.cdt.internal.corext.refactoring.code.flow.Selection; + +public class CodeAnalyzer extends StatementAnalyzer { + + public CodeAnalyzer(ITranslationUnit cunit, Selection selection, boolean traverseSelectedNode) + throws CoreException { + super(cunit, selection, traverseSelectedNode); + } + + @Override + protected final void checkSelectedNodes() { + super.checkSelectedNodes(); + RefactoringStatus status= getStatus(); + if (status.hasFatalError()) + return; + IASTNode node= getFirstSelectedNode(); + if (node instanceof IASTInitializerList) { + status.addFatalError(Messages.CodeAnalyzer_initializer_list); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/Messages.java index 34754225f14..a620a777668 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/Messages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/Messages.java @@ -23,6 +23,16 @@ public final class Messages extends NLS { public static String IdentifierHelper_unidentifiedMistake; public static String Checks_validate_edit; public static String Checks_choose_name; + public static String CodeAnalyzer_initializer_list; + public static String StatementAnalyzer_doesNotCover; + public static String StatementAnalyzer_do_body_expression; + public static String StatementAnalyzer_for_initializer_expression; + public static String StatementAnalyzer_for_expression_updater; + public static String StatementAnalyzer_for_updater_body; + public static String StatementAnalyzer_catch_argument; + public static String StatementAnalyzer_while_expression_body; + public static String StatementAnalyzer_try_statement; + public static String StatementAnalyzer_switch_statement; static { NLS.initializeMessages(Messages.class.getName(), Messages.class); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/Messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/Messages.properties index a8ab5e40a1a..60092dbff40 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/Messages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/Messages.properties @@ -19,3 +19,13 @@ IdentifierHelper_illegalCharacter=Illegal character found in ''{0}''. IdentifierHelper_unidentifiedMistake=''{0}'' contains an unidentified mistake. Checks_validate_edit=Team provider refused file modification. Checks_choose_name=Choose a name. +CodeAnalyzer_initializer_list=Operation is not applicable to an initializer list. +StatementAnalyzer_doesNotCover=The selection does not cover a set of statements or an expression. Extend selection to a valid range using the \'Expand Selection To\' actions from the \'Edit\' menu. +StatementAnalyzer_do_body_expression=Operation not applicable to a \'do\' statement's body and expression. +StatementAnalyzer_for_initializer_expression=Operation not applicable to a \'for\' statement's initializer and expression part. +StatementAnalyzer_for_expression_updater=Operation not applicable to a \'for\' statement's condition and updater part. +StatementAnalyzer_for_updater_body=Operation not applicable to a \'for\' statement's updater and body part. +StatementAnalyzer_catch_argument=Operation is not applicable to a catch block's argument declaration. +StatementAnalyzer_while_expression_body=Operation not applicable to a while statement's expression and body. +StatementAnalyzer_try_statement=Selection must either cover a whole try statement or parts of try, catch, or finally block. +StatementAnalyzer_switch_statement=Selection must either cover a whole switch statement or parts of a single case block. diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionAnalyzer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionAnalyzer.java new file mode 100644 index 00000000000..8c58d2de1ad --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionAnalyzer.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2000, 2012 IBM Corporation and others. + * 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, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; + +import org.eclipse.cdt.core.dom.ast.ASTGenericVisitor; +import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTNode; + +import org.eclipse.cdt.internal.corext.refactoring.code.flow.Selection; +import org.eclipse.cdt.internal.corext.util.ASTNodes; + +/** + * Maps a selection to a set of AST nodes. + */ +public class SelectionAnalyzer extends ASTGenericVisitor { + private Selection fSelection; + private final boolean fTraverseSelectedNode; + private IASTNode fLastCoveringNode; + + // Selected nodes + private List fSelectedNodes; + + public SelectionAnalyzer(Selection selection, boolean traverseSelectedNode) { + super(true); + Assert.isNotNull(selection); + fSelection= selection; + fTraverseSelectedNode= traverseSelectedNode; + } + + protected void setSelection(Selection selection) { + fSelection= selection; + } + + public boolean hasSelectedNodes() { + return fSelectedNodes != null && !fSelectedNodes.isEmpty(); + } + + public IASTNode[] getSelectedNodes() { + if (fSelectedNodes == null || fSelectedNodes.isEmpty()) + return new IASTNode[0]; + return fSelectedNodes.toArray(new IASTNode[fSelectedNodes.size()]); + } + + public IASTNode getFirstSelectedNode() { + if (fSelectedNodes == null || fSelectedNodes.isEmpty()) + return null; + return fSelectedNodes.get(0); + } + + public IASTNode getLastSelectedNode() { + if (fSelectedNodes == null || fSelectedNodes.isEmpty()) + return null; + return fSelectedNodes.get(fSelectedNodes.size() - 1); + } + + public boolean isExpressionSelected() { + if (!hasSelectedNodes()) + return false; + return fSelectedNodes.get(0) instanceof IASTExpression; + } + + public IRegion getSelectedNodeRange() { + if (fSelectedNodes == null || fSelectedNodes.isEmpty()) + return null; + IASTNode firstNode= fSelectedNodes.get(0); + int start= firstNode.getFileLocation().getNodeOffset(); + IASTNode lastNode= fSelectedNodes.get(fSelectedNodes.size() - 1); + return new Region(start, ASTNodes.endOffset(lastNode) - start); + } + + public IASTNode getLastCoveringNode() { + return fLastCoveringNode; + } + + public Selection getSelection() { + return fSelection; + } + + //--- node management --------------------------------------------------------- + + @Override + protected int genericVisit(IASTNode node) { + // The selection lies behind the node. + if (fSelection.liesOutside(node)) { + return PROCESS_SKIP; + } else if (fSelection.covers(node)) { + if (isFirstNode()) { + handleFirstSelectedNode(node); + } else { + handleNextSelectedNode(node); + } + return fTraverseSelectedNode ? PROCESS_CONTINUE : PROCESS_SKIP; + } else if (fSelection.coveredBy(node)) { + fLastCoveringNode= node; + return PROCESS_CONTINUE; + } else if (fSelection.endsIn(node)) { + return handleSelectionEndsIn(node) ? PROCESS_CONTINUE : PROCESS_SKIP; + } + // There is a possibility that the user has selected trailing semicolons that don't belong + // to the statement. So dive into it to check if sub nodes are fully covered. + return PROCESS_CONTINUE; + } + + protected void reset() { + fSelectedNodes= null; + } + + protected void handleFirstSelectedNode(IASTNode node) { + fSelectedNodes= new ArrayList(5); + fSelectedNodes.add(node); + } + + protected void handleNextSelectedNode(IASTNode node) { + if (getFirstSelectedNode().getParent() == node.getParent()) { + fSelectedNodes.add(node); + } + } + + protected boolean handleSelectionEndsIn(IASTNode node) { + return false; + } + + protected List internalGetSelectedNodes() { + return fSelectedNodes; + } + + private boolean isFirstNode() { + return fSelectedNodes == null; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/StatementAnalyzer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/StatementAnalyzer.java new file mode 100644 index 00000000000..1f96a17e075 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/StatementAnalyzer.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * Copyright (c) 2000, 2012 IBM Corporation and others. + * 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, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.RefactoringStatusContext; + +import org.eclipse.cdt.core.dom.ast.IASTCaseStatement; +import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement; +import org.eclipse.cdt.core.dom.ast.IASTDefaultStatement; +import org.eclipse.cdt.core.dom.ast.IASTDoStatement; +import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTForStatement; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IASTWhileStatement; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement; +import org.eclipse.cdt.core.model.ITranslationUnit; + +import org.eclipse.cdt.internal.corext.refactoring.code.flow.Selection; + +/** + * Analyzer to check if a selection covers a valid set of statements of an abstract syntax + * tree. The selection is valid iff + *
    + *
  • it does not start or end in the middle of a comment.
  • + *
  • no extract characters except the empty statement ";" is included in the selection.
  • + *
+ */ +public class StatementAnalyzer extends SelectionAnalyzer { + protected ITranslationUnit fTranslationUnit; + private final RefactoringStatus fStatus; + + public StatementAnalyzer(ITranslationUnit tu, Selection selection, boolean traverseSelectedNode) + throws CoreException { + super(selection, traverseSelectedNode); + Assert.isNotNull(tu); + this.fTranslationUnit= tu; + this.fStatus= new RefactoringStatus(); + } + + protected void checkSelectedNodes() { + } + + public RefactoringStatus getStatus() { + return fStatus; + } + + protected ITranslationUnit getTranslationUnit() { + return fTranslationUnit; + } + + @Override + public int leave(IASTTranslationUnit node) { + checkSelectedNodes(); + return PROCESS_CONTINUE; + } + + @Override + public int leave(IASTStatement node) { + if (node instanceof IASTDoStatement) { + leave((IASTDoStatement) node); + } else if (node instanceof IASTForStatement) { + leave((IASTForStatement) node); + } else if (node instanceof IASTSwitchStatement) { + leave((IASTSwitchStatement) node); + } else if (node instanceof IASTWhileStatement) { + leave((IASTWhileStatement) node); + } else if (node instanceof ICPPASTTryBlockStatement) { + leave((ICPPASTTryBlockStatement) node); + } + return PROCESS_CONTINUE; + } + + private void leave(IASTDoStatement node) { + IASTNode[] selectedNodes= getSelectedNodes(); + if (doAfterValidation(node, selectedNodes)) { + if (contains(selectedNodes, node.getBody()) && contains(selectedNodes, node.getCondition())) { + invalidSelection(Messages.StatementAnalyzer_do_body_expression); + } + } + } + + private void leave(IASTForStatement node) { + IASTNode[] selectedNodes= getSelectedNodes(); + if (doAfterValidation(node, selectedNodes)) { + boolean hasConditionPart= contains(selectedNodes, node.getConditionExpression()); + boolean hasIterationPart= contains(selectedNodes, node.getIterationExpression()); + if (contains(selectedNodes, node.getInitializerStatement()) && hasConditionPart) { + invalidSelection(Messages.StatementAnalyzer_for_initializer_expression); + } else if (hasConditionPart && hasIterationPart) { + invalidSelection(Messages.StatementAnalyzer_for_expression_updater); + } else if (hasIterationPart && contains(selectedNodes, node.getBody())) { + invalidSelection(Messages.StatementAnalyzer_for_updater_body); + } + } + } + + private void leave(ICPPASTTryBlockStatement node) { + IASTNode firstSelectedNode= getFirstSelectedNode(); + if (getSelection().getEndVisitSelectionMode(node) == Selection.AFTER) { + if (firstSelectedNode == node.getTryBody()) { + invalidSelection(Messages.StatementAnalyzer_try_statement); + } else { + ICPPASTCatchHandler[] catchHandlers = node.getCatchHandlers(); + for (ICPPASTCatchHandler catchHandler : catchHandlers) { + if (catchHandler == firstSelectedNode || catchHandler.getCatchBody() == firstSelectedNode) { + invalidSelection(Messages.StatementAnalyzer_try_statement); + } else if (catchHandler.getDeclaration() == firstSelectedNode) { + invalidSelection(Messages.StatementAnalyzer_catch_argument); + } + } + } + } + } + + private void leave(IASTSwitchStatement node) { + IASTNode[] selectedNodes= getSelectedNodes(); + IASTStatement body = node.getBody(); + IASTNode parent = body instanceof IASTCompoundStatement ? body : node; + if (doAfterValidation(parent, selectedNodes)) { + for (IASTNode n : selectedNodes) { + if (n.getParent() == parent && + (n instanceof IASTCaseStatement || n instanceof IASTDefaultStatement)) { + invalidSelection(Messages.StatementAnalyzer_switch_statement); + break; + } + } + } + } + + private void leave(IASTWhileStatement node) { + IASTNode[] selectedNodes= getSelectedNodes(); + if (doAfterValidation(node, selectedNodes)) { + if (contains(selectedNodes, node.getCondition()) && contains(selectedNodes, node.getBody())) { + invalidSelection(Messages.StatementAnalyzer_while_expression_body); + } + } + } + + private boolean doAfterValidation(IASTNode node, IASTNode[] selectedNodes) { + return selectedNodes.length > 0 && node == selectedNodes[0].getParent() && getSelection().getEndVisitSelectionMode(node) == Selection.AFTER; + } + + protected void invalidSelection(String message) { + fStatus.addFatalError(message); + reset(); + } + + protected void invalidSelection(String message, RefactoringStatusContext context) { + fStatus.addFatalError(message, context); + reset(); + } + + protected static boolean contains(IASTNode[] nodes, IASTNode node) { + for (int i = 0; i < nodes.length; i++) { + if (nodes[i] == node) + return true; + } + return false; + } + + protected static boolean contains(IASTNode[] nodes, List list) { + for (int i = 0; i < nodes.length; i++) { + if (list.contains(nodes[i])) + return true; + } + return false; + } +}