PageRenderTime 98ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/ClosureParameterEnhancer.java

http://github.com/JetBrains/intellij-community
Java | 294 lines | 263 code | 26 blank | 5 comment | 116 complexity | 1f6b981705b4b853acd6d30ec64bd047 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, MPL-2.0-no-copyleft-exception, MIT, EPL-1.0, AGPL-1.0
  1. package org.jetbrains.plugins.groovy.lang.psi;
  2. import com.intellij.psi.*;
  3. import com.intellij.psi.util.InheritanceUtil;
  4. import com.intellij.psi.util.PsiUtil;
  5. import com.intellij.util.containers.hash.HashMap;
  6. import com.intellij.util.containers.hash.HashSet;
  7. import org.jetbrains.annotations.NotNull;
  8. import org.jetbrains.annotations.Nullable;
  9. import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
  10. import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
  11. import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
  12. import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
  13. import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
  14. import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.arithmetic.GrRangeExpression;
  15. import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
  16. import org.jetbrains.plugins.groovy.lang.psi.impl.GrRangeType;
  17. import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
  18. import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
  19. import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
  20. import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
  21. import java.util.Map;
  22. import java.util.Set;
  23. import static org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.skipParentheses;
  24. /**
  25. * @author peter
  26. */
  27. public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer {
  28. private final Map<String, String> simpleTypes = new HashMap<String, String>();
  29. private final Set<String> iterations = new HashSet<String>();
  30. public ClosureParameterEnhancer() {
  31. simpleTypes.put("times", "java.lang.Integer");
  32. simpleTypes.put("upto", "java.lang.Integer");
  33. simpleTypes.put("downto", "java.lang.Integer");
  34. simpleTypes.put("step", "java.lang.Integer");
  35. simpleTypes.put("withObjectOutputStream", "java.io.ObjectOutputStream");//todo
  36. simpleTypes.put("withObjectInputStream", "java.io.ObjectInputStream");
  37. simpleTypes.put("withOutputStream", "java.io.OutputStream");
  38. simpleTypes.put("withInputStream", "java.io.InputStream");
  39. simpleTypes.put("withDataOutputStream", "java.io.DataOutputStream");
  40. simpleTypes.put("withDataInputStream", "java.io.DataInputStream");
  41. simpleTypes.put("eachLine", "java.lang.String");
  42. simpleTypes.put("eachFile", "java.io.File");
  43. simpleTypes.put("eachDir", "java.io.File");
  44. simpleTypes.put("eachFileRecurse", "java.io.File");
  45. simpleTypes.put("traverse", "java.io.File");
  46. simpleTypes.put("eachDirRecurse", "java.io.File");
  47. simpleTypes.put("eachFileMatch", "java.io.File");
  48. simpleTypes.put("eachDirMatch", "java.io.File");
  49. simpleTypes.put("withReader", "java.io.Reader");
  50. simpleTypes.put("withWriter", "java.io.Writer");
  51. simpleTypes.put("withWriterAppend", "java.io.Writer");
  52. simpleTypes.put("withPrintWriter", "java.io.PrintWriter");
  53. simpleTypes.put("eachByte", "byte");
  54. simpleTypes.put("transformChar", "String");
  55. simpleTypes.put("transformLine", "String");
  56. simpleTypes.put("filterLine", "String");
  57. simpleTypes.put("accept", "java.net.Socket");
  58. iterations.add("each");
  59. iterations.add("any");
  60. iterations.add("every");
  61. iterations.add("reverseEach");
  62. iterations.add("collect");
  63. iterations.add("collectAll");
  64. iterations.add("collectEntries");
  65. iterations.add("find");
  66. iterations.add("findAll");
  67. iterations.add("retainAll");
  68. iterations.add("removeAll");
  69. iterations.add("split");
  70. iterations.add("groupBy");
  71. iterations.add("groupEntriesBy");
  72. iterations.add("findLastIndexOf");
  73. iterations.add("findIndexValues");
  74. iterations.add("findIndexOf");
  75. iterations.add("count");
  76. }
  77. @Override
  78. @Nullable
  79. protected PsiType getClosureParameterType(GrClosableBlock closure, int index) {
  80. PsiElement parent = closure.getParent();
  81. if (parent instanceof GrStringInjection && index == 0) {
  82. return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_STRING_BUILDER, closure);
  83. }
  84. if (parent instanceof GrArgumentList) parent = parent.getParent();
  85. if (!(parent instanceof GrMethodCall)) {
  86. return null;
  87. }
  88. String methodName = findMethodName((GrMethodCall)parent);
  89. GrExpression expression = ((GrMethodCall)parent).getInvokedExpression();
  90. if (!(expression instanceof GrReferenceExpression)) return null;
  91. // final PsiElement resolved = ((GrReferenceExpression)expression).resolve();
  92. // if (!(resolved instanceof GrGdkMethod)) return null;
  93. GrExpression qualifier = ((GrReferenceExpression)expression).getQualifierExpression();
  94. if (qualifier == null) return null;
  95. PsiType type = qualifier.getType();
  96. if (type == null) {
  97. return null;
  98. }
  99. final PsiParameter[] params = closure.getAllParameters();
  100. if (params.length == 1 && simpleTypes.containsKey(methodName)) {
  101. return TypesUtil.createTypeByFQClassName(simpleTypes.get(methodName), closure);
  102. }
  103. if (iterations.contains(methodName)) {
  104. if (params.length == 1) {
  105. return findTypeForIteration(qualifier, closure);
  106. }
  107. if (params.length == 2 && InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
  108. if (index == 0) {
  109. return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 0, true);
  110. }
  111. return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 1, true);
  112. }
  113. }
  114. else if (GdkMethodUtil.WITH.equals(methodName) && params.length == 1) {
  115. return type;
  116. }
  117. else if (GdkMethodUtil.EACH_WITH_INDEX.equals(methodName)) {
  118. PsiType res = findTypeForIteration(qualifier, closure);
  119. if (params.length == 2 && res != null) {
  120. if (index == 0) {
  121. return res;
  122. }
  123. return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_INTEGER, closure);
  124. }
  125. if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
  126. if (params.length == 2) {
  127. if (index == 0) {
  128. return getEntryForMap(type, closure);
  129. }
  130. return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_INTEGER, closure);
  131. }
  132. if (params.length == 3) {
  133. if (index == 0) {
  134. return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 0, true);
  135. }
  136. if (index == 1) {
  137. return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 1, true);
  138. }
  139. return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_INTEGER, closure);
  140. }
  141. }
  142. }
  143. else if (GdkMethodUtil.INJECT.equals(methodName) && params.length == 2) {
  144. if (index == 0) {
  145. return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, closure);
  146. }
  147. PsiType res = findTypeForIteration(qualifier, closure);
  148. if (res != null) {
  149. return res;
  150. }
  151. if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
  152. return getEntryForMap(type, closure);
  153. }
  154. }
  155. else if (GdkMethodUtil.EACH_PERMUTATION.equals(methodName) && params.length == 1) {
  156. final PsiType itemType = findTypeForIteration(qualifier, closure);
  157. if (itemType != null) {
  158. return JavaPsiFacade.getElementFactory(closure.getProject()).createTypeFromText(
  159. CommonClassNames.JAVA_UTIL_ARRAY_LIST + "<" + itemType.getCanonicalText() + ">", closure);
  160. }
  161. return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_UTIL_ARRAY_LIST, closure);
  162. }
  163. else if (GdkMethodUtil.WITH_DEFAULT.equals(methodName)) {
  164. if (params.length == 1 && InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
  165. return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 0, true);
  166. }
  167. }
  168. else if (GdkMethodUtil.SORT.equals(methodName)) {
  169. if (params.length < 3) {
  170. return findTypeForIteration(qualifier, closure);
  171. }
  172. }
  173. else if (GdkMethodUtil.WITH_STREAM.equals(methodName)) {
  174. final PsiMethod method = ((GrMethodCall)parent).resolveMethod();
  175. if (method != null) {
  176. final PsiParameter[] parameters = method.getParameterList().getParameters();
  177. if (parameters.length > 0) {
  178. return parameters[0].getType();
  179. }
  180. }
  181. }
  182. else if (GdkMethodUtil.WITH_STREAMS.equals(methodName)) {
  183. if (index == 0) {
  184. return TypesUtil.createTypeByFQClassName("java.io.InputStream", closure);
  185. }
  186. else if (index == 1) return TypesUtil.createTypeByFQClassName("java.io.OutputStream", closure);
  187. }
  188. else if (GdkMethodUtil.WITH_OBJECT_STREAMS.equals(methodName)) {
  189. if (index == 0) {
  190. return TypesUtil.createTypeByFQClassName("java.io.ObjectInputStream", closure);
  191. }
  192. else if (index == 1) return TypesUtil.createTypeByFQClassName("java.io.ObjectOutputStream", closure);
  193. }
  194. return null;
  195. }
  196. @Nullable
  197. private static PsiType getEntryForMap(PsiType map, PsiElement context) {
  198. PsiType key = PsiUtil.substituteTypeParameter(map, CommonClassNames.JAVA_UTIL_MAP, 0, true);
  199. PsiType value = PsiUtil.substituteTypeParameter(map, CommonClassNames.JAVA_UTIL_MAP, 1, true);
  200. if (key != null && key != PsiType.NULL && value != null && value != PsiType.NULL) {
  201. return JavaPsiFacade.getElementFactory(context.getProject()).createTypeFromText(
  202. "java.util.Map.Entry<" + key.getCanonicalText() + ", " + value.getCanonicalText() + ">", context);
  203. }
  204. return TypesUtil.createTypeByFQClassName("java.util.Map.Entry", context);
  205. }
  206. @Nullable
  207. public static PsiType findTypeForIteration(@NotNull GrExpression qualifier, PsiElement context) {
  208. PsiType iterType = qualifier.getType();
  209. if (iterType == null) return null;
  210. if (iterType instanceof PsiArrayType) {
  211. return TypesUtil.boxPrimitiveType(((PsiArrayType)iterType).getComponentType(), context.getManager(), context.getResolveScope());
  212. }
  213. if (iterType instanceof GrTupleType) {
  214. PsiType[] types = ((GrTupleType)iterType).getParameters();
  215. return types.length == 1 ? types[0] : null;
  216. }
  217. if (iterType instanceof GrRangeType) {
  218. return ((GrRangeType)iterType).getIterationType();
  219. }
  220. if (InheritanceUtil.isInheritor(iterType, GroovyCommonClassNames.GROOVY_LANG_INT_RANGE)) {
  221. return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_INTEGER, context);
  222. }
  223. if (InheritanceUtil.isInheritor(iterType, GroovyCommonClassNames.GROOVY_LANG_OBJECT_RANGE)) {
  224. PsiElement element = qualifier;
  225. element = skipParentheses(element, false);
  226. if (element instanceof GrReferenceExpression) {
  227. GrReferenceExpression ref = (GrReferenceExpression)element;
  228. element = skipParentheses(ref.resolve(), false);
  229. }
  230. if (element instanceof GrRangeExpression) {
  231. return getRangeElementType((GrRangeExpression)element);
  232. }
  233. return null;
  234. }
  235. PsiType res = PsiUtil.extractIterableTypeParameter(iterType, true);
  236. if (res != null) {
  237. return res;
  238. }
  239. if (TypesUtil.isClassType(iterType, CommonClassNames.JAVA_LANG_STRING) || TypesUtil.isClassType(iterType, "java.io.File")) {
  240. return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_STRING, context);
  241. }
  242. if (InheritanceUtil.isInheritor(iterType, CommonClassNames.JAVA_UTIL_MAP)) {
  243. return getEntryForMap(iterType, context);
  244. }
  245. return null;
  246. }
  247. @Nullable
  248. private static PsiType getRangeElementType(GrRangeExpression range) {
  249. GrExpression left = range.getLeftOperand();
  250. GrExpression right = range.getRightOperand();
  251. if (right != null) {
  252. final PsiType leftType = left.getType();
  253. final PsiType rightType = right.getType();
  254. if (leftType != null && rightType != null) {
  255. return TypesUtil.getLeastUpperBound(leftType, rightType, range.getManager());
  256. }
  257. }
  258. return null;
  259. }
  260. @Nullable
  261. private static String findMethodName(@NotNull GrMethodCall methodCall) {
  262. GrExpression expression = methodCall.getInvokedExpression();
  263. if (expression instanceof GrReferenceExpression) {
  264. return ((GrReferenceExpression)expression).getReferenceName();
  265. }
  266. return null;
  267. }
  268. }