/plugins/IntentionPowerPak/src/com/siyeh/ipp/forloop/ReplaceForEachLoopWithIndexedForLoopIntention.java

https://bitbucket.org/nbargnesi/idea · Java · 272 lines · 244 code · 10 blank · 18 comment · 51 complexity · 8a701b491fbe4e6a41326587318fa59f MD5 · raw file

  1. /*
  2. * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
  3. *
  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
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  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.
  15. */
  16. package com.siyeh.ipp.forloop;
  17. import com.intellij.openapi.project.Project;
  18. import com.intellij.openapi.util.text.StringUtil;
  19. import com.intellij.psi.*;
  20. import com.intellij.psi.codeStyle.*;
  21. import com.intellij.util.IncorrectOperationException;
  22. import com.siyeh.ipp.base.Intention;
  23. import com.siyeh.ipp.base.PsiElementPredicate;
  24. import org.jetbrains.annotations.NonNls;
  25. import org.jetbrains.annotations.NotNull;
  26. import org.jetbrains.annotations.Nullable;
  27. public class ReplaceForEachLoopWithIndexedForLoopIntention extends Intention {
  28. @Override
  29. @NotNull
  30. public PsiElementPredicate getElementPredicate() {
  31. return new IndexedForEachLoopPredicate();
  32. }
  33. @Override
  34. public void processIntention(@NotNull PsiElement element) throws IncorrectOperationException {
  35. final PsiForeachStatement statement = (PsiForeachStatement)element.getParent();
  36. if (statement == null) {
  37. return;
  38. }
  39. final PsiExpression iteratedValue = statement.getIteratedValue();
  40. if (iteratedValue == null) {
  41. return;
  42. }
  43. final PsiParameter iterationParameter =
  44. statement.getIterationParameter();
  45. final PsiType type = iterationParameter.getType();
  46. final PsiType iteratedValueType = iteratedValue.getType();
  47. if (iteratedValueType == null) {
  48. return;
  49. }
  50. final boolean isArray = iteratedValueType instanceof PsiArrayType;
  51. final PsiElement grandParent = statement.getParent();
  52. final PsiStatement context;
  53. if (grandParent instanceof PsiLabeledStatement) {
  54. context = (PsiStatement)grandParent;
  55. } else {
  56. context = statement;
  57. }
  58. final String iteratedValueText = getReferenceToIterate(iteratedValue, context);
  59. @NonNls final StringBuilder newStatement = new StringBuilder();
  60. final String indexText = createVariableName("i", PsiType.INT, statement);
  61. createForLoopDeclaration(statement, iteratedValue, isArray, iteratedValueText, newStatement, indexText);
  62. final Project project = statement.getProject();
  63. final CodeStyleSettings codeStyleSettings =
  64. CodeStyleSettingsManager.getSettings(project);
  65. if (codeStyleSettings.GENERATE_FINAL_LOCALS) {
  66. newStatement.append("final ");
  67. }
  68. newStatement.append(type.getCanonicalText());
  69. newStatement.append(' ');
  70. newStatement.append(iterationParameter.getName());
  71. newStatement.append(" = ");
  72. newStatement.append(iteratedValueText);
  73. if (isArray) {
  74. newStatement.append('[');
  75. newStatement.append(indexText);
  76. newStatement.append("];");
  77. }
  78. else {
  79. newStatement.append(".get(");
  80. newStatement.append(indexText);
  81. newStatement.append(");");
  82. }
  83. final PsiStatement body = statement.getBody();
  84. if (body == null) {
  85. return;
  86. }
  87. if (body instanceof PsiBlockStatement) {
  88. final PsiCodeBlock block = ((PsiBlockStatement)body).getCodeBlock();
  89. final PsiElement[] children = block.getChildren();
  90. for (int i = 1; i < children.length - 1; i++) {
  91. //skip the braces
  92. newStatement.append(children[i].getText());
  93. }
  94. }
  95. else {
  96. newStatement.append(body.getText());
  97. }
  98. newStatement.append('}');
  99. replaceStatementAndShorten(newStatement.toString(), statement);
  100. }
  101. protected void createForLoopDeclaration(PsiForeachStatement statement,
  102. PsiExpression iteratedValue,
  103. boolean array,
  104. String iteratedValueText, StringBuilder newStatement,
  105. final String indexText) {
  106. newStatement.append("for(int ");
  107. newStatement.append(indexText);
  108. newStatement.append(" = 0; ");
  109. newStatement.append(indexText);
  110. newStatement.append('<');
  111. if (iteratedValue instanceof PsiTypeCastExpression) {
  112. newStatement.append('(');
  113. newStatement.append(iteratedValueText);
  114. newStatement.append(')');
  115. }
  116. else {
  117. newStatement.append(iteratedValueText);
  118. }
  119. if (array) {
  120. newStatement.append(".length");
  121. }
  122. else {
  123. newStatement.append(".size()");
  124. }
  125. newStatement.append(';');
  126. newStatement.append(indexText);
  127. newStatement.append("++)");
  128. newStatement.append("{ ");
  129. }
  130. private static String getVariableName(PsiExpression expression) {
  131. if (expression instanceof PsiMethodCallExpression) {
  132. final PsiMethodCallExpression methodCallExpression =
  133. (PsiMethodCallExpression)expression;
  134. final PsiReferenceExpression methodExpression =
  135. methodCallExpression.getMethodExpression();
  136. final String name = methodExpression.getReferenceName();
  137. if (name == null) {
  138. return null;
  139. }
  140. if (name.startsWith("to") && name.length() > 2) {
  141. return StringUtil.decapitalize(name.substring(2));
  142. }
  143. else if (name.startsWith("get") && name.length() > 3) {
  144. return StringUtil.decapitalize(name.substring(3));
  145. }
  146. else {
  147. return name;
  148. }
  149. }
  150. else if (expression instanceof PsiTypeCastExpression) {
  151. final PsiTypeCastExpression castExpression =
  152. (PsiTypeCastExpression)expression;
  153. final PsiExpression operand = castExpression.getOperand();
  154. return getVariableName(operand);
  155. }
  156. else if (expression instanceof PsiArrayAccessExpression) {
  157. final PsiArrayAccessExpression arrayAccessExpression =
  158. (PsiArrayAccessExpression)expression;
  159. final PsiExpression arrayExpression =
  160. arrayAccessExpression.getArrayExpression();
  161. return StringUtil.unpluralize(getVariableName(arrayExpression));
  162. }
  163. else if (expression instanceof PsiParenthesizedExpression) {
  164. final PsiParenthesizedExpression parenthesizedExpression =
  165. (PsiParenthesizedExpression)expression;
  166. final PsiExpression innerExpression =
  167. parenthesizedExpression.getExpression();
  168. return getVariableName(innerExpression);
  169. }
  170. else if (expression instanceof PsiJavaCodeReferenceElement) {
  171. final PsiJavaCodeReferenceElement referenceElement =
  172. (PsiJavaCodeReferenceElement)expression;
  173. final String referenceName = referenceElement.getReferenceName();
  174. if (referenceName == null) {
  175. return expression.getText();
  176. }
  177. return referenceName;
  178. }
  179. return null;
  180. }
  181. private static String getReferenceToIterate(
  182. PsiExpression expression, PsiElement context) {
  183. if (expression instanceof PsiMethodCallExpression ||
  184. expression instanceof PsiTypeCastExpression ||
  185. expression instanceof PsiArrayAccessExpression ||
  186. expression instanceof PsiNewExpression) {
  187. final String variableName = getVariableName(expression);
  188. return createVariable(variableName, expression, context);
  189. }
  190. else if (expression instanceof PsiParenthesizedExpression) {
  191. final PsiParenthesizedExpression parenthesizedExpression =
  192. (PsiParenthesizedExpression)expression;
  193. final PsiExpression innerExpression =
  194. parenthesizedExpression.getExpression();
  195. return getReferenceToIterate(innerExpression, context);
  196. }
  197. else if (expression instanceof PsiJavaCodeReferenceElement) {
  198. final PsiJavaCodeReferenceElement referenceElement =
  199. (PsiJavaCodeReferenceElement)expression;
  200. final String variableName = getVariableName(expression);
  201. if (referenceElement.isQualified()) {
  202. return createVariable(variableName, expression, context);
  203. }
  204. final PsiElement target = referenceElement.resolve();
  205. if (target instanceof PsiVariable) {
  206. // maybe should not do this for local variables outside of
  207. // anonymous classes
  208. return variableName;
  209. }
  210. return createVariable(variableName, expression, context);
  211. }
  212. return null;
  213. }
  214. private static String createVariable(String variableNameRoot,
  215. PsiExpression iteratedValue,
  216. PsiElement context) {
  217. final String variableName =
  218. createVariableName(variableNameRoot, iteratedValue);
  219. final Project project = context.getProject();
  220. final PsiType iteratedValueType = iteratedValue.getType();
  221. assert iteratedValueType != null;
  222. final PsiElementFactory elementFactory =
  223. JavaPsiFacade.getElementFactory(project);
  224. final PsiDeclarationStatement declarationStatement =
  225. elementFactory.createVariableDeclarationStatement(variableName,
  226. iteratedValueType, iteratedValue);
  227. context.getParent().addBefore(declarationStatement, context);
  228. return variableName;
  229. }
  230. public static String createVariableName(
  231. @Nullable String baseName,
  232. @NotNull PsiExpression assignedExpression) {
  233. final Project project = assignedExpression.getProject();
  234. final JavaCodeStyleManager codeStyleManager =
  235. JavaCodeStyleManager.getInstance(project);
  236. final SuggestedNameInfo names =
  237. codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE,
  238. baseName, assignedExpression, null);
  239. if (names.names.length == 0) {
  240. return codeStyleManager.suggestUniqueVariableName(baseName,
  241. assignedExpression, true);
  242. }
  243. return codeStyleManager.suggestUniqueVariableName(names.names[0],
  244. assignedExpression, true);
  245. }
  246. public static String createVariableName(@Nullable String baseName,
  247. @NotNull PsiType type,
  248. @NotNull PsiElement context) {
  249. final Project project = context.getProject();
  250. final JavaCodeStyleManager codeStyleManager =
  251. JavaCodeStyleManager.getInstance(project);
  252. final SuggestedNameInfo names =
  253. codeStyleManager.suggestVariableName(
  254. VariableKind.LOCAL_VARIABLE, baseName, null, type);
  255. if (names.names.length == 0) {
  256. return codeStyleManager.suggestUniqueVariableName(baseName,
  257. context, true);
  258. }
  259. return codeStyleManager.suggestUniqueVariableName(names.names[0],
  260. context, true);
  261. }
  262. }