PageRenderTime 54ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/intellij/ibatis/provider/FieldAccessMethodReferenceProvider.java

http://ibatis-plugin.googlecode.com/
Java | 341 lines | 280 code | 20 blank | 41 comment | 97 complexity | 435b3ecc6f26f823441fed9a4d94b294 MD5 | raw file
  1. package org.intellij.ibatis.provider;
  2. import com.intellij.codeInsight.lookup.LookupValueFactory;
  3. import com.intellij.openapi.util.text.StringUtil;
  4. import com.intellij.psi.*;
  5. import com.intellij.psi.xml.XmlAttribute;
  6. import com.intellij.psi.xml.XmlAttributeValue;
  7. import com.intellij.psi.xml.XmlTag;
  8. import com.intellij.util.IncorrectOperationException;
  9. import org.intellij.ibatis.IbatisManager;
  10. import org.intellij.ibatis.util.IbatisConstants;
  11. import org.jetbrains.annotations.NotNull;
  12. import org.jetbrains.annotations.Nullable;
  13. import java.util.ArrayList;
  14. import java.util.HashMap;
  15. import java.util.List;
  16. import java.util.Map;
  17. /**
  18. * filed access method reference provider
  19. */
  20. public class FieldAccessMethodReferenceProvider extends BaseReferenceProvider {
  21. @NotNull
  22. public PsiReference[] getReferencesByElement(PsiElement psiElement) {
  23. final XmlAttributeValue xmlAttributeValue = (XmlAttributeValue) psiElement;
  24. final XmlTag xmlTag = (XmlTag) xmlAttributeValue.getParent().getParent(); //result or parameter tag
  25. final PsiClass psiClass;
  26. XmlAttributeValuePsiReference psiReference = null;
  27. if (xmlTag.getName().equals("result") || xmlTag.getName().equals("resultMap") || xmlTag.getName().equals("parameter") || xmlTag.getName().equals("parameterMap")) {
  28. psiClass = getPsiClassForMap(xmlTag, xmlAttributeValue);
  29. if (psiClass != null)
  30. psiReference = new XmlAttributeValuePsiReference(xmlAttributeValue) {
  31. @Nullable
  32. public PsiElement resolve() {
  33. if (!IbatisClassShortcutsReferenceProvider.isDomain(psiClass.getName())) { //none domain class, validate is not necessary, such as hashmap
  34. return null;
  35. }
  36. PsiClass referencedClass = psiClass;
  37. String[] referencePath = getCanonicalText().split("\\.");
  38. if (xmlTag.getName().equals("result") || xmlTag.getName().equals("resultMap")) {
  39. String methodName = "set" + StringUtil.capitalize(referencePath[referencePath.length - 1]);
  40. for (int i = 0; i < referencePath.length - 1; i++) {
  41. referencedClass = findGetterMethodReturnType(referencedClass, "get" + StringUtil.capitalize(referencePath[i]));
  42. if (referencedClass == null) break;
  43. }
  44. if (referencedClass != null) {
  45. PsiMethod[] methods = referencedClass.findMethodsByName(methodName, true);
  46. if (methods.length > 0) return methods[0];
  47. }
  48. } else {
  49. String methodName = "get" + StringUtil.capitalize(referencePath[referencePath.length - 1]);
  50. for (int i = 0; i < referencePath.length - 1; i++) {
  51. referencedClass = findGetterMethodReturnType(referencedClass, "get" + StringUtil.capitalize(referencePath[i]));
  52. if (referencedClass == null) break;
  53. }
  54. if (referencedClass != null) {
  55. PsiMethod[] methods = referencedClass.findMethodsByName(methodName, true);
  56. if (methods.length > 0) return methods[0];
  57. }
  58. methodName = "is" + StringUtil.capitalize(referencePath[referencePath.length - 1]);
  59. for (int i = 0; i < referencePath.length - 1; i++) {
  60. referencedClass = findGetterMethodReturnType(referencedClass, "get" + StringUtil.capitalize(referencePath[i]));
  61. if (referencedClass == null) break;
  62. }
  63. if (referencedClass != null) {
  64. PsiMethod[] methods = referencedClass.findMethodsByName(methodName, true);
  65. if (methods.length > 0) return methods[0];
  66. }
  67. }
  68. return null;
  69. }
  70. public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
  71. String referencePath = getCanonicalText();
  72. String newFieldName = StringUtil.decapitalize(newElementName.replace("set", ""));
  73. if (!referencePath.contains(".")) { //flat field
  74. ((XmlAttribute) xmlAttributeValue.getParent()).setValue(StringUtil.decapitalize(newElementName.replace("set", "")));
  75. } else //deep field
  76. {
  77. String field1 = referencePath.substring(0, referencePath.lastIndexOf("."));
  78. String newReferencePath;
  79. if (psiClass.findMethodsByName("set" + StringUtil.capitalize(field1), true).length > 0) { //field2 changed
  80. newReferencePath = field1 + "." + newFieldName;
  81. } else //field1 changed
  82. {
  83. newReferencePath = referencePath.replace(field1, newFieldName);
  84. }
  85. ((XmlAttribute) xmlAttributeValue.getParent()).setValue(newReferencePath);
  86. }
  87. return resolve();
  88. }
  89. public PsiElement bindToElement(PsiElement element) throws IncorrectOperationException {
  90. return super.bindToElement(element);
  91. }
  92. public boolean isReferenceTo(PsiElement element) {
  93. return super.isReferenceTo(element);
  94. }
  95. public Object[] getVariants() {
  96. if ("Map".equals(psiClass.getName())) {
  97. return null;
  98. }
  99. Map<String, String> setterMethods = getAllSetterMethods(psiClass, getCanonicalText());
  100. List<Object> variants = new ArrayList<Object>();
  101. for (Map.Entry<String, String> entry : setterMethods.entrySet()) {
  102. variants.add(LookupValueFactory.createLookupValueWithHint(entry.getKey(), IbatisConstants.CLASS_FIELD, entry.getValue()));
  103. }
  104. return variants.toArray();
  105. }
  106. public boolean isSoft() {
  107. return "Map".equals(psiClass.getName());
  108. }
  109. };
  110. } else {
  111. psiClass = getPsiClassForDynamicProperty(xmlTag, xmlAttributeValue);
  112. if (psiClass != null)
  113. psiReference = new XmlAttributeValuePsiReference(xmlAttributeValue) {
  114. @Nullable
  115. public PsiElement resolve() {
  116. if ("Map".equals(psiClass.getName())) {
  117. return null;
  118. }
  119. PsiClass referencedClass = psiClass;
  120. String referencePath = getCanonicalText().replace("IntellijIdeaRulezzz ", "");
  121. String methodName = "get" + StringUtil.capitalize(referencePath);
  122. if (referencePath.contains(".")) {
  123. String fieldName = referencePath.substring(0, referencePath.lastIndexOf('.'));
  124. methodName = "get" + StringUtil.capitalize(referencePath.substring(referencePath.lastIndexOf('.') + 1));
  125. referencedClass = findGetterMethodReturnType(psiClass, "get" + StringUtil.capitalize(fieldName));
  126. }
  127. if (referencedClass != null) {
  128. PsiMethod[] methods = referencedClass.findMethodsByName(methodName, true);
  129. if(methods.length ==0) {
  130. methods= referencedClass.findMethodsByName("is"+methodName.substring(3), true);
  131. }
  132. if (methods.length > 0) return methods[0];
  133. }
  134. return null;
  135. }
  136. public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
  137. String referencePath = getCanonicalText();
  138. String newFieldName = StringUtil.decapitalize(newElementName.replace("get", ""));
  139. if (!referencePath.contains(".")) { //flat field
  140. ((XmlAttribute) xmlAttributeValue.getParent()).setValue(StringUtil.decapitalize(newElementName.replace("get", "")));
  141. } else //deep field
  142. {
  143. String field1 = referencePath.substring(0, referencePath.indexOf("."));
  144. String newReferencePath;
  145. if (psiClass.findMethodsByName("get" + StringUtil.capitalize(field1), true).length > 0) { //field2 changed
  146. newReferencePath = field1 + "." + newFieldName;
  147. } else //field1 changed
  148. {
  149. newReferencePath = referencePath.replace(field1, newFieldName);
  150. }
  151. ((XmlAttribute) xmlAttributeValue.getParent()).setValue(newReferencePath);
  152. }
  153. return resolve();
  154. }
  155. public PsiElement bindToElement(PsiElement element) throws IncorrectOperationException {
  156. return super.bindToElement(element);
  157. }
  158. public boolean isReferenceTo(PsiElement element) {
  159. return super.isReferenceTo(element);
  160. }
  161. public Object[] getVariants() {
  162. if ("Map".equals(psiClass.getName())) {
  163. return null;
  164. }
  165. Map<String, String> setterMethods = getAllGetterMethods(psiClass, getCanonicalText().replace("IntellijIdeaRulezzz ", ""));
  166. List<Object> variants = new ArrayList<Object>();
  167. for (String setterMethod : setterMethods.keySet()) {
  168. variants.add(LookupValueFactory.createLookupValueWithHint(setterMethod, IbatisConstants.CLASS_FIELD, setterMethods.get(setterMethod)));
  169. }
  170. return variants.toArray();
  171. }
  172. public boolean isSoft() {
  173. return "Map".equals(psiClass.getName());
  174. }
  175. };
  176. }
  177. if (psiReference == null) return PsiReference.EMPTY_ARRAY;
  178. return new PsiReference[]{psiReference};
  179. }
  180. /**
  181. * find getter method return type
  182. *
  183. * @param psiClass PsiClass
  184. * @param methodName getter method name
  185. * @return PsiClass
  186. */
  187. @Nullable
  188. public static PsiClass findGetterMethodReturnType(PsiClass psiClass, String methodName) {
  189. PsiMethod[] methods = psiClass.findMethodsByName(methodName, true);
  190. if(methods.length == 0) {
  191. methods = psiClass.findMethodsByName("is"+methodName.substring(3), true);
  192. }
  193. //getter method find for current
  194. if (methods.length > 0) {
  195. PsiMethod psiGetterMethod = methods[0];
  196. PsiType returnType = psiGetterMethod.getReturnType();
  197. if (returnType != null)
  198. return IbatisClassShortcutsReferenceProvider.getPsiClass(psiClass, returnType.getCanonicalText());
  199. }
  200. return null;
  201. }
  202. /**
  203. * get all set method for psiClass with type added
  204. *
  205. * @param psiClass PsiClass object
  206. * @param currentMethodName current set method
  207. * @return set method list without prefix
  208. */
  209. public static Map<String, String> getAllSetterMethods(PsiClass psiClass, String currentMethodName) {
  210. Map<String, String> methodNames = new HashMap<String, String>();
  211. PsiMethod[] psiMethods;
  212. String prefix = "";
  213. //flat field
  214. if (!currentMethodName.contains(".")) {
  215. psiMethods = psiClass.getAllMethods();
  216. } else {
  217. String[] path = (currentMethodName + " ").split("\\."); //space added to avoid "." ended property
  218. PsiClass tempClass = psiClass;
  219. for (int i = 0; i < path.length - 1; i++) {
  220. String getterMethod = "get" + StringUtil.capitalize(path[i]);
  221. tempClass = findGetterMethodReturnType(tempClass, getterMethod);
  222. if (tempClass == null) break;
  223. prefix = prefix + path[i] + ".";
  224. }
  225. psiMethods = tempClass != null ? tempClass.getAllMethods() : null;
  226. }
  227. if (psiMethods != null && psiMethods.length > 0) {
  228. for (PsiMethod psiMethod : psiMethods) {
  229. String methodName = psiMethod.getName();
  230. if (methodName.startsWith("set") && psiMethod.getParameterList().getParametersCount() == 1) {
  231. String name = prefix + StringUtil.decapitalize(methodName.replaceFirst("set", ""));
  232. String type = psiMethod.getParameterList().getParameters()[0].getType().getPresentableText();
  233. methodNames.put(name, type);
  234. }
  235. }
  236. }
  237. return methodNames;
  238. }
  239. /**
  240. * get all get method in psi class with return type
  241. *
  242. * @param psiClass PsiClass object
  243. * @param currentMethodName current methodName for children
  244. * @return get method list without prefix
  245. */
  246. @SuppressWarnings({"ConstantConditions"})
  247. @NotNull
  248. public static Map<String, String> getAllGetterMethods(PsiClass psiClass, String currentMethodName) {
  249. Map<String, String> methodNames = new HashMap<String, String>();
  250. PsiMethod[] psiMethods = null;
  251. String prefix = "";
  252. //flat field
  253. if (!currentMethodName.contains(".")) {
  254. psiMethods = psiClass.getAllMethods();
  255. } else {
  256. String[] path = (currentMethodName + " ").split("\\."); //space added to avoid "." ended property
  257. PsiClass tempClass = psiClass;
  258. for (int i = 0; i < path.length - 1; i++) {
  259. String getterMethod = "get" + StringUtil.capitalize(path[i]);
  260. tempClass = findGetterMethodReturnType(tempClass, getterMethod);
  261. if (tempClass == null) break;
  262. prefix = prefix + path[i] + ".";
  263. }
  264. }
  265. if (psiMethods != null && psiMethods.length > 0) {
  266. for (PsiMethod psiMethod : psiMethods) {
  267. String methodName = psiMethod.getName();
  268. if (methodName.startsWith("get") && psiMethod.getParameterList().getParametersCount() == 0) {
  269. String name = prefix + StringUtil.decapitalize(methodName.replaceFirst("get", ""));
  270. String type = psiMethod.getReturnType().getPresentableText();
  271. methodNames.put(name, type);
  272. }
  273. if (methodName.startsWith("is") && psiMethod.getParameterList().getParametersCount() == 0) {
  274. String name = prefix + StringUtil.decapitalize(methodName.replaceFirst("is", ""));
  275. String type = psiMethod.getReturnType().getPresentableText();
  276. methodNames.put(name, type);
  277. }
  278. }
  279. }
  280. methodNames.remove("class"); //getClass is controled by JVM
  281. return methodNames;
  282. }
  283. /**
  284. * get the psi class for dynamic property
  285. *
  286. * @param xmlTag xml tag
  287. * @param xmlAttributeValue xml attribute value
  288. * @return psi class
  289. */
  290. public PsiClass getPsiClassForDynamicProperty(XmlTag xmlTag, XmlAttributeValue xmlAttributeValue) {
  291. XmlTag parentTag = xmlTag.getParentTag();
  292. if (parentTag != null) {
  293. if (parentTag.getAttribute("parameterClass") != null) {
  294. String className = parentTag.getAttributeValue("parameterClass");
  295. return IbatisClassShortcutsReferenceProvider.getPsiClass(xmlAttributeValue, className);
  296. } else if (parentTag.getAttribute("parameterMap") != null) {
  297. String parameterMapId = parentTag.getAttributeValue("parameterMap");
  298. return IbatisManager.getInstance().getAllParameterMap(xmlAttributeValue).get(parameterMapId);
  299. } else {
  300. return getPsiClassForDynamicProperty(parentTag, xmlAttributeValue);
  301. }
  302. }
  303. return null;
  304. }
  305. /**
  306. * get Psi Class for resultMap for parameterMap
  307. *
  308. * @param xmlTag tag for xml attribute
  309. * @param xmlAttributeValue xml attribute value
  310. * @return psiClass
  311. */
  312. public PsiClass getPsiClassForMap(XmlTag xmlTag, XmlAttributeValue xmlAttributeValue) {
  313. XmlTag parentTag = xmlTag.getParentTag(); //resultMap or parameterMap element
  314. if (xmlTag.getName().equals("resultMap")) parentTag = xmlTag; //resultMap's groupBy
  315. if (parentTag != null && parentTag.getAttribute("class") != null) {
  316. String className = parentTag.getAttributeValue("class");
  317. return IbatisClassShortcutsReferenceProvider.getPsiClass(xmlAttributeValue, className);
  318. }
  319. return null;
  320. }
  321. }