rename: check overriden for getters/setters (IDEA-22871)
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / extractMethod / ControlFlowWrapper.java
blobffe10f406a718f7ddbc6affc4b9bd91cd4b0a8a3
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.refactoring.extractMethod;
18 import com.intellij.codeInsight.PsiEquivalenceUtil;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.util.TextRange;
22 import com.intellij.psi.*;
23 import com.intellij.psi.controlFlow.*;
24 import com.intellij.psi.search.LocalSearchScope;
25 import com.intellij.psi.search.searches.ReferencesSearch;
26 import com.intellij.psi.util.PsiTreeUtil;
27 import com.intellij.psi.util.PsiUtil;
28 import com.intellij.refactoring.RefactoringBundle;
29 import com.intellij.util.containers.HashSet;
30 import com.intellij.util.containers.IntArrayList;
32 import java.util.*;
34 public class ControlFlowWrapper {
35 private static final Logger LOG = Logger.getInstance("#" + ControlFlowWrapper.class.getName());
37 private ControlFlow myControlFlow;
38 private int myFlowStart;
40 private int myFlowEnd;
41 private boolean myGenerateConditionalExit;
42 private Collection<PsiStatement> myExitStatements;
43 private PsiStatement myFirstExitStatementCopy;
44 private IntArrayList myExitPoints;
46 public ControlFlowWrapper(Project project, PsiElement codeFragment, PsiElement[] elements) throws PrepareFailedException {
47 try {
48 myControlFlow =
49 ControlFlowFactory.getInstance(project).getControlFlow(codeFragment, new LocalsControlFlowPolicy(codeFragment), false, true);
51 catch (AnalysisCanceledException e) {
52 throw new PrepareFailedException(RefactoringBundle.message("extract.method.control.flow.analysis.failed"), e.getErrorElement());
55 if (LOG.isDebugEnabled()) {
56 LOG.debug(myControlFlow.toString());
59 myFlowStart = -1;
60 int index = 0;
61 while (index < elements.length) {
62 myFlowStart = myControlFlow.getStartOffset(elements[index]);
63 if (myFlowStart >= 0) break;
64 index++;
66 if (myFlowStart < 0) {
67 // no executable code
68 myFlowStart = 0;
69 myFlowEnd = 0;
71 else {
72 index = elements.length - 1;
73 while (true) {
74 myFlowEnd = myControlFlow.getEndOffset(elements[index]);
75 if (myFlowEnd >= 0) break;
76 index--;
79 if (LOG.isDebugEnabled()) {
80 LOG.debug("start offset:" + myFlowStart);
81 LOG.debug("end offset:" + myFlowEnd);
85 public PsiStatement getFirstExitStatementCopy() {
86 return myFirstExitStatementCopy;
89 public Collection<PsiStatement> prepareExitStatements(final PsiElement[] elements) throws ExitStatementsNotSameException {
90 myExitPoints = new IntArrayList();
91 myExitStatements = ControlFlowUtil
92 .findExitPointsAndStatements(myControlFlow, myFlowStart, myFlowEnd, myExitPoints, ControlFlowUtil.DEFAULT_EXIT_STATEMENTS_CLASSES);
93 if (LOG.isDebugEnabled()) {
94 LOG.debug("exit points:");
95 for (int i = 0; i < myExitPoints.size(); i++) {
96 LOG.debug(" " + myExitPoints.get(i));
98 LOG.debug("exit statements:");
99 for (PsiStatement exitStatement : myExitStatements) {
100 LOG.debug(" " + exitStatement);
103 if (myExitPoints.isEmpty()) {
104 // if the fragment never exits assume as if it exits in the end
105 myExitPoints.add(myControlFlow.getEndOffset(elements[elements.length - 1]));
108 if (myExitPoints.size() != 1) {
109 areExitStatementsTheSame();
110 myGenerateConditionalExit = true;
112 return myExitStatements;
116 private void areExitStatementsTheSame() throws ExitStatementsNotSameException {
117 if (myExitStatements.isEmpty()) {
118 throw new ExitStatementsNotSameException();
120 PsiStatement first = null;
121 for (PsiStatement statement : myExitStatements) {
122 if (first == null) {
123 first = statement;
124 continue;
126 if (!PsiEquivalenceUtil.areElementsEquivalent(first, statement)) {
127 throw new ExitStatementsNotSameException();
131 myFirstExitStatementCopy = (PsiStatement)first.copy();
134 public boolean isGenerateConditionalExit() {
135 return myGenerateConditionalExit;
138 public Collection<PsiStatement> getExitStatements() {
139 return myExitStatements;
142 public static class ExitStatementsNotSameException extends Exception {}
145 public PsiVariable[] getOutputVariables() {
146 PsiVariable[] myOutputVariables = ControlFlowUtil.getOutputVariables(myControlFlow, myFlowStart, myFlowEnd, myExitPoints.toArray());
147 if (myGenerateConditionalExit) {
148 //variables declared in selected block used in return statements are to be considered output variables when extracting guard methods
149 final Set<PsiVariable> outputVariables = new HashSet<PsiVariable>(Arrays.asList(myOutputVariables));
150 for (PsiStatement statement : myExitStatements) {
151 statement.accept(new JavaRecursiveElementVisitor() {
153 @Override
154 public void visitReferenceExpression(PsiReferenceExpression expression) {
155 super.visitReferenceExpression(expression);
156 final PsiElement resolved = expression.resolve();
157 if (resolved instanceof PsiVariable) {
158 final PsiVariable variable = (PsiVariable)resolved;
159 if (isWrittenInside(variable)) {
160 outputVariables.add(variable);
165 private boolean isWrittenInside(final PsiVariable variable) {
166 final List<Instruction> instructions = myControlFlow.getInstructions();
167 for (int i = myFlowStart; i < myFlowEnd; i++) {
168 Instruction instruction = instructions.get(i);
169 if (instruction instanceof WriteVariableInstruction && variable.equals(((WriteVariableInstruction)instruction).variable)) {
170 return true;
174 return false;
179 myOutputVariables = outputVariables.toArray(new PsiVariable[outputVariables.size()]);
181 Arrays.sort(myOutputVariables, PsiUtil.BY_POSITION);
182 return myOutputVariables;
185 public boolean isReturnPresentBetween() {
186 return ControlFlowUtil.returnPresentBetween(myControlFlow, myFlowStart, myFlowEnd);
189 private void removeParametersUsedInExitsOnly(PsiElement codeFragment, List<PsiVariable> inputVariables) {
190 LocalSearchScope scope = new LocalSearchScope(codeFragment);
191 Variables:
192 for (Iterator<PsiVariable> iterator = inputVariables.iterator(); iterator.hasNext();) {
193 PsiVariable variable = iterator.next();
194 for (PsiReference ref : ReferencesSearch.search(variable, scope)) {
195 PsiElement element = ref.getElement();
196 int elementOffset = myControlFlow.getStartOffset(element);
197 if (elementOffset >= myFlowStart && elementOffset <= myFlowEnd) {
198 if (!isInExitStatements(element, myExitStatements)) continue Variables;
200 if (elementOffset == -1) { //references in local/anonymous classes should not be skipped
201 final PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
202 if (psiClass != null) {
203 final TextRange textRange = psiClass.getTextRange();
204 if (myControlFlow.getElement(myFlowStart).getTextOffset() <= textRange.getStartOffset() &&
205 textRange.getEndOffset() <= myControlFlow.getElement(myFlowEnd).getTextRange().getEndOffset()) {
206 continue Variables;
211 iterator.remove();
216 private static boolean isInExitStatements(PsiElement element, Collection<PsiStatement> exitStatements) {
217 for (PsiStatement exitStatement : exitStatements) {
218 if (PsiTreeUtil.isAncestor(exitStatement, element, false)) return true;
220 return false;
223 private boolean needExitStatement(final PsiStatement exitStatement) {
224 if (exitStatement instanceof PsiContinueStatement) {
225 //IDEADEV-11748
226 PsiStatement statement = ((PsiContinueStatement)exitStatement).findContinuedStatement();
227 if (statement == null) return true;
228 if (statement instanceof PsiLoopStatement) statement = ((PsiLoopStatement)statement).getBody();
229 int endOffset = myControlFlow.getEndOffset(statement);
230 return endOffset > myFlowEnd;
232 return true;
235 public List<PsiVariable> getInputVariables(final PsiElement codeFragment) {
236 final List<PsiVariable> inputVariables = ControlFlowUtil.getInputVariables(myControlFlow, myFlowStart, myFlowEnd);
237 List<PsiVariable> myInputVariables;
238 if (myGenerateConditionalExit) {
239 List<PsiVariable> inputVariableList = new ArrayList<PsiVariable>(inputVariables);
240 removeParametersUsedInExitsOnly(codeFragment, inputVariableList);
241 myInputVariables = inputVariableList;
243 else {
244 myInputVariables = inputVariables;
246 //varargs variables go last, otherwise order is induced by original ordering
247 Collections.sort(myInputVariables, new Comparator<PsiVariable>() {
248 public int compare(final PsiVariable v1, final PsiVariable v2) {
249 if (v1.getType() instanceof PsiEllipsisType) {
250 return 1;
252 if (v2.getType() instanceof PsiEllipsisType) {
253 return -1;
255 return v1.getTextOffset() - v2.getTextOffset();
258 return myInputVariables;
261 public PsiStatement getExitStatementCopy(PsiElement returnStatement,
262 final PsiElement[] elements) {
263 PsiStatement exitStatementCopy = null;
264 // replace all exit-statements such as break's or continue's with appropriate return
265 for (PsiStatement exitStatement : myExitStatements) {
266 if (exitStatement instanceof PsiReturnStatement) {
267 if (!myGenerateConditionalExit) continue;
269 else if (exitStatement instanceof PsiBreakStatement) {
270 PsiStatement statement = ((PsiBreakStatement)exitStatement).findExitedStatement();
271 if (statement == null) continue;
272 int startOffset = myControlFlow.getStartOffset(statement);
273 int endOffset = myControlFlow.getEndOffset(statement);
274 if (myFlowStart <= startOffset && endOffset <= myFlowEnd) continue;
276 else if (exitStatement instanceof PsiContinueStatement) {
277 PsiStatement statement = ((PsiContinueStatement)exitStatement).findContinuedStatement();
278 if (statement == null) continue;
279 int startOffset = myControlFlow.getStartOffset(statement);
280 int endOffset = myControlFlow.getEndOffset(statement);
281 if (myFlowStart <= startOffset && endOffset <= myFlowEnd) continue;
283 else {
284 LOG.assertTrue(false, exitStatement);
285 continue;
288 int index = -1;
289 for (int j = 0; j < elements.length; j++) {
290 if (exitStatement.equals(elements[j])) {
291 index = j;
292 break;
295 if (exitStatementCopy == null) {
296 if (needExitStatement(exitStatement)) {
297 exitStatementCopy = (PsiStatement)exitStatement.copy();
300 PsiElement result = exitStatement.replace(returnStatement);
301 if (index >= 0) {
302 elements[index] = result;
305 return exitStatementCopy;
308 public List<PsiVariable> getUsedVariables(int start) {
309 return getUsedVariables(start, myControlFlow.getSize());
312 public List<PsiVariable> getUsedVariables(int start, int end) {
313 return ControlFlowUtil.getUsedVariables(myControlFlow, start, end);
316 public Collection<ControlFlowUtil.VariableInfo> getInitializedTwice(int start) {
317 return ControlFlowUtil.getInitializedTwice(myControlFlow, start, myControlFlow.getSize());
320 public List<PsiVariable> getUsedVariables() {
321 return getUsedVariables(myFlowEnd);
324 public List<PsiVariable> getUsedVariablesInBody() {
325 return getUsedVariables(myFlowStart, myFlowEnd);
328 public Collection<ControlFlowUtil.VariableInfo> getInitializedTwice() {
329 return getInitializedTwice(myFlowEnd);
332 public void setGenerateConditionalExit(boolean generateConditionalExit) {
333 myGenerateConditionalExit = generateConditionalExit;